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