be170c75b0db23c91e65f4ada96fcc10f919b11c
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.xtend
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.restconf.impl
9
10 import com.google.common.base.Preconditions
11 import java.net.URI
12 import java.text.ParseException
13 import java.text.SimpleDateFormat
14 import java.util.ArrayList
15 import java.util.HashMap
16 import java.util.List
17 import java.util.Set
18 import javax.ws.rs.core.Response
19 import org.opendaylight.controller.md.sal.common.api.TransactionStatus
20 import org.opendaylight.controller.sal.core.api.mount.MountInstance
21 import org.opendaylight.controller.sal.rest.api.RestconfService
22 import org.opendaylight.yangtools.yang.common.QName
23 import org.opendaylight.yangtools.yang.common.RpcResult
24 import org.opendaylight.yangtools.yang.data.api.CompositeNode
25 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
27 import org.opendaylight.yangtools.yang.data.api.Node
28 import org.opendaylight.yangtools.yang.data.impl.NodeFactory
29 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.Module
36 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext
38 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
39 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
40
41 import static javax.ws.rs.core.Response.Status.*
42 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
43 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
44 import org.opendaylight.yangtools.yang.model.util.EmptyType
45 import com.google.common.base.Splitter
46 import com.google.common.collect.Lists
47
48 class RestconfImpl implements RestconfService {
49
50     val static RestconfImpl INSTANCE = new RestconfImpl
51     val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
52     val static REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
53     val static RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19"
54     val static RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf"
55     val static RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"
56     val static RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf"
57     val static RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"
58     val static RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules"
59     val static RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module"
60     val static RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations"
61
62     @Property
63     BrokerFacade broker
64
65     @Property
66     extension ControllerContext controllerContext
67
68     private new() {
69         if (INSTANCE !== null) {
70             throw new IllegalStateException("Already instantiated");
71         }
72     }
73
74     static def getInstance() {
75         return INSTANCE
76     }
77
78     override getModules() {
79         val restconfModule = getRestconfModule()
80         val List<Node<?>> modulesAsData = new ArrayList
81         val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
82         for (module : allModules) {
83             modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
84         }
85         val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
86         val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
87         return new StructuredData(modulesNode, modulesSchemaNode, null)
88     }
89
90     override getModules(String identifier) {
91         var Set<Module> modules = null
92         var MountInstance mountPoint = null
93         if (identifier.contains(ControllerContext.MOUNT)) {
94             mountPoint = identifier.toMountPointIdentifier.mountPoint
95             modules = mountPoint.allModules
96         } else {
97             throw new ResponseException(BAD_REQUEST, "URI has bad format. If modules behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
98         }
99         val List<Node<?>> modulesAsData = new ArrayList
100         val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
101         for (module : modules) {
102             modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
103         }
104         val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
105         val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
106         return new StructuredData(modulesNode, modulesSchemaNode, mountPoint)
107     }
108
109     override getModule(String identifier) {
110         val moduleNameAndRevision = identifier.moduleNameAndRevision
111         var Module module = null
112         var MountInstance mountPoint = null
113         if (identifier.contains(ControllerContext.MOUNT)) {
114             mountPoint = identifier.toMountPointIdentifier.mountPoint
115             module = mountPoint.findModuleByNameAndRevision(moduleNameAndRevision)
116         } else {
117             module = findModuleByNameAndRevision(moduleNameAndRevision)
118         }
119         if (module === null) {
120             throw new ResponseException(BAD_REQUEST,
121                 "Module with name '" + moduleNameAndRevision.localName + "' and revision '" +
122                     moduleNameAndRevision.revision + "' was not found.")
123         }
124         val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
125         val moduleNode = module.toModuleCompositeNode(moduleSchemaNode)
126         return new StructuredData(moduleNode, moduleSchemaNode, mountPoint)
127     }
128
129     override getOperations() {
130         return operationsFromModulesToStructuredData(allModules,null)
131     }
132     
133     override getOperations(String identifier) {
134         var Set<Module> modules = null
135         var MountInstance mountPoint = null
136         if (identifier.contains(ControllerContext.MOUNT)) {
137             mountPoint = identifier.toMountPointIdentifier.mountPoint
138             modules = mountPoint.allModules
139         } else {
140             throw new ResponseException(BAD_REQUEST, "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
141         }
142         return operationsFromModulesToStructuredData(modules,mountPoint)
143     }
144     
145     private def StructuredData operationsFromModulesToStructuredData(Set<Module> modules,MountInstance mountPoint) {
146         val List<Node<?>> operationsAsData = new ArrayList
147         val operationsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)        
148         val fakeOperationsSchemaNode = new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, operationsSchemaNode.QName, operationsSchemaNode.path)
149         for (module : modules) {
150             for (rpc : module.rpcs) {
151                 operationsAsData.add(NodeFactory.createImmutableSimpleNode(rpc.QName, null, null))
152                 val fakeRpcSchemaNode = new LeafSchemaNodeBuilder(module.name, 0, rpc.QName, null)
153                 fakeRpcSchemaNode.setAugmenting(true)
154                 fakeRpcSchemaNode.setType(EmptyType.instance)
155                 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build)                
156             }
157         }
158         val operationsNode = NodeFactory.createImmutableCompositeNode(operationsSchemaNode.QName, null, operationsAsData)
159         return new StructuredData(operationsNode, fakeOperationsSchemaNode.build, mountPoint)        
160     }
161
162     private def Module getRestconfModule() {
163         val restconfModule = findModuleByNameAndRevision(
164             QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
165                 RESTCONF_MODULE_DRAFT02_NAME))
166         if (restconfModule === null) {
167             throw new ResponseException(INTERNAL_SERVER_ERROR, "Restconf module was not found.")
168         }
169         return restconfModule
170     }
171
172     private def QName getModuleNameAndRevision(String identifier) {
173         val indexOfMountPointFirstLetter = identifier.indexOf(ControllerContext.MOUNT)
174         var moduleNameAndRevision = "";
175         if (indexOfMountPointFirstLetter !== -1) { // module and revision is behind mount point string
176             moduleNameAndRevision = identifier.substring(indexOfMountPointFirstLetter + ControllerContext.MOUNT.length)
177         } else (
178             moduleNameAndRevision = identifier
179         )
180         val  pathArgs = Lists.newArrayList(Splitter.on("/").omitEmptyStrings.split(moduleNameAndRevision))
181         if (pathArgs.length < 2) {
182             throw new ResponseException(BAD_REQUEST,
183                 "URI has bad format. End of URI should be in format 'moduleName/yyyy-MM-dd'")
184         }
185         try {
186             val moduleName = pathArgs.head
187             val moduleRevision = REVISION_FORMAT.parse(pathArgs.get(1))
188             return QName.create(null, moduleRevision, moduleName)
189         } catch(ParseException e) {
190             throw new ResponseException(BAD_REQUEST, "URI has bad format. It should be 'moduleName/yyyy-MM-dd'")
191         }
192     }
193
194     private def CompositeNode toModuleCompositeNode(Module module, DataSchemaNode moduleSchemaNode) {
195         val List<Node<?>> moduleNodeValues = new ArrayList
196         val nameSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
197         moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, module.name))
198         val revisionSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("revision").head
199         moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.QName, null, REVISION_FORMAT.format(module.revision)))
200         val namespaceSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("namespace").head
201         moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.QName, null, module.namespace.toString))
202         val featureSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("feature").head
203         for (feature : module.features) {
204             moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.QName, null, feature.QName.localName))
205         }
206         return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.QName, null, moduleNodeValues)
207     }
208
209     private def DataSchemaNode getSchemaNode(Module restconfModule, String schemaNodeName) {
210         val restconfGrouping = restconfModule.groupings.filter[g|g.QName.localName == RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE].head
211         val restconfContainer = restconfGrouping.findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE).head
212         if (schemaNodeName == RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE) {
213             return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE).head
214         } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE) {
215             return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
216         } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE) {
217             val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
218             return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE).head
219         }
220         return null
221     }
222
223     override getRoot() {
224         return null;
225     }
226
227     override invokeRpc(String identifier, CompositeNode payload) {
228         return callRpc(identifier.rpcDefinition, payload)
229     }
230
231     override invokeRpc(String identifier, String noPayload) {
232         if (!noPayload.nullOrEmpty) {
233             throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
234         }
235         return callRpc(identifier.rpcDefinition, null)
236     }
237
238     private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
239         if (rpc === null) {
240             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
241         }
242         var CompositeNode rpcRequest;
243         if (payload === null) {
244             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
245         } else {
246             val value = normalizeNode(payload, rpc.input, null)
247             val List<Node<?>> input = new ArrayList
248             input.add(value)
249             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
250         }
251         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
252         if (!rpcResult.successful) {
253             throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
254         }
255         if (rpcResult.result === null) {
256             return null
257         }
258         return new StructuredData(rpcResult.result, rpc.output, null)
259     }
260
261     override readConfigurationData(String identifier) {
262         val iiWithData = identifier.toInstanceIdentifier
263         var CompositeNode data = null;
264         if (iiWithData.mountPoint !== null) {
265             data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
266         } else {
267             data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
268         }
269         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
270     }
271
272     override readOperationalData(String identifier) {
273         val iiWithData = identifier.toInstanceIdentifier
274         var CompositeNode data = null;
275         if (iiWithData.mountPoint !== null) {
276             data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
277         } else {
278             data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
279         }
280         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
281     }
282
283     override updateConfigurationData(String identifier, CompositeNode payload) {
284         val iiWithData = identifier.toInstanceIdentifier
285         val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
286         var RpcResult<TransactionStatus> status = null
287         if (iiWithData.mountPoint !== null) {
288             status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
289                 iiWithData.instanceIdentifier, value).get()
290         } else {
291             status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
292         }
293         switch status.result {
294             case TransactionStatus.COMMITED: Response.status(OK).build
295             default: Response.status(INTERNAL_SERVER_ERROR).build
296         }
297     }
298
299     override createConfigurationData(String identifier, CompositeNode payload) {
300         if (payload.namespace === null) {
301             throw new ResponseException(BAD_REQUEST,
302                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
303         }
304         var InstanceIdWithSchemaNode iiWithData;
305         var CompositeNode value;
306         if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
307             if (identifier.endsWithMountPoint) {
308                 throw new ResponseException(BAD_REQUEST,
309                     "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
310             }
311             val completIdentifier = identifier.addMountPointIdentifier
312             iiWithData = completIdentifier.toInstanceIdentifier
313             value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
314         } else {
315             val uncompleteInstIdWithData = identifier.toInstanceIdentifier
316             val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
317             val module = uncompleteInstIdWithData.mountPoint.findModule(payload)
318             if (module === null) {
319                 throw new ResponseException(BAD_REQUEST, "Module was not found for \"" + payload.namespace + "\"")
320             }
321             val schemaNode = parentSchema.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
322             value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
323             iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
324         }
325         var RpcResult<TransactionStatus> status = null
326         if (iiWithData.mountPoint !== null) {
327             status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
328                 iiWithData.instanceIdentifier, value)?.get();
329         } else {
330             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
331         }
332         if (status === null) {
333             return Response.status(ACCEPTED).build
334         }
335         switch status.result {
336             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
337             default: Response.status(INTERNAL_SERVER_ERROR).build
338         }
339     }
340
341     override createConfigurationData(CompositeNode payload) {
342         if (payload.namespace === null) {
343             throw new ResponseException(BAD_REQUEST,
344                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
345         }
346         val module = findModule(null, payload)
347         if (module === null) {
348             throw new ResponseException(BAD_REQUEST,
349                 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
350         }
351         val schemaNode = module.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
352         val value = normalizeNode(payload, schemaNode, null)
353         val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
354         var RpcResult<TransactionStatus> status = null
355         if (iiWithData.mountPoint !== null) {
356             status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
357                 iiWithData.instanceIdentifier, value)?.get();
358         } else {
359             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
360         }
361         if (status === null) {
362             return Response.status(ACCEPTED).build
363         }
364         switch status.result {
365             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
366             default: Response.status(INTERNAL_SERVER_ERROR).build
367         }
368     }
369
370     override deleteConfigurationData(String identifier) {
371         val iiWithData = identifier.toInstanceIdentifier
372         var RpcResult<TransactionStatus> status = null
373         if (iiWithData.mountPoint !== null) {
374             status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
375                 iiWithData.getInstanceIdentifier).get;
376         } else {
377             status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
378         }
379         switch status.result {
380             case TransactionStatus.COMMITED: Response.status(OK).build
381             default: Response.status(INTERNAL_SERVER_ERROR).build
382         }
383     }
384
385     private def dispatch URI namespace(CompositeNode data) {
386         return data.nodeType.namespace
387     }
388
389     private def dispatch URI namespace(CompositeNodeWrapper data) {
390         return data.namespace
391     }
392
393     private def dispatch String localName(CompositeNode data) {
394         return data.nodeType.localName
395     }
396
397     private def dispatch String localName(CompositeNodeWrapper data) {
398         return data.localName
399     }
400
401     private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
402         if (mountPoint !== null) {
403             return mountPoint.findModuleByNamespace(data.nodeType.namespace)
404         } else {
405             return findModuleByNamespace(data.nodeType.namespace)
406         }
407     }
408
409     private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
410         Preconditions.checkNotNull(data.namespace)
411         var Module module = null;
412         if (mountPoint !== null) {
413             module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
414             if (module === null) {
415                 module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
416             }
417         } else {
418             module = data.namespace.findModuleByNamespace // namespace from XML
419             if (module === null) {
420                 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
421             }
422         }
423         return module
424     }
425
426     private def dispatch getName(CompositeNode data) {
427         return data.nodeType.localName
428     }
429
430     private def dispatch getName(CompositeNodeWrapper data) {
431         return data.localName
432     }
433
434     private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
435         CompositeNode data, DataSchemaNode schemaOfData) {
436         val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
437         var InstanceIdentifierBuilder iiBuilder = null
438         if (iiOriginal === null) {
439             iiBuilder = InstanceIdentifier.builder
440         } else {
441             iiBuilder = InstanceIdentifier.builder(iiOriginal)
442         }
443
444         if (schemaOfData instanceof ListSchemaNode) {
445             iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
446         } else {
447             iiBuilder.node(schemaOfData.QName)
448         }
449         return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
450     }
451
452     private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
453         val keyValues = new HashMap<QName, Object>();
454         for (key : listNode.keyDefinition) {
455             val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
456             if (dataNodeKeyValueObject === null) {
457                 throw new ResponseException(BAD_REQUEST,
458                     "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
459                         key.localName + "\"")
460             }
461             keyValues.put(key, dataNodeKeyValueObject);
462         }
463         return keyValues
464     }
465
466     private def endsWithMountPoint(String identifier) {
467         return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
468     }
469
470     private def representsMountPointRootData(CompositeNode data) {
471         return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
472             data.localName == SchemaContext.NAME.localName)
473     }
474
475     private def addMountPointIdentifier(String identifier) {
476         if (identifier.endsWith("/")) {
477             return identifier + ControllerContext.MOUNT
478         }
479         return identifier + "/" + ControllerContext.MOUNT
480     }
481
482     private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
483         if (schema === null) {
484             throw new ResponseException(INTERNAL_SERVER_ERROR,
485                 "Data schema node was not found for " + node?.nodeType?.localName)
486         }
487         if (!(schema instanceof DataNodeContainer)) {
488             throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
489         }
490         if (node instanceof CompositeNodeWrapper) {
491             if ((node  as CompositeNodeWrapper).changeAllowed) {
492                 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
493             }
494             return (node as CompositeNodeWrapper).unwrap()
495         }
496         return node
497     }
498
499     private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
500         MountInstance mountPoint) {
501         if (schema === null) {
502             throw new ResponseException(BAD_REQUEST,
503                 "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
504         }
505
506         var QName currentAugment;
507         if (nodeBuilder.qname !== null) {
508             currentAugment = previousAugment
509         } else {
510             currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint)
511             if (nodeBuilder.qname === null) {
512                 throw new ResponseException(BAD_REQUEST,
513                     "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
514                         "\" should be \"" + schema.QName.namespace + "\".\n" +
515                         "If data is in JSON format then module name for \"" + nodeBuilder.localName +
516                          "\" should be corresponding to namespace \"" + schema.QName.namespace + "\".");
517             }
518         }
519
520         if (nodeBuilder instanceof CompositeNodeWrapper) {
521             val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
522             for (child : children) {
523                 val potentialSchemaNodes = (schema as DataNodeContainer).findInstanceDataChildrenByName(child.localName)
524                 if (potentialSchemaNodes.size > 1 && child.namespace === null) {
525                     val StringBuilder namespacesOfPotentialModules = new StringBuilder;
526                     for (potentialSchemaNode : potentialSchemaNodes) {
527                         namespacesOfPotentialModules.append("   ").append(potentialSchemaNode.QName.namespace.toString).append("\n")
528                     }
529                     throw new ResponseException(BAD_REQUEST,
530                         "Node \"" + child.localName + "\" is added as augment from more than one module. " 
531                         + "Therefore node must have namespace (XML format) or module name (JSON format)."
532                         + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
533                 }
534                 var rightNodeSchemaFound = false
535                 for (potentialSchemaNode : potentialSchemaNodes) {
536                     if (!rightNodeSchemaFound) {
537                         val potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment,
538                             mountPoint)
539                         if (child.qname !== null) {
540                             normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint)
541                             rightNodeSchemaFound = true
542                         }
543                     }
544                 }
545                 if (!rightNodeSchemaFound) {
546                     throw new ResponseException(BAD_REQUEST,
547                         "Schema node \"" + child.localName + "\" was not found in module.")
548                 }
549             }
550             if (schema instanceof ListSchemaNode) {
551                 val listKeys = (schema as ListSchemaNode).keyDefinition
552                 for (listKey : listKeys) {
553                     var foundKey = false
554                     for (child : children) {
555                         if (child.unwrap.nodeType.localName == listKey.localName) {
556                             foundKey = true;
557                         }
558                     }
559                     if (!foundKey) {
560                         throw new ResponseException(BAD_REQUEST,
561                             "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName +
562                                 "\"")
563                     }
564                 }
565             }
566         } else if (nodeBuilder instanceof SimpleNodeWrapper) {
567             val simpleNode = (nodeBuilder as SimpleNodeWrapper)
568             val value = simpleNode.value
569             var inputValue = value;
570
571             if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
572                 if (value instanceof String) {
573                     inputValue = new IdentityValuesDTO(nodeBuilder.namespace.toString, value as String, null)
574                 } // else value is already instance of IdentityValuesDTO
575             }
576             
577             val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.deserialize(inputValue);
578             simpleNode.setValue(outputValue)
579         } else if (nodeBuilder instanceof EmptyNodeWrapper) {
580             val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
581             if (schema instanceof LeafSchemaNode) {
582                 emptyNodeBuilder.setComposite(false);
583             } else if (schema instanceof ContainerSchemaNode) {
584
585                 // FIXME: Add presence check
586                 emptyNodeBuilder.setComposite(true);
587             }
588         }
589     }
590
591     private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
592         var baseType = node.type
593         while (baseType.baseType !== null) {
594             baseType = baseType.baseType;
595         }
596         baseType
597     }
598
599     private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
600         var TypeDefinition<?> baseType = node.type
601         while (baseType.baseType !== null) {
602             baseType = baseType.baseType;
603         }
604         baseType
605     }
606     
607     private def QName normalizeNodeName(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
608         MountInstance mountPoint) {
609         var validQName = schema.QName
610         var currentAugment = previousAugment;
611         if (schema.augmenting) {
612             currentAugment = schema.QName
613         } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
614             validQName = QName.create(currentAugment, schema.QName.localName);
615         }
616         var String moduleName = null;
617         if (mountPoint === null) {
618             moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
619         } else {
620             moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
621         }
622         if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
623             nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
624             nodeBuilder.qname = validQName
625         }
626         return currentAugment
627     }
628
629 }