Refactored parsing of uses and augment statements.
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / util / ParserUtils.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.parser.util;
9
10 import java.net.URI;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Date;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.TreeMap;
19
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
23 import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
24 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
28 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.Module;
32 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
33 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
34 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
35 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
39 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
40 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
42 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
43 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
47 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
48 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
53 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
54 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
55 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
57 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
58 import org.opendaylight.yangtools.yang.model.util.BinaryType;
59 import org.opendaylight.yangtools.yang.model.util.BitsType;
60 import org.opendaylight.yangtools.yang.model.util.BooleanType;
61 import org.opendaylight.yangtools.yang.model.util.Decimal64;
62 import org.opendaylight.yangtools.yang.model.util.EmptyType;
63 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
64 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
65 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
66 import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.model.util.Leafref;
68 import org.opendaylight.yangtools.yang.model.util.UnionType;
69 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
70 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
71 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
72 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
73 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
74 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
75 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
76 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
77 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
78 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
79 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
80 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder;
81 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
82 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder.ChoiceNodeImpl;
83 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
84 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder.ChoiceCaseNodeImpl;
85 import org.opendaylight.yangtools.yang.parser.builder.impl.ConstraintsBuilder;
86 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
87 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder.ContainerSchemaNodeImpl;
88 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingBuilderImpl;
89 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
90 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
91 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
92 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder;
93 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder.ListSchemaNodeImpl;
94 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
95 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder;
96 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder.NotificationDefinitionImpl;
97 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl;
98 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
99
100 public final class ParserUtils {
101
102     private ParserUtils() {
103     }
104
105     /**
106      * Create new SchemaPath from given path and qname.
107      *
108      * @param schemaPath
109      * @param qname
110      * @return
111      */
112     public static SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
113         List<QName> path = new ArrayList<>(schemaPath.getPath());
114         path.addAll(Arrays.asList(qname));
115         return new SchemaPath(path, schemaPath.isAbsolute());
116     }
117
118     /**
119      * Get module import referenced by given prefix.
120      *
121      * @param builder
122      *            module to search
123      * @param prefix
124      *            prefix associated with import
125      * @return ModuleImport based on given prefix
126      */
127     public static ModuleImport getModuleImport(final ModuleBuilder builder, final String prefix) {
128         ModuleImport moduleImport = null;
129         for (ModuleImport mi : builder.getModuleImports()) {
130             if (mi.getPrefix().equals(prefix)) {
131                 moduleImport = mi;
132                 break;
133             }
134         }
135         return moduleImport;
136     }
137
138     /**
139      * Find dependent module based on given prefix
140      *
141      * @param modules
142      *            all available modules
143      * @param module
144      *            current module
145      * @param prefix
146      *            target module prefix
147      * @param line
148      *            current line in yang model
149      * @return module builder if found, null otherwise
150      */
151     public static ModuleBuilder findDependentModuleBuilder(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
152             final ModuleBuilder module, final String prefix, final int line) {
153         ModuleBuilder dependentModule = null;
154         Date dependentModuleRevision = null;
155
156         if (prefix.equals(module.getPrefix())) {
157             dependentModule = module;
158         } else {
159             final ModuleImport dependentModuleImport = getModuleImport(module, prefix);
160             if (dependentModuleImport == null) {
161                 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
162             }
163             final String dependentModuleName = dependentModuleImport.getModuleName();
164             dependentModuleRevision = dependentModuleImport.getRevision();
165
166             final TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
167             if (moduleBuildersByRevision == null) {
168                 return null;
169             }
170             if (dependentModuleRevision == null) {
171                 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
172             } else {
173                 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
174             }
175         }
176         return dependentModule;
177     }
178
179     /**
180      * Find module from context based on prefix.
181      *
182      * @param context
183      *            schema context
184      * @param currentModule
185      *            current module
186      * @param prefix
187      *            current prefix used to reference dependent module
188      * @param line
189      *            current line in yang model
190      * @return module based on given prefix if found in context, null otherwise
191      */
192     public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
193             final String prefix, final int line) {
194         TreeMap<Date, Module> modulesByRevision = new TreeMap<Date, Module>();
195
196         final ModuleImport dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
197         if (dependentModuleImport == null) {
198             throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
199         }
200         final String dependentModuleName = dependentModuleImport.getModuleName();
201         final Date dependentModuleRevision = dependentModuleImport.getRevision();
202
203         for (Module contextModule : context.getModules()) {
204             if (contextModule.getName().equals(dependentModuleName)) {
205                 Date revision = contextModule.getRevision();
206                 if (revision == null) {
207                     revision = new Date(0L);
208                 }
209                 modulesByRevision.put(revision, contextModule);
210                 break;
211             }
212         }
213
214         Module result = null;
215         if (dependentModuleRevision == null) {
216             result = modulesByRevision.get(modulesByRevision.firstKey());
217         } else {
218             result = modulesByRevision.get(dependentModuleRevision);
219         }
220
221         return result;
222     }
223
224     /**
225      * Parse XPath string.
226      *
227      * @param xpathString
228      *            as String
229      * @return SchemaPath from given String
230      */
231     public static SchemaPath parseXPathString(final String xpathString) {
232         final boolean absolute = xpathString.startsWith("/");
233         final String[] splittedPath = xpathString.split("/");
234         final List<QName> path = new ArrayList<QName>();
235         QName name;
236         for (String pathElement : splittedPath) {
237             if (pathElement.length() > 0) {
238                 final String[] splittedElement = pathElement.split(":");
239                 if (splittedElement.length == 1) {
240                     name = new QName(null, null, null, splittedElement[0]);
241                 } else {
242                     name = new QName(null, null, splittedElement[0], splittedElement[1]);
243                 }
244                 path.add(name);
245             }
246         }
247         return new SchemaPath(path, absolute);
248     }
249
250     /**
251      * Add all augment's child nodes to given target.
252      *
253      * @param augment
254      *            builder of augment statement
255      * @param target
256      *            augmentation target node
257      */
258     public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
259         for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
260             DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
261             childCopy.setAugmenting(true);
262             correctNodePath(child, target.getPath());
263             correctNodePath(childCopy, target.getPath());
264             try {
265                 target.addChildNode(childCopy);
266             } catch(YangParseException e) {
267                 // more descriptive message
268                 throw new YangParseException(augment.getModuleName(), augment.getLine(), "Failed to perform augmentation: "+ e.getMessage());
269             }
270
271         }
272         for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
273             target.addUsesNode(CopyUtils.copyUses(usesNode, target));
274         }
275     }
276
277     /**
278      * Add all augment's child nodes to given target.
279      *
280      * @param augment
281      *            builder of augment statement
282      * @param target
283      *            augmentation target choice node
284      */
285     public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
286         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
287             DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
288             childCopy.setAugmenting(true);
289             correctNodePath(builder, target.getPath());
290             correctNodePath(childCopy, target.getPath());
291             target.addCase(childCopy);
292         }
293         for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
294             if (usesNode != null) {
295                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
296                         "Error in augment parsing: cannot augment uses to choice");
297             }
298         }
299
300     }
301
302     static void correctNodePath(final SchemaNodeBuilder node, final SchemaPath parentSchemaPath) {
303         // set correct path
304         List<QName> targetNodePath = new ArrayList<QName>(parentSchemaPath.getPath());
305         targetNodePath.add(node.getQName());
306         node.setPath(new SchemaPath(targetNodePath, true));
307
308         // set correct path for all child nodes
309         if (node instanceof DataNodeContainerBuilder) {
310             DataNodeContainerBuilder dataNodeContainer = (DataNodeContainerBuilder) node;
311             for (DataSchemaNodeBuilder child : dataNodeContainer.getChildNodeBuilders()) {
312                 correctNodePath(child, node.getPath());
313             }
314         }
315
316         // set correct path for all cases
317         if (node instanceof ChoiceBuilder) {
318             ChoiceBuilder choiceBuilder = (ChoiceBuilder) node;
319             for (ChoiceCaseBuilder choiceCaseBuilder : choiceBuilder.getCases()) {
320                 correctNodePath(choiceCaseBuilder, node.getPath());
321             }
322         }
323
324         // if node can contains type, correct path for this type too
325         if (node instanceof TypeAwareBuilder) {
326             TypeAwareBuilder nodeBuilder = (TypeAwareBuilder) node;
327             correctTypeAwareNodePath(nodeBuilder, node.getPath());
328         }
329     }
330
331     /**
332      * Repair schema path of node type.
333      *
334      * @param node
335      *            node which contains type statement
336      * @param parentSchemaPath
337      *            schema path of parent node
338      */
339     private static void correctTypeAwareNodePath(final TypeAwareBuilder node, final SchemaPath parentSchemaPath) {
340         final QName nodeBuilderQName = node.getQName();
341         final TypeDefinition<?> nodeType = node.getType();
342
343         Integer fd = null;
344         List<LengthConstraint> lengths = null;
345         List<PatternConstraint> patterns = null;
346         List<RangeConstraint> ranges = null;
347
348         if (nodeType != null) {
349             if (nodeType instanceof ExtendedType) {
350                 ExtendedType et = (ExtendedType) nodeType;
351                 if (nodeType.getQName().getLocalName().equals(nodeType.getBaseType().getQName().getLocalName())) {
352                     fd = et.getFractionDigits();
353                     lengths = et.getLengths();
354                     patterns = et.getPatterns();
355                     ranges = et.getRanges();
356                     if (!hasConstraints(fd, lengths, patterns, ranges)) {
357                         return;
358                     }
359                 }
360             }
361             TypeDefinition<?> newType = createCorrectTypeDefinition(parentSchemaPath, nodeBuilderQName, nodeType);
362             node.setType(newType);
363         } else {
364             TypeDefinitionBuilder nodeBuilderTypedef = node.getTypedef();
365
366             fd = nodeBuilderTypedef.getFractionDigits();
367             lengths = nodeBuilderTypedef.getLengths();
368             patterns = nodeBuilderTypedef.getPatterns();
369             ranges = nodeBuilderTypedef.getRanges();
370
371             String tdbTypeName = nodeBuilderTypedef.getQName().getLocalName();
372             String baseTypeName = null;
373             if (nodeBuilderTypedef.getType() == null) {
374                 baseTypeName = nodeBuilderTypedef.getTypedef().getQName().getLocalName();
375             } else {
376                 baseTypeName = nodeBuilderTypedef.getType().getQName().getLocalName();
377             }
378             if (!(tdbTypeName.equals(baseTypeName))) {
379                 return;
380             }
381
382             if (!hasConstraints(fd, lengths, patterns, ranges)) {
383                 return;
384             }
385
386             SchemaPath newSchemaPath = createSchemaPath(nodeBuilderTypedef.getPath(), nodeBuilderQName,
387                     nodeBuilderTypedef.getQName());
388             nodeBuilderTypedef.setPath(newSchemaPath);
389         }
390     }
391
392     /**
393      * Check if there are some constraints.
394      *
395      * @param fd
396      *            fraction digits
397      * @param lengths
398      *            length constraints
399      * @param patterns
400      *            pattern constraints
401      * @param ranges
402      *            range constraints
403      * @return true, if any of constraints are present, false otherwise
404      */
405     private static boolean hasConstraints(final Integer fd, final List<LengthConstraint> lengths,
406             final List<PatternConstraint> patterns, final List<RangeConstraint> ranges) {
407         if (fd == null && (lengths == null || lengths.isEmpty()) && (patterns == null || patterns.isEmpty())
408                 && (ranges == null || ranges.isEmpty())) {
409             return false;
410         } else {
411             return true;
412         }
413     }
414
415     private static TypeDefinition<?> createCorrectTypeDefinition(SchemaPath parentSchemaPath, QName nodeQName,
416             TypeDefinition<?> nodeType) {
417         TypeDefinition<?> result = null;
418
419         if (nodeType != null) {
420             QName nodeTypeQName = nodeType.getQName();
421             SchemaPath newSchemaPath = createSchemaPath(parentSchemaPath, nodeQName, nodeTypeQName);
422
423             if (nodeType instanceof BinaryTypeDefinition) {
424                 BinaryTypeDefinition binType = (BinaryTypeDefinition) nodeType;
425
426                 // List<Byte> bytes = (List<Byte>) binType.getDefaultValue();
427                 // workaround to get rid of 'Unchecked cast' warning
428                 List<Byte> bytes = new ArrayList<Byte>();
429                 Object defaultValue = binType.getDefaultValue();
430                 if (defaultValue instanceof List) {
431                     for (Object o : List.class.cast(defaultValue)) {
432                         if (o instanceof Byte) {
433                             bytes.add((Byte) o);
434                         }
435                     }
436                 }
437                 result = new BinaryType(newSchemaPath, bytes);
438             } else if (nodeType instanceof BitsTypeDefinition) {
439                 BitsTypeDefinition bitsType = (BitsTypeDefinition) nodeType;
440                 result = new BitsType(newSchemaPath, bitsType.getBits());
441             } else if (nodeType instanceof BooleanTypeDefinition) {
442                 result = new BooleanType(newSchemaPath);
443             } else if (nodeType instanceof DecimalTypeDefinition) {
444                 DecimalTypeDefinition decimalType = (DecimalTypeDefinition) nodeType;
445                 result = new Decimal64(newSchemaPath, decimalType.getFractionDigits());
446             } else if (nodeType instanceof EmptyTypeDefinition) {
447                 result = new EmptyType(newSchemaPath);
448             } else if (nodeType instanceof EnumTypeDefinition) {
449                 EnumTypeDefinition enumType = (EnumTypeDefinition) nodeType;
450                 result = new EnumerationType(newSchemaPath, (EnumPair) enumType.getDefaultValue(), enumType.getValues());
451             } else if (nodeType instanceof IdentityrefTypeDefinition) {
452                 IdentityrefTypeDefinition idrefType = (IdentityrefTypeDefinition) nodeType;
453                 result = new IdentityrefType(idrefType.getIdentity(), newSchemaPath);
454             } else if (nodeType instanceof InstanceIdentifierTypeDefinition) {
455                 InstanceIdentifierTypeDefinition instIdType = (InstanceIdentifierTypeDefinition) nodeType;
456                 return new InstanceIdentifier(newSchemaPath, instIdType.getPathStatement(),
457                         instIdType.requireInstance());
458             } else if (nodeType instanceof StringTypeDefinition) {
459                 result = TypeUtils.createNewStringType(parentSchemaPath, nodeQName, (StringTypeDefinition) nodeType);
460             } else if (nodeType instanceof IntegerTypeDefinition) {
461                 result = TypeUtils.createNewIntType(parentSchemaPath, nodeQName, (IntegerTypeDefinition) nodeType);
462             } else if (nodeType instanceof UnsignedIntegerTypeDefinition) {
463                 result = TypeUtils.createNewUintType(parentSchemaPath, nodeQName,
464                         (UnsignedIntegerTypeDefinition) nodeType);
465             } else if (nodeType instanceof LeafrefTypeDefinition) {
466                 result = new Leafref(newSchemaPath, ((LeafrefTypeDefinition) nodeType).getPathStatement());
467             } else if (nodeType instanceof UnionTypeDefinition) {
468                 UnionTypeDefinition unionType = (UnionTypeDefinition) nodeType;
469                 return new UnionType(newSchemaPath, unionType.getTypes());
470             } else if (nodeType instanceof ExtendedType) {
471                 ExtendedType extType = (ExtendedType) nodeType;
472                 result = TypeUtils.createNewExtendedType(extType, newSchemaPath);
473             }
474         }
475         return result;
476     }
477
478     /**
479      * Create LeafSchemaNodeBuilder from given LeafSchemaNode.
480      *
481      * @param leaf
482      *            leaf from which to create builder
483      * @param qname
484      * @param moduleName
485      *            current module name
486      * @param line
487      *            line in module
488      * @return leaf builder based on given leaf node
489      */
490     public static LeafSchemaNodeBuilder createLeafBuilder(LeafSchemaNode leaf, QName qname, String moduleName, int line) {
491         final LeafSchemaNodeBuilder builder = new LeafSchemaNodeBuilder(moduleName, line, qname, leaf.getPath());
492         convertDataSchemaNode(leaf, builder);
493         builder.setConfiguration(leaf.isConfiguration());
494         final TypeDefinition<?> type = leaf.getType();
495         builder.setType(type);
496         builder.setPath(leaf.getPath());
497         builder.setUnknownNodes(leaf.getUnknownSchemaNodes());
498         builder.setDefaultStr(leaf.getDefault());
499         builder.setUnits(leaf.getUnits());
500         return builder;
501     }
502
503     /**
504      * Create ContainerSchemaNodeBuilder from given ContainerSchemaNode.
505      *
506      * @param container
507      * @param qname
508      * @param moduleName
509      *            current module name
510      * @param line
511      *            current line in module
512      * @return container builder based on given container node
513      */
514     public static ContainerSchemaNodeBuilder createContainer(ContainerSchemaNode container, QName qname,
515             String moduleName, int line) {
516         final ContainerSchemaNodeBuilder builder = new ContainerSchemaNodeBuilder(moduleName, line, qname,
517                 container.getPath());
518         convertDataSchemaNode(container, builder);
519         builder.setConfiguration(container.isConfiguration());
520         builder.setUnknownNodes(container.getUnknownSchemaNodes());
521         builder.setChildNodes(container.getChildNodes());
522         builder.setGroupings(container.getGroupings());
523         builder.setTypedefs(container.getTypeDefinitions());
524         builder.setAugmentations(container.getAvailableAugmentations());
525         builder.setUsesnodes(container.getUses());
526         builder.setPresence(container.isPresenceContainer());
527         return builder;
528     }
529
530     /**
531      * Create ListSchemaNodeBuilder from given ListSchemaNode.
532      *
533      * @param list
534      * @param qname
535      * @param moduleName
536      *            current module name
537      * @param line
538      *            current line in module
539      * @return list builder based on given list node
540      */
541     public static ListSchemaNodeBuilder createList(ListSchemaNode list, QName qname, String moduleName, int line) {
542         ListSchemaNodeBuilder builder = new ListSchemaNodeBuilder(moduleName, line, qname, list.getPath());
543         convertDataSchemaNode(list, builder);
544         builder.setConfiguration(list.isConfiguration());
545         builder.setUnknownNodes(list.getUnknownSchemaNodes());
546         builder.setTypedefs(list.getTypeDefinitions());
547         builder.setChildNodes(list.getChildNodes());
548         builder.setGroupings(list.getGroupings());
549         builder.setAugmentations(list.getAvailableAugmentations());
550         builder.setUsesnodes(list.getUses());
551         builder.setUserOrdered(builder.isUserOrdered());
552         return builder;
553     }
554
555     /**
556      * Create LeafListSchemaNodeBuilder from given LeafListSchemaNode.
557      *
558      * @param leafList
559      * @param qname
560      * @param moduleName
561      *            current module name
562      * @param line
563      *            current line in module
564      * @return leaf-list builder based on given leaf-list node
565      */
566     public static LeafListSchemaNodeBuilder createLeafList(LeafListSchemaNode leafList, QName qname, String moduleName,
567             int line) {
568         final LeafListSchemaNodeBuilder builder = new LeafListSchemaNodeBuilder(moduleName, line, qname,
569                 leafList.getPath());
570         convertDataSchemaNode(leafList, builder);
571         builder.setConfiguration(leafList.isConfiguration());
572         builder.setType(leafList.getType());
573         builder.setUnknownNodes(leafList.getUnknownSchemaNodes());
574         builder.setUserOrdered(leafList.isUserOrdered());
575         return builder;
576     }
577
578     /**
579      * Create ChoiceBuilder from given ChoiceNode.
580      *
581      * @param choice
582      * @param qname
583      * @param moduleName
584      *            current module name
585      * @param line
586      *            current line in module
587      * @return choice builder based on given choice node
588      */
589     public static ChoiceBuilder createChoice(ChoiceNode choice, QName qname, String moduleName, int line) {
590         final ChoiceBuilder builder = new ChoiceBuilder(moduleName, line, qname);
591         convertDataSchemaNode(choice, builder);
592         builder.setConfiguration(choice.isConfiguration());
593         builder.setCases(choice.getCases());
594         builder.setUnknownNodes(choice.getUnknownSchemaNodes());
595         builder.setDefaultCase(choice.getDefaultCase());
596         return builder;
597     }
598
599     /**
600      * Create AnyXmlBuilder from given AnyXmlSchemaNode.
601      *
602      * @param anyxml
603      * @param qname
604      * @param moduleName
605      *            current module name
606      * @param line
607      *            current line in module
608      * @return anyxml builder based on given anyxml node
609      */
610     public static AnyXmlBuilder createAnyXml(AnyXmlSchemaNode anyxml, QName qname, String moduleName, int line) {
611         final AnyXmlBuilder builder = new AnyXmlBuilder(moduleName, line, qname, anyxml.getPath());
612         convertDataSchemaNode(anyxml, builder);
613         builder.setConfiguration(anyxml.isConfiguration());
614         builder.setUnknownNodes(anyxml.getUnknownSchemaNodes());
615         return builder;
616     }
617
618     /**
619      * Create GroupingBuilder from given GroupingDefinition.
620      *
621      * @param grouping
622      * @param qname
623      * @param moduleName
624      *            current module name
625      * @param line
626      *            current line in module
627      * @return grouping builder based on given grouping node
628      */
629     public static GroupingBuilder createGrouping(GroupingDefinition grouping, QName qname, String moduleName, int line) {
630         final GroupingBuilderImpl builder = new GroupingBuilderImpl(moduleName, line, qname);
631         builder.setPath(grouping.getPath());
632         builder.setChildNodes(grouping.getChildNodes());
633         builder.setGroupings(grouping.getGroupings());
634         builder.setTypedefs(grouping.getTypeDefinitions());
635         builder.setUsesnodes(grouping.getUses());
636         builder.setUnknownNodes(grouping.getUnknownSchemaNodes());
637         builder.setDescription(grouping.getDescription());
638         builder.setReference(grouping.getReference());
639         builder.setStatus(grouping.getStatus());
640         return builder;
641     }
642
643     /**
644      * Create TypeDefinitionBuilder from given ExtendedType.
645      *
646      * @param typedef
647      * @param qname
648      * @param moduleName
649      *            current module name
650      * @param line
651      *            current line in module
652      * @return typedef builder based on given typedef node
653      */
654     public static TypeDefinitionBuilder createTypedef(ExtendedType typedef, QName qname, String moduleName, int line) {
655         final TypeDefinitionBuilderImpl builder = new TypeDefinitionBuilderImpl(moduleName, line, qname);
656         builder.setPath(typedef.getPath());
657         builder.setDefaultValue(typedef.getDefaultValue());
658         builder.setUnits(typedef.getUnits());
659         builder.setDescription(typedef.getDescription());
660         builder.setReference(typedef.getReference());
661         builder.setStatus(typedef.getStatus());
662         builder.setRanges(typedef.getRanges());
663         builder.setLengths(typedef.getLengths());
664         builder.setPatterns(typedef.getPatterns());
665         builder.setFractionDigits(typedef.getFractionDigits());
666         final TypeDefinition<?> type = typedef.getBaseType();
667         builder.setType(type);
668         builder.setUnits(typedef.getUnits());
669         builder.setUnknownNodes(typedef.getUnknownSchemaNodes());
670         return builder;
671     }
672
673     /**
674      * Create UnknownSchemaNodeBuilder from given UnknownSchemaNode.
675      *
676      * @param unknownNode
677      * @param qname
678      * @param moduleName
679      *            current module name
680      * @param line
681      *            current line in module
682      * @return unknown node builder based on given unknown node
683      */
684     public static UnknownSchemaNodeBuilder createUnknownSchemaNode(UnknownSchemaNode unknownNode, QName qname,
685             String moduleName, int line) {
686         final UnknownSchemaNodeBuilder builder = new UnknownSchemaNodeBuilder(moduleName, line, qname);
687         builder.setPath(unknownNode.getPath());
688         builder.setUnknownNodes(unknownNode.getUnknownSchemaNodes());
689         builder.setDescription(unknownNode.getDescription());
690         builder.setReference(unknownNode.getReference());
691         builder.setStatus(unknownNode.getStatus());
692         builder.setAddedByUses(unknownNode.isAddedByUses());
693         builder.setNodeType(unknownNode.getNodeType());
694         builder.setNodeParameter(unknownNode.getNodeParameter());
695         return builder;
696     }
697
698     /**
699      * Set DataSchemaNode arguments to builder object
700      *
701      * @param node
702      *            node from which arguments should be read
703      * @param builder
704      *            builder to which arguments should be set
705      */
706     private static void convertDataSchemaNode(DataSchemaNode node, DataSchemaNodeBuilder builder) {
707         builder.setPath(node.getPath());
708         builder.setDescription(node.getDescription());
709         builder.setReference(node.getReference());
710         builder.setStatus(node.getStatus());
711         builder.setAugmenting(node.isAugmenting());
712         copyConstraintsFromDefinition(node.getConstraints(), builder.getConstraints());
713     }
714
715     /**
716      * Copy constraints from constraints definition to constraints builder.
717      *
718      * @param nodeConstraints
719      *            definition from which constraints will be copied
720      * @param constraints
721      *            builder to which constraints will be added
722      */
723     private static void copyConstraintsFromDefinition(final ConstraintDefinition nodeConstraints,
724             final ConstraintsBuilder constraints) {
725         final RevisionAwareXPath when = nodeConstraints.getWhenCondition();
726         final Set<MustDefinition> must = nodeConstraints.getMustConstraints();
727
728         if (when != null) {
729             constraints.addWhenCondition(when.toString());
730         }
731         if (must != null) {
732             for (MustDefinition md : must) {
733                 constraints.addMustDefinition(md);
734             }
735         }
736         constraints.setMandatory(nodeConstraints.isMandatory());
737         constraints.setMinElements(nodeConstraints.getMinElements());
738         constraints.setMaxElements(nodeConstraints.getMaxElements());
739     }
740
741     /**
742      * Find augment target node and perform augmentation.
743      *
744      * @param augment
745      * @param firstNodeParent
746      *            parent of first node in path
747      * @param path
748      *            path to augment target
749      * @param isUsesAugment
750      *            if this augment is defined under uses node
751      * @return true if augment process succeed, false otherwise
752      */
753     public static boolean processAugmentation(final AugmentationSchemaBuilder augment, final Builder firstNodeParent,
754             final List<QName> path, boolean isUsesAugment) {
755         // traverse augment target path and try to reach target node
756         String currentName = null;
757         Builder currentParent = firstNodeParent;
758
759         for (int i = 0; i < path.size(); i++) {
760             QName qname = path.get(i);
761
762             currentName = qname.getLocalName();
763             if (currentParent instanceof DataNodeContainerBuilder) {
764                 DataSchemaNodeBuilder nodeFound = ((DataNodeContainerBuilder) currentParent)
765                         .getDataChildByName(currentName);
766                 // if not found as regular child, search in uses
767                 if (nodeFound == null) {
768                     boolean found = false;
769                     for (UsesNodeBuilder unb : ((DataNodeContainerBuilder) currentParent).getUsesNodes()) {
770                         DataSchemaNodeBuilder result = findNodeInUses(currentName, unb);
771                         if (result != null) {
772                             currentParent = result;
773                             found = true;
774                             break;
775                         }
776                     }
777                     // if not found even in uses nodes, return false
778                     if (!found) {
779                         return false;
780                     }
781                 } else {
782                     currentParent = nodeFound;
783                 }
784             } else if (currentParent instanceof ChoiceBuilder) {
785                 currentParent = ((ChoiceBuilder) currentParent).getCaseNodeByName(currentName);
786             } else {
787                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
788                         "Error in augment parsing: failed to find node " + currentName);
789             }
790
791             // if node in path not found, return false
792             if (currentParent == null) {
793                 return false;
794             }
795         }
796         if (!(currentParent instanceof DataSchemaNodeBuilder)) {
797             throw new YangParseException(
798                     augment.getModuleName(),
799                     augment.getLine(),
800                     "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
801         }
802
803         if (currentParent instanceof ChoiceBuilder) {
804             fillAugmentTarget(augment, (ChoiceBuilder) currentParent);
805         } else {
806             fillAugmentTarget(augment, (DataNodeContainerBuilder) currentParent);
807         }
808         ((AugmentationTargetBuilder) currentParent).addAugmentation(augment);
809         SchemaPath oldPath = ((DataSchemaNodeBuilder) currentParent).getPath();
810         augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
811         augment.setResolved(true);
812
813         return true;
814     }
815
816     private static DataSchemaNodeBuilder findNodeInUses(String localName, UsesNodeBuilder uses) {
817         Set<DataSchemaNodeBuilder> usesTargetChildren = uses.getTargetChildren();
818         if (usesTargetChildren != null) {
819             for (DataSchemaNodeBuilder child : uses.getTargetChildren()) {
820                 if (child.getQName().getLocalName().equals(localName)) {
821                     return child;
822                 }
823             }
824         }
825         for (UsesNodeBuilder usesNode : uses.getTargetGroupingUses()) {
826             DataSchemaNodeBuilder result = findNodeInUses(localName, usesNode);
827             if (result != null) {
828                 return result;
829             }
830         }
831         return null;
832     }
833
834     /**
835      * Find augment target node in given context and perform augmentation.
836      *
837      * @param augment
838      * @param path
839      *            path to augment target
840      * @param module
841      *            current module
842      * @param prefix
843      *            current prefix of target module
844      * @param context
845      *            SchemaContext containing already resolved modules
846      * @return true if augment process succeed, false otherwise
847      */
848     public static boolean processAugmentationOnContext(final AugmentationSchemaBuilder augment, final List<QName> path,
849             final ModuleBuilder module, final String prefix, final SchemaContext context) {
850         final int line = augment.getLine();
851         final Module dependentModule = findModuleFromContext(context, module, prefix, line);
852         if (dependentModule == null) {
853             throw new YangParseException(module.getName(), line,
854                     "Error in augment parsing: failed to find module with prefix " + prefix + ".");
855         }
856
857         String currentName = path.get(0).getLocalName();
858         SchemaNode currentParent = dependentModule.getDataChildByName(currentName);
859         if (currentParent == null) {
860             Set<NotificationDefinition> notifications = dependentModule.getNotifications();
861             for (NotificationDefinition ntf : notifications) {
862                 if (ntf.getQName().getLocalName().equals(currentName)) {
863                     currentParent = ntf;
864                     break;
865                 }
866             }
867         }
868         if (currentParent == null) {
869             throw new YangParseException(module.getName(), line, "Error in augment parsing: failed to find node "
870                     + currentName + ".");
871         }
872
873         for (int i = 1; i < path.size(); i++) {
874             currentName = path.get(i).getLocalName();
875             if (currentParent instanceof DataNodeContainer) {
876                 currentParent = ((DataNodeContainer) currentParent).getDataChildByName(currentName);
877             } else if (currentParent instanceof ChoiceNode) {
878                 currentParent = ((ChoiceNode) currentParent).getCaseNodeByName(currentName);
879             } else {
880                 throw new YangParseException(augment.getModuleName(), line,
881                         "Error in augment parsing: failed to find node " + currentName);
882             }
883             // if node in path not found, return false
884             if (currentParent == null) {
885                 throw new YangParseException(module.getName(), line, "Error in augment parsing: failed to find node "
886                         + currentName + ".");
887             }
888         }
889
890         if (currentParent instanceof ContainerSchemaNodeImpl) {
891             // includes container, input and output statement
892             ContainerSchemaNodeImpl c = (ContainerSchemaNodeImpl) currentParent;
893             ContainerSchemaNodeBuilder cb = c.toBuilder();
894             fillAugmentTarget(augment, cb);
895             ((AugmentationTargetBuilder) cb).addAugmentation(augment);
896             SchemaPath oldPath = cb.getPath();
897             cb.rebuild();
898             augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
899             augment.setResolved(true);
900         } else if (currentParent instanceof ListSchemaNodeImpl) {
901             ListSchemaNodeImpl l = (ListSchemaNodeImpl) currentParent;
902             ListSchemaNodeBuilder lb = l.toBuilder();
903             fillAugmentTarget(augment, lb);
904             ((AugmentationTargetBuilder) lb).addAugmentation(augment);
905             SchemaPath oldPath = lb.getPath();
906             lb.rebuild();
907             augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
908             augment.setResolved(true);
909         } else if (currentParent instanceof ChoiceNodeImpl) {
910             ChoiceNodeImpl ch = (ChoiceNodeImpl) currentParent;
911             ChoiceBuilder chb = ch.toBuilder();
912             fillAugmentTarget(augment, chb);
913             ((AugmentationTargetBuilder) chb).addAugmentation(augment);
914             SchemaPath oldPath = chb.getPath();
915             chb.rebuild();
916             augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
917             augment.setResolved(true);
918         } else if (currentParent instanceof ChoiceCaseNodeImpl) {
919             ChoiceCaseNodeImpl chc = (ChoiceCaseNodeImpl) currentParent;
920             ChoiceCaseBuilder chcb = chc.toBuilder();
921             fillAugmentTarget(augment, chcb);
922             ((AugmentationTargetBuilder) chcb).addAugmentation(augment);
923             SchemaPath oldPath = chcb.getPath();
924             chcb.rebuild();
925             augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
926             augment.setResolved(true);
927         } else if (currentParent instanceof NotificationDefinitionImpl) {
928             NotificationDefinitionImpl nd = (NotificationDefinitionImpl) currentParent;
929             NotificationBuilder nb = nd.toBuilder();
930             fillAugmentTarget(augment, nb);
931             ((AugmentationTargetBuilder) nb).addAugmentation(augment);
932             SchemaPath oldPath = nb.getPath();
933             nb.rebuild();
934             augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
935             augment.setResolved(true);
936         } else {
937             throw new YangParseException(module.getName(), line, "Target of type " + currentParent.getClass()
938                     + " cannot be augmented.");
939         }
940
941         return true;
942     }
943
944     public static QName findFullQName(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
945             final ModuleBuilder module, final IdentityrefTypeBuilder idref) {
946         QName result = null;
947         String baseString = idref.getBaseString();
948         if (baseString.contains(":")) {
949             String[] splittedBase = baseString.split(":");
950             if (splittedBase.length > 2) {
951                 throw new YangParseException(module.getName(), idref.getLine(), "Failed to parse identityref base: "
952                         + baseString);
953             }
954             String prefix = splittedBase[0];
955             String name = splittedBase[1];
956             ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, prefix, idref.getLine());
957             result = new QName(dependentModule.getNamespace(), dependentModule.getRevision(), prefix, name);
958         } else {
959             result = new QName(module.getNamespace(), module.getRevision(), module.getPrefix(), baseString);
960         }
961         return result;
962     }
963
964     /**
965      * Load uses target nodes and all uses target uses target nodes. Set this
966      * collection as uses final children.
967      *
968      * @param module
969      *            current module
970      * @param usesNode
971      */
972     public static void processUsesNode(final UsesNodeBuilder usesNode) {
973         ModuleBuilder module = getParentModule(usesNode);
974         DataNodeContainerBuilder parent = usesNode.getParent();
975         URI namespace = null;
976         Date revision = null;
977         String prefix = null;
978         if (parent instanceof ModuleBuilder || parent instanceof AugmentationSchemaBuilder) {
979             namespace = module.getNamespace();
980             revision = module.getRevision();
981             prefix = module.getPrefix();
982         } else {
983             QName parentQName = parent.getQName();
984             namespace = parentQName.getNamespace();
985             revision = parentQName.getRevision();
986             prefix = parentQName.getPrefix();
987         }
988         SchemaPath parentPath = parent.getPath();
989
990         // child nodes
991         Set<DataSchemaNodeBuilder> finalChildren = new HashSet<>();
992         Set<DataSchemaNodeBuilder> newChildren = GroupingUtils.copyUsesTargetNodesWithNewPath(usesNode, parent);
993         finalChildren.addAll(newChildren);
994         usesNode.getFinalChildren().addAll(finalChildren);
995
996         // groupings
997         Set<GroupingBuilder> finalGroupings = new HashSet<>();
998         Set<GroupingBuilder> newGroupings = GroupingUtils.copyUsesTargetGroupingsWithNewPath(usesNode, parentPath,
999                 namespace, revision, prefix);
1000         finalGroupings.addAll(newGroupings);
1001         usesNode.getFinalGroupings().addAll(finalGroupings);
1002
1003         // typedefs
1004         Set<TypeDefinitionBuilder> finalTypedefs = new HashSet<>();
1005         Set<TypeDefinitionBuilder> newTypedefs = GroupingUtils.copyUsesTargetTypedefsWithNewPath(usesNode, parentPath,
1006                 namespace, revision, prefix);
1007         finalTypedefs.addAll(newTypedefs);
1008         usesNode.getFinalTypedefs().addAll(finalTypedefs);
1009
1010         // unknown nodes
1011         List<UnknownSchemaNodeBuilder> finalUnknownNodes = new ArrayList<>();
1012         List<UnknownSchemaNodeBuilder> newUnknownNodes = GroupingUtils.copyUsesTargetUnknownNodesWithNewPath(usesNode,
1013                 parentPath, namespace, revision, prefix);
1014         finalUnknownNodes.addAll(newUnknownNodes);
1015         usesNode.getFinalUnknownNodes().addAll(finalUnknownNodes);
1016     }
1017
1018     /**
1019      * Add nodes defined in uses target grouping to uses parent.
1020      *
1021      * @param usesNode
1022      */
1023     public static void updateUsesParent(UsesNodeBuilder usesNode, DataNodeContainerBuilder parent) {
1024         // child nodes
1025         for (DataSchemaNodeBuilder child : usesNode.getFinalChildren()) {
1026             child.setParent(parent);
1027             parent.addChildNode(child);
1028         }
1029         for (UsesNodeBuilder uses : usesNode.getTargetGroupingUses()) {
1030             updateUsesParent(uses, parent);
1031         }
1032
1033         // groupings
1034         for (GroupingBuilder gb : usesNode.getFinalGroupings()) {
1035             parent.addGrouping(gb);
1036         }
1037         // typedefs
1038         for (TypeDefinitionBuilder tdb : usesNode.getFinalTypedefs()) {
1039             parent.addTypedef(tdb);
1040         }
1041         // unknown nodes
1042         for (UnknownSchemaNodeBuilder un : usesNode.getFinalUnknownNodes()) {
1043             parent.addUnknownNodeBuilder(un);
1044         }
1045     }
1046
1047     public static void fixUsesNodesPath(UsesNodeBuilder usesNode) {
1048         DataNodeContainerBuilder parent = usesNode.getParent();
1049
1050         // child nodes
1051         Set<DataSchemaNodeBuilder> currentChildNodes = parent.getChildNodeBuilders();
1052         Set<DataSchemaNodeBuilder> toRemove = new HashSet<>();
1053         Set<DataSchemaNodeBuilder> toAdd = new HashSet<>();
1054         for (DataSchemaNodeBuilder child : currentChildNodes) {
1055             if (child instanceof GroupingMember) {
1056                 GroupingMember gm = (GroupingMember) child;
1057                 if (gm.isAddedByUses()) {
1058                     toRemove.add(child);
1059                     DataSchemaNodeBuilder copy = CopyUtils.copy(child, parent, true);
1060                     correctNodePath(copy, parent.getPath());
1061                     toAdd.add(copy);
1062                 }
1063             }
1064         }
1065         currentChildNodes.removeAll(toRemove);
1066         currentChildNodes.addAll(toAdd);
1067
1068         // groupings
1069         Set<GroupingBuilder> currentGroupings = parent.getGroupingBuilders();
1070         Set<GroupingBuilder> toRemoveG = new HashSet<>();
1071         Set<GroupingBuilder> toAddG = new HashSet<>();
1072         for (GroupingBuilder child : currentGroupings) {
1073             if (child.isAddedByUses()) {
1074                 toRemoveG.add(child);
1075                 GroupingBuilder copy = CopyUtils.copy(child, parent, true);
1076                 correctNodePath(copy, parent.getPath());
1077                 toAddG.add(copy);
1078             }
1079
1080         }
1081         currentGroupings.removeAll(toRemoveG);
1082         currentGroupings.addAll(toAddG);
1083
1084         // typedefs
1085         Set<TypeDefinitionBuilder> currentTypedefs = parent.getTypeDefinitionBuilders();
1086         Set<TypeDefinitionBuilder> toRemoveTD = new HashSet<>();
1087         Set<TypeDefinitionBuilder> toAddTD = new HashSet<>();
1088         for (TypeDefinitionBuilder child : currentTypedefs) {
1089             if (child.isAddedByUses()) {
1090                 toRemoveTD.add(child);
1091                 TypeDefinitionBuilder copy = CopyUtils.copy(child, parent, true);
1092                 correctNodePath(copy, parent.getPath());
1093                 toAddTD.add(copy);
1094             }
1095
1096         }
1097         currentTypedefs.removeAll(toRemoveTD);
1098         currentTypedefs.addAll(toAddTD);
1099
1100         // unknown nodes
1101         List<UnknownSchemaNodeBuilder> currentUN = parent.getUnknownNodeBuilders();
1102         List<UnknownSchemaNodeBuilder> toRemoveUN = new ArrayList<>();
1103         List<UnknownSchemaNodeBuilder> toAddUN = new ArrayList<>();
1104         for (UnknownSchemaNodeBuilder un : currentUN) {
1105             if (un.isAddedByUses()) {
1106                 toRemoveUN.add(un);
1107                 UnknownSchemaNodeBuilder copy = CopyUtils.copy(un, parent, true);
1108                 correctNodePath(copy, parent.getPath());
1109                 toAddUN.add(copy);
1110             }
1111         }
1112         currentUN.removeAll(toRemoveUN);
1113         currentUN.addAll(toAddUN);
1114     }
1115
1116     /**
1117      * Perform refine process on uses children. It is expected that uses has
1118      * already resolved all dependencies.
1119      *
1120      * @param usesNode
1121      */
1122     public static void performRefine(UsesNodeBuilder usesNode) {
1123         for (RefineHolder refine : usesNode.getRefines()) {
1124             DataSchemaNodeBuilder nodeToRefine = null;
1125             for (DataSchemaNodeBuilder dataNode : usesNode.getFinalChildren()) {
1126                 if (refine.getName().equals(dataNode.getQName().getLocalName())) {
1127                     nodeToRefine = dataNode;
1128                     break;
1129                 }
1130             }
1131             if (nodeToRefine == null) {
1132                 throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
1133                         + refine.getName() + "' not found");
1134             }
1135             RefineUtils.performRefine(nodeToRefine, refine);
1136             usesNode.addRefineNode(nodeToRefine);
1137         }
1138     }
1139
1140     /**
1141      * Get module in which this node is defined.
1142      *
1143      * @param node
1144      * @return builder of module where this node is defined
1145      */
1146     public static ModuleBuilder getParentModule(Builder node) {
1147         Builder parent = node.getParent();
1148         while (!(parent instanceof ModuleBuilder)) {
1149             parent = parent.getParent();
1150         }
1151         return (ModuleBuilder) parent;
1152     }
1153
1154 }