Fixed deserialization of IdentityRefs in Restconf URI.
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / ControllerContext.xtend
index eca4bd257298b78706cbd10321e8484ea06cab87..930aa663bb4cc91b013d42dec40b5775921e95c2 100644 (file)
@@ -27,21 +27,26 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
 import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.yangtools.yang.model.api.SchemaNode
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
 
 import static com.google.common.base.Preconditions.*
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
+import org.slf4j.LoggerFactory
+import com.google.common.collect.FluentIterable
 
 class ControllerContext implements SchemaServiceListener {
-
+    val static LOG = LoggerFactory.getLogger(ControllerContext)
     val static ControllerContext INSTANCE = new ControllerContext
-
     val static NULL_VALUE = "null"
 
     var SchemaContext schemas;
 
     private val BiMap<URI, String> uriToModuleName = HashBiMap.create();
     private val Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
-    private val Map<QName,RpcDefinition> qnameToRpc = new ConcurrentHashMap();
-    
+    private val Map<QName, RpcDefinition> qnameToRpc = new ConcurrentHashMap();
 
     private new() {
         if (INSTANCE !== null) {
@@ -52,7 +57,7 @@ class ControllerContext implements SchemaServiceListener {
     static def getInstance() {
         return INSTANCE
     }
-    
+
     private def void checkPreconditions() {
         if (schemas === null) {
             throw new ResponseException(Response.Status.SERVICE_UNAVAILABLE, RestconfProvider::NOT_INITALIZED_MSG)
@@ -131,7 +136,7 @@ class ControllerContext implements SchemaServiceListener {
     private def dispatch CharSequence toRestconfIdentifier(PathArgument argument, DataSchemaNode node) {
         throw new IllegalArgumentException("Conversion of generic path argument is not supported");
     }
-    
+
     def findModuleByNamespace(URI namespace) {
         checkPreconditions
         var module = uriToModuleName.get(namespace)
@@ -286,9 +291,20 @@ class ControllerContext implements SchemaServiceListener {
     private def void addKeyValue(HashMap<QName, Object> map, DataSchemaNode node, String uriValue) {
         checkNotNull(uriValue);
         checkArgument(node instanceof LeafSchemaNode);
-        val decoded = URLDecoder.decode(uriValue);
+        val urlDecoded = URLDecoder.decode(uriValue);
+        val typedef = (node as LeafSchemaNode).type;
+        
+        var decoded = TypeDefinitionAwareCodec.from(typedef)?.deserialize(urlDecoded)
+        if(decoded == null) {
+            var baseType = typedef
+            while (baseType.baseType != null) {
+                baseType = baseType.baseType;
+            }
+            if(baseType instanceof IdentityrefTypeDefinition) {
+                decoded = toQName(urlDecoded)
+            }
+        }
         map.put(node.QName, decoded);
-
     }
 
     private def String toModuleName(String str) {
@@ -311,24 +327,57 @@ class ControllerContext implements SchemaServiceListener {
             return str;
         }
     }
-    
+
     private def QName toQName(String name) {
         val module = name.toModuleName;
         val node = name.toNodeName;
-        val namespace = moduleNameToUri.get(module);
-        return new QName(namespace,null,node);
+        val namespace = FluentIterable.from(schemas.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)]) //
+            .transform[QName.create(namespace,revision,it.name)].findFirst[module == localName]
+        ;
+        return QName.create(namespace,node);
     }
-    
+
     def getRpcDefinition(String name) {
         return qnameToRpc.get(name.toQName)
     }
 
     override onGlobalContextUpdated(SchemaContext context) {
         this.schemas = context;
-        for(operation : context.operations) {
-            val qname = new QName(operation.QName.namespace,null,operation.QName.localName);
-            qnameToRpc.put(qname,operation);
+        for (operation : context.operations) {
+            val qname = operation.QName;
+            qnameToRpc.put(qname, operation);
+        }
+    }
+
+    /**
+     * Resolve target type from leafref type.
+     * 
+     * According to RFC 6020 referenced element has to be leaf (chapter 9.9).
+     * Therefore if other element is referenced then null value is returned.
+     * 
+     * Currently only cases without path-predicate are supported.
+     * 
+     * @param leafRef
+     * @param schemaNode
+     *            data schema node which contains reference
+     * @return type if leaf is referenced and it is possible to find referenced
+     *         node in schema context. In other cases null value is returned
+     */
+    def LeafSchemaNode resolveTypeFromLeafref(LeafrefTypeDefinition leafRef, DataSchemaNode schemaNode) {
+        val xPath = leafRef.getPathStatement();
+        val module = SchemaContextUtil.findParentModule(schemas, schemaNode);
+
+        var SchemaNode foundSchemaNode
+        if (xPath.isAbsolute()) {
+            foundSchemaNode = SchemaContextUtil.findDataSchemaNode(schemas, module, xPath);
+        } else {
+            foundSchemaNode = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemas, module, schemaNode, xPath);
         }
+
+        if (foundSchemaNode instanceof LeafSchemaNode) {
+            return foundSchemaNode as LeafSchemaNode;
+        }
+
+        return null;
     }
-    
 }