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