BUG-576: modified parser to handle refinement of extension instances.
[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.net.URI;
12 import java.util.Comparator;
13 import java.util.Date;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.TreeMap;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
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 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 public final class GroupingUtils {
30     private static final Logger LOG = LoggerFactory.getLogger(GroupingUtils.class);
31
32     private static final Splitter COLON_SPLITTER = Splitter.on(':');
33     private static final Splitter SLASH_SPLITTER = Splitter.on('/');
34
35     private GroupingUtils() {
36     }
37
38     /**
39      * Search given modules for grouping by name defined in uses node.
40      *
41      * @param usesBuilder
42      *            builder of uses statement
43      * @param modules
44      *            all loaded modules
45      * @param module
46      *            current module
47      * @return grouping with given name if found, null otherwise
48      */
49     public static GroupingBuilder getTargetGroupingFromModules(final UsesNodeBuilder usesBuilder,
50             final Map<URI, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
51         final int line = usesBuilder.getLine();
52
53         SchemaPath groupingPath = usesBuilder.getTargetGroupingPath();
54         QName groupingName = groupingPath.getPathFromRoot().iterator().next();
55         ModuleBuilder dependentModule = BuilderUtils.findModule(groupingName, modules);
56
57         Set<GroupingBuilder> groupings = dependentModule.getGroupingBuilders();
58         GroupingBuilder result = findGroupingBuilder(groupings, groupingName.getLocalName());
59         if (result != null) {
60             return result;
61         }
62
63         Builder parent = usesBuilder.getParent();
64         while (parent != null) {
65             if (parent instanceof DataNodeContainerBuilder) {
66                 groupings = ((DataNodeContainerBuilder) parent).getGroupingBuilders();
67             } else if (parent instanceof RpcDefinitionBuilder) {
68                 groupings = ((RpcDefinitionBuilder) parent).getGroupings();
69             }
70             result = findGroupingBuilder(groupings, groupingName.getLocalName());
71             if (result == null) {
72                 parent = parent.getParent();
73             } else {
74                 break;
75             }
76         }
77
78         if (result == null) {
79             throw new YangParseException(module.getName(), line, "Grouping '" + groupingName + "' not found.");
80         }
81         return result;
82     }
83
84     /**
85      * Find grouping by name.
86      *
87      * @param groupings
88      *            collection of grouping builders to search
89      * @param name
90      *            name of grouping
91      * @return grouping with given name if present in collection, null otherwise
92      */
93     private static GroupingBuilder findGroupingBuilder(final Set<GroupingBuilder> groupings, final String name) {
94         for (GroupingBuilder grouping : groupings) {
95             if (grouping.getQName().getLocalName().equals(name)) {
96                 return grouping;
97             }
98         }
99         return null;
100     }
101
102     /**
103      * Perform refinement of uses target grouping nodes. Uses process has to be
104      * already performed.
105      *
106      * @param usesNode
107      *            uses node containing refine statements
108      */
109     public static void performRefine(final UsesNodeBuilder usesNode) {
110         for (RefineBuilder refine : usesNode.getRefines()) {
111             String refineTargetPath = refine.getTargetPathString();
112
113             Builder currentNode = usesNode.getParent();
114             for (String pathElement : SLASH_SPLITTER.split(refineTargetPath)) {
115                 if (currentNode instanceof DataNodeContainerBuilder) {
116                     currentNode = ((DataNodeContainerBuilder) currentNode).getDataChildByName(pathElement);
117                 } else if (currentNode instanceof ChoiceBuilder) {
118                     currentNode = ((ChoiceBuilder) currentNode).getCaseNodeByName(pathElement);
119                 }
120             }
121
122             DataSchemaNodeBuilder nodeToRefine = (DataSchemaNodeBuilder) currentNode;
123             if (nodeToRefine == null) {
124                 // FIXME: exception replaced with log to avoid breakage when
125                 // user tries to refine instance of extension (unknown node)
126
127                 // throw new YangParseException(refine.getModuleName(),
128                 // refine.getLine(), "Refine target node '" +
129                 // refine.getTargetPathString() + "' not found");
130                 LOG.warn("Error in module {} at line {}: Refine target node {} not found.", refine.getModuleName(),
131                         refine.getLine(), refine.getTargetPathString());
132                 continue;
133             }
134             RefineUtils.performRefine(nodeToRefine, refine);
135             usesNode.addRefineNode(nodeToRefine);
136         }
137     }
138
139     public static class UsesComparator implements Comparator<UsesNodeBuilder> {
140         @Override
141         public int compare(final UsesNodeBuilder o1, final UsesNodeBuilder o2) {
142             return getElementPosition(o2) - getElementPosition(o1);
143         }
144     }
145
146     private static int getElementPosition(final UsesNodeBuilder usesNode) {
147         int i = 0;
148         Builder parent = usesNode.getParent();
149         while (!(parent instanceof ModuleBuilder)) {
150             parent = parent.getParent();
151             i++;
152         }
153         return i;
154     }
155
156 }