BUG-1210: refactored imports handling in parser.
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / builder / impl / BuilderUtils.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.builder.impl;
9
10 import com.google.common.base.Function;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Splitter;
14 import com.google.common.collect.Collections2;
15 import com.google.common.io.ByteSource;
16 import java.io.ByteArrayOutputStream;
17 import java.io.File;
18 import java.io.FileNotFoundException;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TreeMap;
30
31 import org.apache.commons.io.IOUtils;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
35 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
36 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
47 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
48 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
49 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
50 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
52 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
53 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
54 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
55 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
56 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
57 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
58 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
59 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
60 import org.opendaylight.yangtools.yang.parser.util.NamedByteArrayInputStream;
61 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
62 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 public final class BuilderUtils {
67
68     private static final Logger LOG = LoggerFactory.getLogger(BuilderUtils.class);
69     private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
70     private static final Splitter COLON_SPLITTER = Splitter.on(':');
71     private static final Date NULL_DATE = new Date(0L);
72     private static final String INPUT = "input";
73     private static final String OUTPUT = "output";
74
75     private BuilderUtils() {
76     }
77
78     public static Collection<ByteSource> streamsToByteSources(final Collection<InputStream> streams) throws IOException {
79         Collection<ByteSource> result = new HashSet<>();
80         for (InputStream stream : streams) {
81             result.add(new ByteSourceImpl(stream));
82         }
83         return result;
84     }
85
86     public static ByteSource fileToByteSource(final File file) {
87         return new ByteSource() {
88             @Override
89             public InputStream openStream() throws IOException {
90                 return new NamedFileInputStream(file, file.getAbsolutePath());
91             }
92         };
93     }
94
95     public static Collection<ByteSource> filesToByteSources(final Collection<File> streams)
96             throws FileNotFoundException {
97         return Collections2.transform(streams, new Function<File, ByteSource>() {
98             @Override
99             public ByteSource apply(final File input) {
100                 return new ByteSource() {
101                     @Override
102                     public InputStream openStream() throws IOException {
103                         return new NamedFileInputStream(input, input.getAbsolutePath());
104                     }
105                 };
106             }
107         });
108     }
109
110     /**
111      * Create new SchemaPath from given path and qname.
112      *
113      * @param schemaPath
114      *            base path
115      * @param qname
116      *            one or more qnames added to base path
117      * @return new SchemaPath from given path and qname
118      *
119      * @deprecated Use {@link SchemaPath#createChild(QName...)} instead.
120      */
121     @Deprecated
122     public static SchemaPath createSchemaPath(final SchemaPath schemaPath, final QName... qname) {
123         return schemaPath.createChild(qname);
124     }
125
126     /**
127      * Find dependent module based on given prefix
128      *
129      * @param modules
130      *            all available modules
131      * @param module
132      *            current module
133      * @param prefix
134      *            target module prefix
135      * @param line
136      *            current line in yang model
137      * @return module builder if found, null otherwise
138      */
139     public static ModuleBuilder findModuleFromBuilders(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
140             final ModuleBuilder module, final String prefix, final int line) {
141         ModuleBuilder dependentModule;
142         Date dependentModuleRevision;
143
144         if (prefix == null) {
145             dependentModule = module;
146         } else if (prefix.equals(module.getPrefix())) {
147             dependentModule = module;
148         } else {
149             ModuleImport dependentModuleImport = module.getImport(prefix);
150             if (dependentModuleImport == null) {
151                 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
152             }
153             String dependentModuleName = dependentModuleImport.getModuleName();
154             dependentModuleRevision = dependentModuleImport.getRevision();
155
156             TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
157             if (moduleBuildersByRevision == null) {
158                 return null;
159             }
160             if (dependentModuleRevision == null) {
161                 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
162             } else {
163                 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
164             }
165         }
166         return dependentModule;
167     }
168
169     /**
170      * Find module from context based on prefix.
171      *
172      * @param context
173      *            schema context
174      * @param currentModule
175      *            current module
176      * @param prefix
177      *            prefix used to reference dependent module
178      * @param line
179      *            current line in yang model
180      * @return module based on import with given prefix if found in context,
181      *         null otherwise
182      * @throws YangParseException
183      *             if no import found with given prefix
184      */
185     public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
186             final String prefix, final int line) {
187         TreeMap<Date, Module> modulesByRevision = new TreeMap<>();
188
189         ModuleImport dependentModuleImport = currentModule.getImport(prefix);
190         if (dependentModuleImport == null) {
191             throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
192         }
193         String dependentModuleName = dependentModuleImport.getModuleName();
194         Date dependentModuleRevision = dependentModuleImport.getRevision();
195
196         for (Module contextModule : context.getModules()) {
197             if (contextModule.getName().equals(dependentModuleName)) {
198                 Date revision = contextModule.getRevision();
199                 if (revision == null) {
200                     revision = NULL_DATE;
201                 }
202                 modulesByRevision.put(revision, contextModule);
203             }
204         }
205
206         Module result;
207         if (dependentModuleRevision == null) {
208             result = modulesByRevision.get(modulesByRevision.firstKey());
209         } else {
210             result = modulesByRevision.get(dependentModuleRevision);
211         }
212         if (result == null) {
213             throw new YangParseException(currentModule.getName(), line, "Module not found for prefix " + prefix);
214         }
215
216         return result;
217     }
218
219     /**
220      * Parse XPath string.
221      *
222      * @param xpathString
223      *            XPath as String
224      * @return SchemaPath from given String
225      */
226     public static SchemaPath parseXPathString(final String xpathString) {
227         final boolean absolute = !xpathString.isEmpty() && xpathString.charAt(0) == '/';
228
229         final List<QName> path = new ArrayList<>();
230         for (String pathElement : SLASH_SPLITTER.split(xpathString)) {
231             final Iterator<String> it = COLON_SPLITTER.split(pathElement).iterator();
232             final String s = it.next();
233
234             final QName name;
235             if (it.hasNext()) {
236                 name = new QName(null, null, s, it.next());
237             } else {
238                 name = new QName(null, null, null, s);
239             }
240             path.add(name);
241         }
242         return SchemaPath.create(path, absolute);
243     }
244
245     /**
246      * Add all augment's child nodes to given target.
247      *
248      * @param augment
249      *            builder of augment statement
250      * @param target
251      *            augmentation target node
252      */
253     public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final Builder target) {
254         if (target instanceof DataNodeContainerBuilder) {
255             fillAugmentTarget(augment, (DataNodeContainerBuilder) target);
256         } else if (target instanceof ChoiceBuilder) {
257             fillAugmentTarget(augment, (ChoiceBuilder) target);
258         } else {
259             throw new YangParseException(
260                     augment.getModuleName(),
261                     augment.getLine(),
262                     "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
263         }
264     }
265
266     /**
267      * Add all augment's child nodes to given target.
268      *
269      * @param augment
270      *            builder of augment statement
271      * @param target
272      *            augmentation target node
273      */
274     private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
275         for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
276             DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
277             if (augment.getParent() instanceof UsesNodeBuilder) {
278                 setNodeAddedByUses(childCopy);
279             }
280             setNodeAugmenting(childCopy);
281             try {
282                 target.addChildNode(childCopy);
283             } catch (YangParseException e) {
284
285                 // more descriptive message
286                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
287                         "Failed to perform augmentation: " + e.getMessage());
288             }
289         }
290     }
291
292     /**
293      * Add all augment's child nodes to given target.
294      *
295      * @param augment
296      *            builder of augment statement
297      * @param target
298      *            augmentation target choice node
299      */
300     private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
301         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
302             DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
303             if (augment.getParent() instanceof UsesNodeBuilder) {
304                 setNodeAddedByUses(childCopy);
305             }
306             setNodeAugmenting(childCopy);
307             target.addCase(childCopy);
308         }
309         for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
310             if (usesNode != null) {
311                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
312                         "Error in augment parsing: cannot augment choice with nodes from grouping");
313             }
314         }
315     }
316
317     /**
318      * Set augmenting flag to true for node and all its child nodes.
319      *
320      * @param node
321      */
322     private static void setNodeAugmenting(final DataSchemaNodeBuilder node) {
323         node.setAugmenting(true);
324         if (node instanceof DataNodeContainerBuilder) {
325             DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
326             for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
327                 setNodeAugmenting(inner);
328             }
329         } else if (node instanceof ChoiceBuilder) {
330             ChoiceBuilder choiceChild = (ChoiceBuilder) node;
331             for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
332                 setNodeAugmenting(inner);
333             }
334         }
335     }
336
337     /**
338      * Set addedByUses flag to true for node and all its child nodes.
339      *
340      * @param node
341      */
342     public static void setNodeAddedByUses(final GroupingMember node) {
343         node.setAddedByUses(true);
344         if (node instanceof DataNodeContainerBuilder) {
345             DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
346             for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
347                 setNodeAddedByUses(inner);
348             }
349         } else if (node instanceof ChoiceBuilder) {
350             ChoiceBuilder choiceChild = (ChoiceBuilder) node;
351             for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
352                 setNodeAddedByUses(inner);
353             }
354         }
355     }
356
357     public static DataSchemaNodeBuilder findSchemaNode(final List<QName> path, final SchemaNodeBuilder parentNode) {
358         DataSchemaNodeBuilder node = null;
359         SchemaNodeBuilder parent = parentNode;
360         int i = 0;
361         while (i < path.size()) {
362             String name = path.get(i).getLocalName();
363             if (parent instanceof DataNodeContainerBuilder) {
364                 node = ((DataNodeContainerBuilder) parent).getDataChildByName(name);
365             } else if (parent instanceof ChoiceBuilder) {
366                 node = ((ChoiceBuilder) parent).getCaseNodeByName(name);
367             } else if (parent instanceof RpcDefinitionBuilder) {
368                 if ("input".equals(name)) {
369                     node = ((RpcDefinitionBuilder) parent).getInput();
370                 } else if ("output".equals(name)) {
371                     node = ((RpcDefinitionBuilder) parent).getOutput();
372                 } else {
373                     return null;
374                 }
375             } else {
376                 return null;
377             }
378
379             if (i < path.size() - 1) {
380                 parent = node;
381             }
382             i = i + 1;
383         }
384
385         return node;
386     }
387
388     /**
389      *
390      * Find a builder for node in data namespace of YANG module.
391      *
392      * Search is performed on full QName equals, this means builders and schema
393      * path MUST be resolved against imports and their namespaces.
394      *
395      * Search is done in data namespace, this means notification, rpc
396      * definitions and top level data definitions are considered as top-level
397      * items, from which it is possible to traverse.
398      *
399      *
400      * @param schemaPath
401      *            Schema Path to node
402      * @param module
403      *            ModuleBuilder to start lookup in
404      * @return Node Builder if found, {@link Optional#absent()} otherwise.
405      */
406     public static Optional<SchemaNodeBuilder> findSchemaNodeInModule(final SchemaPath schemaPath,
407             final ModuleBuilder module) {
408         Iterator<QName> path = schemaPath.getPathFromRoot().iterator();
409         Preconditions.checkArgument(path.hasNext(), "Schema Path must contain at least one element.");
410         QName first = path.next();
411         Optional<SchemaNodeBuilder> currentNode = getDataNamespaceChild(module, first);
412
413         while (currentNode.isPresent() && path.hasNext()) {
414             currentNode = findDataChild(currentNode.get(), path.next());
415         }
416         return currentNode;
417     }
418
419     private static Optional<SchemaNodeBuilder> findDataChild(final SchemaNodeBuilder parent, final QName child) {
420         if (parent instanceof DataNodeContainerBuilder) {
421             return castOptional(SchemaNodeBuilder.class,
422                     findDataChildInDataNodeContainer((DataNodeContainerBuilder) parent, child));
423         } else if (parent instanceof ChoiceBuilder) {
424             return castOptional(SchemaNodeBuilder.class, findCaseInChoice((ChoiceBuilder) parent, child));
425         } else if (parent instanceof RpcDefinitionBuilder) {
426             return castOptional(SchemaNodeBuilder.class, findContainerInRpc((RpcDefinitionBuilder) parent, child));
427
428         } else {
429             LOG.trace("Child {} not found in node {}", child, parent);
430             return Optional.absent();
431         }
432     }
433
434     /**
435      * Casts optional from one argument to other.
436      *
437      * @param cls
438      *            Class to be checked
439      * @param optional
440      *            Original value
441      * @return
442      */
443     private static <T> Optional<T> castOptional(final Class<T> cls, final Optional<?> optional) {
444         if (optional.isPresent()) {
445             Object value = optional.get();
446             if (cls.isInstance(value)) {
447                 @SuppressWarnings("unchecked")
448                 // Actually checked by outer if
449                 T casted = (T) value;
450                 return Optional.of(casted);
451             }
452         }
453         return Optional.absent();
454     }
455
456     /**
457      *
458      * Gets input / output container from {@link RpcDefinitionBuilder} if QName
459      * is input/output.
460      *
461      *
462      * @param parent
463      *            RPC Definition builder
464      * @param child
465      *            Child QName
466      * @return Optional of input/output if defined and QName is input/output.
467      *         Otherwise {@link Optional#absent()}.
468      */
469     private static Optional<ContainerSchemaNodeBuilder> findContainerInRpc(final RpcDefinitionBuilder parent, final QName child) {
470         if (INPUT.equals(child.getLocalName())) {
471             return Optional.of(parent.getInput());
472         } else if (OUTPUT.equals(child.getLocalName())) {
473             return Optional.of(parent.getOutput());
474         }
475         LOG.trace("Child {} not found in node {}", child, parent);
476         return Optional.absent();
477     }
478
479     /**
480      * Finds case by QName in {@link ChoiceBuilder}
481      *
482      *
483      * @param parent
484      *            DataNodeContainer in which lookup should be performed
485      * @param child
486      *            QName of child
487      * @return Optional of child if found.
488      */
489
490     private static Optional<ChoiceCaseBuilder> findCaseInChoice(final ChoiceBuilder parent, final QName child) {
491         for (ChoiceCaseBuilder caze : parent.getCases()) {
492             if (caze.getQName().equals(child)) {
493                 return Optional.of(caze);
494             }
495         }
496         LOG.trace("Child {} not found in node {}", child, parent);
497         return Optional.absent();
498     }
499
500     /**
501      * Finds direct child by QName in {@link DataNodeContainerBuilder}
502      *
503      *
504      * @param parent
505      *            DataNodeContainer in which lookup should be performed
506      * @param child
507      *            QName of child
508      * @return Optional of child if found.
509      */
510     private static Optional<DataSchemaNodeBuilder> findDataChildInDataNodeContainer(final DataNodeContainerBuilder parent,
511             final QName child) {
512         for (DataSchemaNodeBuilder childNode : parent.getChildNodeBuilders()) {
513             if (childNode.getQName().equals(child)) {
514                 return Optional.of(childNode);
515             }
516         }
517         LOG.trace("Child {} not found in node {}", child, parent);
518         return Optional.absent();
519     }
520
521     /**
522      *
523      * Find a child builder for node in data namespace of YANG module.
524      *
525      * Search is performed on full QName equals, this means builders and schema
526      * path MUST be resolved against imports and their namespaces.
527      *
528      * Search is done in data namespace, this means notification, rpc
529      * definitions and top level data definitions are considered as top-level
530      * items, from which it is possible to traverse.
531      *
532      *
533      * @param child
534      *            Child QName.
535      * @param module
536      *            ModuleBuilder to start lookup in
537      * @return Node Builder if found, {@link Optional#absent()} otherwise.
538      */
539     private static Optional<SchemaNodeBuilder> getDataNamespaceChild(final ModuleBuilder module, final QName child) {
540         /*
541          * First we do lookup in data tree, if node is found we return it.
542          */
543         final Optional<SchemaNodeBuilder> dataTreeNode = getDataChildByQName(module, child);
544         if (dataTreeNode.isPresent()) {
545             return dataTreeNode;
546         }
547
548         /*
549          * We lookup in notifications
550          */
551         Set<NotificationBuilder> notifications = module.getAddedNotifications();
552         for (NotificationBuilder notification : notifications) {
553             if (notification.getQName().equals(child)) {
554                 return Optional.<SchemaNodeBuilder> of(notification);
555             }
556         }
557
558         /*
559          * We lookup in RPCs
560          */
561         Set<RpcDefinitionBuilder> rpcs = module.getAddedRpcs();
562         for (RpcDefinitionBuilder rpc : rpcs) {
563             if (rpc.getQName().equals(child)) {
564                 return Optional.<SchemaNodeBuilder> of(rpc);
565             }
566         }
567         LOG.trace("Child {} not found in data namespace of module {}", child, module);
568         return Optional.absent();
569     }
570
571     private static Optional<SchemaNodeBuilder> getDataChildByQName(final DataNodeContainerBuilder builder, final QName child) {
572         for (DataSchemaNodeBuilder childNode : builder.getChildNodeBuilders()) {
573             if (childNode.getQName().equals(child)) {
574                 return Optional.<SchemaNodeBuilder> of(childNode);
575             }
576         }
577         LOG.trace("Child {} not found in node {}", child, builder);
578         return Optional.absent();
579     }
580
581     /**
582      * Find augment target node and perform augmentation.
583      *
584      * @param augment
585      * @param firstNodeParent
586      *            parent of first node in path
587      * @param path
588      *            path to augment target
589      * @return true if augmentation process succeed, false otherwise
590      */
591     public static boolean processAugmentation(final AugmentationSchemaBuilder augment,
592             final ModuleBuilder firstNodeParent) {
593         Optional<SchemaNodeBuilder> potentialTargetNode = findSchemaNodeInModule(augment.getTargetNodeSchemaPath(),
594                 firstNodeParent);
595         if (!potentialTargetNode.isPresent()) {
596             return false;
597         }
598         SchemaNodeBuilder targetNode = potentialTargetNode.get();
599         fillAugmentTarget(augment, targetNode);
600         Preconditions.checkState(targetNode instanceof AugmentationTargetBuilder,
601                 "Node refered by augmentation must be valid augmentation target");
602         ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
603         augment.setResolved(true);
604         return true;
605     }
606
607     public static IdentitySchemaNodeBuilder findBaseIdentity(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
608             final ModuleBuilder module, final String baseString, final int line) {
609
610         // FIXME: optimize indexOf() away?
611         if (baseString.indexOf(':') != -1) {
612             final Iterator<String> it = COLON_SPLITTER.split(baseString).iterator();
613             final String prefix = it.next();
614             final String name = it.next();
615
616             if (it.hasNext()) {
617                 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
618             }
619
620             ModuleBuilder dependentModule = getModuleByPrefix(module, prefix);
621             if (dependentModule == null) {
622                 return null;
623             }
624
625             return findIdentity(dependentModule.getAddedIdentities(), name);
626         } else {
627             return findIdentity(module.getAddedIdentities(), baseString);
628         }
629     }
630
631     public static IdentitySchemaNodeBuilder findIdentity(final Set<IdentitySchemaNodeBuilder> identities,
632             final String name) {
633         for (IdentitySchemaNodeBuilder identity : identities) {
634             if (identity.getQName().getLocalName().equals(name)) {
635                 return identity;
636             }
637         }
638         return null;
639     }
640
641     /**
642      * Get module in which this node is defined.
643      *
644      * @param node
645      * @return builder of module where this node is defined
646      */
647     public static ModuleBuilder getParentModule(final Builder node) {
648         if (node instanceof ModuleBuilder) {
649             return (ModuleBuilder) node;
650         }
651         Builder parent = node.getParent();
652         while (!(parent instanceof ModuleBuilder)) {
653             parent = parent.getParent();
654         }
655         ModuleBuilder parentModule = (ModuleBuilder) parent;
656         if (parentModule.isSubmodule()) {
657             parentModule = parentModule.getParent();
658         }
659         return parentModule;
660     }
661
662     public static Set<DataSchemaNodeBuilder> wrapChildNodes(final String moduleName, final int line,
663             final Set<DataSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
664         Set<DataSchemaNodeBuilder> result = new HashSet<>();
665
666         for (DataSchemaNode node : nodes) {
667             QName qname = QName.create(parentQName, node.getQName().getLocalName());
668             DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname);
669             result.add(wrapped);
670         }
671         return result;
672     }
673
674     public static DataSchemaNodeBuilder wrapChildNode(final String moduleName, final int line,
675             final DataSchemaNode node, final SchemaPath parentPath, final QName qname) {
676
677         final SchemaPath schemaPath = parentPath.createChild(qname);
678
679         if (node instanceof AnyXmlSchemaNode) {
680             return new AnyXmlBuilder(moduleName, line, qname, schemaPath, ((AnyXmlSchemaNode) node));
681         } else if (node instanceof ChoiceNode) {
682             return new ChoiceBuilder(moduleName, line, qname, schemaPath, ((ChoiceNode) node));
683         } else if (node instanceof ContainerSchemaNode) {
684             return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ContainerSchemaNode) node));
685         } else if (node instanceof LeafSchemaNode) {
686             return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafSchemaNode) node));
687         } else if (node instanceof LeafListSchemaNode) {
688             return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafListSchemaNode) node));
689         } else if (node instanceof ListSchemaNode) {
690             return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ListSchemaNode) node));
691         } else if (node instanceof ChoiceCaseNode) {
692             return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, ((ChoiceCaseNode) node));
693         } else {
694             throw new YangParseException(moduleName, line, "Failed to copy node: Unknown type of DataSchemaNode: "
695                     + node);
696         }
697     }
698
699     public static Set<GroupingBuilder> wrapGroupings(final String moduleName, final int line,
700             final Set<GroupingDefinition> nodes, final SchemaPath parentPath, final QName parentQName) {
701         Set<GroupingBuilder> result = new HashSet<>();
702         for (GroupingDefinition node : nodes) {
703             QName qname = QName.create(parentQName, node.getQName().getLocalName());
704             SchemaPath schemaPath = parentPath.createChild(qname);
705             result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node));
706         }
707         return result;
708     }
709
710     public static Set<TypeDefinitionBuilder> wrapTypedefs(final String moduleName, final int line,
711             final DataNodeContainer dataNode, final SchemaPath parentPath, final QName parentQName) {
712         Set<TypeDefinition<?>> nodes = dataNode.getTypeDefinitions();
713         Set<TypeDefinitionBuilder> result = new HashSet<>();
714         for (TypeDefinition<?> node : nodes) {
715             QName qname = QName.create(parentQName, node.getQName().getLocalName());
716             SchemaPath schemaPath = parentPath.createChild(qname);
717             result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, ((ExtendedType) node)));
718         }
719         return result;
720     }
721
722     public static List<UnknownSchemaNodeBuilderImpl> wrapUnknownNodes(final String moduleName, final int line,
723             final List<UnknownSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
724         List<UnknownSchemaNodeBuilderImpl> result = new ArrayList<>();
725         for (UnknownSchemaNode node : nodes) {
726             QName qname = QName.create(parentQName, node.getQName().getLocalName());
727             SchemaPath schemaPath = parentPath.createChild(qname);
728             result.add(new UnknownSchemaNodeBuilderImpl(moduleName, line, qname, schemaPath, node));
729         }
730         return result;
731     }
732
733     private static final class ByteSourceImpl extends ByteSource {
734         private final String toString;
735         private final ByteArrayOutputStream output = new ByteArrayOutputStream();
736
737         private ByteSourceImpl(final InputStream input) throws IOException {
738             toString = input.toString();
739             IOUtils.copy(input, output);
740         }
741
742         @Override
743         public InputStream openStream() throws IOException {
744             return new NamedByteArrayInputStream(output.toByteArray(), toString);
745         }
746     }
747
748     public static ModuleBuilder getModuleByPrefix(ModuleBuilder module, String prefix) {
749         if (prefix == null || prefix.isEmpty() || prefix.equals(module.getPrefix())) {
750             return module;
751         } else {
752             return module.getImportedModule(prefix);
753         }
754     }
755
756 }