Fixed utility method to validate java package name.
[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             target.addUsesNode(copy);
222         }
223     }
224
225     private static def void setNodeAugmenting(DataSchemaNodeBuilder child, AugmentationSchemaBuilder augment) {
226         child.setAugmenting(true);
227         if (child instanceof DataNodeContainerBuilder) {
228             val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
229             for (inner : dataNodeChild.getChildNodeBuilders()) {
230                 setNodeAugmenting(inner, augment);
231             }
232             for (uses : dataNodeChild.getUsesNodes()) {
233                 uses.setParentAugment(augment);
234                 uses.setAugmenting(true);
235             }
236         } else if (child instanceof ChoiceBuilder) {
237             val ChoiceBuilder choiceChild = child as ChoiceBuilder;
238             for (inner : choiceChild.cases) {
239                 setNodeAugmenting(inner, augment);
240             }
241         }
242     }
243
244     /**
245      * Add all augment's child nodes to given target.
246      *
247      * @param augment
248      *            builder of augment statement
249      * @param target
250      *            augmentation target choice node
251      */
252     public static def void fillAugmentTarget(AugmentationSchemaBuilder augment, ChoiceBuilder target) {
253         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
254             val childCopy = CopyUtils.copy(builder, target, false);
255             setNodeAugmenting(childCopy, augment)
256             correctNodePath(builder, target.getPath());
257             correctNodePath(childCopy, target.getPath());
258             target.addCase(childCopy);
259         }
260         for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
261             if (usesNode !== null) {
262                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
263                     "Error in augment parsing: cannot augment choice with nodes from grouping");
264             }
265         }
266     }
267
268     /**
269      * Create new schema path of node based on parent node schema path.
270      *
271      * @param node
272      *            node to correct
273      * @param parentSchemaPath
274      *            schema path of node parent
275      */
276     static def void correctNodePath(SchemaNodeBuilder node, SchemaPath parentSchemaPath) {
277
278         // set correct path
279         val targetNodePath = new ArrayList<QName>(parentSchemaPath.getPath());
280         targetNodePath.add(node.getQName());
281         node.setPath(new SchemaPath(targetNodePath, true));
282
283         // set correct path for all child nodes
284         if (node instanceof DataNodeContainerBuilder) {
285             val dataNodeContainer = node as DataNodeContainerBuilder;
286             for (DataSchemaNodeBuilder child : dataNodeContainer.getChildNodeBuilders()) {
287                 correctNodePath(child, node.getPath());
288             }
289         }
290
291         // set correct path for all cases
292         if (node instanceof ChoiceBuilder) {
293             val choiceBuilder = node as ChoiceBuilder;
294             for (ChoiceCaseBuilder choiceCaseBuilder : choiceBuilder.getCases()) {
295                 correctNodePath(choiceCaseBuilder, node.getPath());
296             }
297         }
298     }
299
300
301
302     private static def Builder findNode(Builder firstNodeParent, List<QName> path, String moduleName, int line) {
303         var currentName = "";
304         var currentParent = firstNodeParent;
305
306         val max = path.size();
307         var i = 0;
308         while(i < max) {
309             var qname = path.get(i);
310
311             currentName = qname.getLocalName();
312             if (currentParent instanceof DataNodeContainerBuilder) {
313                 var dataNodeContainerParent = currentParent as DataNodeContainerBuilder;
314                 var SchemaNodeBuilder nodeFound = dataNodeContainerParent.getDataChildByName(currentName);
315                 // if not found, search in notifications
316                 if (nodeFound == null && currentParent instanceof ModuleBuilder) {
317                     nodeFound = searchNotifications(currentParent as ModuleBuilder, currentName);
318                 }
319                 // if not found, search in rpcs
320                 if (nodeFound == null && currentParent instanceof ModuleBuilder) {
321                     nodeFound = searchRpcs(currentParent as ModuleBuilder, currentName);
322                 }
323                 // if not found, search in uses
324                 if (nodeFound == null) {
325                     var found = searchUses(dataNodeContainerParent, currentName);
326                     if(found == null) {
327                         return null;
328                     } else {
329                         currentParent = found;
330                     }
331                 } else {
332                     currentParent = nodeFound;
333                 }
334             } else if (currentParent instanceof ChoiceBuilder) {
335                 val choiceParent = currentParent as ChoiceBuilder;
336                 currentParent = choiceParent.getCaseNodeByName(currentName);
337             } else if (currentParent instanceof RpcDefinitionBuilder) {
338                 val rpc = currentParent as RpcDefinitionBuilder;
339                 if ("input".equals(currentName)) {
340                     currentParent = rpc.input;
341                 } else if ("output".equals(currentName)) {
342                     currentParent = rpc.output;
343                 }
344             } else {
345                 throw new YangParseException(moduleName, line,
346                         "Error in augment parsing: failed to find node " + currentName);
347             }
348
349             // if node in path not found, return null
350             if (currentParent == null) {
351                 return null;
352             }
353             i = i + 1; 
354         }
355         return currentParent;
356     }
357
358     private static def searchNotifications(ModuleBuilder parent, String name) {
359         for(notification : parent.notifications) {
360             if(notification.getQName().localName.equals(name)) {
361                 return notification;
362             }
363         }
364         return null;
365     }
366
367     private static def searchRpcs(ModuleBuilder parent, String name) {
368         for(rpc : parent.rpcs) {
369             if(rpc.getQName().localName.equals(name)) {
370                 return rpc;
371             }
372         }
373         return null;
374     }
375
376     private static def searchUses(DataNodeContainerBuilder dataNodeContainerParent, String name) {
377         var currentName = name;
378         for (unb : dataNodeContainerParent.usesNodes) {
379             var result = searchInUsesTarget(currentName, unb);
380             if (result != null) {
381                 return result;
382             }
383
384             result = findNodeInUses(currentName, unb);
385             if (result != null) {
386                 var copy = CopyUtils.copy(result, unb.getParent(), true);
387                 unb.getTargetChildren().add(copy);
388                 return copy;
389             }
390         }
391         return null;
392     }
393     
394     public static def getRpc(ModuleBuilder module,String name) {
395         for(rpc : module.rpcs) {
396             if(name == rpc.QName.localName) {
397                 return rpc;
398             }
399         }
400         return null;
401     }
402     
403     public static def getNotification(ModuleBuilder module,String name) {
404         for(notification : module.notifications) {
405             if(name == notification.QName.localName) {
406                 return notification;
407             }
408         }
409     }
410     
411     private static def nextLevel(List<QName> path){
412         return path.subList(1,path.size)
413     }
414     
415     /**
416      * Find augment target node and perform augmentation.
417      *
418      * @param augment
419      * @param firstNodeParent
420      *            parent of first node in path
421      * @param path
422      *            path to augment target
423      * @return true if augmentation process succeed, false otherwise
424      */
425     public static def boolean processAugmentation(AugmentationSchemaBuilder augment, Builder firstNodeParent,
426         List<QName> path) {
427
428             // traverse augment target path and try to reach target node
429             val targetNode = findNode(firstNodeParent,path,augment.moduleName,augment.line);
430             if (targetNode === null) return false;
431             
432             if ((targetNode instanceof DataNodeContainerBuilder)) {
433                 val targetDataNodeContainer = targetNode as DataNodeContainerBuilder;
434                 augment.setTargetNodeSchemaPath(targetDataNodeContainer.getPath());
435                 fillAugmentTarget(augment, targetDataNodeContainer);
436             } else if (targetNode instanceof ChoiceBuilder) {
437                 val targetChoiceBuilder = targetNode as ChoiceBuilder;
438                 augment.setTargetNodeSchemaPath(targetChoiceBuilder.getPath());
439                 fillAugmentTarget(augment, targetChoiceBuilder);
440             } else {
441                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
442                     "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
443             }
444             (targetNode as AugmentationTargetBuilder).addAugmentation(augment);
445             augment.setResolved(true);
446             return true;
447         }
448
449     private static def DataSchemaNodeBuilder searchInUsesTarget(String localName, UsesNodeBuilder uses) {
450         for(child : uses.targetChildren) {
451             if (child.getQName().getLocalName().equals(localName)) {
452                 return child;
453             }
454         }
455     } 
456
457         /**
458      * Find node with given name in uses target.
459      *
460      * @param localName
461      *            name of node to find
462      * @param uses
463      *            uses node which target grouping should be searched
464      * @return node with given name if found, null otherwise
465      */
466     private static def DataSchemaNodeBuilder findNodeInUses(String localName, UsesNodeBuilder uses) {
467         val target = uses.groupingBuilder;
468         for (child : target.childNodeBuilders) {
469             if (child.getQName().getLocalName().equals(localName)) {
470                 return child;
471             }
472         }
473         for (usesNode : target.usesNodes) {
474             val result = findNodeInUses(localName, usesNode);
475             if (result != null) {
476                 return result;
477             }
478         }
479         return null;
480     }
481
482         /**
483      * Find augment target node in given context and perform augmentation.
484      *
485      * @param augment
486      * @param path
487      *            path to augment target
488      * @param module
489      *            current module
490      * @param prefix
491      *            current prefix of target module
492      * @param context
493      *            SchemaContext containing already resolved modules
494      * @return true if augment process succeed, false otherwise
495      */
496         public static def boolean processAugmentationOnContext(AugmentationSchemaBuilder augment, List<QName> path,
497             ModuleBuilder module, String prefix, SchemaContext context) {
498             val int line = augment.getLine();
499             val Module dependentModule = findModuleFromContext(context, module, prefix, line);
500             if (dependentModule === null) {
501                 throw new YangParseException(module.getName(), line,
502                     "Error in augment parsing: failed to find module with prefix " + prefix + ".");
503             }
504
505             var currentName = path.get(0).getLocalName();
506             var SchemaNode currentParent = dependentModule.getDataChildByName(currentName);
507             if (currentParent === null) {
508                 val notifications = dependentModule.getNotifications();
509                 for (NotificationDefinition ntf : notifications) {
510                     if (ntf.getQName().getLocalName().equals(currentName)) {
511                         currentParent = ntf;
512                     }
513                 }
514             }
515             if (currentParent === null) {
516                 throw new YangParseException(module.getName(), line,
517                     "Error in augment parsing: failed to find node " + currentName + ".");
518             }
519
520             for (qname : path.nextLevel) {
521                 currentName = qname.getLocalName();
522                 if (currentParent instanceof DataNodeContainer) {
523                     currentParent = (currentParent as DataNodeContainer).getDataChildByName(currentName);
524                 } else if (currentParent instanceof ChoiceNode) {
525                     currentParent = (currentParent as ChoiceNode).getCaseNodeByName(currentName);
526                 } else {
527                     throw new YangParseException(augment.getModuleName(), line,
528                         "Error in augment parsing: failed to find node " + currentName);
529                 }
530
531                 // if node in path not found, return false
532                 if (currentParent === null) {
533                     throw new YangParseException(module.getName(), line,
534                         "Error in augment parsing: failed to find node " + currentName + ".");
535                 }
536             }
537
538             val oldPath = currentParent.path;
539
540             if (!(currentParent instanceof AugmentationTarget)) {
541                 throw new YangParseException(module.getName(), line,
542                     "Target of type " + currentParent.class + " cannot be augmented.");
543             }
544
545             switch (currentParent) {
546                 case (currentParent instanceof ContainerSchemaNodeImpl): {
547
548                     // includes container, input and output statement
549                     val c = currentParent as ContainerSchemaNodeImpl;
550                     val cb = c.toBuilder();
551                     fillAugmentTarget(augment, cb);
552                     (cb as AugmentationTargetBuilder ).addAugmentation(augment);
553                     cb.rebuild();
554                 }
555                 case (currentParent instanceof ListSchemaNodeImpl): {
556                     val l = currentParent as ListSchemaNodeImpl;
557                     val lb = l.toBuilder();
558                     fillAugmentTarget(augment, lb);
559                     (lb as AugmentationTargetBuilder ).addAugmentation(augment);
560                     lb.rebuild();
561                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
562                     augment.setResolved(true);
563                 }
564                 case (currentParent instanceof ChoiceNodeImpl): {
565                     val ch = currentParent as ChoiceNodeImpl;
566                     val chb = ch.toBuilder();
567                     fillAugmentTarget(augment, chb);
568                     (chb as AugmentationTargetBuilder ).addAugmentation(augment);
569                     chb.rebuild();
570                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
571                     augment.setResolved(true);
572                 }
573                 case (currentParent instanceof ChoiceCaseNodeImpl): {
574                     val chc = currentParent as ChoiceCaseNodeImpl;
575                     val chcb = chc.toBuilder();
576                     fillAugmentTarget(augment, chcb);
577                     (chcb as AugmentationTargetBuilder ).addAugmentation(augment);
578                     chcb.rebuild();
579                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
580                     augment.setResolved(true);
581                 }
582                 case (currentParent instanceof NotificationDefinitionImpl): {
583                     val nd = currentParent as NotificationDefinitionImpl;
584                     val nb = nd.toBuilder();
585                     fillAugmentTarget(augment, nb);
586                     (nb as AugmentationTargetBuilder ).addAugmentation(augment);
587                     nb.rebuild();
588                     augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
589                     augment.setResolved(true);
590                 }
591             }
592             augment.setTargetNodeSchemaPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
593             augment.setResolved(true);
594             return true;
595         }
596
597     public static def IdentitySchemaNodeBuilder findBaseIdentity(Map<String, TreeMap<Date, ModuleBuilder>> modules,
598         ModuleBuilder module, String baseString, int line) {
599         var IdentitySchemaNodeBuilder result = null;
600         if (baseString.contains(":")) {
601             val String[] splittedBase = baseString.split(":");
602             if (splittedBase.length > 2) {
603                 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " +
604                     baseString);
605             }
606             val prefix = splittedBase.get(0);
607             val name = splittedBase.get(1);
608             val dependentModule = findModuleFromBuilders(modules, module, prefix, line);
609             if (dependentModule !== null) {
610                 result = findIdentity(dependentModule.identities, name);
611             }
612         } else {
613             result = findIdentity(module.identities, baseString);
614         }
615         return result;
616     }
617
618     public static def IdentitySchemaNode findBaseIdentityFromContext(Map<String, TreeMap<Date, ModuleBuilder>> modules,
619             ModuleBuilder module, String baseString, int line, SchemaContext context) {
620         var IdentitySchemaNode result = null;
621
622         val String[] splittedBase = baseString.split(":");
623         if (splittedBase.length > 2) {
624             throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
625         }
626         val prefix = splittedBase.get(0);
627         val name = splittedBase.get(1);
628         val dependentModule = findModuleFromContext(context, module, prefix, line);
629         result = findIdentityNode(dependentModule.identities, name);
630
631         if (result == null) {
632             throw new YangParseException(module.name, line, "Failed to find base identity");
633         }
634         return result;
635     }
636
637     private static def IdentitySchemaNodeBuilder findIdentity(Set<IdentitySchemaNodeBuilder> identities, String name) {
638         for (identity : identities) {
639             if (identity.QName.localName.equals(name)) {
640                 return identity;
641             }
642         }
643         return null;
644     }
645
646     private static def IdentitySchemaNode findIdentityNode(Set<IdentitySchemaNode> identities, String name) {
647         for (identity : identities) {
648             if (identity.QName.localName.equals(name)) {
649                 return identity;
650             }
651         }
652         return null;
653     }
654
655         /**
656      * Get module in which this node is defined.
657      *
658      * @param node
659      * @return builder of module where this node is defined
660      */
661         public static def ModuleBuilder getParentModule(Builder node) {
662             if (node instanceof ModuleBuilder) {
663                 return node as ModuleBuilder;
664             }
665             var parent = node.getParent();
666             while (!(parent instanceof ModuleBuilder)) {
667                 parent = parent.getParent();
668             }
669             return parent as ModuleBuilder;
670         }
671     }
672