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