2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.yangtools.yang.parser.spi;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.Iterables;
14 import com.google.common.collect.MapDifference;
15 import com.google.common.collect.Maps;
16 import java.util.HashMap;
18 import java.util.Map.Entry;
19 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
20 import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
21 import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
22 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
23 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
24 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
26 public final class SubstatementValidator {
28 * @deprecated Deprecated since version 1.1.0. Use {@link Builder#addAny(StatementDefinition)},
29 * {@link Builder#addAtLeast(StatementDefinition, int)},
30 * {@link Builder#addMandatory(StatementDefinition)}, or
31 * {@link Builder#addMultiple(StatementDefinition)} instead.
34 public final static int MAX = Integer.MAX_VALUE;
36 private final Map<StatementDefinition, Cardinality> cardinalityMap;
37 private final StatementDefinition currentStatement;
38 private final SpecialCase specialCase;
40 private SubstatementValidator(final Builder builder, final SpecialCase specialCase) {
41 this.cardinalityMap = builder.cardinalityMap.build();
42 this.currentStatement = builder.currentStatement;
43 this.specialCase = specialCase;
46 public static Builder builder(final StatementDefinition currentStatement) {
47 return new Builder(currentStatement);
50 public static class Builder {
51 private static final Cardinality ONE_MAX = new Cardinality(1, Integer.MAX_VALUE);
52 private static final Cardinality ONE_ONE = new Cardinality(1, 1);
53 private static final Cardinality ZERO_MAX = new Cardinality(0, Integer.MAX_VALUE);
54 private static final Cardinality ZERO_ONE = new Cardinality(0, 1);
56 private final ImmutableMap.Builder<StatementDefinition, Cardinality> cardinalityMap = ImmutableMap.builder();
57 private final StatementDefinition currentStatement;
59 private Builder(final StatementDefinition currentStatement) {
60 this.currentStatement = currentStatement;
63 private Builder add(final StatementDefinition d, final Cardinality c) {
64 cardinalityMap.put(d, c);
68 public Builder add(final StatementDefinition d, final int min, final int max) {
69 if (max == Integer.MAX_VALUE) {
70 return addAtLeast(d, min);
71 } else if (min == 0) {
72 return addAtMost(d, max);
74 return add(d, new Cardinality(min, max));
78 // Equivalent to min .. Integer.MAX_VALUE
79 public Builder addAtLeast(final StatementDefinition d, final int min) {
84 return addMultiple(d);
86 return add(d, new Cardinality(min, Integer.MAX_VALUE));
90 // Equivalent to 0 .. max
91 public Builder addAtMost(final StatementDefinition d, final int max) {
92 return max == Integer.MAX_VALUE ? addAny(d) : add(d, new Cardinality(0, max));
96 // Equivalent to 0 .. Integer.MAX_VALUE
97 public Builder addAny(final StatementDefinition d) {
98 return add(d, ZERO_MAX);
101 // Equivalent to 1 .. 1
102 public Builder addMandatory(final StatementDefinition d) {
103 return add(d, ONE_ONE);
106 // Equivalent to 1 .. MAX
107 public Builder addMultiple(final StatementDefinition d) {
108 return add(d, ONE_MAX);
111 // Equivalent to 0 .. 1
112 public Builder addOptional(final StatementDefinition d) {
113 return add(d, ZERO_ONE);
116 public SubstatementValidator build() {
117 return new SubstatementValidator(this, SpecialCase.NULL);
120 public SubstatementValidator build(final SpecialCase specialCase) {
121 return new SubstatementValidator(this, specialCase);
125 public void validate(final StmtContext<?, ?, ?> ctx) throws InvalidSubstatementException,
126 MissingSubstatementException {
127 final Iterable<StatementContextBase<?, ?, ?>> substatementsInit = Iterables.concat(
128 ctx.declaredSubstatements(), ctx.effectiveSubstatements());
130 final Map<StatementDefinition, Integer> stmtDefMap = new HashMap<>();
131 for (StatementContextBase<?, ?, ?> stmtCtx : substatementsInit) {
132 final StatementDefinition definition = stmtCtx.getPublicDefinition();
133 if (!stmtDefMap.containsKey(definition)) {
134 stmtDefMap.put(definition, 1);
136 stmtDefMap.put(definition, stmtDefMap.get(definition) + 1);
140 if (stmtDefMap.isEmpty() && specialCase == SpecialCase.NOTNULL) {
141 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
142 "%s must contain atleast 1 element. Error in module %s (%s)", currentStatement,
143 ctx.getRoot().getStatementArgument(),
144 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
147 final Map<StatementDefinition, Integer> validatedMap = new HashMap<>();
148 for (Entry<?, ?> entry : stmtDefMap.entrySet()) {
149 final StatementDefinition key = (StatementDefinition) entry.getKey();
150 if (!cardinalityMap.containsKey(key)) {
151 if (ctx.getFromNamespace(ExtensionNamespace.class, key.getStatementName()) != null) {
154 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
155 "%s is not valid for %s. Error in module %s (%s)", key, currentStatement,
156 ctx.getRoot().getStatementArgument(),
157 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
159 if (cardinalityMap.get(key).getMin() > (Integer) entry.getValue()) {
160 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
161 "Minimal count of %s for %s is %s, detected %s. Error in module %s (%s)", key, currentStatement,
162 cardinalityMap.get(key).getMin(), entry.getValue(), ctx.getRoot().getStatementArgument(),
163 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
165 if (cardinalityMap.get(key).getMax() < (Integer) entry.getValue()) {
166 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
167 "Maximal count of %s for %s is %s, detected %s. Error in module %s (%s)", key, currentStatement,
168 cardinalityMap.get(key).getMax(), entry.getValue(), ctx.getRoot().getStatementArgument(),
169 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
171 validatedMap.put(key, 1);
174 final MapDifference<StatementDefinition, Object> diff = Maps.difference(validatedMap, cardinalityMap);
175 for (Entry<?, ?> entry : diff.entriesOnlyOnRight().entrySet()) {
176 final int min = ((Cardinality) entry.getValue()).getMin();
178 throw new MissingSubstatementException(ctx.getStatementSourceReference(),
179 "%s is missing %s. Minimal count is %s. Error in module %s (%s)", currentStatement, entry.getKey(),
180 min, ctx.getRoot().getStatementArgument(),
181 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
186 private static final class Cardinality {
187 private final int min;
188 private final int max;
190 private Cardinality(final int min, final int max) {
191 Preconditions.checkArgument(min >= 0, "Min %s cannot be less than 0!");
192 Preconditions.checkArgument(min <= max, "Min %s can not be greater than max %s!", min, max);
197 private int getMax() {
201 private int getMin() {
206 public enum SpecialCase {