Merge "BUG-1275: teach NormalizedNode builders about size hints"
authorTony Tkacik <ttkacik@cisco.com>
Mon, 4 Aug 2014 18:17:03 +0000 (18:17 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 4 Aug 2014 18:17:03 +0000 (18:17 +0000)
58 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/AbstractTransformerGenerator.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/RuntimeGeneratedMappingServiceImpl.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend [new file with mode: 0644]
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BaseTemplate.xtend
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/UnionTemplate.xtend
common/util/src/main/java/org/opendaylight/yangtools/util/ReadOnlyTrieMap.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapper.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapperTest.java [new file with mode: 0644]
websocket/websocket-client/src/test/java/org/opendaylight/yangtools/websocket/server/WebSocketServer.java
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtils.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RootModificationApplyOperation.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextListener.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java [deleted file]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java with 53% similarity]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java with 58% similarity]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java [deleted file]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java
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
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java [deleted file]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseTypes.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifierType.java [moved from yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifier.java with 87% similarity]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/package-info.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaServiceListener.java with 50% similarity]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/impl/TypesResolutionTest.java

index bacff303f2ac28e5d6edddb6ed3abccb96a767d2..58d38dacd5de6b0123b176223fde172f132780a8 100644 (file)
@@ -12,7 +12,6 @@ import com.google.common.base.Preconditions;
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
 
 import javassist.ClassPool;
 
index 59ea3c53f3ce426324d06e92e924da5a3e4484d3..60120759fabeb7227c770cc9fd540f647306dc60 100644 (file)
@@ -30,7 +30,6 @@ import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findP
 import com.google.common.base.Splitter;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -39,7 +38,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
 import org.opendaylight.yangtools.binding.generator.util.BindingTypes;
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
@@ -139,6 +137,10 @@ public class BindingGeneratorImpl implements BindingGenerator {
      */
     private final static String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
 
+    private final char NEW_LINE = '\n';
+
+    private final char TAB = '\t';
+
     /**
      * Resolves generated types from <code>context</code> schema nodes of all
      * modules.
@@ -284,6 +286,10 @@ public class BindingGeneratorImpl implements BindingGenerator {
         final String packageName = packageNameForGeneratedType(basePackageName, node.getPath());
         final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf);
         genType.addComment(node.getDescription());
+        genType.setDescription(createDescription(node, genType.getFullyQualifiedName()));
+        genType.setModuleName(module.getName());
+        genType.setReference(node.getReference());
+        genType.setSchemaPath(node.getPath().getPathFromRoot());
         if (node instanceof DataNodeContainer) {
             genCtx.get(module).addChildNodeType(node, genType);
             groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings());
@@ -413,6 +419,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         addImplementedInterfaceFromUses(module, moduleDataTypeBuilder);
         moduleDataTypeBuilder.addImplementsType(DATA_ROOT);
         moduleDataTypeBuilder.addComment(module.getDescription());
+        moduleDataTypeBuilder.setDescription(createDescription(module));
+        moduleDataTypeBuilder.setReference(module.getReference());
         return moduleDataTypeBuilder;
     }
 
@@ -444,6 +452,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
         final GeneratedTypeBuilder interfaceBuilder = moduleTypeBuilder(module, "Service");
         interfaceBuilder.addImplementsType(Types.typeForClass(RpcService.class));
+        interfaceBuilder.setDescription(createDescription(rpcDefinitions, module.getName(), module.getModuleSourcePath()));
+
         for (RpcDefinition rpc : rpcDefinitions) {
             if (rpc != null) {
                 final String rpcName = BindingMapping.getClassName(rpc.getQName());
@@ -513,6 +523,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         listenerInterface.addImplementsType(BindingTypes.NOTIFICATION_LISTENER);
         final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
 
+
+
         for (NotificationDefinition notification : notifications) {
             if (notification != null) {
                 processUsesAugments(notification, module);
@@ -531,6 +543,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 .setComment(notification.getDescription()).setReturnType(Types.VOID);
             }
         }
+        listenerInterface.setDescription(createDescription(notifications, module.getName(), module.getModuleSourcePath()));
 
         genCtx.get(module).addTopLevelNodeType(listenerInterface);
     }
@@ -601,9 +614,10 @@ public class BindingGeneratorImpl implements BindingGenerator {
         }
         newType.setAbstract(true);
         newType.addComment(identity.getDescription());
-        newType.setDescription(identity.getDescription());
+        newType.setDescription(createDescription(identity, newType.getFullyQualifiedName()));
         newType.setReference(identity.getReference());
         newType.setModuleName(module.getName());
+        SchemaPath path = identity.getPath();
         newType.setSchemaPath(identity.getPath().getPathFromRoot());
 
         final QName qname = identity.getQName();
@@ -722,7 +736,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         final String moduleName = BindingMapping.getClassName(module.getName()) + postfix;
 
         final GeneratedTypeBuilderImpl moduleBuilder = new GeneratedTypeBuilderImpl(packageName, moduleName);
-        moduleBuilder.setDescription(module.getDescription());
+        moduleBuilder.setDescription(createDescription(module));
         moduleBuilder.setReference(module.getReference());
         moduleBuilder.setModuleName(moduleName);
 
@@ -1665,13 +1679,12 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
         // FIXME: Validation of name conflict
         final GeneratedTypeBuilderImpl newType = new GeneratedTypeBuilderImpl(packageName, genTypeName);
+        final Module module = findParentModule(schemaContext, schemaNode);
         qnameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, schemaNode.getQName());
         newType.addComment(schemaNode.getDescription());
-        newType.setDescription(schemaNode.getDescription());
+        newType.setDescription(createDescription(schemaNode, newType.getFullyQualifiedName()));
         newType.setReference(schemaNode.getReference());
         newType.setSchemaPath(schemaNode.getPath().getPathFromRoot());
-
-        final Module module = findParentModule(schemaContext, schemaNode);
         newType.setModuleName(module.getName());
 
         if (!genTypeBuilders.containsKey(packageName)) {
@@ -1934,13 +1947,151 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     throw new IllegalStateException("Grouping " + usesNode.getGroupingPath() + "is not resolved for "
                             + builder.getName());
                 }
+
                 builder.addImplementsType(genType);
-                builder.addComment(genType.getComment());
+                /*
+                builder.addComment(genType.getDescription());
+                builder.setDescription(genType.getDescription());
+                builder.setModuleName(genType.getModuleName());
+                builder.setReference(genType.getReference());
+                builder.setSchemaPath(genType.getSchemaPath());
+                */
             }
         }
         return builder;
     }
 
