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