Refactored uses statement handling in parser.
[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.Comparator;
13 import java.util.Date;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.TreeMap;
19
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
23 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
26 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.Module;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
33 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
34 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
35 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
36 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
37 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
38 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
39 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
40 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
41 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
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 final class GroupingUtils {
47
48     private GroupingUtils() {
49     }
50
51     /**
52      * Search given modules for grouping by name defined in uses node.
53      *
54      * @param usesBuilder
55      *            builder of uses statement
56      * @param modules
57      *            all loaded modules
58      * @param module
59      *            current module
60      * @return grouping with given name if found, null otherwise
61      */
62     public static GroupingBuilder getTargetGroupingFromModules(final UsesNodeBuilder usesBuilder,
63             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
64         final int line = usesBuilder.getLine();
65
66         final String groupingString = usesBuilder.getGroupingPathAsString();
67         String groupingPrefix;
68         String groupingName;
69
70         if (groupingString.contains(":")) {
71             String[] splitted = groupingString.split(":");
72             if (splitted.length != 2 || groupingString.contains("/")) {
73                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
74             }
75             groupingPrefix = splitted[0];
76             groupingName = splitted[1];
77         } else {
78             groupingPrefix = module.getPrefix();
79             groupingName = groupingString;
80         }
81
82         ModuleBuilder dependentModule;
83         if (groupingPrefix.equals(module.getPrefix())) {
84             dependentModule = module;
85         } else {
86             dependentModule = ParserUtils.findModuleFromBuilders(modules, module, groupingPrefix, line);
87         }
88
89         if (dependentModule == null) {
90             return null;
91         }
92
93         GroupingBuilder result;
94         Set<GroupingBuilder> groupings = dependentModule.getGroupingBuilders();
95         result = findGroupingBuilder(groupings, groupingName);
96         if (result != null) {
97             return result;
98         }
99
100         Builder parent = usesBuilder.getParent();
101
102         while (parent != null) {
103             if (parent instanceof DataNodeContainerBuilder) {
104                 groupings = ((DataNodeContainerBuilder) parent).getGroupingBuilders();
105             } else if (parent instanceof RpcDefinitionBuilder) {
106                 groupings = ((RpcDefinitionBuilder) parent).getGroupings();
107             }
108             result = findGroupingBuilder(groupings, groupingName);
109             if (result == null) {
110                 parent = parent.getParent();
111             } else {
112                 break;
113             }
114         }
115
116         if (result == null) {
117             throw new YangParseException(module.getName(), line, "Referenced grouping '" + groupingName
118                     + "' not found.");
119         }
120         return result;
121     }
122
123     /**
124      * Search context for grouping by name defined in uses node.
125      *
126      * @param usesBuilder
127      *            builder of uses statement
128      * @param module
129      *            current module
130      * @param context
131      *            SchemaContext containing already resolved modules
132      * @return grouping with given name if found, null otherwise
133      */
134     public static GroupingDefinition getTargetGroupingFromContext(final UsesNodeBuilder usesBuilder,
135             final ModuleBuilder module, final SchemaContext context) {
136         final int line = usesBuilder.getLine();
137         String groupingString = usesBuilder.getGroupingPathAsString();
138         String groupingPrefix;
139         String groupingName;
140
141         if (groupingString.contains(":")) {
142             String[] splitted = groupingString.split(":");
143             if (splitted.length != 2 || groupingString.contains("/")) {
144                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
145             }
146             groupingPrefix = splitted[0];
147             groupingName = splitted[1];
148         } else {
149             groupingPrefix = module.getPrefix();
150             groupingName = groupingString;
151         }
152
153         Module dependentModule = ParserUtils.findModuleFromContext(context, module, groupingPrefix, line);
154         return findGroupingDefinition(dependentModule.getGroupings(), groupingName);
155     }
156
157     /**
158      * Find grouping by name.
159      *
160      * @param groupings
161      *            collection of grouping builders to search
162      * @param name
163      *            name of grouping
164      * @return grouping with given name if present in collection, null otherwise
165      */
166     public static GroupingBuilder findGroupingBuilder(Set<GroupingBuilder> groupings, String name) {
167         for (GroupingBuilder grouping : groupings) {
168             if (grouping.getQName().getLocalName().equals(name)) {
169                 return grouping;
170             }
171         }
172         return null;
173     }
174
175     /**
176      * Find grouping by name.
177      *
178      * @param groupings
179      *            collection of grouping definitions to search
180      * @param name
181      *            name of grouping
182      * @return grouping with given name if present in collection, null otherwise
183      */
184     public static GroupingDefinition findGroupingDefinition(Set<GroupingDefinition> groupings, String name) {
185         for (GroupingDefinition grouping : groupings) {
186             if (grouping.getQName().getLocalName().equals(name)) {
187                 return grouping;
188             }
189         }
190         return null;
191     }
192
193     /**
194      * Read data defined in target grouping definition, make a copy and add them
195      * to uses node builder.
196      *
197      * @param usesNode
198      *            used node builder to which are copied nodes from its
199      *            <code>GroupingDefinition</code>
200      * @param namespace
201      *            URI with parent namespace
202      * @param revision
203      *            date with parent revision date
204      * @param prefix
205      *            string with parent prefix
206      * @param moduleName
207      *            string with parent module name
208      * @param line
209      *            line from YANG file where parent node is defined
210      */
211     public static Set<DataSchemaNodeBuilder> getTargetGroupingDefinitionNodesWithNewNamespace(
212             final UsesNodeBuilder usesNode, final URI namespace, final Date revision, final String prefix,
213             final String moduleName, final int line) {
214         final Set<DataSchemaNodeBuilder> newChildren = new HashSet<>();
215         for (DataSchemaNode child : usesNode.getGroupingDefinition().getChildNodes()) {
216             if (child != null) {
217                 DataSchemaNodeBuilder newChild = null;
218                 QName newQName = new QName(namespace, revision, prefix, child.getQName().getLocalName());
219                 if (child instanceof AnyXmlSchemaNode) {
220                     newChild = CopyUtils.createAnyXml((AnyXmlSchemaNode) child, newQName, moduleName, line);
221                 } else if (child instanceof ChoiceNode) {
222                     newChild = CopyUtils.createChoice((ChoiceNode) child, newQName, moduleName, line);
223                 } else if (child instanceof ContainerSchemaNode) {
224                     newChild = CopyUtils.createContainer((ContainerSchemaNode) child, newQName, moduleName, line);
225                 } else if (child instanceof LeafListSchemaNode) {
226                     newChild = CopyUtils.createLeafList((LeafListSchemaNode) child, newQName, moduleName, line);
227                 } else if (child instanceof LeafSchemaNode) {
228                     newChild = CopyUtils.createLeafBuilder((LeafSchemaNode) child, newQName, moduleName, line);
229                 } else if (child instanceof ListSchemaNode) {
230                     newChild = CopyUtils.createList((ListSchemaNode) child, newQName, moduleName, line);
231                 }
232
233                 if (newChild == null) {
234                     throw new YangParseException(moduleName, line,
235                             "Unknown member of target grouping while resolving uses node.");
236                 }
237
238                 ((GroupingMember) newChild).setAddedByUses(true);
239                 newChildren.add(newChild);
240             }
241         }
242         return newChildren;
243     }
244
245     public static Set<TypeDefinitionBuilder> getTargetGroupingDefinitionTypedefsWithNewNamespace(
246             UsesNodeBuilder usesNode, URI namespace, Date revision, String prefix, String moduleName, int line) {
247         final Set<TypeDefinitionBuilder> newTypedefs = new HashSet<>();
248         for (TypeDefinition<?> td : usesNode.getGroupingDefinition().getTypeDefinitions()) {
249             QName newQName = new QName(namespace, revision, prefix, td.getQName().getLocalName());
250             TypeDefinitionBuilder newType = CopyUtils.createTypedef((ExtendedType) td, newQName, moduleName, line);
251             newType.setAddedByUses(true);
252             newTypedefs.add(newType);
253         }
254         return newTypedefs;
255     }
256
257     public static Set<GroupingBuilder> getTargetGroupingDefinitionGroupingsWithNewNamespace(UsesNodeBuilder usesNode,
258             URI namespace, Date revision, String prefix, String moduleName, int line) {
259         final Set<GroupingBuilder> newGroupings = new HashSet<>();
260         for (GroupingDefinition g : usesNode.getGroupingDefinition().getGroupings()) {
261             QName newQName = new QName(namespace, revision, prefix, g.getQName().getLocalName());
262             GroupingBuilder newGrouping = CopyUtils.createGrouping(g, newQName, moduleName, line);
263             newGrouping.setAddedByUses(true);
264             newGroupings.add(newGrouping);
265         }
266         return newGroupings;
267     }
268
269     public static List<UnknownSchemaNodeBuilder> getTargetGroupingDefinitionUnknownNodesWithNewNamespace(
270             UsesNodeBuilder usesNode, URI namespace, Date revision, String prefix, String moduleName, int line) {
271         final List<UnknownSchemaNodeBuilder> newUnknownNodes = new ArrayList<>();
272         for (UnknownSchemaNode un : usesNode.getGroupingDefinition().getUnknownSchemaNodes()) {
273             QName newQName = new QName(namespace, revision, prefix, un.getQName().getLocalName());
274             UnknownSchemaNodeBuilder newNode = CopyUtils.createUnknownSchemaNode(un, newQName, moduleName, line);
275             newNode.setAddedByUses(true);
276             newUnknownNodes.add(newNode);
277         }
278         return newUnknownNodes;
279     }
280
281     /**
282      * Perform refinement of uses target grouping nodes. Uses process has to be
283      * already performed.
284      *
285      * @param usesNode
286      *            uses node containing refine statements
287      */
288     public static void performRefine(UsesNodeBuilder usesNode) {
289         for (RefineHolder refine : usesNode.getRefines()) {
290             String refineTargetPath = refine.getName();
291
292             String[] splitted = refineTargetPath.split("/");
293             Builder currentNode = usesNode.getParent();
294             for (String pathElement : splitted) {
295                 if (currentNode instanceof DataNodeContainerBuilder) {
296                     currentNode = ((DataNodeContainerBuilder) currentNode).getDataChildByName(pathElement);
297                 } else if (currentNode instanceof ChoiceBuilder) {
298                     currentNode = ((ChoiceBuilder) currentNode).getCaseNodeByName(pathElement);
299                 }
300             }
301
302             DataSchemaNodeBuilder nodeToRefine = (DataSchemaNodeBuilder) currentNode;
303             if (nodeToRefine == null) {
304                 throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
305                         + refine.getName() + "' not found");
306             }
307             RefineUtils.performRefine(nodeToRefine, refine);
308             usesNode.addRefineNode(nodeToRefine);
309         }
310     }
311
312     public static class UsesComparator implements Comparator<UsesNodeBuilder> {
313         @Override
314         public int compare(UsesNodeBuilder o1, UsesNodeBuilder o2) {
315             return getElementPosition(o2) - getElementPosition(o1);
316         }
317     }
318
319     private static int getElementPosition(UsesNodeBuilder usesNode) {
320         int i = 0;
321         Builder parent = usesNode.getParent();
322         while (!(parent instanceof ModuleBuilder)) {
323             parent = parent.getParent();
324             i++;
325         }
326         return i;
327     }
328
329 }