+    private boolean isNullOrEmpty(final Collection<?> list) {
+        return (list == null || list.isEmpty() ? true : false);
+    }
+
+    private String createDescription(final Set<? extends SchemaNode> schemaNodes, final String moduleName, final String moduleSourcePath) {
+        final StringBuilder sb = new StringBuilder();
+        final String yangSnipet = YangTemplate.generateYangSnipet(schemaNodes);
+
+        if (!isNullOrEmpty(schemaNodes)) {
+            final SchemaNode node = schemaNodes.iterator().next();
+
+            if (node instanceof RpcDefinition) {
+                sb.append("Interface for implementing the following YANG RPCs defined in module <b>" + moduleName + "</b>");
+            }
+            else if (node instanceof NotificationDefinition) {
+                sb.append("Interface for receiving the following YANG notifications defined in module <b>" + moduleName + "</b>");
+            }
+        }
+        sb.append(NEW_LINE);
+        sb.append("<br />(Source path: <i>");
+        sb.append(moduleSourcePath);
+        sb.append("</i>):");
+        sb.append(NEW_LINE);
+        sb.append("<pre>");
+        sb.append(NEW_LINE);
+        sb.append(yangSnipet);
+        sb.append("</pre>");
+        sb.append(NEW_LINE);
+
+        return sb.toString();
+    }
+
+    private String createDescription(final SchemaNode schemaNode, final String fullyQualifiedName) {
+        final StringBuilder sb = new StringBuilder();
+        final Module module = findParentModule(schemaContext, schemaNode);
+        final String yangSnipet = YangTemplate.generateYangSnipet(schemaNode);
+        final String formattedDescription = YangTemplate.formatToParagraph(schemaNode.getDescription(), 0);
+        final StringBuilder linkToBuilderClass = new StringBuilder();
+        final StringBuilder linkToKeyClass = new StringBuilder();
+        final Splitter splitter = Splitter.on("\\.");
+        final String[] namespace = Iterables.toArray(splitter.split(fullyQualifiedName), String.class);
+        String className = namespace[namespace.length - 1];
+
+        if (hasBuilderClass(schemaNode)) {
+            linkToBuilderClass.append(className);
+            linkToBuilderClass.append("Builder");
+
+            if (schemaNode instanceof ListSchemaNode) {
+                linkToKeyClass.append(className);
+                linkToKeyClass.append("Key");
+            }
+        }
+
+        if (!isNullOrEmpty(formattedDescription)) {
+            sb.append(formattedDescription);
+            sb.append(NEW_LINE);
+        }
+        sb.append("<p>");
+        sb.append("This class represents the following YANG schema fragment defined in module <b>");
+        sb.append(module.getName());
+        sb.append("</b>");
+        sb.append(NEW_LINE);
+        sb.append("<br />(Source path: <i>");
+        sb.append(module.getModuleSourcePath());
+        sb.append("</i>):");
+        sb.append(NEW_LINE);
+        sb.append("<pre>");
+        sb.append(NEW_LINE);
+        sb.append(yangSnipet);
+        sb.append("</pre>");
+        sb.append(NEW_LINE);
+        sb.append("The schema path to identify an instance is");
+        sb.append(NEW_LINE);
+        sb.append("<i>");
+        sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
+        sb.append("</i>");
+        sb.append(NEW_LINE);
+
+        if (hasBuilderClass(schemaNode)) {
+            sb.append(NEW_LINE);
+            sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+            sb.append(NEW_LINE);
+            sb.append("@see ");
+            sb.append(linkToBuilderClass);
+            if (schemaNode instanceof ListSchemaNode) {
+                sb.append("@see ");
+                sb.append(linkToKeyClass);
+            }
+            sb.append(NEW_LINE);
+        }
+
+        return sb.toString();
+    }
+
+    private boolean hasBuilderClass(final SchemaNode schemaNode) {
+        if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode ||
+                schemaNode instanceof RpcDefinition || schemaNode instanceof NotificationDefinition)
+            return true;
+        return false;
+    }
+
+    private boolean isNullOrEmpty(final String string) {
+        return (string == null || string.isEmpty() ? true : false);
+    }
+
+    private String createDescription(final Module module) {
+        final StringBuilder sb = new StringBuilder();
+        final String yangSnipet = YangTemplate.generateYangSnipet(module);
+        final String formattedDescription = YangTemplate.formatToParagraph(module.getDescription(), 0);
+
+        if (!isNullOrEmpty(formattedDescription)) {
+            sb.append(formattedDescription);
+            sb.append(NEW_LINE);
+        }
+        sb.append("<p>");
+        sb.append("This class represents the following YANG schema fragment defined in module <b>");
+        sb.append(module.getName());
+        sb.append("</b>");
+        sb.append(NEW_LINE);
+        sb.append("<br />Source path: <i>");
+        sb.append(module.getModuleSourcePath());
+        sb.append("</i>):");
+        sb.append(NEW_LINE);
+        sb.append("<pre>");
+        sb.append(NEW_LINE);
+        sb.append(yangSnipet);
+        sb.append("</pre>");
+
+        return sb.toString();
+    }
+
     private GeneratedTypeBuilder findChildNodeByPath(final SchemaPath path) {
         for (ModuleContext ctx : genCtx.values()) {
             GeneratedTypeBuilder result = ctx.getChildNode(path);
index 731b8e5a0aad1b3677f79eded435fc724b59ae68..b711c3985fd5afa081e8a857ebbefc43a46d6c07 100644 (file)
@@ -119,7 +119,7 @@ SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
         binding.setListener(registry);
 
         // if (ctx !== null) {
-        // listenerRegistration = ctx.registerService(SchemaServiceListener,
+        // listenerRegistration = ctx.registerService(SchemaContextListener,
         // this, new Hashtable<String, String>());
         // }
     }
index 769a9e5bb77c054510fdca274afbeb3d44a91d12..473c8e644c0749ad757c7f06b8578c3d4fd01fdc 100644 (file)
@@ -30,6 +30,8 @@ import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
 import org.opendaylight.yangtools.binding.generator.util.Types
 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException
+import org.opendaylight.yangtools.sal.binding.generator.util.SourceCodeGenerator
+import org.opendaylight.yangtools.sal.binding.generator.util.SourceCodeGeneratorFactory
 import org.opendaylight.yangtools.sal.binding.generator.util.XtendHelper
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
@@ -43,7 +45,6 @@ import org.opendaylight.yangtools.yang.binding.Augmentation
 import org.opendaylight.yangtools.yang.binding.BindingCodec
 import org.opendaylight.yangtools.yang.binding.BindingDeserializer
 import org.opendaylight.yangtools.yang.binding.BindingMapping
-import org.opendaylight.yangtools.yang.binding.DataObject
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
 import org.opendaylight.yangtools.yang.common.QName
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
@@ -71,10 +72,6 @@ import static javassist.Modifier.*
 import static org.opendaylight.yangtools.sal.binding.generator.impl.CodecMapping.*
 
 import static extension org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils.*
-import java.util.ArrayList
-import org.opendaylight.yangtools.sal.binding.generator.util.DefaultSourceCodeGenerator
-import org.opendaylight.yangtools.sal.binding.generator.util.SourceCodeGeneratorFactory
-import org.opendaylight.yangtools.sal.binding.generator.util.SourceCodeGenerator
 
 class TransformerGenerator extends AbstractTransformerGenerator {
     private static val LOG = LoggerFactory.getLogger(TransformerGenerator)
diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend
new file mode 100644 (file)
index 0000000..396001d
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.sal.binding.generator.impl
+
+import java.text.SimpleDateFormat
+import java.util.Collection
+import java.util.Date
+import java.util.List
+import java.util.Map
+import java.util.Set
+import java.util.StringTokenizer
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
+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.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.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.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.api.type.EnumTypeDefinition
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair
+
+class YangTemplate {
+
+    private static var Module module = null
+
+    def static String generateYangSnipet(SchemaNode schemaNode) {
+        if (schemaNode == null)
+            return ''
+
+        '''
+            Â«IF schemaNode instanceof DataSchemaNode»
+            Â«writeDataSchemaNode(schemaNode as DataSchemaNode)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof EnumTypeDefinition.EnumPair»
+            Â«writeEnumPair(schemaNode as EnumTypeDefinition.EnumPair)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof ExtensionDefinition»
+            Â«writeExtension(schemaNode as ExtensionDefinition)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof FeatureDefinition»
+            Â«writeFeature(schemaNode as FeatureDefinition)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof GroupingDefinition»
+            Â«writeGroupingDef(schemaNode as GroupingDefinition)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof IdentitySchemaNode»
+            Â«writeIdentity(schemaNode as IdentitySchemaNode)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof NotificationDefinition»
+            Â«writeNotification(schemaNode as NotificationDefinition)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof RpcDefinition»
+            Â«writeRPC(schemaNode as RpcDefinition)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof TypeDefinition<?>»
+            Â«writeTypeDefinition(schemaNode as TypeDefinition<?>)»
+            Â«ENDIF»
+            Â«IF schemaNode instanceof UnknownSchemaNode»
+            Â«writeUnknownSchemaNode(schemaNode as UnknownSchemaNode)»
+            Â«ENDIF»
+        '''
+    }
+    
+    def static String generateYangSnipet(Set<? extends SchemaNode> nodes) {
+        if (nodes.nullOrEmpty)
+            return ''
+        
+        '''
+            Â«FOR node : nodes»
+                Â«IF node instanceof NotificationDefinition»
+                Â«writeNotification(node as NotificationDefinition)»
+                Â«ELSEIF node instanceof RpcDefinition»
+                Â«writeRPC(node as RpcDefinition)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeEnumPair(EnumPair pair) {
+        var boolean hasEnumPairValue = pair.value != null
+        '''
+            enum Â«pair.name»«IF !hasEnumPairValue»;«ELSE»{
+                value Â«pair.value»;
+            }
+            Â«ENDIF»
+        '''
+    }
+
+    def static String writeModuleImports(Set<ModuleImport> moduleImports) {
+        if (moduleImports.nullOrEmpty)
+            return ''
+
+        '''
+            Â«FOR moduleImport : moduleImports SEPARATOR "\n"»
+                Â«IF moduleImport != null && !moduleImport.moduleName.nullOrEmpty»
+                import Â«moduleImport.moduleName» { prefix "«moduleImport.prefix»"; }
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static formatDate(Date moduleRevision) {
+        val SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd")
+        return dateFormat.format(moduleRevision)
+    }
+
+    def static writeRevision(Date moduleRevision, String moduleDescription) {
+        val revisionIndent = 12
+
+        '''
+            revision Â«formatDate(moduleRevision)» {
+                description "«formatToParagraph(moduleDescription, revisionIndent)»";
+            }
+        '''
+    }
+
+    def static String generateYangSnipet(Module module) {
+
+        '''
+            module Â«module.name» {
+                yang-version Â«module.yangVersion»;
+                namespace "«module.QNameModule.namespace.toString»";
+                prefix "«module.prefix»";
+
+                Â«IF !module.imports.nullOrEmpty»
+                Â«writeModuleImports(module.imports)»
+                Â«ENDIF»
+                Â«IF module.revision != null»
+                Â«writeRevision(module.revision, module.description)»
+                Â«ENDIF»
+                Â«IF !module.childNodes.nullOrEmpty»
+
+                Â«writeDataSchemaNodes(module.childNodes)»
+                Â«ENDIF»
+                Â«IF !module.groupings.nullOrEmpty»
+
+                Â«writeGroupingDefs(module.groupings)»
+                Â«ENDIF»
+                Â«IF !module.augmentations.nullOrEmpty»
+
+                Â«writeAugments(module.augmentations)»
+                Â«ENDIF»
+                Â«IF !module.deviations.nullOrEmpty»
+
+                Â«writeDeviations(module.deviations)»
+                Â«ENDIF»
+                Â«IF !module.extensionSchemaNodes.nullOrEmpty»
+
+                Â«writeExtensions(module.extensionSchemaNodes)»
+                Â«ENDIF»
+                Â«IF !module.features.nullOrEmpty»
+
+                Â«writeFeatures(module.features)»
+                Â«ENDIF»
+                Â«IF !module.identities.nullOrEmpty»
+
+                Â«writeIdentities(module.identities)»
+                Â«ENDIF»
+                Â«IF !module.notifications.nullOrEmpty»
+
+                Â«writeNotifications(module.notifications)»
+                Â«ENDIF»
+                Â«IF !module.rpcs.nullOrEmpty»
+
+                Â«writeRPCs(module.rpcs)»
+                Â«ENDIF»
+                Â«IF !module.unknownSchemaNodes.nullOrEmpty»
+
+                Â«writeUnknownSchemaNodes(module.unknownSchemaNodes)»
+                Â«ENDIF»
+                Â«IF !module.uses.nullOrEmpty»
+
+                Â«writeUsesNodes(module.uses)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeRPCs(Set<RpcDefinition> rpcDefs) {
+        '''
+            Â«FOR rpc : rpcDefs»
+                Â«IF rpc != null»
+                Â«writeRPC(rpc)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeRPC(RpcDefinition rpc) {
+        '''
+            rpc Â«rpc.QName.localName» {
+                Â«IF !rpc.description.nullOrEmpty»
+                    "«rpc.description»";
+                Â«ENDIF»
+                Â«IF !rpc.groupings.nullOrEmpty»
+                    Â«writeGroupingDefs(rpc.groupings)»
+                Â«ENDIF»
+                Â«IF rpc.input != null»
+                    Â«writeRpcInput(rpc.input)»
+                Â«ENDIF»
+                Â«IF rpc.output != null»
+                    Â«writeRpcOutput(rpc.output)»
+                Â«ENDIF»
+                Â«IF !rpc.reference.nullOrEmpty»
+                reference
+                    "«rpc.reference»";
+                Â«ENDIF»
+                Â«IF rpc.status != null»
+                status Â«rpc.status»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeRpcInput(ContainerSchemaNode input) {
+        if(input == null)
+            return ''
+
+        '''
+            input {
+                Â«IF input instanceof DataSchemaNode && !input.childNodes.nullOrEmpty»
+                Â«writeDataSchemaNodes(input.childNodes)»
+                Â«ENDIF»
+            }
+
+        '''
+    }
+
+    def static writeRpcOutput(ContainerSchemaNode output) {
+        if(output == null)
+            return ''
+
+        '''
+            output {
+                Â«IF output instanceof DataSchemaNode && !output.childNodes.nullOrEmpty»
+                Â«writeDataSchemaNodes(output.childNodes)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeNotifications(Set<NotificationDefinition> notifications) {
+        '''
+            Â«FOR notification : notifications»
+                Â«IF notification != null»
+                Â«writeNotification(notification)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeNotification(NotificationDefinition notification) {
+        '''
+            notification Â«notification.QName.localName» {
+                Â«IF !notification.description.nullOrEmpty»
+                description
+                    "«notification.description»";
+                Â«ENDIF»
+                Â«IF !notification.childNodes.nullOrEmpty»
+                    Â«writeDataSchemaNodes(notification.childNodes)»
+                Â«ENDIF»
+                Â«IF !notification.availableAugmentations.nullOrEmpty»
+                    Â«writeAugments(notification.availableAugmentations)»
+                Â«ENDIF»
+                Â«IF !notification.groupings.nullOrEmpty»
+                    Â«writeGroupingDefs(notification.groupings)»
+                Â«ENDIF»
+                Â«IF !notification.uses.nullOrEmpty»
+                    Â«writeUsesNodes(notification.uses)»
+                Â«ENDIF»
+                Â«IF !notification.reference.nullOrEmpty»
+                reference
+                    "«notification.reference»";
+                Â«ENDIF»
+                Â«IF notification.status != null»
+                status Â«notification.status»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeUnknownSchemaNodes(List<UnknownSchemaNode> unknownSchemaNodes) {
+        if (unknownSchemaNodes.nullOrEmpty)
+            return ''
+
+        '''
+            Â«FOR unknownSchemaNode : unknownSchemaNodes»
+                Â«writeUnknownSchemaNode(unknownSchemaNode)»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeUnknownSchemaNode(UnknownSchemaNode unknownSchemaNode) {
+        if (unknownSchemaNode == null)
+            return ''
+
+        '''
+            anyxml Â«unknownSchemaNode.QName.localName» {
+                Â«IF !unknownSchemaNode.description.nullOrEmpty»
+                description
+                    "«unknownSchemaNode.description»";
+                Â«ENDIF»
+                Â«IF !unknownSchemaNode.reference.nullOrEmpty»
+                reference
+                    "«unknownSchemaNode.reference»";
+                Â«ENDIF»
+                Â«IF unknownSchemaNode.status != null»
+                status Â«unknownSchemaNode.status»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeUsesNodes(Set<UsesNode> usesNodes) {
+        if (usesNodes == null) {
+            return ''
+        }
+
+        '''
+            Â«FOR usesNode : usesNodes»
+                Â«IF usesNode != null»
+                Â«writeUsesNode(usesNode)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeUsesNode(UsesNode usesNode) {
+        val hasRefines = !usesNode.refines.empty
+
+        '''
+            uses Â«usesNode.groupingPath.pathFromRoot.head.localName»«IF !hasRefines»;«ELSE» {«ENDIF»
+            Â«IF hasRefines»
+                Â«writeRefines(usesNode.refines)»
+            }
+            Â«ENDIF»
+        '''
+    }
+
+    def static writeRefines(Map<SchemaPath, SchemaNode> refines) {
+        '''
+            Â«FOR path : refines.keySet»
+            Â«val schemaNode = refines.get(path)»
+            Â«writeRefine(path, schemaNode)»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeRefine(SchemaPath path, SchemaNode schemaNode) {
+        '''
+            refine Â«path.pathFromRoot.last» {
+                Â«IF schemaNode instanceof DataSchemaNode»
+                Â«writeDataSchemaNode(schemaNode as DataSchemaNode)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeTypeDefinitions(Set<TypeDefinition<?>> typeDefinitions) {
+        '''
+            Â«FOR typeDefinition : typeDefinitions»
+                Â«IF typeDefinition != null»
+                Â«writeTypeDefinition(typeDefinition)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeTypeDefinition(TypeDefinition<?> typeDefinition) {
+        '''
+            type Â«typeDefinition.QName.localName»;
+        '''
+    }
+
+    def static writeIdentities(Set<IdentitySchemaNode> identities) {
+        if (identities.nullOrEmpty)
+            return ''
+        '''
+            Â«FOR identity : identities»
+                Â«writeIdentity(identity)»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeIdentity(IdentitySchemaNode identity) {
+        if (identity == null)
+            return ''
+        '''
+            identity Â«identity.QName.localName» {
+                Â«IF identity.baseIdentity != null»
+                base "«writeIdentityPrefix(identity.baseIdentity)»«identity.baseIdentity»";
+                Â«ENDIF»
+                Â«IF !identity.description.nullOrEmpty»
+                description
+                    "«identity.description»";
+                Â«ENDIF»
+                Â«IF !identity.reference.nullOrEmpty»
+                reference
+                    "«identity.reference»";
+                Â«ENDIF»
+                Â«IF identity.status != null»
+                status Â«identity.status»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeIdentityPrefix(IdentitySchemaNode identity) {
+        if(module == null)
+            return ''
+
+        if(identity.QName.prefix.nullOrEmpty || module.prefix.nullOrEmpty)
+            return ''
+
+        val identityPrefix = identity.QName.prefix
+
+        if(!module.prefix.equals(identity.QName.prefix))
+            return identityPrefix + ":"
+        return ''
+    }
+
+    def static writeFeatures(Set<FeatureDefinition> features) {
+        '''
+            Â«FOR feature : features»
+                Â«IF feature != null»
+                Â«writeFeature(feature)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeFeature(FeatureDefinition featureDef) {
+        '''
+            feature Â«featureDef.QName.localName» {
+                Â«IF !featureDef.description.nullOrEmpty»
+                description
+                    "«featureDef.description»";
+                Â«ENDIF»
+                Â«IF !featureDef.reference.nullOrEmpty»
+                reference
+                    "«featureDef.reference»";
+                Â«ENDIF»
+                Â«IF featureDef.status != null»
+                status Â«featureDef.status»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeExtensions(List<ExtensionDefinition> extensions) {
+        '''
+            Â«FOR anExtension : extensions»
+                Â«IF anExtension != null»
+                Â«writeExtension(anExtension)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeExtension(ExtensionDefinition extensionDef) {
+        '''
+            extension Â«extensionDef.QName.localName» {
+                Â«IF !extensionDef.description.nullOrEmpty»
+                description
+                    "«extensionDef.description»";
+                Â«ENDIF»
+                Â«IF !extensionDef.argument.nullOrEmpty»
+                argument "«extensionDef.argument»";
+                Â«ENDIF»
+                Â«IF !extensionDef.reference.nullOrEmpty»
+                reference
+                    "«extensionDef.reference»";
+                Â«ENDIF»
+                Â«IF extensionDef.status != null»
+                status Â«extensionDef.status»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeDeviations(Set<Deviation> deviations) {
+        '''
+            Â«FOR deviation : deviations»
+                Â«IF deviation != null»
+                Â«writeDeviation(deviation)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeDeviation(Deviation deviation) {
+        '''
+            deviation Â«deviation.targetPath» {
+                Â«IF !deviation.reference.nullOrEmpty»
+                    reference
+                        "«deviation.reference»";
+                Â«ENDIF»
+                Â«IF deviation.deviate != null && !deviation.deviate.name.nullOrEmpty»
+                    deviation Â«deviation.deviate.name»;
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeAugments(Set<AugmentationSchema> augments) {
+        '''
+            Â«FOR augment : augments»
+                Â«IF augment != null»
+                Â«writeAugment(augment)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeDataSchemaNodes(Collection<DataSchemaNode> dataSchemaNodes) {
+        '''
+            Â«FOR schemaNode : dataSchemaNodes»
+                Â«writeDataSchemaNode(schemaNode)»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static CharSequence writeGroupingDefs(Set<GroupingDefinition> groupingDefs) {
+        '''
+            Â«FOR groupingDef : groupingDefs»
+                Â«IF groupingDef != null»
+                Â«writeGroupingDef(groupingDef)»
+                Â«ENDIF»
+            Â«ENDFOR»
+        '''
+    }
+
+    def static writeAugment(AugmentationSchema augment) {
+        '''
+            augment Â«formatToAugmentPath(augment.targetPath.pathFromRoot)» {
+                Â«IF augment.whenCondition != null && !augment.whenCondition.toString.nullOrEmpty»
+                when "«augment.whenCondition.toString»";
+                Â«ENDIF»
+                Â«IF !augment.description.nullOrEmpty»
+                description
+                    "«augment.description»";
+                Â«ENDIF»
+                Â«IF !augment.reference.nullOrEmpty»
+                reference
+                    "«augment.reference»";
+                Â«ENDIF»
+                Â«IF augment.status != null»
+                status Â«augment.status»;
+                Â«ENDIF»
+                Â«IF !augment.childNodes.nullOrEmpty»
+                Â«writeDataSchemaNodes(augment.childNodes)»
+                Â«ENDIF»
+                Â«IF !augment.uses.nullOrEmpty»
+                Â«writeUsesNodes(augment.uses)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeGroupingDef(GroupingDefinition groupingDef) {
+        '''
+            grouping Â«groupingDef.QName.localName» {
+                Â«IF !groupingDef.groupings.nullOrEmpty»
+                    Â«writeGroupingDefs(groupingDef.groupings)»
+                Â«ENDIF»
+                Â«IF !groupingDef.childNodes.nullOrEmpty»
+                    Â«writeDataSchemaNodes(groupingDef.childNodes)»
+                Â«ENDIF»
+                Â«IF !groupingDef.unknownSchemaNodes.nullOrEmpty»
+                    Â«writeUnknownSchemaNodes(groupingDef.unknownSchemaNodes)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeContSchemaNode(ContainerSchemaNode contSchemaNode) {
+        '''
+            container Â«contSchemaNode.getQName.localName» {
+                Â«IF !contSchemaNode.childNodes.nullOrEmpty»
+                Â«writeDataSchemaNodes(contSchemaNode.childNodes)»
+                Â«ENDIF»
+                Â«IF !contSchemaNode.availableAugmentations.nullOrEmpty»
+                Â«writeAugments(contSchemaNode.availableAugmentations)»
+                Â«ENDIF»
+                Â«IF !contSchemaNode.groupings.nullOrEmpty»
+                Â«writeGroupingDefs(contSchemaNode.groupings)»
+                Â«ENDIF»
+                Â«IF !contSchemaNode.uses.nullOrEmpty»
+                Â«writeUsesNodes(contSchemaNode.uses)»
+                Â«ENDIF»
+                Â«IF !contSchemaNode.unknownSchemaNodes.nullOrEmpty»
+                Â«writeUnknownSchemaNodes(contSchemaNode.unknownSchemaNodes)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static writeAnyXmlSchemaNode(AnyXmlSchemaNode anyXmlSchemaNode) {
+        '''
+            anyxml Â«anyXmlSchemaNode.getQName.localName»;
+        '''
+    }
+
+    def static writeLeafSchemaNode(LeafSchemaNode leafSchemaNode) {
+        '''
+            leaf Â«leafSchemaNode.getQName.localName» {
+                type Â«leafSchemaNode.type.getQName.localName»;
+            }
+        '''
+    }
+
+    def static writeLeafListSchemaNode(LeafListSchemaNode leafListSchemaNode) {
+        '''
+            leaf-list Â«leafListSchemaNode.getQName.localName» {
+                type Â«leafListSchemaNode.type.getQName.localName»;
+            }
+        '''
+    }
+
+    def static writeChoiceCaseNode(ChoiceCaseNode choiceCaseNode) {
+        '''
+            case Â«choiceCaseNode.getQName.localName» {
+                Â«FOR childNode : choiceCaseNode.childNodes»
+                    Â«writeDataSchemaNode(childNode)»
+                Â«ENDFOR»
+            }
+        '''
+    }
+
+    def static writeChoiceNode(ChoiceNode choiceNode) {
+        '''
+            choice Â«choiceNode.getQName.localName» {
+                Â«FOR child : choiceNode.cases»
+                    Â«writeDataSchemaNode(child)»
+                Â«ENDFOR»
+            }
+        '''
+    }
+
+    def static writeListSchemaNode(ListSchemaNode listSchemaNode) {
+        '''
+            list Â«listSchemaNode.getQName.localName» {
+                key Â«FOR listKey : listSchemaNode.keyDefinition SEPARATOR " "»"«listKey.localName»"
+                Â«ENDFOR»
+                Â«IF !listSchemaNode.childNodes.nullOrEmpty»
+                    Â«writeDataSchemaNodes(listSchemaNode.childNodes)»
+                Â«ENDIF»
+                Â«IF !listSchemaNode.availableAugmentations.nullOrEmpty»
+                    Â«writeAugments(listSchemaNode.availableAugmentations)»
+                Â«ENDIF»
+                Â«IF !listSchemaNode.groupings.nullOrEmpty»
+                    Â«writeGroupingDefs(listSchemaNode.groupings)»
+                Â«ENDIF»
+                Â«IF !listSchemaNode.uses.nullOrEmpty»
+                    Â«writeUsesNodes(listSchemaNode.uses)»
+                Â«ENDIF»
+                Â«IF !listSchemaNode.unknownSchemaNodes.nullOrEmpty»
+                    Â«writeUnknownSchemaNodes(listSchemaNode.unknownSchemaNodes)»
+                Â«ENDIF»
+            }
+        '''
+    }
+
+    def static CharSequence writeDataSchemaNode(DataSchemaNode child) {
+        '''
+            Â«IF child instanceof ContainerSchemaNode»
+                Â«writeContSchemaNode(child as ContainerSchemaNode)»
+            Â«ENDIF»
+            Â«IF child instanceof AnyXmlSchemaNode»
+                Â«writeAnyXmlSchemaNode(child as AnyXmlSchemaNode)»
+            Â«ENDIF»
+            Â«IF child instanceof LeafSchemaNode»
+                Â«writeLeafSchemaNode(child as LeafSchemaNode)»
+            Â«ENDIF»
+            Â«IF child instanceof LeafListSchemaNode»
+                Â«writeLeafListSchemaNode(child as LeafListSchemaNode)»
+            Â«ENDIF»
+            Â«IF child instanceof ChoiceCaseNode»
+                Â«writeChoiceCaseNode(child as ChoiceCaseNode)»
+            Â«ENDIF»
+            Â«IF child instanceof ChoiceNode»
+                Â«writeChoiceNode(child as ChoiceNode)»
+            Â«ENDIF»
+            Â«IF child instanceof ListSchemaNode»
+                Â«writeListSchemaNode(child as ListSchemaNode)»
+            Â«ENDIF»
+        '''
+    }
+    
+    static def String formatSchemaPath(String moduleName, Iterable<QName> schemaPath) {
+        var currentElement = schemaPath.head
+        val StringBuilder sb = new StringBuilder()
+        sb.append(moduleName)
+
+        for(pathElement : schemaPath) {
+            if(!currentElement.namespace.equals(pathElement.namespace)) {
+                currentElement = pathElement
+                sb.append('/')
+                sb.append(pathElement)
+            }
+            else {
+                sb.append('/')
+                sb.append(pathElement.localName)
+            }
+        }
+        return sb.toString
+    }
+
+    static def String formatToParagraph(String text, int nextLineIndent) {
+        if (text == null || text.isEmpty())
+            return '';
+
+        var String formattedText = text;
+        val StringBuilder sb = new StringBuilder();
+        val StringBuilder lineBuilder = new StringBuilder();
+        var boolean isFirstElementOnNewLineEmptyChar = false;
+        val lineIndent = computeNextLineIndent(nextLineIndent);
+
+        formattedText = formattedText.replace("*/", "&#42;&#47;");
+        formattedText = formattedText.replace("\n", "");
+        formattedText = formattedText.replace("\t", "");
+        formattedText = formattedText.replaceAll(" +", " ");
+
+        val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
+
+        while (tokenizer.hasMoreElements()) {
+            val String nextElement = tokenizer.nextElement().toString();
+
+            if (lineBuilder.length() + nextElement.length() > 80) {
+                if (lineBuilder.charAt(lineBuilder.length() - 1) == ' ') {
+                    lineBuilder.setLength(0);
+                    lineBuilder.append(lineBuilder.substring(0, lineBuilder.length() - 1));
+                }
+                if (lineBuilder.charAt(0) == ' ') {
+                    lineBuilder.setLength(0);
+                    lineBuilder.append(lineBuilder.substring(1));
+                }
+
+                sb.append(lineBuilder);
+                lineBuilder.setLength(0);
+                sb.append("\n");
+
+                if (nextLineIndent > 0) {
+                    sb.append(lineIndent)
+                }
+
+                if (nextElement.toString().equals(" "))
+                    isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
+            }
+            if (isFirstElementOnNewLineEmptyChar) {
+                isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
+            } else {
+                lineBuilder.append(nextElement);
+            }
+        }
+        sb.append(lineBuilder);
+        sb.append("\n");
+
+        return sb.toString();
+    }
+
+    def private static formatToAugmentPath(Iterable<QName> schemaPath) {
+        val StringBuilder sb = new StringBuilder();
+
+        for(pathElement : schemaPath) {
+            val prefix = pathElement.prefix
+            val localName = pathElement.localName
+
+            sb.append("\\")
+            sb.append(prefix)
+            sb.append(":")
+            sb.append(localName)
+        }
+        return sb.toString
+    }
+
+    private static def computeNextLineIndent(int nextLineIndent) {
+        val StringBuilder sb = new StringBuilder()
+        var i = 0
+        while (i < nextLineIndent) {
+            sb.append(' ')
+            i = i + 1
+        }
+        return sb.toString
+    }
+}
index eb7b948c545283c6e7e3d162a202574365eb0723..152d60e6de6b5f933addf4714e41f4da702dbae7 100644 (file)
@@ -190,70 +190,15 @@ abstract class BaseTemplate {
     }
 
     def protected String formatDataForJavaDoc(GeneratedType type) {
-        val typeDescription = type.description
-        val typeReference = type.reference
-        val typeModuleName = type.moduleName
-        val typeSchemaPath = type.schemaPath
+        val typeDescription = type.getDescription();
 
         return '''
-            Â«IF !type.isDocumentationParametersNullOrEmtpy»
-               Â«IF typeDescription != null && !typeDescription.empty»
-                Â«formatToParagraph(typeDescription)»
-               Â«ENDIF»
-               Â«IF typeReference != null && !typeReference.empty»
-                Reference:
-                    Â«formatReference(typeReference)»
-               Â«ENDIF»
-               Â«IF typeModuleName != null && !typeModuleName.empty»
-                Module name:
-                    Â«typeModuleName»
-               Â«ENDIF»
-               Â«IF typeSchemaPath != null && !typeSchemaPath.empty»
-                Schema path:
-                    Â«formatPath(typeSchemaPath)»
-               Â«ENDIF»
+            Â«IF !typeDescription.nullOrEmpty»
+            Â«typeDescription»
             Â«ENDIF»
         '''.toString
     }
 
-    def formatPath(Iterable<QName> schemaPath) {
-        var currentElement = schemaPath.head
-        val StringBuilder sb = new StringBuilder()
-        sb.append('[')
-        sb.append(currentElement)
-
-        for(pathElement : schemaPath) {
-            if(!currentElement.namespace.equals(pathElement.namespace)) {
-                currentElement = pathElement
-                sb.append('/')
-                sb.append(pathElement)
-            }
-            else {
-                sb.append('/')
-                sb.append(pathElement.localName)
-            }
-        }
-        sb.append(']')
-        return sb.toString
-    }
-
-    def formatReference(String reference) {
-        if(reference == null || reference.isEmpty)
-            return reference
-
-        val StringTokenizer tokenizer = new StringTokenizer(reference, " ", true)
-        val StringBuilder sb = new StringBuilder();
-
-        while(tokenizer.hasMoreTokens) {
-            var String oneElement = tokenizer.nextToken
-            if (oneElement.contains("http://")) {
-                oneElement = asLink(oneElement)
-            }
-            sb.append(oneElement)
-        }
-        return sb.toString
-    }
-
     def asLink(String text) {
         val StringBuilder sb = new StringBuilder()
         var tempText = text
@@ -329,29 +274,16 @@ abstract class BaseTemplate {
     }
 
     def isDocumentationParametersNullOrEmtpy(GeneratedType type) {
-        var boolean isNullOrEmpty = true
-        val String typeDescription = type.description
-        val String typeReference = type.reference
-        val String typeModuleName = type.moduleName
-        val Iterable<QName> typeSchemaPath = type.schemaPath
-
-        if(typeDescription != null && !typeDescription.empty) {
-            isNullOrEmpty = false
-            return isNullOrEmpty
-        }
-        if(typeReference != null && !typeReference.empty) {
-            isNullOrEmpty = false
-            return isNullOrEmpty
-        }
-        if(typeModuleName != null && !typeModuleName.empty) {
-            isNullOrEmpty = false
-            return isNullOrEmpty
-        }
-        if(typeSchemaPath != null && !typeSchemaPath.empty) {
-            isNullOrEmpty = false
-            return isNullOrEmpty
+        val boolean isTypeDescriptionNullOrEmpty = type.description.nullOrEmpty
+        val boolean isTypeReferenceNullOrEmpty = type.reference.nullOrEmpty
+        val boolean isTypeModuleNameNullOrEmpty = type.moduleName.nullOrEmpty
+        val boolean isTypeSchemaPathNullOrEmpty = type.schemaPath.nullOrEmpty
+
+        if (isTypeDescriptionNullOrEmpty && isTypeReferenceNullOrEmpty && isTypeModuleNameNullOrEmpty
+            && isTypeSchemaPathNullOrEmpty) {
+            return true
         }
-        return isNullOrEmpty
+        return false
     }
 
     def generateRestrictions(Type type, String paramName, Type returnType) '''
index d73b71e93aaf601b3f8c7e3143a806490611de82..d818c11d3d04e17cad071320888dbd7846dfdfb1 100644 (file)
@@ -736,6 +736,23 @@ class BuilderTemplate extends BaseTemplate {
         return Â«type.importedName».class;
     }
     '''
+    
+    private def createDescription(GeneratedType type) {
+        return '''
+        Class that builds {@link Â«type.importedName»} instances.
+        
+        @see Â«type.importedName»
+    '''
+    }
+    
+    override def protected String formatDataForJavaDoc(GeneratedType type) {
+        val typeDescription = createDescription(type)
 
+        return '''
+            Â«IF !typeDescription.nullOrEmpty»
+            Â«typeDescription»
+            Â«ENDIF»
+        '''.toString
+    }
 }
 
index 94cfe1e8d05cec8076711637a6da1129e06d8fed..2b56179964c464b17ec2259dd5a4835a14d32d4d 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.yangtools.sal.java.api.generator
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
 import java.beans.ConstructorProperties
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
+import static org.opendaylight.yangtools.binding.generator.util.Types.*
 
 /**
  * Template for generating JAVA class. 
@@ -77,6 +78,15 @@ class UnionTemplate extends ClassTemplate {
                                 Â«ELSEIF propRet instanceof GeneratedTransferObject && (propRet as GeneratedTransferObject).unionType»
                                     Â«Â«Â« union type
                                     this.«other.fieldName» = Â«property.fieldName».getValue();
+                                Â«ELSEIF propRet instanceof GeneratedTransferObject // Is it a GeneratedTransferObject
+                                        && (propRet as GeneratedTransferObject).typedef  // Is it a typedef
+                                        && (propRet as GeneratedTransferObject).properties != null 
+                                        && !(propRet as GeneratedTransferObject).properties.empty 
+                                        && ((propRet as GeneratedTransferObject).properties.size == 1) 
+                                        && (propRet as GeneratedTransferObject).properties.get(0).name.equals("value") 
+                                        && BOOLEAN.equals((propRet as GeneratedTransferObject).properties.get(0).returnType)» // And the property value is of type boolean
+                                    Â«Â«Â« generated boolean typedef
+                                    this.«other.fieldName» = Â«property.fieldName».isValue().toString().toCharArray();
                                 Â«ELSE»
                                     Â«Â«Â« generated type
                                     this.«other.fieldName» = Â«property.fieldName».getValue().toString().toCharArray();
index 88da121c8616f44aba44d633d9e599e1396fc5a4..4d429f1aaa11c899f6759cea472d48dabde75171 100644 (file)
@@ -27,7 +27,7 @@ final class ReadOnlyTrieMap<K, V> extends ForwardingMap<K, V> {
     private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTrieMap.class);
     private final TrieMap<K, V> readWrite;
     private final int size;
-    private TrieMap<K, V> readOnly;
+    private volatile TrieMap<K, V> readOnly;
 
     ReadOnlyTrieMap(final TrieMap<K, V> map, final int size) {
         super();
@@ -43,15 +43,18 @@ final class ReadOnlyTrieMap<K, V> extends ForwardingMap<K, V> {
 
     @Override
     protected Map<K, V> delegate() {
-        if (readOnly == null) {
+        TrieMap<K, V> ret = readOnly;
+        if (ret == null) {
             synchronized (this) {
-                if (readOnly == null) {
-                    readOnly = readWrite.readOnlySnapshot();
+                ret = readOnly;
+                if (ret == null) {
+                    ret = readWrite.readOnlySnapshot();
+                    readOnly = ret;
                 }
             }
         }
 
-        return readOnly;
+        return ret;
     }
 
     @Override
index af51032dd27b5b65dedcd96b2a62c9f0c32f8fb7..604ca3692c6f41b3cc74f941a1f16e201ca591a2 100644 (file)
@@ -43,6 +43,15 @@ public abstract class ExceptionMapper<X extends Exception> implements Function<E
         this.opName = Preconditions.checkNotNull(opName);
     }
 
+    /**
+     * Return the exception class produced by this instance.
+     *
+     * @return Exception class.
+     */
+    protected final Class<X> getExceptionType() {
+        return exceptionType;
+    }
+
     /**
      * Invoked to create a new exception instance of the specified type.
      *
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapper.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapper.java
new file mode 100644 (file)
index 0000000..3eb8c13
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Robert Varga.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util.concurrent;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Convenience {@link ExceptionMapper} which instantiates specified Exception using
+ * reflection. The Exception types are expected to declare an accessible constructor
+ * which takes two arguments: a String and a Throwable.
+ *
+ * @param <X> Exception type
+ */
+public final class ReflectiveExceptionMapper<X extends Exception> extends ExceptionMapper<X> {
+    private final Constructor<X> ctor;
+
+    private ReflectiveExceptionMapper(final String opName, final Constructor<X> ctor) {
+        super(opName, ctor.getDeclaringClass());
+        this.ctor = ctor;
+    }
+
+    @Override
+    protected X newWithCause(final String message, final Throwable cause) {
+        try {
+            return ctor.newInstance(message, cause);
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new IllegalStateException("Failed to instantiate exception " + ctor.getDeclaringClass(), e);
+        }
+    }
+
+    /**
+     * Create a new instance of the reflective exception mapper. This method performs basic
+     * sanity checking on the exception class. This method is potentially very costly, so
+     * users are strongly encouraged to cache the returned mapper for reuse.
+     *
+     * @param opName Operation performed
+     * @param exceptionType Exception type
+     * @return A new mapper instance
+     * @throws IllegalArgumentException when the supplied exception class does not pass sanity checks
+     * @throws SecurityException when the required constructor is not accessible
+     */
+    public static <X extends Exception> ReflectiveExceptionMapper<X> create(final String opName, final Class<X> exceptionType) throws SecurityException {
+        final Constructor<X> c;
+        try {
+            c = exceptionType.getConstructor(String.class, Throwable.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("Class does not define a String, Throwable constructor", e);
+        }
+
+        try {
+            c.newInstance(opName, new Throwable());
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new IllegalArgumentException("Constructor " + c.getName() + " failed to pass instantiation test", e);
+        }
+
+        return new ReflectiveExceptionMapper<>(opName, c);
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapperTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapperTest.java
new file mode 100644 (file)
index 0000000..c18ac1f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014 Robert Varga.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+
+public final class ReflectiveExceptionMapperTest {
+    static final class NoArgumentCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public NoArgumentCtorException() {
+            super();
+        }
+    }
+
+    static final class PrivateCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        private PrivateCtorException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    static final class FailingCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public FailingCtorException(final String message, final Throwable cause) {
+            throw new IllegalArgumentException("just for test");
+        }
+    }
+
+    static final class GoodException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public GoodException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNoArgumentsContructor() {
+        ReflectiveExceptionMapper.create("no arguments", NoArgumentCtorException.class);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testPrivateContructor() {
+        ReflectiveExceptionMapper.create("private constructor", PrivateCtorException.class);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFailingContructor() {
+        ReflectiveExceptionMapper.create("failing constructor", FailingCtorException.class);
+    }
+
+    @Test
+    public void testInstantiation() {
+        ReflectiveExceptionMapper<GoodException> mapper = ReflectiveExceptionMapper.create("instantiation", GoodException.class);
+
+        final Throwable cause = new Throwable("some test message");
+
+        GoodException ret = mapper.apply(new ExecutionException("test", cause));
+
+        assertEquals("instantiation execution failed", ret.getMessage());
+        assertEquals(cause, ret.getCause());
+    }
+}
index 8fb379cb79d30182842080c88a9e9b9661d75071..8659eac92de592301d97f1c6693cd7214ff5d29d 100644 (file)
  */
 package org.opendaylight.yangtools.websocket.server;
 
+import com.google.common.util.concurrent.SettableFuture;
+
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.SocketAddress;
 import java.net.InetSocketAddress;
-
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
+import java.net.SocketAddress;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
+import java.util.concurrent.Future;
 
-import com.google.common.util.concurrent.SettableFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A HTTP server which serves Web Socket requests at:
@@ -72,11 +69,12 @@ public class WebSocketServer implements Runnable {
     private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class.toString());
 
 
-    public WebSocketServer(int inPort) {
+    public WebSocketServer(final int inPort) {
         this.inPort = inPort;
         port = SettableFuture.<Integer>create();
     }
 
+    @Override
     public void run(){
         try {
             startServer();
@@ -92,8 +90,8 @@ public class WebSocketServer implements Runnable {
     public void startServer() throws Exception {
         try {
             bootstrap.group(bossGroup, workerGroup)
-             .channel(NioServerSocketChannel.class)
-             .childHandler(new WebSocketServerInitializer());
+            .channel(NioServerSocketChannel.class)
+            .childHandler(new WebSocketServerInitializer());
 
             Channel ch = bootstrap.bind(inPort).sync().channel();
             SocketAddress localSocket = ch.localAddress();
index 0ac89ae8da2c1d634d134bc07977877e5a9c4484..46dfbc6242edbd82198fa8be1879fe8b6e04deff 100644 (file)
@@ -83,7 +83,9 @@ public final class QName implements Immutable, Serializable, Comparable<QName> {
      * @param localName
      *            YANG schema identifier
      *
+     * @deprecated Prefix storage in QNames is deprecated.
      */
+    @Deprecated
     public QName(final URI namespace, final Date revision, final String prefix, final String localName) {
         this(QNameModule.create(namespace, revision), prefix, localName);
     }
@@ -183,7 +185,10 @@ public final class QName implements Immutable, Serializable, Comparable<QName> {
      * Returns locally defined prefix assigned to local name
      *
      * @return locally defined prefix assigned to local name
+     *
+     * @deprecated Prefix storage in QNames is deprecated.
      */
+    @Deprecated
     public String getPrefix() {
         return prefix;
     }
@@ -241,7 +246,10 @@ public final class QName implements Immutable, Serializable, Comparable<QName> {
      * @param localName
      *            Local name part of QName. MUST NOT BE null.
      * @return Instance of QName
+     *
+     * @deprecated Prefix storage in QNames is deprecated.
      */
+    @Deprecated
     public static QName create(final QNameModule module, final String prefix, final String localName) {
         if (module == null) {
             throw new NullPointerException("module may not be null");
index 3233b78ce93c3c6cb473d7066abcb0bbf30bba4d..17d4e31919cb2d6302eb3a51beccaed821cc4173 100644 (file)
@@ -221,7 +221,7 @@ public class XmlDocumentUtils {
         }
         final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
 
-        if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+        if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType) {
             value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
         } else if(baseType instanceof IdentityrefTypeDefinition){
             value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
@@ -243,7 +243,7 @@ public class XmlDocumentUtils {
         if (codec != null) {
             value = codec.deserialize(text);
         }
-        if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+        if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType) {
             value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
         }
         if (value == null) {
index 8d42a8c46acdd5ac57e7450a3887de74adba750e..a9993778d5445765d56a281bd205e72099f6a3fe 100644 (file)
@@ -5,7 +5,6 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 import java.net.URI;
-import java.util.Map;
 import java.util.Map.Entry;
 
 import javax.annotation.Nonnull;
@@ -16,9 +15,9 @@ import javax.xml.stream.XMLStreamWriter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -150,6 +149,20 @@ public class XmlStreamUtils {
         }
 
         writer.writeStartElement(pfx, qname.getLocalName(), ns);
+        writeValue(writer, data, schema);
+        writer.writeEndElement();
+    }
+
+    /**
+     * 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 data data node
+     * @param schema Schema node
+     * @throws XMLStreamException if an encoding problem occurs
+     */
+    public void writeValue(final XMLStreamWriter writer, final @Nonnull Node<?> data, final SchemaNode schema) throws XMLStreamException {
         if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
             RandomPrefix randomPrefix = new RandomPrefix();
             for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
@@ -185,8 +198,6 @@ public class XmlStreamUtils {
                 writeElement(writer, child, childSchema);
             }
         }
-
-        writer.writeEndElement();
     }
 
     @VisibleForTesting
@@ -203,8 +214,8 @@ public class XmlStreamUtils {
      * emitted by the caller.
      *
      * @param writer XML Stream writer
-     * @param data data node
-     * @param schema Schema node
+     * @param type data type
+     * @param value data value
      * @throws XMLStreamException if an encoding problem occurs
      */
     public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition<?> type, final Object value) throws XMLStreamException {
index 3007324bed0a8a50d2a1fa0dcbf870d070775ba9..5cddb0d6343c7bd21b9ca7ab89b0aee6d8449f1f 100644 (file)
@@ -7,15 +7,59 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
+import com.google.common.base.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.tree.DataValidationFailedException;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
 
-import com.google.common.base.Optional;
-
-public abstract class RootModificationApplyOperation implements ModificationApplyOperation {
+/**
+ * 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 RootModificationApplyOperation} 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.
+ *
+ * <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.
+ *
+ * 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 RootModificationApplyOperation implements ModificationApplyOperation {
 
     @Override
     public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
@@ -29,7 +73,8 @@ public abstract class RootModificationApplyOperation implements ModificationAppl
     }
 
     @Override
-    public final Optional<TreeNode> apply(final ModifiedNode modification, final Optional<TreeNode> currentMeta, final Version version) {
+    public final Optional<TreeNode> apply(final ModifiedNode modification, final Optional<TreeNode> currentMeta,
+            final Version version) {
         return getDelegate().apply(modification, currentMeta, version);
     }
 
@@ -53,27 +98,58 @@ public abstract class RootModificationApplyOperation implements ModificationAppl
         getDelegate().verifyStructure(modification);
     }
 
+    /**
+     * Return the underlying delegate.
+     *
+     * @return Underlying delegate.
+     */
     abstract ModificationApplyOperation getDelegate();
 
+    /**
+     * 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 modi
+     *
+     * @return Derived {@link RootModificationApplyOperation} with separate
+     *         upgrade lifecycle.
+     */
     public abstract RootModificationApplyOperation snapshot();
 
+    /**
+     * Upgrades backing implementation to latest available, if possible.
+     * <p>
+     * Latest implementation of {@link RootModificationApplyOperation} is
+     * managed by {@link LatestOperationHolder} which was used to construct this
+     * operation and latest operation is updated by
+     * {@link LatestOperationHolder#setCurrent(ModificationApplyOperation)}.
+     */
     public abstract void upgradeIfPossible();
 
-
-
     public static RootModificationApplyOperation from(final ModificationApplyOperation resolver) {
-        if(resolver instanceof RootModificationApplyOperation) {
+        if (resolver instanceof RootModificationApplyOperation) {
             return ((RootModificationApplyOperation) resolver).snapshot();
         }
         return new NotUpgradable(resolver);
     }
 
+    /**
+     * Implementation of Upgradable {@link RootModificationApplyOperation}
+     *
+     * This implementation is associated with {@link LatestOperationHolder}
+     * which holds latest available implementation, which may be used for
+     * upgrade.
+     *
+     * Upgrading {@link LatestOperationHolder} will not affect any instance,
+     * unless client invoked {@link #upgradeIfPossible()} which will result in
+     * changing delegate to the latest one.
+     *
+     */
     private static final class Upgradable extends RootModificationApplyOperation {
 
         private final LatestOperationHolder holder;
         private ModificationApplyOperation delegate;
 
-
         public Upgradable(final LatestOperationHolder holder, final ModificationApplyOperation delegate) {
             this.holder = holder;
             this.delegate = delegate;
@@ -83,8 +159,9 @@ public abstract class RootModificationApplyOperation implements ModificationAppl
         @Override
         public void upgradeIfPossible() {
             ModificationApplyOperation holderCurrent = holder.getCurrent();
-            if(holderCurrent != delegate) {
-                // FIXME: Allow update only if there is addition of models, not removals.
+            if (holderCurrent != delegate) {
+                // FIXME: Allow update only if there is addition of models, not
+                // removals.
                 delegate = holderCurrent;
             }
 
@@ -97,7 +174,7 @@ public abstract class RootModificationApplyOperation implements ModificationAppl
 
         @Override
         public RootModificationApplyOperation snapshot() {
-            return new Upgradable(holder,getDelegate());
+            return new Upgradable(holder, getDelegate());
         }
 
     }
@@ -126,20 +203,55 @@ public abstract class RootModificationApplyOperation implements ModificationAppl
         }
     }
 
-    public static class LatestOperationHolder {
+    /**
+     * Holder and factory for upgradable root modifications
+     *
+     * This class is factory for upgradable root modifications and provides an
+     * access to set latest backing implementation.
+     *
+     */
+    static class LatestOperationHolder {
 
         private ModificationApplyOperation current = new AlwaysFailOperation();
 
+        /**
+         * Return latest backing implemenation
+         *
+         * @return
+         */
         public ModificationApplyOperation getCurrent() {
             return current;
         }
 
+        /**
+         * Sets latest backing implementation of associated
+         * {@link RootModificationApplyOperation}.
+         * <p>
+         * Note: This does not result in upgrading implementation of already
+         * existing {@link RootModificationApplyOperation}. Users, which
+         * obtained instances using {@link #newSnapshot()}, deriving
+         * {@link RootModificationApplyOperation} from this modification must
+         * explicitly invoke
+         * {@link RootModificationApplyOperation#upgradeIfPossible()} on their
+         * instance to be updated to latest backing implementation.
+         *
+         * @param newApplyOper
+         *            New backing implementation
+         */
         public void setCurrent(final ModificationApplyOperation newApplyOper) {
             current = newApplyOper;
         }
 
+        /**
+         *
+         * Creates new upgradable {@link RootModificationApplyOperation}
+         * associated with holder.
+         *
+         * @return New upgradable {@link RootModificationApplyOperation} with
+         *         {@link #getCurrent()} used as backing implementation.
+         */
         public RootModificationApplyOperation newSnapshot() {
-            return new Upgradable(this,current);
+            return new Upgradable(this, current);
         }
 
     }
index 2ff674016794442a5cad3d7e1ab03d7cc20bc954..89bbffa058f27338f4bea6a07c79abedfb161032 100644 (file)
@@ -7,7 +7,18 @@
  */
 package org.opendaylight.yangtools.yang.model.api;
 
-public interface SchemaContextListener extends SchemaServiceListener {
-    @Override
+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/repo/api/AcceptingSchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java
deleted file mode 100644 (file)
index e101d9d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/eplv10.html
- */
-package org.opendaylight.yangtools.yang.model.repo.api;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
- */
-public final class AcceptingSchemaSourceFilter implements SchemaSourceFilter {
-    private static final AcceptingSchemaSourceFilter INSTANCE = new AcceptingSchemaSourceFilter();
-
-    private final Iterable<Class<? extends SchemaSourceRepresentation>> representations;
-
-    private AcceptingSchemaSourceFilter() {
-        final Builder<Class<? extends SchemaSourceRepresentation>> b = ImmutableList.builder();
-        b.add(SchemaSourceRepresentation.class);
-        representations = b.build();
-    }
-
-    /**
-     * Return the singleton instance of this filter.
-     *
-     * @return Singleton shared instance.
-     */
-    public static final AcceptingSchemaSourceFilter getSingletonInstance() {
-        return INSTANCE;
-    }
-
-    @Override
-    public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
-        return representations;
-    }
-
-    @Override
-    public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
-        return Futures.immediateFuture(Boolean.TRUE);
-    }
-}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java
new file mode 100644 (file)
index 0000000..17c9c90
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception thrown when a the specified schema source is not available.
+ */
+@Beta
+public class MissingSchemaSourceException extends SchemaSourceException {
+    private static final long serialVersionUID = 1L;
+
+    public MissingSchemaSourceException(final String message) {
+        super(message);
+    }
+
+    public MissingSchemaSourceException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
index 8869c9eb3ddb11a9d17732ba33bb0e0056e01986..2597e9c185481d8886d4ee65155e74d9264cdc37 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.CheckedFuture;
 
 import java.util.Collection;
@@ -20,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * based on a specification of what {@link SourceIdentifier}s are required
  * and dynamic recursive resolution.
  */
+@Beta
 public interface SchemaContextFactory {
     /**
      * Create a new schema context containing specified sources, pulling in
index 807bbf05de62bcf4ae7d3863fa47a6e843e214d8..73569d384d3cd76091466ebe5474c70a96b57e6c 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.CheckedFuture;
+
 import javax.annotation.Nonnull;
 
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -15,6 +18,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * Interface exposed by repository implementations. A schema repository is a logically
  * centralized place for model storage and creation of {@link SchemaContext} instances.
  */
+@Beta
 public interface SchemaRepository {
     /**
      * Instantiate a new {@link SchemaContextFactory}, which will filter available schema
@@ -26,4 +30,6 @@ public interface SchemaRepository {
      * @return A new schema context factory.
      */
     SchemaContextFactory createSchemaContextFactory(@Nonnull SchemaSourceFilter filter);
+
+    <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(@Nonnull SourceIdentifier id, @Nonnull Class<T> represetation);
 }
index b47e366a79755273b37be9e2a1a25335d3c6123a..e0be4adb7936c6afab7cd2be00e69440b6f4af0d 100644 (file)
@@ -7,38 +7,48 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
-import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.annotation.Nonnull;
 
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+
 /**
  * Exception thrown when a Schema Source fails to resolve.
  */
-public class SchemaResolutionException extends Exception {
+@Beta
+public class SchemaResolutionException extends SchemaSourceException {
     private static final long serialVersionUID = 1L;
-    private final Map<SourceIdentifier, Throwable> unresolvedSources;
+    private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+    private final Collection<SourceIdentifier> resolvedSources;
 
     public SchemaResolutionException(final @Nonnull String message) {
         this(message, (Throwable)null);
     }
 
     public SchemaResolutionException(final @Nonnull String message, final Throwable cause) {
-        this(message, cause, ImmutableMap.<SourceIdentifier, Exception>of());
+        this(message, cause, Collections.<SourceIdentifier>emptySet(), ImmutableMultimap.<SourceIdentifier, ModuleImport>of());
     }
 
-    public SchemaResolutionException(final @Nonnull String message, final @Nonnull Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
-        super(Preconditions.checkNotNull(message));
-        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+    public SchemaResolutionException(final @Nonnull String message, final Collection<SourceIdentifier> resolvedSources,
+            final @Nonnull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        this(message, null, Collections.<SourceIdentifier>emptySet(), unsatisfiedImports);
     }
 
-    public SchemaResolutionException(final @Nonnull String message, final Throwable cause, @Nonnull final Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+    public SchemaResolutionException(final @Nonnull String message, final Throwable cause,
+            @Nonnull final Collection<SourceIdentifier> resolvedSources,
+            @Nonnull final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
         super(message, cause);
-        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+        this.unsatisfiedImports = ImmutableMultimap.copyOf(unsatisfiedImports);
+        this.resolvedSources = ImmutableList.copyOf(resolvedSources);
     }
 
     /**
@@ -47,13 +57,19 @@ public class SchemaResolutionException extends Exception {
      *
      * @return Source/reason map.
      */
-    public final Map<SourceIdentifier, Throwable> getUnresolvedSources() {
-        return unresolvedSources;
+    public final Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+
+
+    // FIXME: should be leak actual mapping?
+    public final Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
     }
 
     @Override
     public final String toString() {
-        return addToStringAttributes(Objects.toStringHelper(this).add("unresolvedSources", unresolvedSources)).toString();
+        return addToStringAttributes(Objects.toStringHelper(this).add("unsatisfiedImports", unsatisfiedImports)).toString();
     }
 
     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
@@ -5,20 +5,22 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/eplv10.html
  */
-package org.opendaylight.yangtools.yang.model.repo.spi;
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
 
 /**
- * Exception thrown when a failure to translate a schema source between
- * representations.
+ * Exception thrown when a failure to acquire a schema source occurs.
  */
-public class SchemaSourceTransformationException extends Exception {
+@Beta
+public class SchemaSourceException extends Exception {
     private static final long serialVersionUID = 1L;
 
-    public SchemaSourceTransformationException(final String message) {
+    public SchemaSourceException(final String message) {
         super(message);
     }
 
-    public SchemaSourceTransformationException(final String message, final Throwable cause) {
+    public SchemaSourceException(final String message, final Throwable cause) {
         super(message, cause);
     }
 }
index 188f83c0c592c47b6134bf080d4ed0a99924dfce..f881900754a53642d3696bfab3e67e2a47934ac2 100644 (file)
@@ -7,9 +7,55 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.Collections;
+
+/*
+ * A filter of schema sources. This is used to restrict which sources representation
+ * instances are allowed to participate in construction of a schema context. This
+ * allows, for example, to create an non-shared island, or require the sources to
+ * be certified before use.
+ */
+@Beta
 public interface SchemaSourceFilter {
+    /**
+     * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
+     */
+    public static final SchemaSourceFilter ALWAYS_ACCEPT = new SchemaSourceFilter() {
+        private final Iterable<Class<? extends SchemaSourceRepresentation>> REPRESENTATIONS =
+                Collections.<Class<? extends SchemaSourceRepresentation>>singletonList(SchemaSourceRepresentation.class);
+
+        @Override
+        public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
+            return REPRESENTATIONS;
+        }
+
+        @Override
+        public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
+            return Futures.immediateFuture(Boolean.TRUE);
+        }
+    };
+
+    /**
+     * Get the representations this filter supports. A schema source is translated
+     * into one of these representations before it is presented for filtering.
+     *
+     * @return Set of supported representations.
+     */
     Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations();
+
+    /**
+     * Check if a particular schema source is acceptable to the filter. The process
+     * of checking may be asynchronous, but at some point it needs to produce an
+     * affirmative or negative answer before the schema context construction can
+     * proceed.
+     *
+     * @param schemaSource Schema source to be filtered
+     * @return Promise of a filtering decision. The result should be {@link Boolean#TRUE}
+     *         if the source is acceptable.
+     */
     ListenableFuture<Boolean> apply(SchemaSourceRepresentation schemaSource);
 }
index b1261a9cd48a5883c86b955b12c3f187315a5cf8..90fc5ae740c2bf5cf2d07f0328681d1e1f987f07 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.concepts.Immutable;
 
@@ -35,6 +37,7 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * Implementations of this interface expected to comply with the {@link Immutable}
  * contract.
  */
+@Beta
 public interface SchemaSourceRepresentation extends Identifiable<SourceIdentifier>, Immutable {
     /**
      * {@inheritDoc}
index cd3a0fbefcaaed7198980ee545f7fe510654ac0b..58b011c36811cd60c3f9c0af9a1a7f93d9990988 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
@@ -14,7 +15,6 @@ import org.opendaylight.yangtools.concepts.Identifier;
 import org.opendaylight.yangtools.concepts.Immutable;
 
 /**
- *
  * YANG Schema source identifier
  *
  * Simple transfer object represents identifier of source for YANG schema (module or submodule),
@@ -34,9 +34,8 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * <p>
  * (For further reference see: http://tools.ietf.org/html/rfc6020#section-5.2 and
  * http://tools.ietf.org/html/rfc6022#section-3.1 ).
- *
- *
  */
+@Beta
 public final class SourceIdentifier implements Identifier, Immutable {
     private static final long serialVersionUID = 1L;
     private final String revision;
index be52a90f777131055f9423d98d9893d0568c3371..50ee12ef4d03358729456081c76216a020f5db71 100644 (file)
@@ -7,8 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.io.ByteSource;
 
@@ -21,6 +25,7 @@ import org.opendaylight.yangtools.concepts.Delegator;
  * YANG text schema source representation. Exposes an RFC6020 text representation
  * as an {@link InputStream}.
  */
+@Beta
 public abstract class YangTextSchemaSource extends ByteSource implements SchemaSourceRepresentation {
     private final SourceIdentifier identifier;
 
@@ -28,6 +33,12 @@ public abstract class YangTextSchemaSource extends ByteSource implements SchemaS
         this.identifier = Preconditions.checkNotNull(identifier);
     }
 
+    public static SourceIdentifier identifierFromFilename(final String name) {
+        checkArgument(name.endsWith(".yang"), "Filename %s does not have a .yang extension", name);
+        // FIXME: add revision-awareness
+        return SourceIdentifier.create(name.substring(0, name.length() - 5), Optional.<String>absent());
+    }
+
     /**
      * {@inheritDoc}
      */
index b67aae628914c20e7b5fe2779152abb63814c2a1..f09f525533e62982f6610c96344501414bcfa8b4 100644 (file)
@@ -7,12 +7,15 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+
 import org.w3c.dom.Document;
 
 /**
  * Yin schema source representation. Exposes an RFC6020 YIN XML representation
  * as an W3C {@link Document}.
  */
+@Beta
 public interface YinSchemaSource extends SchemaSourceRepresentation {
     /**
      * {@inheritDoc}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java
new file mode 100644 (file)
index 0000000..fae4467
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * A potential schema source. Instances of this class track the various
+ * representations of a schema source and the cost attached to obtaining
+ * the source from them.
+ */
+@Beta
+public final class PotentialSchemaSource<T extends SchemaSourceRepresentation> {
+    /**
+     * Each registered source has a cost associated with it. Since a particular
+     * representation can be acquired by various means, here are general constants
+     * for common cases.
+     */
+    public enum Costs {
+        /**
+         * The source is immediately available, via a lookup or similar.
+         */
+        IMMEDIATE(0),
+        /**
+         * The source is available via a computation. For transformation-type
+         * computation, the cost of acquiring the cost needs to be added, too.
+         */
+        COMPUTATION(1),
+        /**
+         * The source is available by performing local IO, such that reading
+         * from a disk.
+         */
+        LOCAL_IO(4),
+        /**
+         * The source is available by performing remote IO, such as fetching
+         * from an HTTP server or similar.
+         */
+        REMOTE_IO(8);
+
+        private final int value;
+
+        private Costs(final int value) {
+            this.value = value;
+        }
+
+        /**
+         * The the cost value.
+         *
+         * @return Const constant.
+         */
+        public int getValue() {
+            return value;
+        }
+    }
+
+    private final Class<? extends T> representation;
+    private final SourceIdentifier sourceIdentifier;
+    private final int cost;
+
+    private PotentialSchemaSource(final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        this.representation = Preconditions.checkNotNull(representation);
+        this.sourceIdentifier = Preconditions.checkNotNull(sourceIdentifier);
+        Preconditions.checkArgument(cost >= 0, "cost has to be non-negative");
+        this.cost = cost;
+    }
+
+    public static final <T extends SchemaSourceRepresentation> PotentialSchemaSource<T> create(final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        return new PotentialSchemaSource<>(sourceIdentifier, representation, cost);
+    }
+
+    public SourceIdentifier getSourceIdentifier() {
+        return sourceIdentifier;
+    }
+
+    public Class<? extends T> getRepresentation() {
+        return representation;
+    }
+
+    public int getCost() {
+        return cost;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + cost;
+        result = prime * result + representation.hashCode();
+        result = prime * result + sourceIdentifier.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PotentialSchemaSource)) {
+            return false;
+        }
+        final PotentialSchemaSource<?> other = (PotentialSchemaSource<?>) obj;
+        if (cost != other.cost) {
+            return false;
+        }
+        if (!representation.equals(other.representation)) {
+            return false;
+        }
+        if (!sourceIdentifier.equals(other.sourceIdentifier)) {
+            return false;
+        }
+        return true;
+    }
+}
@@ -7,9 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import com.google.common.annotations.Beta;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Registration of a SchemaSourceListener.
+ */
+@Beta
+public interface SchemaListenerRegistration extends ListenerRegistration<SchemaSourceListener> {
 
-public interface SchemaTransformerRegistration extends ObjectRegistration<SchemaSourceTransformer<?, ?>> {
-    @Override
-    void close();
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java
new file mode 100644 (file)
index 0000000..b4f55a8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import com.google.common.annotations.Beta;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * Listener for schema source lifecycle events.
+ */
+@Beta
+public interface SchemaSourceListener extends EventListener {
+    /**
+     * Invoked when the registry sees a concrete source. This callback is typically
+     * used by cache-type listeners, who intercept the source, store it locally and
+     * announce themselves as a provider of that particular schema source.
+     *
+     * @param source Schema source
+     */
+    void schemaSourceEncountered(SchemaSourceRepresentation source);
+
+    /**
+     * Invoked when a new schema source is registered by a provider. This call
+     * callback, along with {@link #schemaSourceUnregistered(PotentialSchemaSource)}
+     * is typically used by transformer-type listeners, who intercept the registration
+     * if the advertised representation matches their input type and register
+     * themselves as a potential provider of the same source in their output
+     * representation type.
+     *
+     * @param sources Newly available sources
+     */
+    void schemaSourceRegistered(Iterable<PotentialSchemaSource<?>> sources);
+
+    /**
+     * Invoked when a schema source is unregistered.
+     *
+     * @param source Schema source representation
+     */
+    void schemaSourceUnregistered(PotentialSchemaSource<?> source);
+}
index a0a141bd3759f4f1a4dac00e3f3cb2706d1114bd..70ad6813e8286f4c22025b312395af34ebbc6418 100644 (file)
@@ -8,9 +8,10 @@
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
 
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -29,10 +30,9 @@ public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
      *
      * <ul>
      * <li> If the source identifier specifies a revision, this method returns either
-     * a representation of that particular revision, or report the identifier as absent
-     * by returning {@link Optional#absent()}.
+     * a representation of that particular revision or throw {@link MissingSchemaSourceException}.
      * <li> If the source identifier does not specify a revision, this method returns
-     * the newest available revision, or {@link Optional#absent()}.
+     * the newest available revision, or throws {@link MissingSchemaSourceException}.
      *
      * In either case the returned representation is required to report a non-null
      * revision in the {@link SourceIdentifier} returned from
@@ -43,7 +43,7 @@ public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
      *
      * @param sourceIdentifier source identifier
      * @return source representation if supplied YANG module is available
-     *         {@link Optional#absent()} otherwise.
+     *
      */
-    ListenableFuture<Optional<T>> getSource(SourceIdentifier sourceIdentifier);
+    CheckedFuture<? extends T, SchemaSourceException> getSource(SourceIdentifier sourceIdentifier);
 }
index 3c3c5fef7748dda7a9bdbe3ae507118a46c4f617..6937c53e98ecbc42544cf61fa7f00b0ee792e880 100644 (file)
@@ -7,10 +7,16 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 
-public interface SchemaSourceRegistration extends ObjectRegistration<SourceIdentifier> {
+/**
+ * Registration of a schema source.
+ */
+@Beta
+public interface SchemaSourceRegistration<T extends SchemaSourceRepresentation> extends ObjectRegistration<PotentialSchemaSource<T>> {
     @Override
     void close();
 }
index f9acf3d456cb260f4f924356c08caad549e4b128..075fcb5382d44ffb34532d20685c92de4cf86c7a 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -17,29 +19,28 @@ import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
  * {@link SchemaSourceProvider} instances which would then acquire the schema
  * source.
  */
+@Beta
 public interface SchemaSourceRegistry {
     /**
      * Register a new schema source which is potentially available from a provider.
      * A registration does not guarantee that a subsequent call to
      * {@link SchemaSourceProvider#getSource(SourceIdentifier)} will succeed.
      *
-     * @param identifier Schema source identifier
      * @param provider Resolver which can potentially resolve the identifier
-     * @param representation Schema source representation which the source may
-     *                       be available.
+     * @param source Schema source details
      * @return A registration handle. Invoking {@link SchemaSourceRegistration#close()}
      *         will cancel the registration.
      */
-    <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
-            SourceIdentifier identifier, SchemaSourceProvider<? super T> provider, Class<T> representation);
+    <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(SchemaSourceProvider<? super T> provider, PotentialSchemaSource<T> source);
 
     /**
-     * Register a schema transformer. The registry can invoke it to transform between
-     * the various schema source formats.
+     * Register a schema source listener. The listener will be notified as new
+     * sources and their representations become available, subject to the provided
+     * filter.
      *
-     * @param transformer Schema source transformer
-     * @return A registration handle. Invoking {@link SchemaTransformerRegistration#close()}
+     * @param listener Schema source listener
+     * @return A registration handle. Invoking {@link SchemaListenerRegistration#close()}
      *         will cancel the registration.
      */
-    SchemaTransformerRegistration registerSchemaSourceTransformer(SchemaSourceTransformer<?, ?> transformer);
+    SchemaListenerRegistration registerSchemaSourceListener(SchemaSourceListener listener);
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java
deleted file mode 100644 (file)
index 13d309e..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/eplv10.html
- */
-package org.opendaylight.yangtools.yang.model.repo.spi;
-
-import com.google.common.util.concurrent.CheckedFuture;
-
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
-
-/**
- * An schema source representation transformation service. An instance can create
- * some output schema source representation based on some input source representation.
- *
- * @param <I> Input {@link SchemaSourceRepresentation}
- * @param <O> Output {@link SchemaSourceRepresentation}
- */
-public interface SchemaSourceTransformer<I extends SchemaSourceRepresentation, O extends SchemaSourceRepresentation> {
-    /**
-     * Return the {@link SchemaSourceRepresentation} which this transformer
-     * accepts on its input.
-     *
-     * @return The input source representation type.
-     */
-    Class<I> getInputRepresentation();
-
-    /**
-     * Return the {@link SchemeSourceRepresentation} which this transformer
-     * produces on its output.
-     *
-     * @return The output source representation type.
-     */
-    Class<O> getOutputRepresentation();
-
-    /**
-     * Transform a schema source representation from its input form to
-     * the transformers output form.
-     *
-     * @param source Schema source in its source representation
-     * @return A future which produces the output schema source representation.
-     */
-    CheckedFuture<O, SchemaSourceTransformationException> transformSchemaSource(I source);
-
-    /**
-     * Return the relative cost of performing the transformation. When in doubt,
-     * return 1.
-     *
-     * @return Relative cost.
-     */
-    int getCost();
-}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java
new file mode 100644 (file)
index 0000000..e720f8d
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+
+public abstract class AbstractSchemaListenerRegistration extends AbstractListenerRegistration<SchemaSourceListener> implements SchemaListenerRegistration {
+    protected AbstractSchemaListenerRegistration(final SchemaSourceListener listener) {
+        super(listener);
+    }
+}
index 4dd30b90a99c3470c92cbd1082ed2793598d4bd1..53563fde69cea887b7d0a90e5be70350f58e8925 100644 (file)
  */
 package org.opendaylight.yangtools.yang.model.repo.util;
 
-import com.google.common.base.Optional;
+import com.google.common.annotations.Beta;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
-import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureFallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
+/**
+ * Abstract base class for {@link SchemaRepository} implementations. It handles registration
+ * and lookup of schema sources, subclasses need only to provide their own
+ * {@link #createSchemaContextFactory(SchemaSourceFilter)} implementation.
+ */
+@Beta
+public abstract class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class);
-    private static final Comparator<SchemaTransformerRegistration> TRANSFORMER_COST_COMPARATOR = new Comparator<SchemaTransformerRegistration>() {
-        @Override
-        public int compare(final SchemaTransformerRegistration o1, final SchemaTransformerRegistration o2) {
-            return o1.getInstance().getCost() - o2.getInstance().getCost();
-        }
-    };
+    private static final ExceptionMapper<SchemaSourceException> FETCH_MAPPER = ReflectiveExceptionMapper.create("Schema source fetch", SchemaSourceException.class);
 
     /*
-     * Output-type -> transformer map. Our usage involves knowing the destination type,
-     * so we have to work backwards and find a transformer chain which will get us
-     * to that representation given our available sources.
+     * Source identifier -> representation -> provider map. We usually are looking for
+     * a specific representation of a source.
      */
-    private final Multimap<Class<? extends SchemaSourceRepresentation>, SchemaTransformerRegistration> transformers =
-            HashMultimap.create();
+    @GuardedBy("this")
+    private final Map<SourceIdentifier, Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>>> sources = new HashMap<>();
 
     /*
-     * Source identifier -> representation -> provider map. We usually are looking for
-     * a specific representation a source.
+     * Schema source listeners.
      */
-    private final Map<SourceIdentifier, Multimap<Class<?>, AbstractSchemaSourceRegistration>> sources = new HashMap<>();
+    @GuardedBy("this")
+    private final Collection<SchemaListenerRegistration> listeners = new ArrayList<>();
 
+    private static final <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
+        final AbstractSchemaSourceRegistration<?> reg = it.next();
 
-    private static final <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration> it) {
-        if (!it.hasNext()) {
-            return Futures.immediateFuture(Optional.<T>absent());
-        }
-
-        return Futures.transform(((SchemaSourceProvider<T>)it.next().getProvider()).getSource(id), new AsyncFunction<Optional<T>, Optional<T>>() {
+        @SuppressWarnings("unchecked")
+        final CheckedFuture<? extends T, SchemaSourceException> f = ((SchemaSourceProvider<T>)reg.getProvider()).getSource(id);
+        return Futures.makeChecked(Futures.withFallback(f, new FutureFallback<T>() {
             @Override
-            public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
-                if (input.isPresent()) {
-                    return Futures.immediateFuture(input);
-                } else {
+            public ListenableFuture<T> create(final Throwable t) throws SchemaSourceException {
+                LOG.debug("Failed to acquire source from {}", reg, t);
+
+                if (it.hasNext()) {
                     return fetchSource(id, it);
                 }
-            }
-        });
-    }
-
-    private <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> transformSchemaSource(final SourceIdentifier id, final Class<T> representation) {
-        final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
-        if (srcs.isEmpty()) {
-            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
-                    String.format("No providers producing a representation of %s registered", id)));
-        }
-
-        final Collection<SchemaTransformerRegistration> ts = transformers.get(representation);
-        if (ts.isEmpty()) {
-            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
-                    String.format("No transformers producing representation %s registered", representation)));
-        }
 
-        // Build up the candidate list
-        final List<SchemaTransformerRegistration> candidates = new ArrayList<>();
-        for (SchemaTransformerRegistration tr : ts) {
-            final SchemaSourceTransformer<?, ?> t = tr.getInstance();
-            final Class<?> i = t.getInputRepresentation();
-            if (srcs.containsKey(i)) {
-                candidates.add(tr);
-            } else {
-                LOG.debug("Provider for {} in {} not found, skipping transfomer {}", id, i, t);
+                throw new MissingSchemaSourceException("All available providers exhausted");
             }
-        }
-
-        if (candidates.isEmpty()) {
-            return Futures.immediateFailedFuture(new SchemaSourceTransformationException(
-                    String.format("No matching source/transformer pair for source %s representation %s found", id, representation)));
-        }
-
-        Collections.sort(candidates, TRANSFORMER_COST_COMPARATOR);
-        // return transform(candidates.iterator(), id);
-        return null;
+        }), FETCH_MAPPER);
     }
 
-    /**
-     * Obtain a SchemaSource is selected representation
-     */
-    protected <T extends SchemaSourceRepresentation> ListenableFuture<Optional<T>> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
-        final Multimap<Class<?>, AbstractSchemaSourceRegistration> srcs = sources.get(id);
+    @Override
+    public <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
+        final Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> srcs = sources.get(id);
         if (srcs == null) {
-            LOG.debug("No providers registered for source {}", id);
-            return Futures.immediateFuture(Optional.<T>absent());
+            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("No providers registered for source" + id));
         }
 
-        final Collection<AbstractSchemaSourceRegistration> candidates = srcs.get(representation);
-        return Futures.transform(AbstractSchemaRepository.<T>fetchSource(id, candidates.iterator()), new AsyncFunction<Optional<T>, Optional<T>>() {
-            @Override
-            public ListenableFuture<Optional<T>> apply(final Optional<T> input) throws Exception {
-                if (input.isPresent()) {
-                    return Futures.immediateFuture(input);
-                }
-
-                return transformSchemaSource(id, representation);
-            }
-        });
-    }
+        final Iterator<AbstractSchemaSourceRegistration<?>> regs = srcs.get(representation).iterator();
+        if (!regs.hasNext()) {
+            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(
+                    new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available"));
+        }
 
-    @Override
-    public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) {
-        // TODO Auto-generated method stub
-        return null;
+        return fetchSource(id, regs);
     }
 
-    private void addSource(final SourceIdentifier id, final Class<?> rep, final AbstractSchemaSourceRegistration reg) {
-        Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+    private synchronized <T extends SchemaSourceRepresentation> void addSource(final PotentialSchemaSource<T> source, final AbstractSchemaSourceRegistration<T> reg) {
+        Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
         if (m == null) {
             m = HashMultimap.create();
-            sources.put(id, m);
+            sources.put(source.getSourceIdentifier(), m);
         }
 
-        m.put(rep, reg);
+        m.put(source.getRepresentation(), reg);
+
+        final Collection<PotentialSchemaSource<?>> reps = Collections.<PotentialSchemaSource<?>>singleton(source);
+        for (SchemaListenerRegistration l : listeners) {
+            l.getInstance().schemaSourceRegistered(reps);
+        }
     }
 
-    private void removeSource(final SourceIdentifier id, final Class<?> rep, final SchemaSourceRegistration reg) {
-        final Multimap<Class<?>, AbstractSchemaSourceRegistration> m = sources.get(id);
+    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(rep, reg);
+            m.remove(source.getRepresentation(), reg);
+
+            for (SchemaListenerRegistration l : listeners) {
+                l.getInstance().schemaSourceUnregistered(source);
+            }
+
             if (m.isEmpty()) {
                 sources.remove(m);
             }
@@ -163,29 +130,40 @@ public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceR
     }
 
     @Override
-    public <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
-            final SourceIdentifier identifier, final SchemaSourceProvider<? super T> provider, final Class<T> representation) {
-        final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(identifier, provider) {
+    public <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(final SchemaSourceProvider<? super T> provider, final PotentialSchemaSource<T> source) {
+        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(provider, source) {
             @Override
             protected void removeRegistration() {
-                removeSource(identifier, representation, this);
+                removeSource(source, this);
             }
         };
 
-        addSource(identifier, representation, ret);
+        addSource(source, ret);
         return ret;
     }
 
     @Override
-    public SchemaTransformerRegistration registerSchemaSourceTransformer(final SchemaSourceTransformer<?, ?> transformer) {
-        final SchemaTransformerRegistration ret = new AbstractSchemaTransformerRegistration(transformer) {
+    public SchemaListenerRegistration registerSchemaSourceListener(final SchemaSourceListener listener) {
+        final SchemaListenerRegistration ret = new AbstractSchemaListenerRegistration(listener) {
             @Override
             protected void removeRegistration() {
-                transformers.remove(transformer.getOutputRepresentation(), this);
+                listeners.remove(this);
             }
         };
 
-        transformers.put(transformer.getOutputRepresentation(), ret);
+        synchronized (this) {
+            final Collection<PotentialSchemaSource<?>> col = new ArrayList<>();
+            for (Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m : sources.values()) {
+                for (AbstractSchemaSourceRegistration<?> r : m.values()) {
+                    col.add(r.getInstance());
+                }
+            }
+
+            // Notify first, so translator-type listeners, who react by registering a source
+            // do not cause infinite loop.
+            listener.schemaSourceRegistered(col);
+            listeners.add(ret);
+        }
         return ret;
     }
 }
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java
new file mode 100644 (file)
index 0000000..498f403
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+/**
+ * Abstract base class for cache-type SchemaSourceListeners. It needs to be
+ * registered with a {@link SchemaSourceRegistry}, where it gets notifications
+ * from. It performs filtering and {@link #offer(SchemaSourceRepresentation)}s
+ * conforming sources to the subclass.
+ *
+ * @param <T> Cached schema source type.
+ */
+public abstract class AbstractSchemaSourceCache<T extends SchemaSourceRepresentation> implements SchemaSourceListener, SchemaSourceProvider<T> {
+    private final SchemaSourceRegistry consumer;
+    private final Class<T> representation;
+    private final Costs cost;
+
+    protected AbstractSchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final Costs cost) {
+        this.consumer = Preconditions.checkNotNull(consumer);
+        this.representation = Preconditions.checkNotNull(representation);
+        this.cost = Preconditions.checkNotNull(cost);
+    }
+
+    /**
+     * Offer a schema source in requested representation for caching. Subclasses
+     * need to implement this method to store the schema source. Once they have
+     * determined to cache the source, they should call {@link #register(SourceIdentifier)}.
+     *
+     * @param source schema source
+     */
+    protected abstract void offer(T source);
+
+    /**
+     * Register the presence of a cached schema source with the consumer. Subclasses
+     * need to call this method once they have cached a schema source representation,
+     * or when they have determined they have a schema source is available -- like
+     * when a persistent cache reads its cache index.
+     *
+     * @param sourceIdentifier Source identifier
+     * @return schema source registration, which the subclass needs to
+     *         {@link SchemaSourceRegistration#close() once it expunges the source
+     *         from the cache.
+     */
+    protected final SchemaSourceRegistration<T> register(final SourceIdentifier sourceIdentifier) {
+        final PotentialSchemaSource<T> src = PotentialSchemaSource.create(sourceIdentifier, representation, cost.getValue());
+        return consumer.registerSchemaSource(this, src);
+    }
+
+    @Override
+    public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        if (representation.isAssignableFrom(source.getType())) {
+            @SuppressWarnings("unchecked")
+            final T src = (T)source;
+            offer(src);
+        }
+    }
+
+    @Override
+    public final void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        // Not interesting
+    }
+
+    @Override
+    public final void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        // Not interesting
+    }
+}
index 9ed0afefc727eb1771d8055a7a661433cafd6128..241cc15996426321cf2f4051cd5a8c8a0f1e0444 100644 (file)
@@ -10,19 +10,20 @@ package org.opendaylight.yangtools.yang.model.repo.util;
 import com.google.common.base.Preconditions;
 
 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+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 extends AbstractObjectRegistration<SourceIdentifier> implements SchemaSourceRegistration {
+public abstract class AbstractSchemaSourceRegistration<T extends SchemaSourceRepresentation> extends AbstractObjectRegistration<PotentialSchemaSource<T>> implements SchemaSourceRegistration<T> {
     private final SchemaSourceProvider<?> provider;
 
-    protected AbstractSchemaSourceRegistration(final SourceIdentifier identifier, final SchemaSourceProvider<?> provider) {
-        super(identifier);
+    protected AbstractSchemaSourceRegistration(final SchemaSourceProvider<?> provider, final PotentialSchemaSource<T> source) {
+        super(source);
         this.provider = Preconditions.checkNotNull(provider);
     }
 
-    protected SchemaSourceProvider<?> getProvider() {
+    protected final SchemaSourceProvider<?> getProvider() {
         return provider;
     }
 }
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java
deleted file mode 100644 (file)
index d264ae0..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/eplv10.html
- */
-package org.opendaylight.yangtools.yang.model.repo.util;
-
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration;
-
-public abstract class AbstractSchemaTransformerRegistration extends AbstractObjectRegistration<SchemaSourceTransformer<?, ?>> implements SchemaTransformerRegistration {
-    protected AbstractSchemaTransformerRegistration(
-            final SchemaSourceTransformer<?, ?> transformer) {
-        super(transformer);
-    }
-}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java
new file mode 100644 (file)
index 0000000..ac820d2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T> {
+    private static final class CacheEntry<T extends SchemaSourceRepresentation> {
+        private final SchemaSourceRegistration<T> reg;
+        private final T source;
+
+        public CacheEntry(final T source, final SchemaSourceRegistration<T> reg) {
+            this.source = Preconditions.checkNotNull(source);
+            this.reg = Preconditions.checkNotNull(reg);
+        }
+    }
+
+    private static final RemovalListener<SourceIdentifier, CacheEntry<?>> LISTENER = new RemovalListener<SourceIdentifier, CacheEntry<?>>() {
+        @Override
+        public void onRemoval(final RemovalNotification<SourceIdentifier, CacheEntry<?>> notification) {
+            notification.getValue().reg.close();
+        }
+    };
+
+    private final Cache<SourceIdentifier, CacheEntry<T>> cache;
+
+    protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final int maxSize) {
+        super(consumer, representation, Costs.IMMEDIATE);
+        cache = CacheBuilder.newBuilder().softValues().maximumSize(maxSize).removalListener(LISTENER).build();
+    }
+
+    @Override
+    public CheckedFuture<? extends T, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final CacheEntry<T> present = cache.getIfPresent(sourceIdentifier);
+        if (present != null) {
+            return Futures.immediateCheckedFuture(present.source);
+        }
+
+        return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found"));
+    }
+
+    @Override
+    protected void offer(final T source) {
+        final CacheEntry<T> present = cache.getIfPresent(source.getIdentifier());
+        if (present == null) {
+            final SchemaSourceRegistration<T> reg = register(source.getIdentifier());
+            cache.put(source.getIdentifier(), new CacheEntry<T>(source, reg));
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java
new file mode 100644 (file)
index 0000000..3cd59b2
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+
+final class RefcountedRegistration {
+    private final SchemaSourceRegistration<?> reg;
+    private int refcount = 1;
+
+    RefcountedRegistration(final SchemaSourceRegistration<?> reg) {
+        this.reg = Preconditions.checkNotNull(reg);
+    }
+
+    public void incRef() {
+        refcount++;
+    }
+
+    public boolean decRef() {
+        Preconditions.checkState(refcount > 0, "Refcount underflow: %s", refcount);
+
+        if (0 == --refcount) {
+            reg.close();
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java
new file mode 100644 (file)
index 0000000..7b58006
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+public class SchemaSourceTransformer<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation> implements SchemaSourceListener, SchemaSourceProvider<D> {
+    private static final ExceptionMapper<SchemaSourceException> MAPPER = ReflectiveExceptionMapper.create("Source transformation", SchemaSourceException.class);
+
+    public static interface Transformation<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation> extends AsyncFunction<S, D> {
+        @Override
+        CheckedFuture<D, SchemaSourceException> apply(final S input) throws Exception;
+    }
+
+    private final Map<PotentialSchemaSource<?>, RefcountedRegistration> sources = new HashMap<>();
+    private final SchemaSourceRegistry consumer;
+    private final SchemaRepository provider;
+    private final AsyncFunction<S, D> function;
+    private final Class<S> srcClass;
+    private final Class<D> dstClass;
+
+    public SchemaSourceTransformer(final SchemaRepository provider, final Class<S> srcClass,
+            final SchemaSourceRegistry consumer, final Class<D> dstClass, final AsyncFunction<S, D> function) {
+        this.provider = Preconditions.checkNotNull(provider);
+        this.consumer = Preconditions.checkNotNull(consumer);
+        this.function = Preconditions.checkNotNull(function);
+        this.srcClass = Preconditions.checkNotNull(srcClass);
+        this.dstClass = Preconditions.checkNotNull(dstClass);
+    }
+
+    @Override
+    public CheckedFuture<D, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final CheckedFuture<S, SchemaSourceException> f = provider.getSchemaSource(sourceIdentifier, srcClass);
+        return Futures.makeChecked(Futures.transform(f, function), MAPPER);
+    }
+
+    @Override
+    public final void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        // Not interesting
+    }
+
+    @Override
+    public final void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        for (PotentialSchemaSource<?> src : sources) {
+            final Class<?> rep = src.getRepresentation();
+            if (srcClass.isAssignableFrom(rep) && dstClass != rep) {
+                registerSource(src);
+            }
+        }
+    }
+
+    @Override
+    public final void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        final Class<?> rep = source.getRepresentation();
+        if (srcClass.isAssignableFrom(rep) && dstClass != rep) {
+            unregisterSource(source);
+        }
+    }
+
+    private void registerSource(final PotentialSchemaSource<?> src) {
+        RefcountedRegistration reg = sources.get(src);
+        if (reg != null) {
+            reg.incRef();
+            return;
+        }
+
+        final PotentialSchemaSource<D> newSrc = PotentialSchemaSource.create(src.getSourceIdentifier(), dstClass,
+                src.getCost() + PotentialSchemaSource.Costs.COMPUTATION.getValue());
+
+        final SchemaSourceRegistration<D> r = consumer.registerSchemaSource(this, newSrc);
+        sources.put(src, new RefcountedRegistration(r));
+    }
+
+    private void unregisterSource(final PotentialSchemaSource<?> src) {
+        final RefcountedRegistration reg = sources.get(src);
+        if (reg != null && reg.decRef()) {
+            sources.remove(src);
+        }
+    }
+}
index 002746fa571d8b9616e4ca78ddfd406bcfd005ef..0e40bd2964472c4a354f3d99e61b5c9893043787 100644 (file)
@@ -191,7 +191,7 @@ public final class BaseTypes {
         } else if ("empty".equals(typeName)) {
             return EmptyType.getInstance();
         } else if ("instance-identifier".equals(typeName)) {
-            return InstanceIdentifier.getInstance();
+            return InstanceIdentifierType.getInstance();
         }
         return null;
     }
similarity index 87%
rename from yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifier.java
rename to yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifierType.java
index 138b6a50a9b3e045f39b3c2c9a22e3e637a62d8a..02289efb0531f8b25931c5f513cec7379738baa4 100644 (file)
@@ -29,7 +29,7 @@ import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefi
  * @see InstanceIdentifierTypeDefinition
  *
  */
-public final class InstanceIdentifier implements InstanceIdentifierTypeDefinition, Immutable {
+public final class InstanceIdentifierType implements InstanceIdentifierTypeDefinition, Immutable {
 
     private static final QName NAME = BaseTypes.INSTANCE_IDENTIFIER_QNAME;
     private static final SchemaPath PATH = SchemaPath.create(true, NAME);
@@ -40,8 +40,8 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
     private static final String UNITS = "";
     private final Boolean requireInstance;
 
-    private static final InstanceIdentifier INSTANCE_WITH_REQUIRED_TRUE = new InstanceIdentifier(true);
-    private static final InstanceIdentifier INSTANCE_WITH_REQUIRED_FALSE = new InstanceIdentifier(false);
+    private static final InstanceIdentifierType INSTANCE_WITH_REQUIRED_TRUE = new InstanceIdentifierType(true);
+    private static final InstanceIdentifierType INSTANCE_WITH_REQUIRED_FALSE = new InstanceIdentifierType(false);
 
     /**
      * Constructs new instance identifier.
@@ -50,7 +50,7 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
      * @deprecated Use {@link #getInstance()} for default one, since Instance Identifier does not have xpath.
      */
     @Deprecated
-    public InstanceIdentifier(final RevisionAwareXPath xpath) {
+    public InstanceIdentifierType(final RevisionAwareXPath xpath) {
         requireInstance = true;
     }
 
@@ -62,19 +62,19 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
      * @deprecated Use {@link #create(boolean)}, since Instance Identifier does not have xpath.
      */
     @Deprecated
-    public InstanceIdentifier(final RevisionAwareXPath xpath, final boolean requireInstance) {
+    public InstanceIdentifierType(final RevisionAwareXPath xpath, final boolean requireInstance) {
         this.requireInstance = requireInstance;
     }
 
-    private InstanceIdentifier(final boolean requiredInstance) {
+    private InstanceIdentifierType(final boolean requiredInstance) {
         this.requireInstance = requiredInstance;
     }
 
-    public static InstanceIdentifier getInstance() {
+    public static InstanceIdentifierType getInstance() {
         return INSTANCE_WITH_REQUIRED_TRUE;
     }
 
-    public static InstanceIdentifier create(final boolean requireInstance) {
+    public static InstanceIdentifierType create(final boolean requireInstance) {
         return requireInstance ? INSTANCE_WITH_REQUIRED_TRUE : INSTANCE_WITH_REQUIRED_FALSE;
     }
 
@@ -216,7 +216,7 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
         if (getClass() != obj.getClass()) {
             return false;
         }
-        InstanceIdentifier other = (InstanceIdentifier) obj;
+        InstanceIdentifierType other = (InstanceIdentifierType) obj;
         return requireInstance.equals(other.requireInstance);
     }
 
index ec30f794b8598e88c4c93e605db3ac87761daafb..7f263193f3d3ea58995dc3ab90529009b41a7d95 100644 (file)
@@ -46,7 +46,7 @@
  *       <dt>uint64</dt>
  *       <dd>64-bit unsigned integer -{@link org.opendaylight.yangtools.yang.model.util.Int64}</dd>
  *       <dt>instance-identifier</dt>
- *       <dd>References a data tree node - {@link org.opendaylight.yangtools.yang.model.util.InstanceIdentifier}</dd>
+ *       <dd>References a data tree node - {@link org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType}</dd>
  *       <dt>string</dt>
  *       <dd>{@link org.opendaylight.yangtools.yang.model.util.StringType}</dd>
  *     </dl>
index 542506045d031de4381010e849d29f9aef56b614..3ba54e404f44680010e47722baf848e4b993f590 100644 (file)
@@ -105,7 +105,7 @@ import org.opendaylight.yangtools.yang.model.util.BitsType;
 import org.opendaylight.yangtools.yang.model.util.Decimal64;
 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
-import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
 import org.opendaylight.yangtools.yang.model.util.Int16;
 import org.opendaylight.yangtools.yang.model.util.Int32;
 import org.opendaylight.yangtools.yang.model.util.Int64;
@@ -1210,7 +1210,7 @@ public final class ParserListenerUtils {
             constraints.addLengths(binaryType.getLengthConstraints());
             baseType = binaryType;
         } else if ("instance-identifier".equals(typeName)) {
-            return InstanceIdentifier.create(isRequireInstance(typeBody));
+            return InstanceIdentifierType.create(isRequireInstance(typeBody));
         }
 
         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
index 50354f5ed8956fb29d03520f7affdfdeee54c2d7..45bedd8c3a9b7da64a762d74a141549adc13065a 100644 (file)
@@ -26,6 +26,7 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.st
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
+
 import java.net.URI;
 import java.text.DateFormat;
 import java.text.ParseException;
@@ -33,7 +34,9 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+
 import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext;
@@ -96,6 +99,7 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuil
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 public final class YangParserListenerImpl extends YangParserBaseListener {
     private static final Logger LOG = LoggerFactory.getLogger(YangParserListenerImpl.class);
     private static final Splitter COLON_SPLITTER = Splitter.on(':');
@@ -113,6 +117,24 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
         this.sourcePath = sourcePath;
     }
 
+    /**
+     * Create a new instance.
+     *
+     * FIXME: the resulting type needs to be extracted, such that we can reuse
+     *        the "BaseListener" aspect, which need not be exposed to the user.
+     *        Maybe factor out a base class into repo.spi?
+     *
+     * @param sourcePath
+     * @param walker
+     * @param tree
+     * @return
+     */
+    public static YangParserListenerImpl create(final String sourcePath, final ParseTreeWalker walker, final ParseTree tree) {
+        final YangParserListenerImpl ret = new YangParserListenerImpl(sourcePath);
+        walker.walk(ret, tree);
+        return ret;
+    }
+
     @Override
     public void enterModule_stmt(final YangParser.Module_stmtContext ctx) {
         moduleName = stringFromNode(ctx);
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java
new file mode 100644 (file)
index 0000000..8dee836
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.repo;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public abstract class AbstractURLRegistration extends AbstractObjectRegistration<YangTextSchemaSource> implements URLRegistration{
+    protected AbstractURLRegistration(final YangTextSchemaSource text) {
+        super(text);
+    }
+}
\ No newline at end of file
index 45ad366285af4c7f5244ebed4ff22c6b3429e336..5bc6616c869013f2d13cbb28406ba8df14922dba 100644 (file)
@@ -71,6 +71,8 @@ final class DependencyResolver {
         return rev == null && findWildcard(haystack, mi.getModuleName()) != null;
     }
 
+
+
     public static final DependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
         final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
         final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java
new file mode 100644 (file)
index 0000000..5873856
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.repo;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class SharedSchemaContextFactory implements SchemaContextFactory {
+    private static final ExceptionMapper<SchemaResolutionException> MAPPER = ReflectiveExceptionMapper.create("resolve sources", SchemaResolutionException.class);
+    private static final Logger LOG = LoggerFactory.getLogger(SharedSchemaContextFactory.class);
+
+    private final Function<SourceIdentifier, ListenableFuture<ASTSchemaSource>> requestSources = new Function<SourceIdentifier, ListenableFuture<ASTSchemaSource>>() {
+        @Override
+        public ListenableFuture<ASTSchemaSource> apply(final SourceIdentifier input) {
+            return repository.getSchemaSource(input, ASTSchemaSource.class);
+        }
+    };
+    private final Cache<Collection<SourceIdentifier>, SchemaContext> cache = CacheBuilder.newBuilder().softValues().build();
+
+    private final AsyncFunction<List<ASTSchemaSource>, SchemaContext> assembleSources = new AsyncFunction<List<ASTSchemaSource>, SchemaContext>() {
+        @Override
+        public ListenableFuture<SchemaContext> apply(final List<ASTSchemaSource> sources) throws SchemaResolutionException {
+            final Map<SourceIdentifier, ASTSchemaSource> srcs =
+                    Maps.uniqueIndex(sources, ASTSchemaSource.GET_IDENTIFIER);
+            final Map<SourceIdentifier, YangModelDependencyInfo> deps =
+                    Maps.transformValues(srcs, ASTSchemaSource.GET_DEPINFO);
+
+            LOG.debug("Resolving dependency reactor {}", deps);
+
+            final DependencyResolver res = DependencyResolver.create(deps);
+            if (!res.getUnresolvedSources().isEmpty()) {
+                LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+
+                // FIXME: push into DependencyResolver
+
+                throw new SchemaResolutionException("Failed to resolve required models",
+                        res.getResolvedSources(), res.getUnsatisfiedImports());
+            }
+
+            final Map<SourceIdentifier, ParserRuleContext> asts =
+                    Maps.transformValues(srcs, ASTSchemaSource.GET_AST);
+
+            final ParseTreeWalker walker = new ParseTreeWalker();
+            final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
+
+            for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
+                ModuleBuilder moduleBuilder =
+                        YangParserListenerImpl.create(entry.getKey().getName(), walker, entry.getValue()).getModuleBuilder();
+
+                moduleBuilder.setSource(srcs.get(entry.getKey()).getYangText());
+                sourceToBuilder.put(entry.getKey(), moduleBuilder);
+            }
+            LOG.debug("Modules ready for integration");
+
+            final YangParserImpl parser = YangParserImpl.getInstance();
+            final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
+            LOG.debug("Integrated cross-references modules");
+            return Futures.immediateCheckedFuture(parser.assembleContext(modules));
+        }
+    };
+
+    private final SharedSchemaRepository repository;
+    // FIXME: ignored right now
+    private final SchemaSourceFilter filter;
+
+    public SharedSchemaContextFactory(final SharedSchemaRepository repository, final SchemaSourceFilter filter) {
+        this.repository = Preconditions.checkNotNull(repository);
+        this.filter = Preconditions.checkNotNull(filter);
+    }
+
+    @Override
+    public CheckedFuture<SchemaContext, SchemaResolutionException> createSchemaContext(final Collection<SourceIdentifier> requiredSources) {
+        final SchemaContext existing = cache.getIfPresent(requiredSources);
+        if (existing != null) {
+            LOG.debug("Returning cached context {}", existing);
+            return Futures.immediateCheckedFuture(existing);
+        }
+
+        // Request all sources be loaded
+        final ListenableFuture<List<ASTSchemaSource>> sf = Futures.allAsList(Collections2.transform(requiredSources, requestSources));
+
+        // Assemble sources into a schemacontext
+        final ListenableFuture<SchemaContext> cf = Futures.transform(sf, assembleSources);
+
+        // Populate cache when successful
+        Futures.addCallback(cf, new FutureCallback<SchemaContext>() {
+            @Override
+            public void onSuccess(final SchemaContext result) {
+                cache.put(requiredSources, result);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.info("Failed to assemble sources", t);
+            }
+        });
+
+        return Futures.makeChecked(cf, MAPPER);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java
new file mode 100644 (file)
index 0000000..1ff9f32
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.repo;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.util.AbstractSchemaRepository;
+
+/**
+ * A {@link SchemaRepository} which allows sharing of {@link SchemaContext} as
+ * long as their specification is the same.
+ *
+ * Note: for current implementation, "same" means the same filter and the same
+ * set of {@link SourceIdentifier}s.
+ */
+@Beta
+public final class SharedSchemaRepository extends AbstractSchemaRepository implements Identifiable<String> {
+    private final LoadingCache<SchemaSourceFilter, SchemaContextFactory> cache =
+            CacheBuilder.newBuilder().softValues().build(new CacheLoader<SchemaSourceFilter, SchemaContextFactory>() {
+                @Override
+                public SchemaContextFactory load(final SchemaSourceFilter key) {
+                    return new SharedSchemaContextFactory(SharedSchemaRepository.this, key);
+                }
+            });
+    private final String id;
+
+    public SharedSchemaRepository(final String id) {
+        this.id = Preconditions.checkNotNull(id);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) {
+        return cache.getUnchecked(filter);
+    }
+
+    @Override
+    public String toString() {
+        return "SchemaRepository: " + id;
+    }
+}
similarity index 50%
rename from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaServiceListener.java
rename to yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java
index 6ad231521533268d84af6472ceb83d30909fe2bb..d49ecc9733a4fdc04c5bd4594d1cfb376fc079a6 100644 (file)
@@ -5,14 +5,12 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.yangtools.yang.model.api;
+package org.opendaylight.yangtools.yang.parser.repo;
 
-import java.util.EventListener;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 
-/**
- * @deprecated Please use {@link SchemaContextListener} instead.
- */
-@Deprecated
-public interface SchemaServiceListener extends EventListener {
-    void onGlobalContextUpdated(SchemaContext context);
-}
+public interface URLRegistration extends ObjectRegistration<YangTextSchemaSource> {
+    @Override
+    void close();
+}
\ No newline at end of file
index 8ee18e58a5a562d40bb65e0de26fef2b9ffebb58..47de6b1a540b5bd1b0a14014ce9a5364447977b3 100644 (file)
@@ -9,155 +9,78 @@ package org.opendaylight.yangtools.yang.parser.repo;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.common.base.Function;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Maps.EntryTransformer;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.tree.ParseTreeWalker;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
-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.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
-import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
-import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@ThreadSafe
-public class URLSchemaContextResolver {
+@Beta
+public class URLSchemaContextResolver implements SchemaSourceProvider<YangTextSchemaSource> {
     private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
-    private static final Function<ASTSchemaSource, YangModelDependencyInfo> EXTRACT_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
-        @Override
-        public YangModelDependencyInfo apply(final ASTSchemaSource input) {
-            return input.getDependencyInformation();
-        }
-    };
-    private static final EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo> SQUASH_DEPINFO =
-            new EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo>() {
-        @Override
-        public YangModelDependencyInfo transformEntry(final SourceIdentifier key, final Collection<YangModelDependencyInfo> value) {
-            // FIXME: validate that all the info objects are the same
-            return value.iterator().next();
-        }
-    };
-    private static final Function<ASTSchemaSource, ParserRuleContext> EXTRACT_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
-        @Override
-        public ParserRuleContext apply(final ASTSchemaSource input) {
-            return input.getAST();
-        }
-    };
-    private static final EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext> SQUASH_AST =
-            new EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext>() {
-        @Override
-        public ParserRuleContext transformEntry(final SourceIdentifier key, final Collection<ParserRuleContext> value) {
-            // FIXME: validate that all the info objects are the same
-            return value.iterator().next();
-        }
-    };
-
-    @GuardedBy("this")
-    private final Multimap<SourceIdentifier, ASTSchemaSource> resolvedRegs = ArrayListMultimap.create();
-    private final AtomicReference<Optional<SchemaContext>> currentSchemaContext = new AtomicReference<>(Optional.<SchemaContext>absent());
-    private final Queue<URLRegistration> outstandingRegs = new ConcurrentLinkedQueue<>();
-    private final TextToASTTransformer transformer;
-    @GuardedBy("this")
-    private Object version = new Object();
-    @GuardedBy("this")
-    private Object contextVersion = version;
-
-    private final class URLRegistration extends AbstractObjectRegistration<URL> {
-        @GuardedBy("this")
-        private CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future;
-        @GuardedBy("this")
-        private ASTSchemaSource result;
-
-        protected URLRegistration(final URL url, final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future) {
-            super(url);
-            this.future = Preconditions.checkNotNull(future);
-        }
-
-        private synchronized boolean setResult(final ASTSchemaSource result) {
-            if (future != null) {
-                this.result = result;
-                return true;
-            } else {
-                return false;
-            }
-        }
 
-        @Override
-        protected void removeRegistration() {
-            // Cancel the future, but it may already be completing
-            future.cancel(false);
-
-            synchronized (this) {
-                future = null;
-                outstandingRegs.remove(this);
-                if (result != null) {
-                    removeSchemaSource(result);
-                }
-            }
-        }
-    }
-
-    private URLSchemaContextResolver(final TextToASTTransformer transformer) {
-        this.transformer = Preconditions.checkNotNull(transformer);
+    private final Cache<SourceIdentifier, YangTextSchemaSource> sources = CacheBuilder.newBuilder().build();
+    private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+    private final AtomicReference<Optional<SchemaContext>> currentSchemaContext =
+            new AtomicReference<>(Optional.<SchemaContext>absent());
+    private final SchemaSourceRegistry registry;
+    private final SchemaRepository repository;
+    private volatile Object version = new Object();
+    private volatile Object contextVersion = version;
+
+    private URLSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
+        this.repository = Preconditions.checkNotNull(repository);
+        this.registry = Preconditions.checkNotNull(registry);
     }
 
     public static URLSchemaContextResolver create(final String name) {
-        final ThreadFactory f = new ThreadFactoryBuilder().setDaemon(true).setNameFormat(name + "yangparser-%d").build();
-        final ListeningExecutorService s = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(f));
-
-        return new URLSchemaContextResolver(TextToASTTransformer.create(s));
+        final SharedSchemaRepository sharedRepo = new SharedSchemaRepository(name);
+        return new URLSchemaContextResolver(sharedRepo, sharedRepo);
     }
 
     /**
      * Register a URL hosting a YANG Text file.
      *
      * @param url URL
+     * @throws YangSyntaxErrorException When the YANG file is syntactically invalid
+     * @throws IOException when the URL is not readable
+     * @throws SchemaSourceException When parsing encounters general error
      */
-    public ObjectRegistration<URL> registerSource(final URL url) {
+    public URLRegistration registerSource(final URL url) throws SchemaSourceException, IOException, YangSyntaxErrorException {
         checkArgument(url != null, "Supplied URL must not be null");
 
-        final SourceIdentifier id = SourceIdentifier.create(url.getFile().toString(), Optional.<String>absent());
-        final YangTextSchemaSource text = new YangTextSchemaSource(id) {
+        final SourceIdentifier guessedId = new SourceIdentifier(url.getFile(), Optional.<String>absent());
+        final YangTextSchemaSource text = new YangTextSchemaSource(guessedId) {
             @Override
             public InputStream openStream() throws IOException {
                 return url.openStream();
@@ -169,39 +92,27 @@ public class URLSchemaContextResolver {
             }
         };
 
-        final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> ast = transformer.transformSchemaSource(text);
-        final URLRegistration reg = new URLRegistration(url, ast);
-        outstandingRegs.add(reg);
+        final ASTSchemaSource ast = TextToASTTransformer.TRANSFORMATION.apply(text).checkedGet();
+        LOG.trace("Resolved URL {} to source {}", url, ast);
 
-        Futures.addCallback(ast, new FutureCallback<ASTSchemaSource>() {
-            @Override
-            public void onSuccess(final ASTSchemaSource result) {
-                LOG.trace("Resolved URL {} to source {}", url, result);
+        final SourceIdentifier resolvedId = ast.getIdentifier();
+        final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+                PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0));
 
-                outstandingRegs.remove(reg);
-                if (reg.setResult(result)) {
-                    addSchemaSource(result);
-                }
-            }
+        requiredSources.add(resolvedId);
+        LOG.trace("Added source {} to schema context requirements", resolvedId);
+        version = new Object();
 
+        return new AbstractURLRegistration(text) {
             @Override
-            public void onFailure(final Throwable t) {
-                LOG.warn("Failed to parse YANG text from {}, ignoring it", url, t);
-                outstandingRegs.remove(reg);
+            protected void removeRegistration() {
+                requiredSources.remove(resolvedId);
+                LOG.trace("Removed source {} from schema context requirements", resolvedId);
+                version = new Object();
+                reg.close();
+                sources.invalidate(resolvedId);
             }
-        });
-
-        return reg;
-    }
-
-    private synchronized void addSchemaSource(final ASTSchemaSource src) {
-        resolvedRegs.put(src.getIdentifier(), src);
-        version = new Object();
-    }
-
-    private synchronized void removeSchemaSource(final ASTSchemaSource src) {
-        resolvedRegs.put(src.getIdentifier(), src);
-        version = new Object();
+        };
     }
 
     /**
@@ -210,64 +121,57 @@ public class URLSchemaContextResolver {
      *         new schema context was successfully built.
      */
     public Optional<SchemaContext> getSchemaContext() {
-        while (true) {
-            Optional<SchemaContext> result;
-            final Multimap<SourceIdentifier, ASTSchemaSource> sources;
-            final Object v;
-            synchronized (this) {
-                result = currentSchemaContext.get();
-                if (version == contextVersion) {
-                    return result;
+        final SchemaContextFactory factory = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+        Optional<SchemaContext> sc;
+        Object v;
+        do {
+            // Spin get stable context version
+            Object cv;
+            do {
+                cv = contextVersion;
+                sc = currentSchemaContext.get();
+                if (version == cv) {
+                    return sc;
                 }
+            } while (cv != contextVersion);
 
-                sources = ImmutableMultimap.copyOf(resolvedRegs);
+            // Version has been updated
+            Collection<SourceIdentifier> sources;
+            do {
                 v = version;
-            }
-
-            if (!sources.isEmpty()) {
-                final Map<SourceIdentifier, YangModelDependencyInfo> deps =
-                        Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_DEPINFO).asMap(), SQUASH_DEPINFO);
-
-                LOG.debug("Resolving dependency reactor {}", deps);
-                final DependencyResolver res = DependencyResolver.create(deps);
-                if (!res.getUnresolvedSources().isEmpty()) {
-                    LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+                sources = ImmutableList.copyOf(requiredSources);
+            } while (v != version);
+
+            while (true) {
+                final CheckedFuture<SchemaContext, SchemaResolutionException> f = factory.createSchemaContext(sources);
+                try {
+                    sc = Optional.of(f.checkedGet());
+                    break;
+                } catch (SchemaResolutionException e) {
+                    LOG.info("Failed to fully assemble schema context for {}", sources, e);
+                    sources = e.getResolvedSources();
                 }
-
-                final Map<SourceIdentifier, ParserRuleContext> asts =
-                        Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_AST).asMap(), SQUASH_AST);
-
-                final ParseTreeWalker walker = new ParseTreeWalker();
-                final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
-
-                for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
-                    final YangParserListenerImpl yangModelParser = new YangParserListenerImpl(entry.getKey().getName());
-                    walker.walk(yangModelParser, entry.getValue());
-                    ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
-
-                    moduleBuilder.setSource(sources.get(entry.getKey()).iterator().next().getYangText());
-                    sourceToBuilder.put(entry.getKey(), moduleBuilder);
-                }
-                LOG.debug("Modules ready for integration");
-
-                final YangParserImpl parser = YangParserImpl.getInstance();
-                final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
-                LOG.debug("Integrated cross-references modules");
-
-                result = Optional.of(parser.assembleContext(modules));
-            } else {
-                result = Optional.absent();
             }
 
             synchronized (this) {
-                if (v == version) {
-                    currentSchemaContext.set(result);
-                    contextVersion = version;
-                    return result;
+                if (contextVersion == cv) {
+                    currentSchemaContext.set(sc);
+                    contextVersion = v;
                 }
-
-                LOG.debug("Context version {} expected {}, retry", version, v);
             }
+        } while (version == v);
+
+        return sc;
+    }
+
+    @Override
+    public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier);
+        if (ret == null) {
+            return Futures.<YangTextSchemaSource, SchemaSourceException>immediateFailedCheckedFuture(
+                    new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered"));
         }
+
+        return Futures.immediateCheckedFuture(ret);
     }
 }
index ab353a04ad54c7b221e3bebbc194362d93e6cc6e..5ff245962e11d94856e82c40834863959186fc2a 100644 (file)
@@ -6,6 +6,8 @@
  */
 package org.opendaylight.yangtools.yang.parser.util;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
@@ -27,7 +29,27 @@ import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
  * passes basic semantic validation and we were able to extract dependency
  * information.
  */
+@Beta
 public final class ASTSchemaSource implements SchemaSourceRepresentation {
+    public static final Function<ASTSchemaSource, SourceIdentifier> GET_IDENTIFIER = new Function<ASTSchemaSource, SourceIdentifier>() {
+        @Override
+        public SourceIdentifier apply(final ASTSchemaSource input) {
+            return input.getIdentifier();
+        }
+    };
+    public static final Function<ASTSchemaSource, YangModelDependencyInfo> GET_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
+        @Override
+        public YangModelDependencyInfo apply(final ASTSchemaSource input) {
+            return input.getDependencyInformation();
+        }
+    };
+    public static final Function<ASTSchemaSource, ParserRuleContext> GET_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
+        @Override
+        public ParserRuleContext apply(final ASTSchemaSource input) {
+            return input.getAST();
+        }
+    };
+
     private final YangModelDependencyInfo depInfo;
     private final ParserRuleContext tree;
     private final SourceIdentifier id;
index ce9d6a8ce9331ea2bf1d0149257cb17e46fc630b..211e6a21743a666065e5e0ebc27aebb5cbb8de87 100644 (file)
@@ -6,26 +6,24 @@
  */
 package org.opendaylight.yangtools.yang.parser.util;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Charsets;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.io.CharStreams;
 import com.google.common.io.InputSupplier;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListeningExecutorService;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.concurrent.Callable;
 
 import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
-import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 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.SchemaSourceTransformationException;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.SchemaSourceTransformer;
 import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.slf4j.Logger;
@@ -34,73 +32,43 @@ import org.slf4j.LoggerFactory;
 /**
  * A {@link SchemaSourceTransformer} which handles translation of models from
  * {@link YangTextSchemaSource} representation into {@link ASTSchemaSource}.
- *
- * While this class is currently used explicitly, its long-term purpose is to
- * be registered with a {@link org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry}
- * and be invoked on demand when the processing pipeline requests the
- * ASTSchemaSource representation.
  */
-public final class TextToASTTransformer implements SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
-    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
-    private static final Function<Exception, SchemaSourceTransformationException> MAPPER = new ExceptionMapper<SchemaSourceTransformationException>("Source transformation", SchemaSourceTransformationException.class) {
+@Beta
+public final class TextToASTTransformer extends SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
+    public static final class TextToASTTransformation implements Transformation<YangTextSchemaSource, ASTSchemaSource> {
         @Override
-        protected SchemaSourceTransformationException newWithCause(final String message, final Throwable cause) {
-            return new SchemaSourceTransformationException(message, cause);
-        }
-    };
-
-    private final ListeningExecutorService executor;
-
-    private TextToASTTransformer(final ListeningExecutorService executor) {
-        this.executor = Preconditions.checkNotNull(executor);
-    }
-
-    public static final TextToASTTransformer create(final ListeningExecutorService executor) {
-        return new TextToASTTransformer(executor);
-    }
+        public CheckedFuture<ASTSchemaSource, SchemaSourceException> apply(final YangTextSchemaSource input) throws IOException, YangSyntaxErrorException {
+            try (InputStream is = input.openStream()) {
+                final YangContext ctx = YangParserImpl.parseYangSource(is);
+                LOG.debug("Model {} parsed successfully", input);
 
-    @Override
-    public Class<YangTextSchemaSource> getInputRepresentation() {
-        return YangTextSchemaSource.class;
-    }
-
-    @Override
-    public Class<ASTSchemaSource> getOutputRepresentation() {
-        return ASTSchemaSource.class;
-    }
+                final ParseTreeWalker walker = new ParseTreeWalker();
+                final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
+                walker.walk(validator, ctx);
+                LOG.debug("Model {} validated successfully", input);
 
-    @Override
-    public CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> transformSchemaSource(final YangTextSchemaSource source) {
-        return Futures.makeChecked(executor.submit(new Callable<ASTSchemaSource>() {
-            @Override
-            public ASTSchemaSource call() throws IOException, YangSyntaxErrorException {
-                try (InputStream is = source.openStream()) {
-                    final YangContext ctx = YangParserImpl.parseYangSource(is);
-                    LOG.debug("Model {} parsed successfully", source);
+                // Backwards compatibility
+                final String text = CharStreams.toString(
+                        CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
+                            @Override
+                            public InputStream getInput() throws IOException {
+                                return input.openStream();
+                            }
+                        }, Charsets.UTF_8));
 
-                    final ParseTreeWalker walker = new ParseTreeWalker();
-                    final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
-                    walker.walk(validator, ctx);
-                    LOG.debug("Model {} validated successfully", source);
+                return Futures.immediateCheckedFuture(ASTSchemaSource.create(input.getIdentifier().getName(), ctx, text));
+            }
+        }
+    };
 
-                    // Backwards compatibility
-                    final String text = CharStreams.toString(
-                            CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
-                                @Override
-                                public InputStream getInput() throws IOException {
-                                    return source.openStream();
-                                }
-                            }, Charsets.UTF_8));
+    public static final TextToASTTransformation TRANSFORMATION = new TextToASTTransformation();
+    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
 
-                    return ASTSchemaSource.create(source.getIdentifier().getName(), ctx, text);
-                }
-            }
-        }), MAPPER);
+    private TextToASTTransformer(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        super(provider, YangTextSchemaSource.class, consumer, ASTSchemaSource.class, TRANSFORMATION);
     }
 
-    @Override
-    public int getCost() {
-        // We perform a direct translation, so the cost is 1.
-        return 1;
+    public static final TextToASTTransformer create(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        return new TextToASTTransformer(provider, consumer);
     }
 }
index 022cda45a59eae835f5989e2597f6e492bcbc2ad..53c335b874350c4f5e6b3e40d733d009a3698a41 100644 (file)
@@ -37,7 +37,7 @@ import org.opendaylight.yangtools.yang.model.util.BitsType;
 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
-import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
 import org.opendaylight.yangtools.yang.model.util.UnionType;
 
 public class TypesResolutionTest {
@@ -163,7 +163,7 @@ public class TypesResolutionTest {
     public void testInstanceIdentifier1() {
         Module tested = TestUtils.findModule(testedModules, "custom-types-test");
         LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName("inst-id-leaf1");
-        InstanceIdentifier leafType = (InstanceIdentifier) leaf.getType();
+        InstanceIdentifierType leafType = (InstanceIdentifierType) leaf.getType();
         assertFalse(leafType.requireInstance());
         assertEquals(1, leaf.getUnknownSchemaNodes().size());
     }
@@ -172,7 +172,7 @@ public class TypesResolutionTest {
     public void testInstanceIdentifier2() {
         Module tested = TestUtils.findModule(testedModules, "custom-types-test");
         LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName("inst-id-leaf2");
-        InstanceIdentifier leafType = (InstanceIdentifier) leaf.getType();
+        InstanceIdentifierType leafType = (InstanceIdentifierType) leaf.getType();
         assertTrue(leafType.requireInstance());
     }