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