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