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