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