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