Added more javadocs.
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / util / GroupingUtils.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.util;
9
10 import java.net.URI;
11 import java.util.ArrayList;
12 import java.util.Date;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.TreeMap;
18
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
22 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
25 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.Module;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
32 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
33 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
34 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
36 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
37 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
38 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
39 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
40 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
41 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder;
42 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
43
44 public class GroupingUtils {
45
46     private GroupingUtils() {
47     }
48
49     /**
50      * Search given modules for grouping by name defined in uses node.
51      *
52      * @param usesBuilder
53      *            builder of uses statement
54      * @param modules
55      *            all loaded modules
56      * @param module
57      *            current module
58      * @return grouping with given name if found, null otherwise
59      */
60     public static GroupingBuilder getTargetGroupingFromModules(final UsesNodeBuilder usesBuilder,
61             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
62         final int line = usesBuilder.getLine();
63         final String groupingString = usesBuilder.getGroupingPathAsString();
64         String groupingPrefix;
65         String groupingName;
66
67         if (groupingString.contains(":")) {
68             String[] splitted = groupingString.split(":");
69             if (splitted.length != 2 || groupingString.contains("/")) {
70                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
71             }
72             groupingPrefix = splitted[0];
73             groupingName = splitted[1];
74         } else {
75             groupingPrefix = module.getPrefix();
76             groupingName = groupingString;
77         }
78
79         ModuleBuilder dependentModule = null;
80         if (groupingPrefix.equals(module.getPrefix())) {
81             dependentModule = module;
82         } else {
83             dependentModule = ParserUtils.findDependentModuleBuilder(modules, module, groupingPrefix, line);
84         }
85
86         if (dependentModule == null) {
87             return null;
88         }
89
90         GroupingBuilder result = null;
91         Set<GroupingBuilder> groupings = dependentModule.getGroupingBuilders();
92         result = findGroupingBuilder(groupings, groupingName);
93         if (result != null) {
94             return result;
95         }
96
97         Builder parent = usesBuilder.getParent();
98
99         while (parent != null) {
100             if (parent instanceof DataNodeContainerBuilder) {
101                 groupings = ((DataNodeContainerBuilder) parent).getGroupingBuilders();
102             } else if (parent instanceof RpcDefinitionBuilder) {
103                 groupings = ((RpcDefinitionBuilder) parent).getGroupings();
104             }
105             result = findGroupingBuilder(groupings, groupingName);
106             if (result == null) {
107                 parent = parent.getParent();
108             } else {
109                 break;
110             }
111         }
112
113         if (result == null) {
114             throw new YangParseException(module.getName(), line, "Referenced grouping '" + groupingName
115                     + "' not found.");
116         }
117         return result;
118     }
119
120     /**
121      * Search context for grouping by name defined in uses node.
122      *
123      * @param usesBuilder
124      *            builder of uses statement
125      * @param module
126      *            current module
127      * @param context
128      *            SchemaContext containing already resolved modules
129      * @return grouping with given name if found, null otherwise
130      */
131     public static GroupingDefinition getTargetGroupingFromContext(final UsesNodeBuilder usesBuilder,
132             final ModuleBuilder module, final SchemaContext context) {
133         final int line = usesBuilder.getLine();
134         String groupingString = usesBuilder.getGroupingPathAsString();
135         String groupingPrefix;
136         String groupingName;
137
138         if (groupingString.contains(":")) {
139             String[] splitted = groupingString.split(":");
140             if (splitted.length != 2 || groupingString.contains("/")) {
141                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
142             }
143             groupingPrefix = splitted[0];
144             groupingName = splitted[1];
145         } else {
146             groupingPrefix = module.getPrefix();
147             groupingName = groupingString;
148         }
149
150         Module dependentModule = ParserUtils.findModuleFromContext(context, module, groupingPrefix, line);
151         return findGroupingDefinition(dependentModule.getGroupings(), groupingName);
152     }
153
154     /**
155      * Find grouping by name.
156      *
157      * @param groupings
158      *            collection of grouping builders to search
159      * @param name
160      *            name of grouping
161      * @return grouping with given name if present in collection, null otherwise
162      */
163     public static GroupingBuilder findGroupingBuilder(Set<GroupingBuilder> groupings, String name) {
164         for (GroupingBuilder grouping : groupings) {
165             if (grouping.getQName().getLocalName().equals(name)) {
166                 return grouping;
167             }
168         }
169         return null;
170     }
171
172     /**
173      * Find grouping by name.
174      *
175      * @param groupings
176      *            collection of grouping definitions to search
177      * @param name
178      *            name of grouping
179      * @return grouping with given name if present in collection, null otherwise
180      */
181     public static GroupingDefinition findGroupingDefinition(Set<GroupingDefinition> groupings, String name) {
182         for (GroupingDefinition grouping : groupings) {
183             if (grouping.getQName().getLocalName().equals(name)) {
184                 return grouping;
185             }
186         }
187         return null;
188     }
189
190     /**
191      * Add nodes defined in uses target grouping to uses parent.
192      *
193      * @param usesNode
194      */
195     public static void updateUsesParent(UsesNodeBuilder usesNode) {
196         DataNodeContainerBuilder parent = usesNode.getParent();
197
198         // child nodes
199         for (DataSchemaNodeBuilder child : usesNode.getTargetChildren()) {
200             if (child instanceof GroupingMember) {
201                 ((GroupingMember) child).setAddedByUses(true);
202             }
203             parent.addChildNode(child);
204         }
205
206         // groupings
207         for (GroupingBuilder gb : usesNode.getTargetGroupings()) {
208             gb.setAddedByUses(true);
209             parent.addGrouping(gb);
210         }
211
212         // typedefs
213         for (TypeDefinitionBuilder tdb : usesNode.getTargetTypedefs()) {
214             tdb.setAddedByUses(true);
215             parent.addTypedef(tdb);
216         }
217
218         // unknown nodes
219         for (UnknownSchemaNodeBuilder un : usesNode.getTargetUnknownNodes()) {
220             un.setAddedByUses(true);
221             parent.addUnknownNodeBuilder(un);
222         }
223     }
224
225     /**
226      * Read data defined in target grouping builder, make a copy and add them to
227      * uses node builder.
228      *
229      * @param usesNode
230      *            uses node builder
231      */
232     public static void collectUsesData(UsesNodeBuilder usesNode) {
233         usesNode.setTargetChildren(collectTargetChildNodes(usesNode));
234         usesNode.setTargetTypedefs(collectTargetTypedefs(usesNode));
235         usesNode.setTargetGroupings(collectTargetGroupings(usesNode));
236         usesNode.setTargetUnknownNodes(collectTargetUnknownNodes(usesNode));
237         usesNode.setDataCollected(true);
238     }
239
240     /**
241      * Read child nodes defined in target grouping and make a copy of them.
242      *
243      * @param usesNode
244      * @return copy of child nodes defined in uses target grouping
245      */
246     private static Set<DataSchemaNodeBuilder> collectTargetChildNodes(UsesNodeBuilder usesNode) {
247         final GroupingBuilder target = usesNode.getGroupingBuilder();
248         Set<DataSchemaNodeBuilder> childNodes = target.getChildNodeBuilders();
249         Set<DataSchemaNodeBuilder> copies = new HashSet<>();
250         for (DataSchemaNodeBuilder childNode : childNodes) {
251             copies.add(CopyUtils.copy(childNode, usesNode.getParent(), true));
252         }
253         for (UsesNodeBuilder targetUses : target.getUsesNodes()) {
254             copies.addAll(collectTargetChildNodes(targetUses));
255         }
256         return copies;
257     }
258
259     /**
260      * Read typedefs defined in target grouping and make a copy of them.
261      *
262      * @param usesNode
263      * @return copy of typedefs defined in uses target grouping
264      */
265     private static Set<TypeDefinitionBuilder> collectTargetTypedefs(UsesNodeBuilder usesNode) {
266         final GroupingBuilder target = usesNode.getGroupingBuilder();
267         Set<TypeDefinitionBuilder> typedefs = target.getTypeDefinitionBuilders();
268         Set<TypeDefinitionBuilder> copies = new HashSet<>();
269         for (TypeDefinitionBuilder typedef : typedefs) {
270             copies.add(CopyUtils.copy(typedef, usesNode.getParent(), true));
271         }
272         for (UsesNodeBuilder targetUses : target.getUsesNodes()) {
273             copies.addAll(collectTargetTypedefs(targetUses));
274         }
275         return copies;
276     }
277
278     /**
279      * Read groupings defined in target grouping and make a copy of them.
280      *
281      * @param usesNode
282      * @return copy of groupings defined in uses target grouping
283      */
284     private static Set<GroupingBuilder> collectTargetGroupings(UsesNodeBuilder usesNode) {
285         final GroupingBuilder target = usesNode.getGroupingBuilder();
286         Set<GroupingBuilder> groupings = target.getGroupingBuilders();
287         Set<GroupingBuilder> copies = new HashSet<>();
288         for (GroupingBuilder grouping : groupings) {
289             copies.add(CopyUtils.copy(grouping, usesNode.getParent(), true));
290         }
291         for (UsesNodeBuilder targetUses : target.getUsesNodes()) {
292             copies.addAll(collectTargetGroupings(targetUses));
293         }
294         return copies;
295     }
296
297     /**
298      * Read unknown nodes defined in target grouping and make a copy of them.
299      *
300      * @param usesNode
301      * @return copy of unknown nodes defined in uses target grouping
302      */
303     private static List<UnknownSchemaNodeBuilder> collectTargetUnknownNodes(UsesNodeBuilder usesNode) {
304         final GroupingBuilder target = usesNode.getGroupingBuilder();
305         List<UnknownSchemaNodeBuilder> unknownNodes = target.getUnknownNodeBuilders();
306         List<UnknownSchemaNodeBuilder> copies = new ArrayList<>();
307         for (UnknownSchemaNodeBuilder unknownNode : unknownNodes) {
308             copies.add(CopyUtils.copy(unknownNode, usesNode.getParent(), true));
309         }
310         for (UsesNodeBuilder targetUses : target.getUsesNodes()) {
311             copies.addAll(collectTargetUnknownNodes(targetUses));
312         }
313         return copies;
314     }
315
316     /**
317      * Read data defined in target grouping definition, make a copy and add them
318      * to uses node builder.
319      *
320      * @param usesNode
321      *            uses node builder
322      */
323     public static void collectUsesDataFromContext(UsesNodeBuilder usesNode) {
324         DataNodeContainerBuilder parent = usesNode.getParent();
325         URI namespace = parent.getQName().getNamespace();
326         Date revision = parent.getQName().getRevision();
327         String prefix = parent.getQName().getPrefix();
328         String moduleName = parent.getModuleName();
329         int line = parent.getLine();
330
331         // child nodes
332         final Set<DataSchemaNodeBuilder> newChildren = new HashSet<>();
333         for (DataSchemaNode child : usesNode.getGroupingDefinition().getChildNodes()) {
334             if (child != null) {
335                 DataSchemaNodeBuilder newChild = null;
336                 QName newQName = new QName(namespace, revision, prefix, child.getQName().getLocalName());
337                 if (child instanceof AnyXmlSchemaNode) {
338                     newChild = CopyUtils.createAnyXml((AnyXmlSchemaNode) child, newQName, moduleName, line);
339                 } else if (child instanceof ChoiceNode) {
340                     newChild = CopyUtils.createChoice((ChoiceNode) child, newQName, moduleName, line);
341                 } else if (child instanceof ContainerSchemaNode) {
342                     newChild = CopyUtils.createContainer((ContainerSchemaNode) child, newQName, moduleName, line);
343                 } else if (child instanceof LeafListSchemaNode) {
344                     newChild = CopyUtils.createLeafList((LeafListSchemaNode) child, newQName, moduleName, line);
345                 } else if (child instanceof LeafSchemaNode) {
346                     newChild = CopyUtils.createLeafBuilder((LeafSchemaNode) child, newQName, moduleName, line);
347                 } else if (child instanceof ListSchemaNode) {
348                     newChild = CopyUtils.createList((ListSchemaNode) child, newQName, moduleName, line);
349                 }
350
351                 if (newChild == null) {
352                     throw new YangParseException(moduleName, line,
353                             "Unknown member of target grouping while resolving uses node.");
354                 }
355                 if (newChild instanceof GroupingMember) {
356                     ((GroupingMember) newChild).setAddedByUses(true);
357                 }
358
359                 newChildren.add(newChild);
360             }
361         }
362         usesNode.setTargetChildren(newChildren);
363
364         // groupings
365         final Set<GroupingBuilder> newGroupings = new HashSet<>();
366         for (GroupingDefinition g : usesNode.getGroupingDefinition().getGroupings()) {
367             QName newQName = new QName(namespace, revision, prefix, g.getQName().getLocalName());
368             GroupingBuilder newGrouping = CopyUtils.createGrouping(g, newQName, moduleName, line);
369             newGrouping.setAddedByUses(true);
370             newGroupings.add(newGrouping);
371         }
372         usesNode.setTargetGroupings(newGroupings);
373
374         // typedefs
375         final Set<TypeDefinitionBuilder> newTypedefs = new HashSet<>();
376         for (TypeDefinition<?> td : usesNode.getGroupingDefinition().getTypeDefinitions()) {
377             QName newQName = new QName(namespace, revision, prefix, td.getQName().getLocalName());
378             TypeDefinitionBuilder newType = CopyUtils.createTypedef((ExtendedType) td, newQName, moduleName, line);
379             newType.setAddedByUses(true);
380             newTypedefs.add(newType);
381         }
382         usesNode.setTargetTypedefs(newTypedefs);
383
384         // unknown nodes
385         final List<UnknownSchemaNodeBuilder> newUnknownNodes = new ArrayList<>();
386         for (UnknownSchemaNode un : usesNode.getGroupingDefinition().getUnknownSchemaNodes()) {
387             QName newQName = new QName(namespace, revision, prefix, un.getQName().getLocalName());
388             UnknownSchemaNodeBuilder newNode = CopyUtils.createUnknownSchemaNode(un, newQName, moduleName, line);
389             newNode.setAddedByUses(true);
390             newUnknownNodes.add(newNode);
391         }
392         usesNode.setTargetUnknownNodes(newUnknownNodes);
393
394         usesNode.setDataCollected(true);
395     }
396
397     /**
398      * Fix schema path of all nodes which were defined by this usesNode.
399      *
400      * @param usesNode
401      */
402     public static void fixUsesNodesPath(UsesNodeBuilder usesNode) {
403         DataNodeContainerBuilder parent = usesNode.getParent();
404
405         // child nodes
406         Set<DataSchemaNodeBuilder> currentChildNodes = parent.getChildNodeBuilders();
407         for (DataSchemaNodeBuilder child : currentChildNodes) {
408             if (child instanceof GroupingMember) {
409                 GroupingMember gm = (GroupingMember) child;
410                 if (gm.isAddedByUses()) {
411                     ParserUtils.correctNodePath(child, parent.getPath());
412                 }
413             }
414         }
415
416         // groupings
417         Set<GroupingBuilder> currentGroupings = parent.getGroupingBuilders();
418         for (GroupingBuilder child : currentGroupings) {
419             if (child.isAddedByUses()) {
420                 ParserUtils.correctNodePath(child, parent.getPath());
421             }
422
423         }
424
425         // typedefs
426         Set<TypeDefinitionBuilder> currentTypedefs = parent.getTypeDefinitionBuilders();
427         for (TypeDefinitionBuilder child : currentTypedefs) {
428             if (child.isAddedByUses()) {
429                 ParserUtils.correctNodePath(child, parent.getPath());
430             }
431
432         }
433
434         // unknown nodes
435         List<UnknownSchemaNodeBuilder> currentUN = parent.getUnknownNodeBuilders();
436         for (UnknownSchemaNodeBuilder un : currentUN) {
437             if (un.isAddedByUses()) {
438                 ParserUtils.correctNodePath(un, parent.getPath());
439             }
440         }
441     }
442
443     /**
444      * Perform refinement of uses target grouping nodes. Uses process has to be
445      * already performed.
446      *
447      * @param usesNode
448      */
449     public static void performRefine(UsesNodeBuilder usesNode) {
450         for (RefineHolder refine : usesNode.getRefines()) {
451             DataSchemaNodeBuilder nodeToRefine = null;
452             for (DataSchemaNodeBuilder dataNode : usesNode.getParent().getChildNodeBuilders()) {
453                 if (refine.getName().equals(dataNode.getQName().getLocalName())) {
454                     nodeToRefine = dataNode;
455                     break;
456                 }
457             }
458             if (nodeToRefine == null) {
459                 throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
460                         + refine.getName() + "' not found");
461             }
462             RefineUtils.performRefine(nodeToRefine, refine);
463             usesNode.addRefineNode(nodeToRefine);
464         }
465     }
466
467 }