*/
package org.opendaylight.yangtools.yang.model.util.type;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import java.util.Collection;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.ImmutableRangeSet.Builder;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.util.ArrayList;
import java.util.List;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
-import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
-import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
-public abstract class RangeRestrictedTypeBuilder<T extends TypeDefinition<T>> extends AbstractRestrictedTypeBuilder<T> {
- private List<RangeConstraint> rangeAlternatives;
+public abstract class RangeRestrictedTypeBuilder<T extends RangeRestrictedTypeDefinition<T, N>,
+ N extends Number & Comparable<N>> extends AbstractRestrictedTypeBuilder<T> {
+ private ConstraintMetaDefinition constraint;
+ private ImmutableList<ValueRange> ranges;
- RangeRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
- super(baseType, path);
+ RangeRestrictedTypeBuilder(final T baseType, final QName qname) {
+ super(baseType, qname);
}
- public final void setRangeAlternatives(@Nonnull final Collection<RangeConstraint> rangeAlternatives) {
- Preconditions.checkState(this.rangeAlternatives == null, "Range alternatives already defined as %s",
- this.rangeAlternatives);
- this.rangeAlternatives = ImmutableList.copyOf(rangeAlternatives);
+ @SuppressWarnings("checkstyle:hiddenField")
+ public final void setRangeConstraint(final @NonNull ConstraintMetaDefinition constraint,
+ final @NonNull List<ValueRange> ranges) {
+ checkState(this.ranges == null, "Range constraint already defined as %s %s", this.ranges, this.constraint);
+
+ this.constraint = requireNonNull(constraint);
+ this.ranges = ImmutableList.copyOf(ranges);
touch();
}
- private static List<RangeConstraint> ensureResolvedRanges(final List<RangeConstraint> unresolved,
- final List<RangeConstraint> baseRangeConstraints) {
+ final RangeConstraint<N> calculateRangeConstraint(final RangeConstraint<N> baseRangeConstraint) {
+ if (ranges == null) {
+ return baseRangeConstraint;
+ }
+
+ // Run through alternatives and resolve them against the base type
+ final RangeSet<N> baseRangeSet = baseRangeConstraint.getAllowedRanges();
+ Verify.verify(!baseRangeSet.isEmpty(), "Base type %s does not define constraints", getBaseType());
+
+ final Range<N> baseRange = baseRangeSet.span();
+ final List<ValueRange> resolvedRanges = ensureResolvedRanges(ranges, baseRange);
+
+ // Next up, ensure the of boundaries match base constraints
+ final RangeSet<N> typedRanges = ensureTypedRanges(resolvedRanges, baseRange.lowerEndpoint().getClass());
+
+ // Now verify if new ranges are strict subset of base ranges
+ if (!baseRangeSet.enclosesAll(typedRanges)) {
+ throw new InvalidRangeConstraintException(typedRanges,
+ "Range constraint %s is not a subset of parent constraint %s", typedRanges, baseRangeSet);
+ }
+
+ return new ResolvedRangeConstraint<>(constraint, typedRanges);
+ }
+
+ private static <C extends Number & Comparable<C>> List<ValueRange> ensureResolvedRanges(
+ final List<ValueRange> unresolved, final Range<C> baseRange) {
// First check if we need to resolve anything at all
- for (RangeConstraint c : unresolved) {
- if (c.getMax() instanceof UnresolvedNumber || c.getMin() instanceof UnresolvedNumber) {
- return resolveRanges(unresolved, baseRangeConstraints);
+ for (ValueRange c : unresolved) {
+ if (c.lowerBound() instanceof UnresolvedNumber || c.upperBound() instanceof UnresolvedNumber) {
+ return resolveRanges(unresolved, baseRange);
}
}
return unresolved;
}
- private static List<RangeConstraint> resolveRanges(final List<RangeConstraint> unresolved,
- final List<RangeConstraint> baseRangeConstraints) {
- final Builder<RangeConstraint> builder = ImmutableList.builder();
-
- for (RangeConstraint c : unresolved) {
- final Number max = c.getMax();
- final Number min = c.getMin();
+ private static <T extends Number & Comparable<T>> List<ValueRange> resolveRanges(final List<ValueRange> unresolved,
+ final Range<T> baseRange) {
+ final List<ValueRange> ret = new ArrayList<>(unresolved.size());
+ for (ValueRange range : unresolved) {
+ final Number min = range.lowerBound();
+ final Number max = range.upperBound();
if (max instanceof UnresolvedNumber || min instanceof UnresolvedNumber) {
- final Number rMax = max instanceof UnresolvedNumber ?
- ((UnresolvedNumber)max).resolve(baseRangeConstraints) : max;
- final Number rMin = min instanceof UnresolvedNumber ?
- ((UnresolvedNumber)min).resolve(baseRangeConstraints) : min;
-
- builder.add(BaseConstraints.newRangeConstraint(rMin, rMax, Optional.fromNullable(c.getDescription()),
- Optional.fromNullable(c.getReference())));
+ final @NonNull Number rMin = min instanceof UnresolvedNumber
+ ? ((UnresolvedNumber)min).resolveRange(baseRange) : min;
+ final @NonNull Number rMax = max instanceof UnresolvedNumber
+ ? ((UnresolvedNumber)max).resolveRange(baseRange) : max;
+ ret.add(ValueRange.of(rMin, rMax));
} else {
- builder.add(c);
+ ret.add(range);
}
-
}
- return builder.build();
+ return ret;
}
- private static List<RangeConstraint> ensureTypedRanges(final List<RangeConstraint> ranges,
+ @SuppressWarnings("unchecked")
+ private static <T extends Number & Comparable<T>> RangeSet<T> ensureTypedRanges(final List<ValueRange> ranges,
final Class<? extends Number> clazz) {
- for (RangeConstraint c : ranges) {
- if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
+ final Builder<T> builder = ImmutableRangeSet.builder();
+ for (ValueRange range : ranges) {
+ if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
return typedRanges(ranges, clazz);
}
+
+ builder.add(Range.closed((T) range.lowerBound(), (T)range.upperBound()));
}
- return ranges;
+ return builder.build();
}
- private static List<RangeConstraint> typedRanges(final List<RangeConstraint> ranges, final Class<? extends Number> clazz) {
- final Function<Number, Number> function = NumberUtil.converterTo(clazz);
+ @SuppressWarnings("unchecked")
+ private static <T extends Number & Comparable<T>> RangeSet<T> typedRanges(final List<ValueRange> ranges,
+ final Class<? extends Number> clazz) {
+ final Function<Number, ? extends Number> function = NumberUtil.converterTo(clazz);
Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
- final Builder<RangeConstraint> builder = ImmutableList.builder();
+ final Builder<T> builder = ImmutableRangeSet.builder();
- for (RangeConstraint c : ranges) {
- if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
- final Number min, max;
+ for (ValueRange range : ranges) {
+ if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
+ final Number min;
+ final Number max;
try {
- min = function.apply(c.getMin());
- max = function.apply(c.getMax());
+ min = function.apply(range.lowerBound());
+ max = function.apply(range.upperBound());
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format("Constraint %s does not fit into range of %s",
- c, clazz.getSimpleName()), e);
+ range, clazz.getSimpleName()), e);
}
- builder.add(BaseConstraints.newRangeConstraint(min, max, Optional.fromNullable(c.getDescription()),
- Optional.fromNullable(c.getReference())));
+
+ builder.add(Range.closed((T)min, (T)max));
} else {
- builder.add(c);
+ builder.add(Range.closed((T) range.lowerBound(), (T)range.upperBound()));
}
}
return builder.build();
}
-
- private static boolean rangeCovered(final List<RangeConstraint> where,
- final RangeConstraint what) {
- for (RangeConstraint c : where) {
- if (NumberUtil.isRangeCovered(what.getMin(), what.getMax(), c.getMin(), c.getMax())) {
- return true;
- }
- }
-
- return false;
- }
-
- final List<RangeConstraint> calculateRangeConstraints(final List<RangeConstraint> baseRangeConstraints) {
- if (rangeAlternatives == null || rangeAlternatives.isEmpty()) {
- return baseRangeConstraints;
- }
-
- // Run through alternatives and resolve them against the base type
- Verify.verify(!baseRangeConstraints.isEmpty(), "Base type %s does not define constraints", getBaseType());
- final List<RangeConstraint> resolvedRanges = ensureResolvedRanges(rangeAlternatives, baseRangeConstraints);
-
- // Next up, ensure the of boundaries match base constraints
- final Class<? extends Number> clazz = baseRangeConstraints.get(0).getMin().getClass();
- final List<RangeConstraint> typedRanges = ensureTypedRanges(resolvedRanges, clazz);
-
- // Now verify if new ranges are strict subset of base ranges
- for (RangeConstraint c : typedRanges) {
- Preconditions.checkArgument(rangeCovered(baseRangeConstraints, c),
- "Range constraint %s is not a subset of parent constraints %s", c, baseRangeConstraints);
- }
-
- // FIXME: merge adjacent ranges and sort them
- return typedRanges;
- }
}