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.collect.ImmutableMap;
12 import com.google.common.collect.MapDifference;
13 import com.google.common.collect.Maps;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
18 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
19 import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
20 import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
21 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
22 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
23 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
24 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
26 public final class SubstatementValidator {
27 private final Map<StatementDefinition, Cardinality> cardinalityMap;
28 private final StatementDefinition currentStatement;
29 private final SpecialCase specialCase;
30 public final static int MAX = Integer.MAX_VALUE;
32 private SubstatementValidator(Builder builder, SpecialCase specialCase) {
33 this.cardinalityMap = builder.cardinalityMap.build();
34 this.currentStatement = builder.currentStatement;
35 this.specialCase = specialCase;
38 public static Builder builder(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(StatementDefinition currentStatement) {
47 this.currentStatement = currentStatement;
50 public Builder add(StatementDefinition d, int min, 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(SpecialCase specialCase) {
60 return new SubstatementValidator(this, specialCase);
64 public void validate(final StmtContext<?, ?, ?> ctx) throws InvalidSubstatementException,
65 MissingSubstatementException {
66 final Map<StatementDefinition, Integer> stmtDefMap = new HashMap<>();
67 final Map<StatementDefinition, Integer> validatedMap = new HashMap<>();
68 final Collection<StatementContextBase<?, ?, ?>> substatementsInit = new ArrayList<>();
69 substatementsInit.addAll(ctx.declaredSubstatements());
70 substatementsInit.addAll(ctx.effectiveSubstatements());
72 for (StatementContextBase<?, ?, ?> stmtCtx : substatementsInit) {
73 final StatementDefinition definition = stmtCtx.getPublicDefinition();
74 if (!stmtDefMap.containsKey(definition)) {
75 stmtDefMap.put(definition, 1);
77 stmtDefMap.put(definition, stmtDefMap.get(definition) + 1);
81 if (stmtDefMap.isEmpty() && specialCase == SpecialCase.NOTNULL) {
82 throw new InvalidSubstatementException(String.format("%s must contain atleast 1 element. Error in module "
83 + "%s (%s)", currentStatement, ctx.getRoot().getStatementArgument(),
84 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot())),
85 ctx.getStatementSourceReference());
88 for (Map.Entry entry : stmtDefMap.entrySet()) {
89 final StatementDefinition key = (StatementDefinition) entry.getKey();
90 if (!cardinalityMap.containsKey(key)) {
91 if (ctx.getFromNamespace(ExtensionNamespace.class, key.getStatementName()) != null) {
94 throw new InvalidSubstatementException(String.format("%s is not valid for %s. Error in module %s (%s)",
95 key, currentStatement, ctx.getRoot().getStatementArgument(),
96 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot())),
97 ctx.getStatementSourceReference());
99 if (cardinalityMap.get(key).getMin() > (Integer) entry.getValue()) {
100 throw new InvalidSubstatementException(String.format("Minimal count of %s for %s is %s, detected %s. "
101 + "Error in module %s (%s)", key, currentStatement, cardinalityMap.get(key).getMin(),
102 entry.getValue(), ctx.getRoot().getStatementArgument(),
103 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot())),
104 ctx.getStatementSourceReference());
106 if (cardinalityMap.get(key).getMax() < (Integer) entry.getValue()) {
107 throw new InvalidSubstatementException(String.format("Maximal count of %s for %s is %s, detected %s. "
108 + "Error in module %s (%s)", key, currentStatement, cardinalityMap.get(key).getMax(),
109 entry.getValue(), ctx.getRoot().getStatementArgument(),
110 ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot())),
111 ctx.getStatementSourceReference());
113 validatedMap.put(key, 1);
116 final MapDifference<StatementDefinition, Object> diff = Maps.difference(validatedMap, cardinalityMap);
118 for (Map.Entry entry : diff.entriesOnlyOnRight().entrySet()) {
119 final int min = ((Cardinality) entry.getValue()).getMin();
121 throw new MissingSubstatementException(String.format("%s is missing %s. Minimal count is %s. Error in"
122 + " module %s (%s)", currentStatement, entry.getKey(), min, ctx.getRoot()
123 .getStatementArgument(), ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot())),
124 ctx.getStatementSourceReference());
129 private static class Cardinality {
130 private final int min;
131 private final int max;
133 private Cardinality(int min, int max) throws YangParseException {
135 throw new IllegalArgumentException("Min can not be greater than max!");
138 throw new IllegalArgumentException("Min can not be les than 0!");
144 private int getMax() {
148 private int getMin() {
153 public enum SpecialCase {