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