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