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 {
27 public final static int MAX = Integer.MAX_VALUE;
28 private final Map<StatementDefinition, Cardinality> cardinalityMap;
29 private final StatementDefinition currentStatement;
30 private final SpecialCase specialCase;
32 private SubstatementValidator(final Builder builder, final SpecialCase specialCase) {
33 this.cardinalityMap = builder.cardinalityMap.build();
34 this.currentStatement = builder.currentStatement;
35 this.specialCase = specialCase;
38 public static Builder builder(final StatementDefinition currentStatement) {
39 return new Builder(currentStatement);
42 public static class Builder {
43 private final ImmutableMap.Builder<StatementDefinition, Cardinality> cardinalityMap = ImmutableMap.builder();
44 private final StatementDefinition currentStatement;
46 private Builder(final StatementDefinition currentStatement) {
47 this.currentStatement = currentStatement;
50 public Builder add(final StatementDefinition d, final int min, final int max) {
51 this.cardinalityMap.put(d, new Cardinality(min, max));
55 public SubstatementValidator build() {
56 return new SubstatementValidator(this, SpecialCase.NULL);
59 public SubstatementValidator build(final SpecialCase specialCase) {
60 return new SubstatementValidator(this, specialCase);
64 public void validate(final StmtContext<?, ?, ?> ctx) throws InvalidSubstatementException,
65 MissingSubstatementException {
66 final Iterable<StatementContextBase<?, ?, ?>> substatementsInit = Iterables.concat(
67 ctx.declaredSubstatements(), ctx.effectiveSubstatements());
69 final Map<StatementDefinition, Integer> stmtDefMap = new HashMap<>();
70 for (StatementContextBase<?, ?, ?> stmtCtx : substatementsInit) {
71 final StatementDefinition definition = stmtCtx.getPublicDefinition();
72 if (!stmtDefMap.containsKey(definition)) {
73 stmtDefMap.put(definition, 1);
75 stmtDefMap.put(definition, stmtDefMap.get(definition) + 1);
79 if (stmtDefMap.isEmpty() && specialCase == SpecialCase.NOTNULL) {
80 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
81 "%s must contain atleast 1 element. Error in module %s (%s)", currentStatement,
82 ctx.getRoot().getStatementArgument(),
83 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
86 final Map<StatementDefinition, Integer> validatedMap = new HashMap<>();
87 for (Entry<?, ?> entry : stmtDefMap.entrySet()) {
88 final StatementDefinition key = (StatementDefinition) entry.getKey();
89 if (!cardinalityMap.containsKey(key)) {
90 if (ctx.getFromNamespace(ExtensionNamespace.class, key.getStatementName()) != null) {
93 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
94 "%s is not valid for %s. Error in module %s (%s)", key, currentStatement,
95 ctx.getRoot().getStatementArgument(),
96 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
98 if (cardinalityMap.get(key).getMin() > (Integer) entry.getValue()) {
99 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
100 "Minimal count of %s for %s is %s, detected %s. Error in module %s (%s)", key, currentStatement,
101 cardinalityMap.get(key).getMin(), entry.getValue(), ctx.getRoot().getStatementArgument(),
102 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
104 if (cardinalityMap.get(key).getMax() < (Integer) entry.getValue()) {
105 throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
106 "Maximal count of %s for %s is %s, detected %s. Error in module %s (%s)", key, currentStatement,
107 cardinalityMap.get(key).getMax(), entry.getValue(), ctx.getRoot().getStatementArgument(),
108 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
110 validatedMap.put(key, 1);
113 final MapDifference<StatementDefinition, Object> diff = Maps.difference(validatedMap, cardinalityMap);
114 for (Entry<?, ?> entry : diff.entriesOnlyOnRight().entrySet()) {
115 final int min = ((Cardinality) entry.getValue()).getMin();
117 throw new MissingSubstatementException(ctx.getStatementSourceReference(),
118 "%s is missing %s. Minimal count is %s. Error in module %s (%s)", currentStatement, entry.getKey(),
119 min, ctx.getRoot().getStatementArgument(),
120 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
125 private static class Cardinality {
126 private final int min;
127 private final int max;
129 private Cardinality(final int min, final int max) {
130 Preconditions.checkArgument(min >= 0, "Min %s cannot be less than 0!");
131 Preconditions.checkArgument(min <= max, "Min %s can not be greater than max %s!", min, max);
136 private int getMax() {
140 private int getMin() {
145 public enum SpecialCase {