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