Revert "Revert "BUG-994: reorganize SchemaPath into a tree""
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / SchemaContextUtil.java
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.model.util;
9
10 import com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Splitter;
13 import com.google.common.collect.Iterables;
14
15 import java.net.URI;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.Date;
20 import java.util.Iterator;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Set;
24
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.Module;
36 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
37 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
38 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
39 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
43 import org.opendaylight.yangtools.yang.model.api.UsesNode;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * The Schema Context Util contains support methods for searching through Schema
49  * Context modules for specified schema nodes via Schema Path or Revision Aware
50  * XPath. The Schema Context Util is designed as mixin, so it is not
51  * instantiable.
52  *
53  */
54 public final class SchemaContextUtil {
55     private static final Logger LOG = LoggerFactory.getLogger(SchemaContextUtil.class);
56     private static final Splitter COLON_SPLITTER = Splitter.on(':');
57     private static final Splitter SLASH_SPLITTER = Splitter.on('/');
58
59     private SchemaContextUtil() {
60     }
61
62     /**
63      * Method attempts to find DataSchemaNode in Schema Context via specified
64      * Schema Path. The returned DataSchemaNode from method will be the node at
65      * the end of the SchemaPath. If the DataSchemaNode is not present in the
66      * Schema Context the method will return <code>null</code>. <br>
67      * In case that Schema Context or Schema Path are not specified correctly
68      * (i.e. contains <code>null</code> values) the method will return
69      * IllegalArgumentException.
70      *
71      * @throws IllegalArgumentException
72      *
73      * @param context
74      *            Schema Context
75      * @param schemaPath
76      *            Schema Path to search for
77      * @return SchemaNode from the end of the Schema Path or <code>null</code>
78      *         if the Node is not present.
79      */
80     public static SchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) {
81         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
82         Preconditions.checkArgument(schemaPath != null, "Schema Path reference cannot be NULL");
83
84         final List<QName> prefixedPath = (schemaPath.getPath());
85         if (prefixedPath == null) {
86             LOG.debug("Schema path {} has null path", schemaPath);
87             return null;
88         }
89
90         LOG.trace("Looking for path {} in context {}", schemaPath, context);
91         return findNodeInSchemaContext(context, prefixedPath);
92     }
93
94     /**
95      * Method attempts to find DataSchemaNode inside of provided Schema Context
96      * and Yang Module accordingly to Non-conditional Revision Aware XPath. The
97      * specified Module MUST be present in Schema Context otherwise the
98      * operation would fail and return <code>null</code>. <br>
99      * The Revision Aware XPath MUST be specified WITHOUT the conditional
100      * statement (i.e. without [cond]) in path, because in this state the Schema
101      * Context is completely unaware of data state and will be not able to
102      * properly resolve XPath. If the XPath contains condition the method will
103      * return IllegalArgumentException. <br>
104      * In case that Schema Context or Module or Revision Aware XPath contains
105      * <code>null</code> references the method will throw
106      * IllegalArgumentException <br>
107      * If the Revision Aware XPath is correct and desired Data Schema Node is
108      * present in Yang module or in depending module in Schema Context the
109      * method will return specified Data Schema Node, otherwise the operation
110      * will fail and method will return <code>null</code>.
111      *
112      * @throws IllegalArgumentException
113      *
114      * @param context
115      *            Schema Context
116      * @param module
117      *            Yang Module
118      * @param nonCondXPath
119      *            Non Conditional Revision Aware XPath
120      * @return Returns Data Schema Node for specified Schema Context for given
121      *         Non-conditional Revision Aware XPath, or <code>null</code> if the
122      *         DataSchemaNode is not present in Schema Context.
123      */
124     public static SchemaNode findDataSchemaNode(final SchemaContext context, final Module module, final RevisionAwareXPath nonCondXPath) {
125         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
126         Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
127         Preconditions.checkArgument(nonCondXPath != null, "Non Conditional Revision Aware XPath cannot be NULL");
128
129         String strXPath = nonCondXPath.toString();
130         if (strXPath != null) {
131             Preconditions.checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition");
132             if (nonCondXPath.isAbsolute()) {
133                 List<QName> qnamedPath = xpathToQNamePath(context, module, strXPath);
134                 if (qnamedPath != null) {
135                     return findNodeInSchemaContext(context, qnamedPath);
136                 }
137             }
138         }
139         return null;
140     }
141
142     /**
143      * Method attempts to find DataSchemaNode inside of provided Schema Context
144      * and Yang Module accordingly to Non-conditional relative Revision Aware
145      * XPath. The specified Module MUST be present in Schema Context otherwise
146      * the operation would fail and return <code>null</code>. <br>
147      * The relative Revision Aware XPath MUST be specified WITHOUT the
148      * conditional statement (i.e. without [cond]) in path, because in this
149      * state the Schema Context is completely unaware of data state and will be
150      * not able to properly resolve XPath. If the XPath contains condition the
151      * method will return IllegalArgumentException. <br>
152      * The Actual Schema Node MUST be specified correctly because from this
153      * Schema Node will search starts. If the Actual Schema Node is not correct
154      * the operation will simply fail, because it will be unable to find desired
155      * DataSchemaNode. <br>
156      * In case that Schema Context or Module or Actual Schema Node or relative
157      * Revision Aware XPath contains <code>null</code> references the method
158      * will throw IllegalArgumentException <br>
159      * If the Revision Aware XPath doesn't have flag
160      * <code>isAbsolute == false</code> the method will throw
161      * IllegalArgumentException. <br>
162      * If the relative Revision Aware XPath is correct and desired Data Schema
163      * Node is present in Yang module or in depending module in Schema Context
164      * the method will return specified Data Schema Node, otherwise the
165      * operation will fail and method will return <code>null</code>.
166      *
167      * @throws IllegalArgumentException
168      *
169      * @param context
170      *            Schema Context
171      * @param module
172      *            Yang Module
173      * @param actualSchemaNode
174      *            Actual Schema Node
175      * @param relativeXPath
176      *            Relative Non Conditional Revision Aware XPath
177      * @return DataSchemaNode if is present in specified Schema Context for
178      *         given relative Revision Aware XPath, otherwise will return
179      *         <code>null</code>.
180      */
181     public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
182             final SchemaNode actualSchemaNode, final RevisionAwareXPath relativeXPath) {
183         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
184         Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
185         Preconditions.checkArgument(actualSchemaNode != null, "Actual Schema Node reference cannot be NULL");
186         Preconditions.checkArgument(relativeXPath != null, "Non Conditional Revision Aware XPath cannot be NULL");
187         Preconditions.checkState(!relativeXPath.isAbsolute(),
188                 "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
189                         + "for non relative Revision Aware XPath use findDataSchemaNode method");
190
191         SchemaPath actualNodePath = actualSchemaNode.getPath();
192         if (actualNodePath != null) {
193             Iterable<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode);
194
195             if (qnamePath != null) {
196                 return findNodeInSchemaContext(context, qnamePath);
197             }
198         }
199         return null;
200     }
201
202     /**
203      * Returns parent Yang Module for specified Schema Context in which Schema
204      * Node is declared. If the Schema Node is not present in Schema Context the
205      * operation will return <code>null</code>. <br>
206      * If Schema Context or Schema Node contains <code>null</code> references
207      * the method will throw IllegalArgumentException
208      *
209      * @throws IllegalArgumentException
210      *
211      * @param context
212      *            Schema Context
213      * @param schemaNode
214      *            Schema Node
215      * @return Yang Module for specified Schema Context and Schema Node, if
216      *         Schema Node is NOT present, the method will returns
217      *         <code>null</code>
218      */
219     public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) {
220         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL!");
221         Preconditions.checkArgument(schemaNode != null, "Schema Node cannot be NULL!");
222         Preconditions.checkState(schemaNode.getPath() != null, "Schema Path for Schema Node is not "
223                 + "set properly (Schema Path is NULL)");
224
225         final QName qname = Iterables.getFirst(schemaNode.getPath().getPathTowardsRoot(), null);
226         Preconditions.checkState(qname != null,
227                 "Schema Path contains invalid state of path parts. " +
228                 "The Schema Path MUST contain at least ONE QName which defines namespace and Local name of path.");
229         return context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision());
230     }
231
232     public static SchemaNode findNodeInSchemaContext(final SchemaContext context, final Iterable<QName> path) {
233         final QName current = path.iterator().next();
234
235         LOG.trace("Looking up module {} in context {}", current, path);
236         final Module module = context.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision());
237         if (module == null) {
238             LOG.debug("Module {} not found", current);
239             return null;
240         }
241
242         return findNodeInModule(module, path);
243     }
244
245     public static GroupingDefinition findGrouping(final SchemaContext context, final Module module, final List<QName> path) {
246         QName first = path.get(0);
247         Module m = context.findModuleByNamespace(first.getNamespace()).iterator().next();
248         DataNodeContainer currentParent = m;
249         for (QName qname : path) {
250             boolean found = false;
251             DataNodeContainer node = (DataNodeContainer) currentParent.getDataChildByName(qname.getLocalName());
252             if (node == null) {
253                 Set<GroupingDefinition> groupings = currentParent.getGroupings();
254                 for (GroupingDefinition gr : groupings) {
255                     if (gr.getQName().getLocalName().equals(qname.getLocalName())) {
256                         currentParent = gr;
257                         found = true;
258                     }
259                 }
260             } else {
261                 found = true;
262                 currentParent = node;
263             }
264
265             Preconditions.checkArgument(found, "Failed to find referenced grouping: %s(%s)", path, qname.getLocalName());
266         }
267
268         return (GroupingDefinition) currentParent;
269     }
270
271     private static SchemaNode findNodeInModule(final Module module, final Iterable<QName> path) {
272         final QName current = path.iterator().next();
273
274         LOG.trace("Looking for data container {} in module {}", current, module);
275         SchemaNode parent = module.getDataChildByName(current);
276         if (parent != null) {
277             final SchemaNode ret = findNode((DataSchemaNode) parent, nextLevel(path));
278             if (ret != null) {
279                 return ret;
280             }
281         }
282
283         LOG.trace("Looking for RPC {} in module {}", current, module);
284         parent = getRpcByName(module, current);
285         if (parent != null) {
286             final SchemaNode ret = findNodeInRpc((RpcDefinition) parent, nextLevel(path));
287             if (ret != null) {
288                 return ret;
289             }
290         }
291
292         LOG.trace("Looking for notification {} in module {}", current, module);
293         parent = getNotificationByName(module, current);
294         if (parent != null) {
295             final SchemaNode ret = findNodeInNotification((NotificationDefinition) parent, nextLevel(path));
296             if (ret != null) {
297                 return ret;
298             }
299         }
300
301         LOG.trace("Looking for grouping {} in module {}", current, module);
302         parent = getGroupingByName(module, current);
303         if (parent != null) {
304             final SchemaNode ret = findNodeInGrouping((GroupingDefinition) parent, nextLevel(path));
305             if (ret != null) {
306                 return ret;
307             }
308         }
309
310         LOG.debug("No node matching {} found in module {}", path, module);
311         return null;
312     }
313
314     private static SchemaNode findNodeInGrouping(final GroupingDefinition grouping, final Iterable<QName> path) {
315         final QName current = Iterables.getFirst(path, null);
316         if (current == null) {
317             LOG.debug("Found grouping {}", grouping);
318             return grouping;
319         }
320
321         LOG.trace("Looking for path {} in grouping {}", path, grouping);
322         final DataSchemaNode node = grouping.getDataChildByName(current);
323         if (node == null) {
324             LOG.debug("No node matching {} found in grouping {}", current, grouping);
325             return null;
326         }
327
328         return findNode(node, nextLevel(path));
329     }
330
331     private static SchemaNode findNodeInRpc(final RpcDefinition rpc, final Iterable<QName> path) {
332         final QName current = Iterables.getFirst(path, null);
333         if (current == null) {
334             LOG.debug("Found RPC {}", rpc);
335             return rpc;
336         }
337
338         LOG.trace("Looking for path {} in rpc {}", path, rpc);
339         switch (current.getLocalName()) {
340         case "input":
341             return findNode(rpc.getInput(), nextLevel(path));
342         case "output":
343             return findNode(rpc.getOutput(), nextLevel(path));
344         default:
345             LOG.debug("Invalid component {} of path {} in RPC {}", current, path, rpc);
346             return null;
347         }
348     }
349
350     private static SchemaNode findNodeInNotification(final NotificationDefinition ntf, final Iterable<QName> path) {
351         final QName current = Iterables.getFirst(path, null);
352         if (current == null) {
353             LOG.debug("Found notification {}", ntf);
354             return ntf;
355         }
356
357         LOG.trace("Looking for path {} in notification {}", path, ntf);
358         DataSchemaNode node = ntf.getDataChildByName(current);
359         if (node == null) {
360             LOG.debug("No node matching {} found in notification {}", current, ntf);
361             return null;
362         }
363
364         return findNode(node, nextLevel(path));
365     }
366
367     private static SchemaNode findNode(final ChoiceNode parent, final Iterable<QName> path) {
368         final QName current = Iterables.getFirst(path, null);
369         if (current == null) {
370             return parent;
371         }
372         ChoiceCaseNode node = parent.getCaseNodeByName(current);
373         if (node != null) {
374             return findNodeInCase(node, nextLevel(path));
375         }
376         return null;
377     }
378
379     private static SchemaNode findNode(final ContainerSchemaNode parent, final Iterable<QName> path) {
380         final QName current = Iterables.getFirst(path, null);
381         if (current == null) {
382             return parent;
383         }
384
385         final DataSchemaNode node = parent.getDataChildByName(current);
386         if (node == null) {
387             LOG.debug("Failed to find {} in parent {}", path, parent);
388             return null;
389         }
390
391         return findNode(node, nextLevel(path));
392     }
393
394     private static SchemaNode findNode(final ListSchemaNode parent, final Iterable<QName> path) {
395         final QName current = Iterables.getFirst(path, null);
396         if (current == null) {
397             return parent;
398         }
399
400         DataSchemaNode node = parent.getDataChildByName(current);
401         if (node == null) {
402             LOG.debug("Failed to find {} in parent {}", path, parent);
403             return null;
404         }
405         return findNode(node, nextLevel(path));
406     }
407
408     private static SchemaNode findNode(final DataSchemaNode parent, final Iterable<QName> path) {
409         final SchemaNode node;
410         if (!Iterables.isEmpty(path)) {
411             if (parent instanceof ContainerSchemaNode) {
412                 node = findNode((ContainerSchemaNode) parent, path);
413             } else if (parent instanceof ListSchemaNode) {
414                 node = findNode((ListSchemaNode) parent, path);
415             } else if (parent instanceof ChoiceNode) {
416                 node = findNode((ChoiceNode) parent, path);
417             } else {
418                 throw new IllegalArgumentException(
419                         String.format("Path nesting violation in parent %s path %s", parent, path));
420             }
421         } else {
422             node = parent;
423         }
424
425         if (node == null) {
426             LOG.debug("Failed to find {} in parent {}", path, parent);
427             return null;
428         }
429         return node;
430     }
431
432     public static SchemaNode findNodeInCase(final ChoiceCaseNode parent, final Iterable<QName> path) {
433         final QName current = Iterables.getFirst(path, null);
434         if (current == null) {
435             return parent;
436         }
437
438         DataSchemaNode node = parent.getDataChildByName(current);
439         if (node == null) {
440             LOG.debug("Failed to find {} in parent {}", path, parent);
441             return null;
442         }
443         return findNode(node, nextLevel(path));
444     }
445
446     public static RpcDefinition getRpcByName(final Module module, final QName name) {
447         for (RpcDefinition rpc : module.getRpcs()) {
448             if (rpc.getQName().equals(name)) {
449                 return rpc;
450             }
451         }
452         return null;
453     }
454
455     private static Iterable<QName> nextLevel(final Iterable<QName> path) {
456         return Iterables.skip(path, 1);
457     }
458
459     public static NotificationDefinition getNotificationByName(final Module module, final QName name) {
460         for (NotificationDefinition notification : module.getNotifications()) {
461             if (notification.getQName().equals(name)) {
462                 return notification;
463             }
464         }
465         return null;
466     }
467
468     public static GroupingDefinition getGroupingByName(final Module module, final QName name) {
469         for (GroupingDefinition grouping : module.getGroupings()) {
470             if (grouping.getQName().equals(name)) {
471                 return grouping;
472             }
473         }
474         return null;
475     }
476
477     /**
478      * Utility method which search for original node defined in grouping.
479      *
480      * @param node
481      * @return
482      */
483     public static DataSchemaNode findOriginal(final DataSchemaNode node, final SchemaContext ctx) {
484         DataSchemaNode result = findCorrectTargetFromGrouping(node, ctx);
485         if (result == null) {
486             result = findCorrectTargetFromAugment(node, ctx);
487             if (result != null) {
488                 if (result.isAddedByUses()) {
489                     result = findOriginal(result, ctx);
490                 }
491             }
492         }
493         return result;
494     }
495
496     private static DataSchemaNode findCorrectImmediateTargetFromGrouping(final DataSchemaNode node, final SchemaContext ctx) {
497         // uses is under module statement
498         final Module m = findParentModule(ctx, node);
499         Preconditions.checkArgument(m != null, "Failed to find module for node {} in context {}", node, ctx);
500
501         for (final UsesNode u : m.getUses()) {
502             final SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPathFromRoot());
503             Preconditions.checkArgument(targetGrouping instanceof GroupingDefinition,
504                     "Failed to generate code for augment in %s", u);
505
506             LOG.trace("Checking grouping {} for node {}", targetGrouping, node);
507             final GroupingDefinition gr = (GroupingDefinition) targetGrouping;
508             final DataSchemaNode result = gr.getDataChildByName(node.getQName().getLocalName());
509             if (result != null) {
510                 return result;
511             }
512
513             LOG.debug("Skipped grouping {}, no matching node found", gr);
514         }
515
516         throw new IllegalArgumentException(
517                 String.format("Failed to find uses node matching {} in context {}", node, ctx));
518     }
519
520     private static DataSchemaNode findCorrectTargetFromGrouping(final DataSchemaNode node, final SchemaContext ctx) {
521         if (node.getPath().getPath().size() != 1) {
522             QName currentName = node.getQName();
523             // tmpPath is used to track level of nesting
524             List<QName> tmpPath = new ArrayList<>();
525             Object parent = null;
526
527             // create schema path of parent node
528             SchemaPath sp = node.getPath().getParent();
529             parent = findDataSchemaNode(ctx, sp);
530
531             do {
532                 tmpPath.add(currentName);
533
534                 DataSchemaNode result = null;
535                 // search parent node's used groupings for presence of wanted
536                 // node
537                 if (parent instanceof DataNodeContainer) {
538                     DataNodeContainer dataNodeParent = (DataNodeContainer) parent;
539                     for (UsesNode u : dataNodeParent.getUses()) {
540                         result = getResultFromUses(u, currentName.getLocalName(), ctx);
541                         if (result != null) {
542                             break;
543                         }
544                     }
545                 }
546
547                 // if node is not found in any of current parent's used
548                 // groupings => parent is added by grouping too, so repeat same
549                 // process for parent
550                 if (result == null) {
551                     final SchemaNode sn = (SchemaNode) parent;
552
553                     // set current name to name of parent node
554                     currentName = sn.getQName();
555                     Preconditions.checkArgument(parent instanceof SchemaNode,
556                             "Failed to generate code for augmend node {} at parent {}", node, parent);
557
558                     // create schema path for parent of current parent
559                     final SchemaPath parentSp = sn.getPath().getParent();
560                     parent = parentSp.getPathFromRoot().iterator().hasNext() ? findDataSchemaNode(ctx, parentSp)
561                             : getParentModule(sn, ctx);
562                 } else {
563                     // if wanted node was found in grouping, traverse this node
564                     // based on level of nesting
565                     return getTargetNode(tmpPath, result, ctx);
566                 }
567             } while (!(parent instanceof Module));
568
569             return null;
570         } else {
571             return findCorrectImmediateTargetFromGrouping(node, ctx);
572         }
573     }
574
575     private static DataSchemaNode findCorrectTargetFromAugment(final DataSchemaNode node, final SchemaContext ctx) {
576         if (!node.isAugmenting()) {
577             return null;
578         }
579
580         QName currentName = node.getQName();
581         Object currentNode = node;
582         Object parent = node;
583         List<QName> tmpPath = new ArrayList<QName>();
584         List<SchemaNode> tmpTree = new ArrayList<SchemaNode>();
585
586         AugmentationSchema augment = null;
587         do {
588             SchemaPath sp = ((SchemaNode) parent).getPath();
589             parent = findDataSchemaNode(ctx, sp.getParent());
590             if (parent instanceof AugmentationTarget) {
591                 tmpPath.add(currentName);
592                 tmpTree.add((SchemaNode) currentNode);
593                 augment = findNodeInAugment(((AugmentationTarget) parent).getAvailableAugmentations(), currentName);
594                 if (augment == null) {
595                     currentName = ((DataSchemaNode) parent).getQName();
596                     currentNode = parent;
597                 }
598             }
599         } while (((DataSchemaNode) parent).isAugmenting() && augment == null);
600
601         if (augment == null) {
602             return null;
603         } else {
604             Collections.reverse(tmpPath);
605             Collections.reverse(tmpTree);
606             Object actualParent = augment;
607             DataSchemaNode result = null;
608             for (QName name : tmpPath) {
609                 if (actualParent instanceof DataNodeContainer) {
610                     result = ((DataNodeContainer) actualParent).getDataChildByName(name.getLocalName());
611                     actualParent = ((DataNodeContainer) actualParent).getDataChildByName(name.getLocalName());
612                 } else {
613                     if (actualParent instanceof ChoiceNode) {
614                         result = ((ChoiceNode) actualParent).getCaseNodeByName(name.getLocalName());
615                         actualParent = ((ChoiceNode) actualParent).getCaseNodeByName(name.getLocalName());
616                     }
617                 }
618             }
619
620             if (result.isAddedByUses()) {
621                 result = findCorrectTargetFromAugmentGrouping(result, augment, tmpTree, ctx);
622             }
623
624             return result;
625         }
626     }
627
628     private static DataSchemaNode getResultFromUses(final UsesNode u, final String currentName, final SchemaContext ctx) {
629         SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPathFromRoot());
630
631         Preconditions.checkArgument(targetGrouping instanceof GroupingDefinition,
632                 "Failed to generate code for augment in %s", u);
633         GroupingDefinition gr = (GroupingDefinition) targetGrouping;
634         return gr.getDataChildByName(currentName);
635     }
636
637     private static Module getParentModule(final SchemaNode node, final SchemaContext ctx) {
638         QName qname = node.getPath().getPathFromRoot().iterator().next();
639         URI namespace = qname.getNamespace();
640         Date revision = qname.getRevision();
641         return ctx.findModuleByNamespaceAndRevision(namespace, revision);
642     }
643
644     private static DataSchemaNode getTargetNode(final List<QName> tmpPath, final DataSchemaNode node, final SchemaContext ctx) {
645         DataSchemaNode result = node;
646         if (tmpPath.size() == 1) {
647             if (result != null && result.isAddedByUses()) {
648                 result = findOriginal(result, ctx);
649             }
650             return result;
651         } else {
652             DataSchemaNode newParent = result;
653             Collections.reverse(tmpPath);
654
655             tmpPath.remove(0);
656             for (QName name : tmpPath) {
657                 // searching by local name is must, because node has different
658                 // namespace in its original location
659                 if (newParent == null) {
660                     break;
661                 }
662                 if (newParent instanceof DataNodeContainer) {
663                     newParent = ((DataNodeContainer) newParent).getDataChildByName(name.getLocalName());
664                 } else {
665                     newParent = ((ChoiceNode) newParent).getCaseNodeByName(name.getLocalName());
666                 }
667             }
668             if (newParent != null && newParent.isAddedByUses()) {
669                 newParent = findOriginal(newParent, ctx);
670             }
671             return newParent;
672         }
673     }
674
675     private static AugmentationSchema findNodeInAugment(final Collection<AugmentationSchema> augments, final QName name) {
676         for (AugmentationSchema augment : augments) {
677             DataSchemaNode node = augment.getDataChildByName(name);
678             if (node != null) {
679                 return augment;
680             }
681         }
682         return null;
683     }
684
685     private static DataSchemaNode findCorrectTargetFromAugmentGrouping(final DataSchemaNode node,
686             final AugmentationSchema parentNode, final List<SchemaNode> dataTree, final SchemaContext ctx) {
687
688         DataSchemaNode result = null;
689         QName currentName = node.getQName();
690         List<QName> tmpPath = new ArrayList<>();
691         tmpPath.add(currentName);
692         int i = 1;
693         Object parent = null;
694
695         do {
696             if (dataTree.size() < 2 || dataTree.size() == i) {
697                 parent = parentNode;
698             } else {
699                 parent = dataTree.get(dataTree.size() - (i + 1));
700                 tmpPath.add(((SchemaNode) parent).getQName());
701             }
702
703             if (parent instanceof DataNodeContainer) {
704                 DataNodeContainer dataNodeParent = (DataNodeContainer) parent;
705                 for (UsesNode u : dataNodeParent.getUses()) {
706                     if (result == null) {
707                         result = getResultFromUses(u, currentName.getLocalName(), ctx);
708                     }
709                 }
710             }
711
712             if (result == null) {
713                 i = i + 1;
714                 currentName = ((SchemaNode) parent).getQName();
715             }
716         } while (result == null);
717
718         if (result != null) {
719             result = getTargetNode(tmpPath, result, ctx);
720         }
721         return result;
722     }
723
724     /**
725      * Transforms string representation of XPath to Queue of QNames. The XPath
726      * is split by "/" and for each part of XPath is assigned correct module in
727      * Schema Path. <br>
728      * If Schema Context, Parent Module or XPath string contains
729      * <code>null</code> values, the method will throws IllegalArgumentException
730      *
731      * @throws IllegalArgumentException
732      *
733      * @param context
734      *            Schema Context
735      * @param parentModule
736      *            Parent Module
737      * @param xpath
738      *            XPath String
739      * @return return a list of QName
740      */
741     private static List<QName> xpathToQNamePath(final SchemaContext context, final Module parentModule, final String xpath) {
742         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
743         Preconditions.checkArgument(parentModule != null, "Parent Module reference cannot be NULL");
744         Preconditions.checkArgument(xpath != null, "XPath string reference cannot be NULL");
745
746         List<QName> path = new LinkedList<QName>();
747         for (String pathComponent : SLASH_SPLITTER.split(xpath)) {
748             if (!pathComponent.isEmpty()) {
749                 path.add(stringPathPartToQName(context, parentModule, pathComponent));
750             }
751         }
752         return path;
753     }
754
755     /**
756      * Transforms part of Prefixed Path as java String to QName. <br>
757      * If the string contains module prefix separated by ":" (i.e.
758      * mod:container) this module is provided from from Parent Module list of
759      * imports. If the Prefixed module is present in Schema Context the QName
760      * can be constructed. <br>
761      * If the Prefixed Path Part does not contains prefix the Parent's Module
762      * namespace is taken for construction of QName. <br>
763      * If Schema Context, Parent Module or Prefixed Path Part refers to
764      * <code>null</code> the method will throw IllegalArgumentException
765      *
766      * @throws IllegalArgumentException
767      *
768      * @param context
769      *            Schema Context
770      * @param parentModule
771      *            Parent Module
772      * @param prefixedPathPart
773      *            Prefixed Path Part string
774      * @return QName from prefixed Path Part String.
775      */
776     private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule, final String prefixedPathPart) {
777         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
778         Preconditions.checkArgument(parentModule != null, "Parent Module reference cannot be NULL");
779         Preconditions.checkArgument(prefixedPathPart != null, "Prefixed Path Part cannot be NULL!");
780
781         if (prefixedPathPart.indexOf(':') != -1) {
782             final Iterator<String> prefixedName = COLON_SPLITTER.split(prefixedPathPart).iterator();
783             final String modulePrefix = prefixedName.next();
784
785             Module module = resolveModuleForPrefix(context, parentModule, modulePrefix);
786             Preconditions.checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s",
787                     modulePrefix, parentModule.getName());
788
789             // FIXME: Module should have a QNameModule handle
790             return QName.create(module.getNamespace(), module.getRevision(), prefixedName.next());
791         } else {
792             return QName.create(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
793         }
794     }
795
796     /**
797      * Method will attempt to resolve and provide Module reference for specified
798      * module prefix. Each Yang module could contains multiple imports which
799      * MUST be associated with corresponding module prefix. The method simply
800      * looks into module imports and returns the module that is bounded with
801      * specified prefix. If the prefix is not present in module or the prefixed
802      * module is not present in specified Schema Context, the method will return
803      * <code>null</code>. <br>
804      * If String prefix is the same as prefix of the specified Module the
805      * reference to this module is returned. <br>
806      * If Schema Context, Module or Prefix are referring to <code>null</code>
807      * the method will return IllegalArgumentException
808      *
809      * @throws IllegalArgumentException
810      *
811      * @param context
812      *            Schema Context
813      * @param module
814      *            Yang Module
815      * @param prefix
816      *            Module Prefix
817      * @return Module for given prefix in specified Schema Context if is
818      *         present, otherwise returns <code>null</code>
819      */
820     private static Module resolveModuleForPrefix(final SchemaContext context, final Module module, final String prefix) {
821         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
822         Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
823         Preconditions.checkArgument(prefix != null, "Prefix string cannot be NULL");
824
825         if (prefix.equals(module.getPrefix())) {
826             return module;
827         }
828
829         Set<ModuleImport> imports = module.getImports();
830         for (ModuleImport mi : imports) {
831             if (prefix.equals(mi.getPrefix())) {
832                 return context.findModuleByName(mi.getModuleName(), mi.getRevision());
833             }
834         }
835         return null;
836     }
837
838     /**
839      * @throws IllegalArgumentException
840      *
841      * @param context
842      *            Schema Context
843      * @param module
844      *            Yang Module
845      * @param relativeXPath
846      *            Non conditional Revision Aware Relative XPath
847      * @param leafrefSchemaPath
848      *            Schema Path for Leafref
849      * @return list of QName
850      */
851     private static Iterable<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
852             final RevisionAwareXPath relativeXPath, final SchemaNode leafrefParentNode) {
853         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
854         Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
855         Preconditions.checkArgument(relativeXPath != null, "Non Conditional Revision Aware XPath cannot be NULL");
856         Preconditions.checkState(!relativeXPath.isAbsolute(),
857                 "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
858                         + "for non relative Revision Aware XPath use findDataSchemaNode method");
859         Preconditions.checkState(leafrefParentNode.getPath() != null,
860                 "Schema Path reference for Leafref cannot be NULL");
861
862         final Iterable<String> xpaths = SLASH_SPLITTER.split(relativeXPath.toString());
863
864         // Find out how many "parent" components there are
865         // FIXME: is .contains() the right check here?
866         int colCount = 0;
867         for (Iterator<String> it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) {
868             ++colCount;
869         }
870
871         final Iterable<QName> parent = leafrefParentNode.getPath().getPathFromRoot();
872         return Iterables.concat(Iterables.limit(parent, Iterables.size(parent) - colCount),
873                 Iterables.transform(Iterables.skip(xpaths, colCount), new Function<String, QName>() {
874                     @Override
875                     public QName apply(final String input) {
876                         return stringPathPartToQName(context, module, input);
877                     }
878                 }));
879     }
880 }