Populate xpath/ hierarchy
[yangtools.git] / parser / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / EffectiveStmtUtils.java
1 /*
2  * Copyright (c) 2015 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.rfc7950.stmt;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Strings;
12 import com.google.common.collect.ImmutableList;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.Optional;
17 import java.util.Set;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.yangtools.yang.common.YangVersion;
20 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
21 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
22 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
23 import org.opendaylight.yangtools.yang.model.api.UsesNode;
24 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
25 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsEffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx;
35 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
36
37 @Beta
38 public final class EffectiveStmtUtils {
39     // FIXME: this should reside somewhere in max_elements
40     private static final String UNBOUNDED_STR = "unbounded";
41
42     private EffectiveStmtUtils() {
43         // Hidden on purpose
44     }
45
46     public static SourceException createNameCollisionSourceException(final EffectiveStmtCtx.Current<?, ?> stmt,
47             final EffectiveStatement<?, ?> effectiveStatement) {
48         return new SourceException(stmt,
49             "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared.",
50             stmt.moduleName().getLocalName(), effectiveStatement.argument(), effectiveStatement.argument());
51     }
52
53     public static Optional<ElementCountConstraint> createElementCountConstraint(final EffectiveStatement<?, ?> stmt) {
54         return createElementCountConstraint(
55             stmt.findFirstEffectiveSubstatementArgument(MinElementsEffectiveStatement.class).orElse(null),
56             stmt.findFirstEffectiveSubstatementArgument(MaxElementsEffectiveStatement.class).orElse(null));
57     }
58
59     public static Optional<ElementCountConstraint> createElementCountConstraint(
60             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
61         return createElementCountConstraint(
62             AbstractStatementSupport.findFirstArgument(substatements, MinElementsEffectiveStatement.class, null),
63             AbstractStatementSupport.findFirstArgument(substatements, MaxElementsEffectiveStatement.class, null));
64     }
65
66     private static Optional<ElementCountConstraint> createElementCountConstraint(
67             final @Nullable Integer min, final @Nullable String max) {
68         final Integer minElements;
69         if (min != null) {
70             minElements = min > 0 ? min : null;
71         } else {
72             minElements = null;
73         }
74
75         final Integer maxElements;
76         if (max != null && !UNBOUNDED_STR.equals(max)) {
77             final Integer m = Integer.valueOf(max);
78             maxElements = m < Integer.MAX_VALUE ? m : null;
79         } else {
80             maxElements = null;
81         }
82
83         return ElementCountConstraint.forNullable(minElements, maxElements);
84     }
85
86     /**
87      * Checks whether supplied type has any of specified default values marked
88      * with an if-feature. This method creates mutable copy of supplied set of
89      * default values.
90      *
91      * @param yangVersion
92      *            yang version
93      * @param typeStmt
94      *            type statement which should be checked
95      * @param defaultValues
96      *            set of default values which should be checked. The method
97      *            creates mutable copy of this set
98      *
99      * @return true if any of specified default values is marked with an
100      *         if-feature, otherwise false
101      */
102     public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
103             final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
104         return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
105                 && isRelevantForIfFeatureCheck(typeStmt)
106                 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
107     }
108
109     /**
110      * Checks whether supplied type has specified default value marked with an
111      * if-feature. This method creates mutable set of supplied default value.
112      *
113      * @param yangVersion
114      *            yang version
115      * @param typeStmt
116      *            type statement which should be checked
117      * @param defaultValue
118      *            default value to be checked
119      *
120      * @return true if specified default value is marked with an if-feature,
121      *         otherwise false
122      */
123     public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
124             final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
125         final HashSet<String> defaultValues = new HashSet<>();
126         defaultValues.add(defaultValue);
127         return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
128                 && isRelevantForIfFeatureCheck(typeStmt)
129                 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
130     }
131
132     private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
133         final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
134         return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
135                 || typeDefinition instanceof UnionTypeDefinition;
136     }
137
138     private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
139             final Set<String> defaultValues) {
140         final Iterator<? extends EffectiveStatement<?, ?>> iter = typeStmt.effectiveSubstatements().iterator();
141         while (iter.hasNext() && !defaultValues.isEmpty()) {
142             final EffectiveStatement<?, ?> effectiveSubstatement = iter.next();
143             if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
144                 final String bitName = (String) effectiveSubstatement.argument();
145                 if (defaultValues.remove(bitName) && containsIfFeature(effectiveSubstatement)) {
146                     return true;
147                 }
148             } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
149                     && defaultValues.remove(effectiveSubstatement.argument())
150                     && containsIfFeature(effectiveSubstatement)) {
151                 return true;
152             } else if (effectiveSubstatement instanceof TypeEffectiveStatement && isAnyDefaultValueMarkedWithIfFeature(
153                     (TypeEffectiveStatement<?>) effectiveSubstatement, defaultValues)) {
154                 return true;
155             }
156         }
157
158         return false;
159     }
160
161     private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
162         for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
163             if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {
164                 return true;
165             }
166         }
167         return false;
168     }
169
170     public static void checkUniqueGroupings(final EffectiveStmtCtx.Current<?, ?> stmt,
171             final Collection<? extends EffectiveStatement<?, ?>> statements) {
172         checkUniqueNodes(stmt, statements, GroupingDefinition.class);
173     }
174
175     public static void checkUniqueTypedefs(final EffectiveStmtCtx.Current<?, ?> stmt,
176             final Collection<? extends EffectiveStatement<?, ?>> statements) {
177         final Set<Object> typedefs = new HashSet<>();
178         for (EffectiveStatement<?, ?> statement : statements) {
179             if (statement instanceof TypedefEffectiveStatement
180                     && !typedefs.add(((TypedefEffectiveStatement) statement).getTypeDefinition())) {
181                 throw EffectiveStmtUtils.createNameCollisionSourceException(stmt, statement);
182             }
183         }
184     }
185
186     public static void checkUniqueUses(final EffectiveStmtCtx.Current<?, ?> stmt,
187             final Collection<? extends EffectiveStatement<?, ?>> statements) {
188         checkUniqueNodes(stmt, statements, UsesNode.class);
189     }
190
191     private static void checkUniqueNodes(final EffectiveStmtCtx.Current<?, ?> stmt,
192             final Collection<? extends EffectiveStatement<?, ?>> statements, final Class<?> type) {
193         final Set<Object> nodes = new HashSet<>();
194         for (EffectiveStatement<?, ?> statement : statements) {
195             if (type.isInstance(statement) && !nodes.add(statement)) {
196                 throw EffectiveStmtUtils.createNameCollisionSourceException(stmt, statement);
197             }
198         }
199     }
200 }