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