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