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 static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Verify;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableRangeSet;
17 import com.google.common.collect.ImmutableRangeSet.Builder;
18 import com.google.common.collect.Range;
19 import com.google.common.collect.RangeSet;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.function.Function;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
25 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
26 import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
27 import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
28 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
29 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
31 public abstract class RangeRestrictedTypeBuilder<T extends RangeRestrictedTypeDefinition<T, N>,
32 N extends Number & Comparable<N>> extends AbstractRestrictedTypeBuilder<T> {
33 private ConstraintMetaDefinition constraint;
34 private List<ValueRange> ranges;
36 RangeRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
37 super(baseType, path);
40 @SuppressWarnings("checkstyle:hiddenField")
41 public final void setRangeConstraint(final @NonNull ConstraintMetaDefinition constraint,
42 final @NonNull List<ValueRange> ranges) {
43 checkState(this.ranges == null, "Range constraint already defined as %s %s", this.ranges, this.constraint);
45 this.constraint = requireNonNull(constraint);
46 this.ranges = ImmutableList.copyOf(ranges);
50 final RangeConstraint<N> calculateRangeConstraint(final RangeConstraint<N> baseRangeConstraint) {
52 return baseRangeConstraint;
55 // Run through alternatives and resolve them against the base type
56 final RangeSet<N> baseRangeSet = baseRangeConstraint.getAllowedRanges();
57 Verify.verify(!baseRangeSet.isEmpty(), "Base type %s does not define constraints", getBaseType());
59 final Range<N> baseRange = baseRangeSet.span();
60 final List<ValueRange> resolvedRanges = ensureResolvedRanges(ranges, baseRange);
62 // Next up, ensure the of boundaries match base constraints
63 final RangeSet<N> typedRanges = ensureTypedRanges(resolvedRanges, baseRange.lowerEndpoint().getClass());
65 // Now verify if new ranges are strict subset of base ranges
66 if (!baseRangeSet.enclosesAll(typedRanges)) {
67 throw new InvalidRangeConstraintException(typedRanges,
68 "Range constraint %s is not a subset of parent constraint %s", typedRanges, baseRangeSet);
71 return new ResolvedRangeConstraint<>(constraint, typedRanges);
74 private static <C extends Number & Comparable<C>> List<ValueRange> ensureResolvedRanges(
75 final List<ValueRange> unresolved, final Range<C> baseRange) {
76 // First check if we need to resolve anything at all
77 for (ValueRange c : unresolved) {
78 if (c.lowerBound() instanceof UnresolvedNumber || c.upperBound() instanceof UnresolvedNumber) {
79 return resolveRanges(unresolved, baseRange);
83 // No need, just return the same list
87 private static <T extends Number & Comparable<T>> List<ValueRange> resolveRanges(final List<ValueRange> unresolved,
88 final Range<T> baseRange) {
89 final List<ValueRange> ret = new ArrayList<>(unresolved.size());
90 for (ValueRange range : unresolved) {
91 final Number min = range.lowerBound();
92 final Number max = range.upperBound();
94 if (max instanceof UnresolvedNumber || min instanceof UnresolvedNumber) {
95 final @NonNull Number rMin = min instanceof UnresolvedNumber
96 ? ((UnresolvedNumber)min).resolveRange(baseRange) : min;
97 final @NonNull Number rMax = max instanceof UnresolvedNumber
98 ? ((UnresolvedNumber)max).resolveRange(baseRange) : max;
99 ret.add(ValueRange.of(rMin, rMax));
108 @SuppressWarnings("unchecked")
109 private static <T extends Number & Comparable<T>> RangeSet<T> ensureTypedRanges(final List<ValueRange> ranges,
110 final Class<? extends Number> clazz) {
111 final Builder<T> builder = ImmutableRangeSet.builder();
112 for (ValueRange range : ranges) {
113 if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
114 return typedRanges(ranges, clazz);
117 builder.add(Range.closed((T) range.lowerBound(), (T)range.upperBound()));
120 return builder.build();
123 @SuppressWarnings("unchecked")
124 private static <T extends Number & Comparable<T>> RangeSet<T> typedRanges(final List<ValueRange> ranges,
125 final Class<? extends Number> clazz) {
126 final Function<Number, ? extends Number> function = NumberUtil.converterTo(clazz);
127 Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
129 final Builder<T> builder = ImmutableRangeSet.builder();
131 for (ValueRange range : ranges) {
132 if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
137 min = function.apply(range.lowerBound());
138 max = function.apply(range.upperBound());
139 } catch (NumberFormatException e) {
140 throw new IllegalArgumentException(String.format("Constraint %s does not fit into range of %s",
141 range, clazz.getSimpleName()), e);
144 builder.add(Range.closed((T)min, (T)max));
146 builder.add(Range.closed((T) range.lowerBound(), (T)range.upperBound()));
150 return builder.build();