Merge "Remove unused code from Immutables"
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / util / ParserUtils.xtend
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.util;
9
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Date;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.TreeMap;
17
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
20 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.Module;
22 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
23 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
24 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
25 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
26 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
27 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
28 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
29 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
30 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
31 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
32 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
33 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
34 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
36 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder
37 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
39 import java.util.HashSet
40 import java.net.URI
41 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
42 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
44 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
45 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
46 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
47 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
48 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
49 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder
50 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder
51 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
52 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
53 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder
54 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingBuilderImpl
55 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
56 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl
57 import org.opendaylight.yangtools.yang.model.util.ExtendedType
58 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder
59 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder
60 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode
61 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
62 import com.google.common.base.Preconditions
63
64 public final class ParserUtils {
65
66     private new() {
67     }
68
69     /**
70      * Create new SchemaPath from given path and qname.
71      *
72      * @param schemaPath
73      * @param qname
74      * @return new SchemaPath from given path and qname
75      */
76     public static def SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
77         val path = new ArrayList<QName>(schemaPath.getPath());
78         path.addAll(Arrays.asList(qname));
79         return new SchemaPath(path, schemaPath.isAbsolute());
80     }
81
82     /**
83      * Get module import referenced by given prefix.
84      *
85      * @param builder
86      *            module to search
87      * @param prefix
88      *            prefix associated with import
89      * @return ModuleImport based on given prefix
90      */
91     public static def ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
92         for (ModuleImport mi : builder.getModuleImports()) {
93             if (mi.getPrefix().equals(prefix)) {
94                 return mi;
95
96             }
97         }
98         return null;
99     }
100
101     /**
102      * Find dependent module based on given prefix
103      *
104      * @param modules
105      *            all available modules
106      * @param module
107      *            current module
108      * @param prefix
109      *            target module prefix
110      * @param line
111      *            current line in yang model
112      * @return module builder if found, null otherwise
113      */
114     public static def ModuleBuilder findModuleFromBuilders(Map<String, TreeMap<Date, ModuleBuilder>> modules,
115         ModuleBuilder module, String prefix, int line) {
116         var ModuleBuilder dependentModule = null;
117         var Date dependentModuleRevision = null;
118
119         if(prefix == null) {
120             dependentModule = module;
121         } else if (prefix.equals(module.getPrefix())) {
122             dependentModule = module;
123         } else {
124             val ModuleImport dependentModuleImport = getModuleImport(module, prefix);
125             if (dependentModuleImport === null) {
126                 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
127             }
128             val String dependentModuleName = dependentModuleImport.getModuleName();
129             dependentModuleRevision = dependentModuleImport.getRevision();
130
131             val TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
132             if (moduleBuildersByRevision === null) {
133                 return null;
134             }
135             if (dependentModuleRevision === null) {
136                 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
137             } else {
138                 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
139             }
140         }
141         return dependentModule;
142     }
143
144     /**
145      * Find module from context based on prefix.
146      *
147      * @param context
148      *            schema context
149      * @param currentModule
150      *            current module
151      * @param prefix
152      *            current prefix used to reference dependent module
153      * @param line
154      *            current line in yang model
155      * @return module based on given prefix if found in context, null otherwise
156      */
157     public static def Module findModuleFromContext(SchemaContext context, ModuleBuilder currentModule,
158         String prefix, int line) {
159         if (context === null) {
160             throw new YangParseException(currentModule.getName(), line,
161                 "Cannot find module with prefix '" + prefix + "'.");
162         }
163         val modulesByRevision = new TreeMap<Date, Module>();
164
165         val dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
166         if (dependentModuleImport === null) {
167             throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
168         }
169         val dependentModuleName = dependentModuleImport.getModuleName();
170         val dependentModuleRevision = dependentModuleImport.getRevision();
171
172         for (Module contextModule : context.getModules()) {
173             if (contextModule.getName().equals(dependentModuleName)) {
174                 var revision = contextModule.getRevision();
175                 if (revision === null) {
176                     revision = new Date(0L);
177                 }
178                 modulesByRevision.put(revision, contextModule);
179             }
180         }
181
182         var Module result = null;
183         if (dependentModuleRevision === null) {
184             result = modulesByRevision.get(modulesByRevision.firstKey());
185         } else {
186             result = modulesByRevision.get(dependentModuleRevision);
187         }
188         return result;
189     }
190
191     /**
192      * Parse XPath string.
193      *
194      * @param xpathString
195      *            as String
196      * @return SchemaPath from given String
197      */
198     public static def SchemaPath parseXPathString(String xpathString) {
199         val absolute = xpathString.startsWith("/");
200         val String[] splittedPath = xpathString.split("/");
201         val path = new ArrayList<QName>();
202         var QName name;
203         for (String pathElement : splittedPath) {
204             if (pathElement.length() > 0) {
205                 val String[] splittedElement = pathElement.split(":");
206                 if (splittedElement.length == 1) {
207                     name = new QName(null, null, null, splittedElement.get(0));
208                 } else {
209                     name = new QName(null, null, splittedElement.get(0), splittedElement.get(1));
210                 }
211                 path.add(name);
212             }
213         }
214         return new SchemaPath(path, absolute);
215     }
216
217     public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, Builder target) {
218     }
219
220     /**
221      * Add all augment's child nodes to given target.
222      *
223      * @param augment
224      *            builder of augment statement
225      * @param target
226      *            augmentation target node
227      */
228     public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, DataNodeContainerBuilder target) {
229         for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
230             val childCopy = CopyUtils.copy(child, target, false);
231             if (augment.parent instanceof UsesNodeBuilder) {
232                 setNodeAddedByUses(childCopy);
233             }
234             setNodeAugmenting(childCopy);
235             try {
236                 target.addChildNode(childCopy);
237             } catch (YangParseException e) {
238
239                 // more descriptive message
240                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
241                     "Failed to perform augmentation: " + e.getMessage());
242             }
243         }
244     }
245
246     /**
247      * Add all augment's child nodes to given target.
248      *
249      * @param augment
250      *            builder of augment statement
251      * @param target
252      *            augmentation target choice node
253      */
254     public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, ChoiceBuilder target) {
255         for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
256             val childCopy = CopyUtils.copy(builder, target, false);
257             if (augment.parent instanceof UsesNodeBuilder) {
258                 setNodeAddedByUses(childCopy);
259             }
260             setNodeAugmenting(childCopy)
261             target.addCase(childCopy);
262         }
263         for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
264             if (usesNode !== null) {
265                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
266                     "Error in augment parsing: cannot augment choice with nodes from grouping");
267             }
268         }
269     }
270
271     /**
272      * Set augmenting flag to true for node and all its child nodes.
273      */
274     private static def void setNodeAugmenting(DataSchemaNodeBuilder child) {
275         child.setAugmenting(true);
276         if (child instanceof DataNodeContainerBuilder) {
277             val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
278             for (inner : dataNodeChild.getChildNodeBuilders()) {
279                 setNodeAugmenting(inner);
280             }
281         } else if (child instanceof ChoiceBuilder) {
282             val ChoiceBuilder choiceChild = child as ChoiceBuilder;
283             for (inner : choiceChild.cases) {
284                 setNodeAugmenting(inner);
285             }
286         }
287     }
288
289     /**
290      * Set addedByUses flag to true for node and all its child nodes. 
291      */
292     public static def void setNodeAddedByUses(GroupingMember child) {
293         child.setAddedByUses(true);
294         if (child instanceof DataNodeContainerBuilder) {
295             val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
296             for (inner : dataNodeChild.getChildNodeBuilders()) {
297                 setNodeAddedByUses(inner);
298             }
299         } else if (child instanceof ChoiceBuilder) {
300             val ChoiceBuilder choiceChild = child as ChoiceBuilder;
301             for (inner : choiceChild.cases) {
302                 setNodeAddedByUses(inner);
303             }
304         }
305     }
306
307     public static def void setNodeConfig(DataSchemaNodeBuilder child, Boolean config) {
308         if (child instanceof ContainerSchemaNodeBuilder || child instanceof LeafSchemaNodeBuilder ||
309             child instanceof LeafListSchemaNodeBuilder || child instanceof ListSchemaNodeBuilder ||
310             child instanceof ChoiceBuilder || child instanceof AnyXmlBuilder) {
311             child.setConfiguration(config);
312         }
313         if (child instanceof DataNodeContainerBuilder) {
314             val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
315             for (inner : dataNodeChild.getChildNodeBuilders()) {
316                 setNodeConfig(inner, config);
317             }
318         } else if (child instanceof ChoiceBuilder) {
319             val ChoiceBuilder choiceChild = child as ChoiceBuilder;
320             for (inner : choiceChild.cases) {
321                 setNodeConfig(inner, config);
322             }
323         }
324     }
325
326     public static def DataSchemaNodeBuilder findSchemaNode(List<QName> path, SchemaNodeBuilder parentNode) {
327         var DataSchemaNodeBuilder node
328         var SchemaNodeBuilder parent = parentNode
329         var int i = 0;
330         while (i < path.size) {
331             val String name = path.get(i).localName
332             if (parent instanceof DataNodeContainerBuilder) {
333                 node = (parent as DataNodeContainerBuilder).getDataChildByName(name)
334             } else if (parent instanceof ChoiceBuilder) {
335                 node = (parent as ChoiceBuilder).getCaseNodeByName(name)
336             } else if (parent instanceof RpcDefinitionBuilder) {
337                 if ("input".equals(name)) {
338                     node = (parent as RpcDefinitionBuilder).input
339                 } else if ("output".equals(name)) {
340                     node = (parent as RpcDefinitionBuilder).output
341                 } else {
342                     return null
343                 }
344             } else {
345                 return null
346             }
347
348             if (i < path.size - 1) {
349                 parent = node
350             }
351             i = i + 1
352         }
353
354         return node
355     }
356
357     public static def SchemaNodeBuilder findSchemaNodeInModule(List<QName> pathToNode, ModuleBuilder module) {
358         val List<QName> path = new ArrayList(pathToNode)
359         val QName first = path.remove(0)
360
361         var SchemaNodeBuilder node = module.getDataChildByName(first.localName)
362         if (node == null) {
363             val notifications = module.getAddedNotifications
364             for (notification : notifications) {
365                 if (notification.QName.localName.equals(first.localName)) {
366                     node = notification
367                 }
368             }
369         }
370         if (node == null) {
371             val rpcs = module.getAddedRpcs
372             for (rpc : rpcs) {
373                 if (rpc.QName.localName.equals(first.localName)) {
374                     node = rpc
375                 }
376             }
377         }
378         if (node == null) {
379             return null;
380         }
381
382         if (!path.empty) {
383             node = findSchemaNode(path, node)
384         }
385
386         return node
387     }
388
389     /**
390      * Find augment target node and perform augmentation.
391      *
392      * @param augment
393      * @param firstNodeParent
394      *            parent of first node in path
395      * @param path
396      *            path to augment target
397      * @return true if augmentation process succeed, false otherwise
398      */
399     public static def boolean processAugmentation(AugmentationSchemaBuilder augment, ModuleBuilder firstNodeParent) {
400         val path = augment.targetPath.path
401         var Builder targetNode = findSchemaNodeInModule(path, firstNodeParent)
402         if(targetNode === null) return false;
403
404         if (!(targetNode instanceof DataNodeContainerBuilder || targetNode instanceof ChoiceBuilder)) {
405             throw new YangParseException(augment.getModuleName(), augment.getLine(),
406                 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
407         }
408         fillAugmentTarget(augment, targetNode);
409         (targetNode as AugmentationTargetBuilder).addAugmentation(augment);
410         augment.setResolved(true);
411         return true;
412     }
413
414     public static def IdentitySchemaNodeBuilder findBaseIdentity(Map<String, TreeMap<Date, ModuleBuilder>> modules,
415         ModuleBuilder module, String baseString, int line) {
416         var IdentitySchemaNodeBuilder result = null;
417         if (baseString.contains(":")) {
418             val String[] splittedBase = baseString.split(":");
419             if (splittedBase.length > 2) {
420                 throw new YangParseException(module.getName(), line,
421                     "Failed to parse identityref base: " + baseString);
422             }
423             val prefix = splittedBase.get(0);
424             val name = splittedBase.get(1);
425             val dependentModule = findModuleFromBuilders(modules, module, prefix, line);
426             if (dependentModule !== null) {
427                 result = findIdentity(dependentModule.getAddedIdentities, name);
428             }
429         } else {
430             result = findIdentity(module.getAddedIdentities, baseString);
431         }
432         return result;
433     }
434
435     public static def IdentitySchemaNode findBaseIdentityFromContext(Map<String, TreeMap<Date, ModuleBuilder>> modules,
436         ModuleBuilder module, String baseString, int line, SchemaContext context) {
437         var IdentitySchemaNode result = null;
438
439         val String[] splittedBase = baseString.split(":");
440         if (splittedBase.length > 2) {
441             throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
442         }
443         val prefix = splittedBase.get(0);
444         val name = splittedBase.get(1);
445         val dependentModule = findModuleFromContext(context, module, prefix, line);
446         result = findIdentityNode(dependentModule.identities, name);
447
448         if (result == null) {
449             throw new YangParseException(module.name, line, "Failed to find base identity");
450         }
451         return result;
452     }
453
454     private static def IdentitySchemaNodeBuilder findIdentity(Set<IdentitySchemaNodeBuilder> identities, String name) {
455         for (identity : identities) {
456             if (identity.QName.localName.equals(name)) {
457                 return identity;
458             }
459         }
460         return null;
461     }
462
463     private static def IdentitySchemaNode findIdentityNode(Set<IdentitySchemaNode> identities, String name) {
464         for (identity : identities) {
465             if (identity.QName.localName.equals(name)) {
466                 return identity;
467             }
468         }
469         return null;
470     }
471
472     /**
473      * Get module in which this node is defined.
474      *
475      * @param node
476      * @return builder of module where this node is defined
477      */
478     public static def ModuleBuilder getParentModule(Builder node) {
479         if (node instanceof ModuleBuilder) {
480             return node as ModuleBuilder;
481         }
482         var parent = node.getParent();
483         while (!(parent instanceof ModuleBuilder)) {
484             parent = parent.getParent();
485         }
486         Preconditions.checkState(parent instanceof ModuleBuilder)
487         var parentModule = parent as ModuleBuilder
488         if(parentModule.submodule) {
489            parentModule = parentModule.parent; 
490         }
491         return parentModule;
492     }
493
494     public static def Set<DataSchemaNodeBuilder> wrapChildNodes(String moduleName, int line, Set<DataSchemaNode> nodes,
495         SchemaPath parentPath, URI ns, Date rev, String pref) {
496         val Set<DataSchemaNodeBuilder> result = new HashSet()
497
498         for (DataSchemaNode node : nodes) {
499             val qname = new QName(ns, rev, pref, node.QName.localName)
500             val DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname)
501             result.add(wrapped)
502         }
503         return result
504     }
505
506     public static def DataSchemaNodeBuilder wrapChildNode(String moduleName, int line, DataSchemaNode node,
507         SchemaPath parentPath, QName qname) {
508         val List<QName> path = new ArrayList(parentPath.getPath())
509         path.add(qname)
510         val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
511
512         if (node instanceof AnyXmlSchemaNode) {
513             return new AnyXmlBuilder(moduleName, line, qname, schemaPath, (node as AnyXmlSchemaNode));
514         } else if (node instanceof ChoiceNode) {
515             return new ChoiceBuilder(moduleName, line, qname, schemaPath, (node as ChoiceNode));
516         } else if (node instanceof ContainerSchemaNode) {
517             return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as ContainerSchemaNode));
518         } else if (node instanceof LeafSchemaNode) {
519             return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as LeafSchemaNode));
520         } else if (node instanceof LeafListSchemaNode) {
521             return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as LeafListSchemaNode));
522         } else if (node instanceof ListSchemaNode) {
523             return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as ListSchemaNode));
524         } else if (node instanceof ChoiceCaseNode) {
525             return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, (node as ChoiceCaseNode));
526         } else {
527             throw new YangParseException(moduleName, line,
528                 "Failed to copy node: Unknown type of DataSchemaNode: " + node)
529         }
530     }
531
532     public static def Set<GroupingBuilder> wrapGroupings(String moduleName, int line, Set<GroupingDefinition> nodes,
533         SchemaPath parentPath, URI ns, Date rev, String pref) {
534         val Set<GroupingBuilder> result = new HashSet()
535         for (GroupingDefinition node : nodes) {
536             val qname = new QName(ns, rev, pref, node.QName.localName)
537             val List<QName> path = new ArrayList(parentPath.getPath())
538             path.add(qname)
539             val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
540             result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node))
541         }
542         return result
543     }
544
545     public static def Set<TypeDefinitionBuilder> wrapTypedefs(String moduleName, int line, DataNodeContainer dataNode,
546         SchemaPath parentPath, URI ns, Date rev, String pref) {
547         val Set<TypeDefinition<?>> nodes = dataNode.typeDefinitions
548         val Set<TypeDefinitionBuilder> result = new HashSet()
549         for (TypeDefinition<?> node : nodes) {
550             val qname = new QName(ns, rev, pref, node.QName.localName)
551             val List<QName> path = new ArrayList(parentPath.getPath())
552             path.add(qname)
553             val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
554             result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, (node as ExtendedType)))
555         }
556         return result
557     }
558
559     public static def List<UnknownSchemaNodeBuilder> wrapUnknownNodes(String moduleName, int line,
560         List<UnknownSchemaNode> nodes, SchemaPath parentPath, URI ns, Date rev, String pref) {
561         val List<UnknownSchemaNodeBuilder> result = new ArrayList()
562         for (UnknownSchemaNode node : nodes) {
563             val qname = new QName(ns, rev, pref, node.QName.localName)
564             val List<QName> path = new ArrayList(parentPath.getPath())
565             path.add(qname)
566             val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
567             result.add(new UnknownSchemaNodeBuilder(moduleName, line, qname, schemaPath, node))
568         }
569         return result
570     }
571
572 }