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