2 * Copyright (c) 2015 Pantheon Technologies s.r.o. 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
8 package org.opendaylight.yangtools.yang.model.util.type;
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Verify;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableList.Builder;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Optional;
17 import java.util.function.Function;
18 import javax.annotation.Nonnull;
19 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
20 import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
21 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
22 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
23 import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
25 public abstract class RangeRestrictedTypeBuilder<T extends RangeRestrictedTypeDefinition<T>>
26 extends AbstractRestrictedTypeBuilder<T> {
27 private List<RangeConstraint> rangeAlternatives;
29 RangeRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
30 super(baseType, path);
33 public final void setRangeAlternatives(@Nonnull final Collection<RangeConstraint> rangeAlternatives) {
34 Preconditions.checkState(this.rangeAlternatives == null, "Range alternatives already defined as %s",
35 this.rangeAlternatives);
36 this.rangeAlternatives = ImmutableList.copyOf(rangeAlternatives);
40 private static List<RangeConstraint> ensureResolvedRanges(final List<RangeConstraint> unresolved,
41 final List<RangeConstraint> baseRangeConstraints) {
42 // First check if we need to resolve anything at all
43 for (RangeConstraint c : unresolved) {
44 if (c.getMax() instanceof UnresolvedNumber || c.getMin() instanceof UnresolvedNumber) {
45 return resolveRanges(unresolved, baseRangeConstraints);
49 // No need, just return the same list
53 private static List<RangeConstraint> resolveRanges(final List<RangeConstraint> unresolved,
54 final List<RangeConstraint> baseRangeConstraints) {
55 final Builder<RangeConstraint> builder = ImmutableList.builder();
57 for (RangeConstraint c : unresolved) {
58 final Number max = c.getMax();
59 final Number min = c.getMin();
61 if (max instanceof UnresolvedNumber || min instanceof UnresolvedNumber) {
62 final Number rMax = max instanceof UnresolvedNumber
63 ? ((UnresolvedNumber)max).resolveRange(baseRangeConstraints) : max;
64 final Number rMin = min instanceof UnresolvedNumber
65 ? ((UnresolvedNumber)min).resolveRange(baseRangeConstraints) : min;
67 builder.add(BaseConstraints.newRangeConstraint(rMin, rMax, Optional.ofNullable(c.getDescription()),
68 Optional.ofNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
75 return builder.build();
78 private static List<RangeConstraint> ensureTypedRanges(final List<RangeConstraint> ranges,
79 final Class<? extends Number> clazz) {
80 for (RangeConstraint c : ranges) {
81 if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
82 return typedRanges(ranges, clazz);
89 private static List<RangeConstraint> typedRanges(final List<RangeConstraint> ranges,
90 final Class<? extends Number> clazz) {
91 final Function<Number, ? extends Number> function = NumberUtil.converterTo(clazz);
92 Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
94 final Builder<RangeConstraint> builder = ImmutableList.builder();
96 for (RangeConstraint c : ranges) {
97 if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
102 min = function.apply(c.getMin());
103 max = function.apply(c.getMax());
104 } catch (NumberFormatException e) {
105 throw new IllegalArgumentException(String.format("Constraint %s does not fit into range of %s",
106 c, clazz.getSimpleName()), e);
108 builder.add(BaseConstraints.newRangeConstraint(min, max, Optional.ofNullable(c.getDescription()),
109 Optional.ofNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
115 return builder.build();
118 private static boolean rangeCovered(final List<RangeConstraint> where,
119 final RangeConstraint what) {
120 for (RangeConstraint c : where) {
121 if (NumberUtil.isRangeCovered(what.getMin(), what.getMax(), c.getMin(), c.getMax())) {
129 final List<RangeConstraint> calculateRangeConstraints(final List<RangeConstraint> baseRangeConstraints) {
130 if (rangeAlternatives == null || rangeAlternatives.isEmpty()) {
131 return baseRangeConstraints;
134 // Run through alternatives and resolve them against the base type
135 Verify.verify(!baseRangeConstraints.isEmpty(), "Base type %s does not define constraints", getBaseType());
136 final List<RangeConstraint> resolvedRanges = ensureResolvedRanges(rangeAlternatives, baseRangeConstraints);
138 // Next up, ensure the of boundaries match base constraints
139 final Class<? extends Number> clazz = baseRangeConstraints.get(0).getMin().getClass();
140 final List<RangeConstraint> typedRanges = ensureTypedRanges(resolvedRanges, clazz);
142 // Now verify if new ranges are strict subset of base ranges
143 for (RangeConstraint c : typedRanges) {
144 if (!rangeCovered(baseRangeConstraints, c)) {
145 throw new InvalidRangeConstraintException(c,
146 "Range constraint %s is not a subset of parent constraints %s", c, baseRangeConstraints);