Merge "BUG-1119: optimize length and range checks in generated sources."
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / builder / impl / 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.builder.impl;
9
10 import java.util.Comparator;
11 import java.util.Date;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.TreeMap;
15 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
16 import org.opendaylight.yangtools.yang.model.api.Module;
17 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
18 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
19 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
20 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
21 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
22 import org.opendaylight.yangtools.yang.parser.builder.api.RefineBuilder;
23 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
24 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
25
26 public final class GroupingUtils {
27
28     private GroupingUtils() {
29     }
30
31     /**
32      * Search given modules for grouping by name defined in uses node.
33      *
34      * @param usesBuilder
35      *            builder of uses statement
36      * @param modules
37      *            all loaded modules
38      * @param module
39      *            current module
40      * @return grouping with given name if found, null otherwise
41      */
42     public static GroupingBuilder getTargetGroupingFromModules(final UsesNodeBuilder usesBuilder,
43             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
44         final int line = usesBuilder.getLine();
45
46         final String groupingString = usesBuilder.getGroupingPathAsString();
47         String groupingPrefix;
48         String groupingName;
49
50         if (groupingString.contains(":")) {
51             String[] splitted = groupingString.split(":");
52             if (splitted.length != 2 || groupingString.contains("/")) {
53                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
54             }
55             groupingPrefix = splitted[0];
56             groupingName = splitted[1];
57         } else {
58             groupingPrefix = module.getPrefix();
59             groupingName = groupingString;
60         }
61
62         ModuleBuilder dependentModule;
63
64         if(groupingPrefix == null) {
65             dependentModule = module;
66         } else if (groupingPrefix.equals(module.getPrefix())) {
67             dependentModule = module;
68         } else {
69             dependentModule = BuilderUtils.findModuleFromBuilders(modules, module, groupingPrefix, line);
70         }
71
72         if (dependentModule == null) {
73             return null;
74         }
75
76         GroupingBuilder result;
77         Set<GroupingBuilder> groupings = dependentModule.getGroupingBuilders();
78         result = findGroupingBuilder(groupings, groupingName);
79         if (result != null) {
80             return result;
81         }
82
83         Builder parent = usesBuilder.getParent();
84
85         while (parent != null) {
86             if (parent instanceof DataNodeContainerBuilder) {
87                 groupings = ((DataNodeContainerBuilder) parent).getGroupingBuilders();
88             } else if (parent instanceof RpcDefinitionBuilder) {
89                 groupings = ((RpcDefinitionBuilder) parent).getGroupings();
90             }
91             result = findGroupingBuilder(groupings, groupingName);
92             if (result == null) {
93                 parent = parent.getParent();
94             } else {
95                 break;
96             }
97         }
98
99         if (result == null) {
100             throw new YangParseException(module.getName(), line, "Referenced grouping '" + groupingName
101                     + "' not found.");
102         }
103         return result;
104     }
105
106     /**
107      * Search context for grouping by name defined in uses node.
108      *
109      * @param usesBuilder
110      *            builder of uses statement
111      * @param module
112      *            current module
113      * @param context
114      *            SchemaContext containing already resolved modules
115      * @return grouping with given name if found, null otherwise
116      */
117     public static GroupingDefinition getTargetGroupingFromContext(final UsesNodeBuilder usesBuilder,
118             final ModuleBuilder module, final SchemaContext context) {
119         final int line = usesBuilder.getLine();
120         String groupingString = usesBuilder.getGroupingPathAsString();
121         String groupingPrefix;
122         String groupingName;
123
124         if (groupingString.contains(":")) {
125             String[] splitted = groupingString.split(":");
126             if (splitted.length != 2 || groupingString.contains("/")) {
127                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
128             }
129             groupingPrefix = splitted[0];
130             groupingName = splitted[1];
131         } else {
132             groupingPrefix = module.getPrefix();
133             groupingName = groupingString;
134         }
135
136         Module dependentModule = BuilderUtils.findModuleFromContext(context, module, groupingPrefix, line);
137         return findGroupingDefinition(dependentModule.getGroupings(), groupingName);
138     }
139
140     /**
141      * Find grouping by name.
142      *
143      * @param groupings
144      *            collection of grouping builders to search
145      * @param name
146      *            name of grouping
147      * @return grouping with given name if present in collection, null otherwise
148      */
149     private static GroupingBuilder findGroupingBuilder(final Set<GroupingBuilder> groupings, final String name) {
150         for (GroupingBuilder grouping : groupings) {
151             if (grouping.getQName().getLocalName().equals(name)) {
152                 return grouping;
153             }
154         }
155         return null;
156     }
157
158     /**
159      * Find grouping by name.
160      *
161      * @param groupings
162      *            collection of grouping definitions to search
163      * @param name
164      *            name of grouping
165      * @return grouping with given name if present in collection, null otherwise
166      */
167     private static GroupingDefinition findGroupingDefinition(final Set<GroupingDefinition> groupings, final String name) {
168         for (GroupingDefinition grouping : groupings) {
169             if (grouping.getQName().getLocalName().equals(name)) {
170                 return grouping;
171             }
172         }
173         return null;
174     }
175
176     /**
177      * Perform refinement of uses target grouping nodes. Uses process has to be
178      * already performed.
179      *
180      * @param usesNode
181      *            uses node containing refine statements
182      */
183     public static void performRefine(final UsesNodeBuilder usesNode) {
184         for (RefineBuilder refine : usesNode.getRefines()) {
185             String refineTargetPath = refine.getTargetPathString();
186
187             String[] splitted = refineTargetPath.split("/");
188             Builder currentNode = usesNode.getParent();
189             for (String pathElement : splitted) {
190                 if (currentNode instanceof DataNodeContainerBuilder) {
191                     currentNode = ((DataNodeContainerBuilder) currentNode).getDataChildByName(pathElement);
192                 } else if (currentNode instanceof ChoiceBuilder) {
193                     currentNode = ((ChoiceBuilder) currentNode).getCaseNodeByName(pathElement);
194                 }
195             }
196
197             DataSchemaNodeBuilder nodeToRefine = (DataSchemaNodeBuilder) currentNode;
198             if (nodeToRefine == null) {
199                 throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
200                         + refine.getTargetPathString() + "' not found");
201             }
202             RefineUtils.performRefine(nodeToRefine, refine);
203             usesNode.addRefineNode(nodeToRefine);
204         }
205     }
206
207     public static class UsesComparator implements Comparator<UsesNodeBuilder> {
208         @Override
209         public int compare(final UsesNodeBuilder o1, final UsesNodeBuilder o2) {
210             return getElementPosition(o2) - getElementPosition(o1);
211         }
212     }
213
214     private static int getElementPosition(final UsesNodeBuilder usesNode) {
215         int i = 0;
216         Builder parent = usesNode.getParent();
217         while (!(parent instanceof ModuleBuilder)) {
218             parent = parent.getParent();
219             i++;
220         }
221         return i;
222     }
223
224 }