Improve SchemaInferenceStack.enterSchemaTree()
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / SchemaNodeUtils.java
1 /*
2  * Copyright (c) 2014 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.model.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
23 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
28 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.Module;
31 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
32 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
34 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
36
37 public final class SchemaNodeUtils {
38     private SchemaNodeUtils() {
39         // Hidden on purpose
40     }
41
42     public static Optional<SchemaNode> getOriginalIfPossible(final SchemaNode node) {
43         if (node instanceof DerivableSchemaNode) {
44             @SuppressWarnings("unchecked")
45             final Optional<SchemaNode> ret  = (Optional<SchemaNode>) ((DerivableSchemaNode) node).getOriginal();
46             return ret;
47         }
48         return Optional.empty();
49     }
50
51     public static SchemaNode getRootOriginalIfPossible(final SchemaNode data) {
52         Optional<SchemaNode> previous = Optional.empty();
53         Optional<SchemaNode> next = getOriginalIfPossible(data);
54         while (next.isPresent()) {
55             previous = next;
56             next = getOriginalIfPossible(next.get());
57         }
58         return previous.orElse(null);
59     }
60
61     /**
62      * Returns RPC input or output schema based on supplied QName.
63      *
64      * @param rpc RPC Definition
65      * @param qname input or output QName with namespace same as RPC
66      * @return input or output schema. Returns null if RPC does not have input/output specified.
67      */
68     public static @Nullable ContainerLike getRpcDataSchema(final @NonNull RpcDefinition rpc,
69             final @NonNull QName qname) {
70         requireNonNull(rpc, "Rpc Schema must not be null");
71         switch (requireNonNull(qname, "QName must not be null").getLocalName()) {
72             case "input":
73                 return rpc.getInput();
74             case "output":
75                 return rpc.getOutput();
76             default:
77                 throw new IllegalArgumentException("Supplied qname " + qname
78                         + " does not represent rpc input or output.");
79         }
80     }
81
82     @Beta
83     public static @NonNull Collection<? extends TypeDefinition<?>> getAllTypeDefinitions(
84             final DataNodeContainer parent) {
85         final List<TypeDefinition<?>> typedefs = new ArrayList<>();
86         traverse(new DataNodeAggregator() {
87             @Override
88             protected void addTypedefs(final Collection<? extends TypeDefinition<?>> typeDefs) {
89                 typedefs.addAll(typeDefs);
90             }
91         }, requireNonNull(parent));
92         return typedefs;
93     }
94
95     @Beta
96     public static @NonNull Collection<? extends ContainerSchemaNode> getAllContainers(final DataNodeContainer parent) {
97         final List<ContainerSchemaNode> containers = new ArrayList<>();
98         traverse(new DataNodeAggregator() {
99             @Override
100             protected void addContainer(final ContainerSchemaNode containerNode) {
101                 containers.add(containerNode);
102             }
103         }, requireNonNull(parent));
104         return containers;
105     }
106
107     @Beta
108     public static void traverse(final @NonNull DataNodeAggregator aggregator, final DataNodeContainer dataNode) {
109         if (dataNode == null) {
110             return;
111         }
112
113         for (DataSchemaNode childNode : dataNode.getChildNodes()) {
114             if (childNode.isAugmenting()) {
115                 continue;
116             }
117             aggregator.addChild(childNode);
118             if (childNode instanceof ContainerSchemaNode) {
119                 final ContainerSchemaNode containerNode = (ContainerSchemaNode) childNode;
120                 aggregator.addContainer(containerNode);
121                 traverse(aggregator, containerNode);
122             } else if (childNode instanceof ListSchemaNode) {
123                 final ListSchemaNode list = (ListSchemaNode) childNode;
124                 aggregator.addList(list);
125                 traverse(aggregator, list);
126             } else if (childNode instanceof ChoiceSchemaNode) {
127                 final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) childNode;
128                 aggregator.addChoice(choiceNode);
129                 for (final CaseSchemaNode caseNode : choiceNode.getCases()) {
130                     traverse(aggregator, caseNode);
131                 }
132             }
133         }
134
135         aggregator.addTypedefs(dataNode.getTypeDefinitions());
136
137         traverseModule(aggregator, dataNode);
138         traverseGroupings(aggregator, dataNode);
139     }
140
141     private static void traverseModule(final DataNodeAggregator aggregator, final DataNodeContainer dataNode) {
142         final Module module;
143         if (dataNode instanceof Module) {
144             module = (Module) dataNode;
145         } else {
146             return;
147         }
148
149         for (NotificationDefinition notificationDefinition : module.getNotifications()) {
150             traverse(aggregator, notificationDefinition);
151         }
152
153         for (RpcDefinition rpcDefinition : module.getRpcs()) {
154             aggregator.addTypedefs(rpcDefinition.getTypeDefinitions());
155             InputSchemaNode input = rpcDefinition.getInput();
156             if (input != null) {
157                 traverse(aggregator, input);
158             }
159             OutputSchemaNode output = rpcDefinition.getOutput();
160             if (output != null) {
161                 traverse(aggregator, output);
162             }
163         }
164     }
165
166     private static void traverseGroupings(final DataNodeAggregator aggregator, final DataNodeContainer dataNode) {
167         for (GroupingDefinition grouping : dataNode.getGroupings()) {
168             aggregator.addGrouping(grouping);
169             traverse(aggregator, grouping);
170         }
171     }
172 }