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