dbd78e582a5bec446d405d146ec46f80190bba95
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / util / ParserUtils.xtend
1 /*
2  * Copyright (c) 2013 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.yangtools.yang.parser.util;
9
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Date;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.TreeMap;
17
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
20 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
21 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.Module;
23 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
24 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
25 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
26 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
28 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
29 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
30 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
31 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
32 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
33 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
34 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
36 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder.ChoiceNodeImpl;
37 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
38 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder.ChoiceCaseNodeImpl;
39 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder.ContainerSchemaNodeImpl;
40 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
41 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder.ListSchemaNodeImpl;
42 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
43 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder.NotificationDefinitionImpl;
44 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
45 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder
46
47 public final class ParserUtils {
48
49     private new() {
50     }
51
52     /**
53      * Create new SchemaPath from given path and qname.
54      *
55      * @param schemaPath
56      * @param qname
57      * @return new SchemaPath from given path and qname
58      */
59     public static def SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
60         val path = new ArrayList<QName>(schemaPath.getPath());
61         path.addAll(Arrays.asList(qname));
62         return new SchemaPath(path, schemaPath.isAbsolute());
63     }
64
65     /**
66      * Get module import referenced by given prefix.
67      *
68      * @param builder
69      *            module to search
70      * @param prefix
71      *            prefix associated with import
72      * @return ModuleImport based on given prefix
73      */
74     public static def ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
75         for (ModuleImport mi : builder.getModuleImports()) {
76             if (mi.getPrefix().equals(prefix)) {
77                 return mi;
78
79             }
80         }
81         return null;
82     }
83
84     /**
85      * Find dependent module based on given prefix
86      *
87      * @param modules
88      *            all available modules
89      * @param module
90      *            current module
91      * @param prefix
92      *            target module prefix
93      * @param line
94      *            current line in yang model
95      * @return module builder if found, null otherwise
96      */
97     public static def ModuleBuilder findModuleFromBuilders(Map<String, TreeMap<Date, ModuleBuilder>> modules,
98         ModuleBuilder module, String prefix, int line) {
99         var ModuleBuilder dependentModule = null;
100         var Date dependentModuleRevision = null;
101
102         if (prefix.equals(module.getPrefix())) {
103             dependentModule = module;
104         } else {
105             val ModuleImport dependentModuleImport = getModuleImport(module, prefix);
106             if (dependentModuleImport === null) {
107                 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
108             }
109             val String dependentModuleName = dependentModuleImport.getModuleName();
110             dependentModuleRevision = dependentModuleImport.getRevision();
111
112             val TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
113             if (moduleBuildersByRevision === null) {
114                 return null;
115             }
116             if (dependentModuleRevision === null) {
117                 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
118             } else {
119                 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
120             }
121         }
122         return dependentModule;
123     }
124
125     /**
126      * Find module from context based on prefix.
127      *
128      * @param context
129      *            schema context
130      * @param currentModule
131      *            current module
132      * @param prefix
133      *            current prefix used to reference dependent module
134      * @param line
135      *            current line in yang model
136      * @return module based on given prefix if found in context, null otherwise
137      */
138     public static def Module findModuleFromContext(SchemaContext context, ModuleBuilder currentModule,
139         String prefix, int line) {
140         if (context === null) {
141                 throw new YangParseException(currentModule.getName(), line, "Cannot find module with prefix '" + prefix + "'.");
142         }
143         val modulesByRevision = new TreeMap<Date, Module>();
144
145         val dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
146         if (dependentModuleImport === null) {
147             throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
148         }
149         val dependentModuleName = dependentModuleImport.getModuleName();
150         val dependentModuleRevision = dependentModuleImport.getRevision();
151
152         for (Module contextModule : context.getModules()) {
153             if (contextModule.getName().equals(dependentModuleName)) {
154                 var revision = contextModule.getRevision();
155                 if (revision === null) {
156                     revision = new Date(0L);
157                 }
158                 modulesByRevision.put(revision, contextModule);
159             }
160         }
161
162         var Module result = null;
163         if (dependentModuleRevision === null) {
164             result = modulesByRevision.get(modulesByRevision.firstKey());
165         } else {
166             result = modulesByRevision.get(dependentModuleRevision);
167         }
168         return result;
169     }
170
171     /**
172      * Parse XPath string.
173      *
174      * @param xpathString
175      *            as String
176      * @return SchemaPath from given String
177      */
178     public static def SchemaPath parseXPathString(String xpathString) {
179         val absolute = xpathString.startsWith("/");
180         val String[] splittedPath = xpathString.split("/");
181         val path = new ArrayList<QName>();
182         var QName name;
183         for (String pathElement : splittedPath) {
184             if (pathElement.length() > 0) {
185                 val String[] splittedElement = pathElement.split(":");
186                 if (splittedElement.length == 1) {
187                     name = new QName(null, null, null, splittedElement.get(0));
188                 } else {
189                     name = new QName(null, null, splittedElement.get(0), splittedElement.get(1));
190                 }
191                 path.add(name);
192             }
193         }
194         return new SchemaPath(path, absolute);
195     }
196
197     /**
198      * Add all augment's child nodes to given target.
199      *
200      * @param augment
201      *            builder of augment statement
202      * @param target
203      *            augmentation target node
204      */
205     public static def void fillAugmentTarget(AugmentationSchemaBuilder augment, DataNodeContainerBuilder target) {
206         for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
207             val childCopy = CopyUtils.copy(child, target, false);
208             setNodeAugmenting(childCopy, augment);
209             correctNodePath(child, target.getPath());
210             correctNodePath(childCopy, target.getPath());
211             try {
212                 target.addChildNode(childCopy);
213             } catch (YangParseException e) {
214                 // more descriptive message
215                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
216                     "Failed to perform augmentation: " + e.getMessage());
217             }
218         }
219         for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
220             val copy = CopyUtils.copyUses(usesNode, target);
221             copy.setAugmenting(true);
222             target.addUsesNode(copy);
223         }
224     }
225
226     private static def void setNodeAugmenting(DataSchemaNodeBuilder child, AugmentationSchemaBuilder augment) {
227         child.setAugmenting(true);
228         if (child instanceof DataNodeContainerBuilder) {
229             val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
230             for (inner : dataNodeChild.getChildNodeBuilders()) {
231                 setNodeAugmenting(inner, augment);
232             }
233             for (uses : dataNodeChild.getUsesNodes()) {
234                 uses.setParentAugment(augment);
235                 uses.setAugmenting(true);
236             }
237         } else if (child instanceof ChoiceBuilder) {
238             val ChoiceBuilder choiceChild = child as ChoiceBuilder;
239             for (inner : choiceChild.cases) {
240                 setNodeAugmenting(inner, augment);
241             }
242         }
243     }
244
245     /**
246      * Add all augment's child nodes to given target.
247      *
248      * @param augment
249      *            builder of augment statement
250      * @param target
251      *            augmentation target choice node
252      */
253     public static def void fillAugmentTarget(AugmentationSchemaBuilder augment, ChoiceBuilder target) {
254         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
255             val childCopy = CopyUtils.copy(builder, target, false);
256             setNodeAugmenting(childCopy, augment)
257             correctNodePath(builder, target.getPath());
258             correctNodePath(childCopy, target.getPath());
259             target.addCase(childCopy);
260         }
261         for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
262             if (usesNode !== null) {
263                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
264                     "Error in augment parsing: cannot augment choice with nodes from grouping");
265             }
266         }
267     }
268
269     /**
270      * Create new schema path of node based on parent node schema path.
271      *
272      * @param node
273      *            node to correct
274      * @param parentSchemaPath
275      *            schema path of node parent
276      */
277     static def void correctNodePath(SchemaNodeBuilder node, SchemaPath parentSchemaPath) {
278
279         // set correct path
280         val targetNodePath = new ArrayList<QName>(parentSchemaPath.getPath());
281         targetNodePath.add(node.getQName());
282         node.setPath(new SchemaPath(targetNodePath, true));
283
284         // set correct path for all child nodes
285         if (node instanceof DataNodeContainerBuilder) {
286             val dataNodeContainer = node as DataNodeContainerBuilder;
287             for (DataSchemaNodeBuilder child : dataNodeContainer.getChildNodeBuilders()) {
288                 correctNodePath(child, node.getPath());
289             }
290         }
291
292         // set correct path for all cases
293         if (node instanceof ChoiceBuilder) {
294             val choiceBuilder = node as ChoiceBuilder;
295             for (ChoiceCaseBuilder choiceCaseBuilder : choiceBuilder.getCases()) {
296                 correctNodePath(choiceCaseBuilder, node.getPath());
297             }
298         }
299     }
300
301
302
303     private static def Builder findNode(Builder firstNodeParent, List<QName> path, String moduleName, int line) {
304         var currentName = "";
305         var currentParent = firstNodeParent;
306
307         val max = path.size();
308         var i = 0;
309         while(i < max) {
310             var qname = path.get(i);
311
312             currentName = qname.getLocalName();
313             if (currentParent instanceof DataNodeContainerBuilder) {
314                 var dataNodeContainerParent = currentParent as DataNodeContainerBuilder;
315                 var SchemaNodeBuilder nodeFound = dataNodeContainerParent.getDataChildByName(currentName);
316                 // if not found, search in notifications
317                 if (nodeFound == null && currentParent instanceof ModuleBuilder) {
318                     nodeFound = searchNotifications(currentParent as ModuleBuilder, currentName);
319                 }
320                 // if not found, search in rpcs
321                 if (nodeFound == null && currentParent instanceof ModuleBuilder) {
322                     nodeFound = searchRpcs(currentParent as ModuleBuilder, currentName);
323                 }
324                 // if not found, search in uses
325                 if (nodeFound == null) {
326                     var found = searchUses(dataNodeContainerParent, currentName);
327                     if(found == null) {
328                         return null;
329                     } else {
330                         currentParent = found;
331                     }
332                 } else {
333                     currentParent = nodeFound;
334                 }
335             } else if (currentParent instanceof ChoiceBuilder) {
336                 val choiceParent = currentParent as ChoiceBuilder;
337                 currentParent = choiceParent.getCaseNodeByName(currentName);
338             } else if (currentParent instanceof RpcDefinitionBuilder) {
339                 val rpc = currentParent as RpcDefinitionBuilder;
340                 if ("input".equals(currentName)) {
341                     currentParent = rpc.input;
342                 } else if ("output".equals(currentName)) {
343                     currentParent = rpc.output;
344                 }
345             } else {
346                 throw new YangParseException(moduleName, line,
347                         "Error in augment parsing: failed to find node " + currentName);
348             }
349
350             // if node in path not found, return null
351             if (currentParent == null) {
352                 return null;
353             }
354             i = i + 1; 
355         }
356         return currentParent;
357     }
358
359     private static def searchNotifications(ModuleBuilder parent, String name) {
360         for(notification : parent.notifications) {
361             if(notification.getQName().localName.equals(name)) {
362                 return notification;
363             }
364         }
365         return null;
366     }
367
368     private static def searchRpcs(ModuleBuilder parent, String name) {
369         for(rpc : parent.rpcs) {
370             if(rpc.getQName().localName.equals(name)) {
371                 return rpc;
372             }
373         }
374         return null;
375     }
376
377     private static def searchUses(DataNodeContainerBuilder dataNodeContainerParent, String name) {
378         var currentName = name;
379         for (unb : dataNodeContainerParent.usesNodes) {
380             var result = searchInUsesTarget(currentName, unb);
381             if (result != null) {
382                 return result;
383             }
384
385             result = findNodeInUses(currentName, unb);
386             if (result != null) {
387                 var copy = CopyUtils.copy(result, unb.getParent(), true);
388                 unb.getTargetChildren().add(copy);
389                 return copy;
390             }
391         }
392         return null;
393     }
394     
395     public static def getRpc(ModuleBuilder module,String name) {
396         for(rpc : module.getRpcs()) {
397             if(name == rpc.QName.localName) {
398                 return rpc;
399             }
400         }
401         return null;
402     }
403     
404     public static def getNotification(ModuleBuilder module,String name) {
405         for(notification : module.getNotifications()) {
406             if(name == notification.QName.localName) {
407                 return notification;
408             }
409         }
410     }
411     
412     private static def nextLevel(List<QName> path){
413         return path.subList(1,path.size)
414     }
415     
416     /**
417      * Find augment target node and perform augmentation.
418      *
419      * @param augment
420      * @param firstNodeParent
421      *            parent of first node in path
422      * @param path
423      *            path to augment target
424      * @return true if augmentation process succeed, false otherwise
425      */
426     public static def boolean processAugmentation(AugmentationSchemaBuilder augment, Builder firstNodeParent,
427         List<QName> path) {
428
429             // traverse augment target path and try to reach target node
430             val targetNode = findNode(firstNodeParent,path,augment.moduleName,augment.line);
431             if (targetNode === null) return false;
432             
433             if ((targetNode instanceof DataNodeContainerBuilder)) {
434                 val targetDataNodeContainer = targetNode as DataNodeContainerBuilder;
435                 augment.setTargetNodeSchemaPath(targetDataNodeContainer.getPath());
436                 fillAugmentTarget(augment, targetDataNodeContainer);
437             } else if (targetNode instanceof ChoiceBuilder) {
438                 val targetChoiceBuilder = targetNode as ChoiceBuilder;
439                 augment.setTargetNodeSchemaPath(targetChoiceBuilder.getPath());
440                 fillAugmentTarget(augment, targetChoiceBuilder);
441             } else {
442                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
443                     "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
444             }
445             (targetNode as AugmentationTargetBuilder).addAugmentation(augment);
446             augment.setResolved(true);
447             return true;
448         }
449
450     private static def DataSchemaNodeBuilder searchInUsesTarget(String localName, UsesNodeBuilder uses) {
451         for(child : uses.targetChildren) {
452             if (child.getQName().getLocalName().equals(localName)) {
453                 return child;
454             }
455         }
456     } 
457
458         /**
459      * Find node with given name in uses target.
460      *
461      * @param localName
462      *            name of node to find
463      * @param uses
464      *            uses node which target grouping should be searched
465      * @return node with given name if found, null otherwise
466      */
467     private static def DataSchemaNodeBuilder findNodeInUses(String localName, UsesNodeBuilder uses) {
468         val target = uses.groupingBuilder;
469         for (child : target.childNodeBuilders) {
470             if (child.getQName().getLocalName().equals(localName)) {
471                 return child;
472             }
473         }
474         for (usesNode : target.usesNodes) {
475             val result = findNodeInUses(localName, usesNode);
476             if (result != null) {
477                 return result;
478             }
479         }
480         return null;
481     }
482
483         /**
484      * Find augment target node in given context and perform augmentation.
485      *
486      * @param augment
487      * @param path
488      *            path to augment target
489      * @param module
490      *            current module
491      * @param prefix
492      *            current prefix of target module
493      * @param context
494      *            SchemaContext containing already resolved modules
495      * @return true if augment process succeed, false otherwise
496      */
497         public static def boolean processAugmentationOnContext(AugmentationSchemaBuilder augment, List<QName> path,
498             ModuleBuilder module, String prefix, SchemaContext context) {
499             val int line = augment.getLine();
500             val Module dependentModule = findModuleFromContext(context, module, prefix, line);
501             if (dependentModule === null) {
502                 throw new YangParseException(module.getName(), line,
503                     "Error in augment parsing: failed to find module with prefix " + prefix + ".");
504             }
505
506             var currentName = path.get(0).getLocalName();
507             var SchemaNode currentParent = dependentModule.getDataChildByName(currentName);
508             if (currentParent === null) {
509                 val notifications = dependentModule.getNotifications();
510                 for (NotificationDefinition ntf : notifications) {
511                     if (ntf.getQName().getLocalName().equals(currentName)) {
512                         currentParent = ntf;
513                     }
514                 }
515             }
516             if (currentParent === null) {
517                 throw new YangParseException(module.getName(), line,
518                     "Error in augment parsing: failed to find node " + currentName + ".");
519             }
520
521             for (qname : path.nextLevel) {
522                 currentName = qname.getLocalName();
523                 if (currentParent instanceof DataNodeContainer) {
524                     currentParent = (currentParent as DataNodeContainer).getDataChildByName(currentName);
525                 } else if (currentParent instanceof ChoiceNode) {
526                     currentParent = (currentParent as ChoiceNode).getCaseNodeByName(currentName);
527                 } else {
528                     throw new YangParseException(augment.getModuleName(), line,
529                         "Error in augment parsing: failed to find node " + currentName);
530                 }
531
532                 // if node in path not found, return false
533                 if (currentParent === null) {
534                     throw new YangParseException(module.getName(), line,
535                         "Error in augment parsing: failed to find node " + currentName + ".");
536                 }
537             }
538
539             val oldPath = currentParent.path;
540
541             if (!(currentParent instanceof AugmentationTarget)) {
542                 throw new YangParseException(module.getName(), line,
543                     "Target of type " + currentParent.class + " cannot be augmented.");
544             }
545
546             switch (currentParent) {
547                 case (currentParent instanceof ContainerSchemaNodeImpl): {
548
549                     // includes container, input and output statement
550                     val c = currentParent as ContainerSchemaNodeImpl;
551                     val cb = c.toBuilder();
552                     fillAugmentTarget(augment, cb);
553                     (cb as AugmentationTargetBuilder ).addAugmentation(augment);
554                     cb.rebuild();
555                 }
556                 case (currentParent instanceof ListSchemaNodeImpl): {
557                     val l = currentParent as ListSchemaNodeImpl;
558                     val lb = l.toBuilder();
559                     fillAugmentTarget(augment, lb);
560                     (lb as AugmentationTargetBuilder ).addAugmentation(augment);
561                     lb.rebuild();
562                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
563                     augment.setResolved(true);
564                 }
565                 case (currentParent instanceof ChoiceNodeImpl): {
566                     val ch = currentParent as ChoiceNodeImpl;
567                     val chb = ch.toBuilder();
568                     fillAugmentTarget(augment, chb);
569                     (chb as AugmentationTargetBuilder ).addAugmentation(augment);
570                     chb.rebuild();
571                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
572                     augment.setResolved(true);
573                 }
574                 case (currentParent instanceof ChoiceCaseNodeImpl): {
575                     val chc = currentParent as ChoiceCaseNodeImpl;
576                     val chcb = chc.toBuilder();
577                     fillAugmentTarget(augment, chcb);
578                     (chcb as AugmentationTargetBuilder ).addAugmentation(augment);
579                     chcb.rebuild();
580                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
581                     augment.setResolved(true);
582                 }
583                 case (currentParent instanceof NotificationDefinitionImpl): {
584                     val nd = currentParent as NotificationDefinitionImpl;
585                     val nb = nd.toBuilder();
586                     fillAugmentTarget(augment, nb);
587                     (nb as AugmentationTargetBuilder ).addAugmentation(augment);
588                     nb.rebuild();
589                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
590                     augment.setResolved(true);
591                 }
592             }
593             augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
594             augment.setResolved(true);
595             return true;
596         }
597
598     public static def IdentitySchemaNodeBuilder findBaseIdentity(Map<String, TreeMap<Date, ModuleBuilder>> modules,
599         ModuleBuilder module, String baseString, int line) {
600         var IdentitySchemaNodeBuilder result = null;
601         if (baseString.contains(":")) {
602             val String[] splittedBase = baseString.split(":");
603             if (splittedBase.length > 2) {
604                 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " +
605                     baseString);
606             }
607             val prefix = splittedBase.get(0);
608             val name = splittedBase.get(1);
609             val dependentModule = findModuleFromBuilders(modules, module, prefix, line);
610             if (dependentModule !== null) {
611                 result = findIdentity(dependentModule.identities, name);
612             }
613         } else {
614             result = findIdentity(module.identities, baseString);
615         }
616         return result;
617     }
618
619     public static def IdentitySchemaNode findBaseIdentityFromContext(Map<String, TreeMap<Date, ModuleBuilder>> modules,
620             ModuleBuilder module, String baseString, int line, SchemaContext context) {
621         var IdentitySchemaNode result = null;
622
623         val String[] splittedBase = baseString.split(":");
624         if (splittedBase.length > 2) {
625             throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
626         }
627         val prefix = splittedBase.get(0);
628         val name = splittedBase.get(1);
629         val dependentModule = findModuleFromContext(context, module, prefix, line);
630         result = findIdentityNode(dependentModule.identities, name);
631
632         if (result == null) {
633             throw new YangParseException(module.name, line, "Failed to find base identity");
634         }
635         return result;
636     }
637
638     private static def IdentitySchemaNodeBuilder findIdentity(Set<IdentitySchemaNodeBuilder> identities, String name) {
639         for (identity : identities) {
640             if (identity.QName.localName.equals(name)) {
641                 return identity;
642             }
643         }
644         return null;
645     }
646
647     private static def IdentitySchemaNode findIdentityNode(Set<IdentitySchemaNode> identities, String name) {
648         for (identity : identities) {
649             if (identity.QName.localName.equals(name)) {
650                 return identity;
651             }
652         }
653         return null;
654     }
655
656         /**
657      * Get module in which this node is defined.
658      *
659      * @param node
660      * @return builder of module where this node is defined
661      */
662         public static def ModuleBuilder getParentModule(Builder node) {
663             if (node instanceof ModuleBuilder) {
664                 return node as ModuleBuilder;
665             }
666             var parent = node.getParent();
667             while (!(parent instanceof ModuleBuilder)) {
668                 parent = parent.getParent();
669             }
670             return parent as ModuleBuilder;
671         }
672
673 }
674