*/
package org.opendaylight.yangtools.yang.model.util.type;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import java.util.Collection;
+import com.google.common.base.Verify;
+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.List;
import java.util.Optional;
-import java.util.function.Function;
-import javax.annotation.Nonnull;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+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.LengthConstraint;
import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
-import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
public abstract class LengthRestrictedTypeBuilder<T extends LengthRestrictedTypeDefinition<T>>
extends AbstractRestrictedTypeBuilder<T> {
- private List<LengthConstraint> lengthAlternatives;
+ private LengthConstraint lengthConstraint;
LengthRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
- super(Preconditions.checkNotNull(baseType), path);
+ super(requireNonNull(baseType), path);
}
- public final void setLengthAlternatives(@Nonnull final Collection<LengthConstraint> lengthAlternatives) {
- Preconditions.checkState(this.lengthAlternatives == null, "Range alternatives already defined as %s",
- lengthAlternatives);
- this.lengthAlternatives = ImmutableList.copyOf(lengthAlternatives);
- touch();
- }
-
- private static List<LengthConstraint> ensureResolvedLengths(final List<LengthConstraint> unresolved,
- final List<LengthConstraint> baseRangeConstraints) {
- // First check if we need to resolve anything at all
- for (LengthConstraint c : unresolved) {
- if (c.getMax() instanceof UnresolvedNumber || c.getMin() instanceof UnresolvedNumber) {
- return resolveLengths(unresolved, baseRangeConstraints);
- }
+ /**
+ * Set a new length constraint.
+ *
+ * @param constraint Constraint metadata
+ * @param ranges Allowed ranges
+ * @throws IllegalStateException if the constraint has already been set
+ * @throws InvalidLengthConstraintException if one of the proposed ranges does not overlap with supertype
+ * @throws NullPointerException if any of the arguments is null
+ */
+ public final void setLengthConstraint(final @NonNull ConstraintMetaDefinition constraint,
+ final @NonNull List<ValueRange> ranges) throws InvalidLengthConstraintException {
+ Preconditions.checkState(lengthConstraint == null, "Length alternatives already defined as %s",
+ lengthConstraint);
+ final LengthConstraint baseLengths = findLenghts();
+ if (ranges.isEmpty()) {
+ lengthConstraint = baseLengths;
+ return;
}
- // No need, just return the same list
- return unresolved;
- }
-
- private static List<LengthConstraint> resolveLengths(final List<LengthConstraint> unresolved,
- final List<LengthConstraint> baseLengthConstraints) {
- final Builder<LengthConstraint> builder = ImmutableList.builder();
-
- for (LengthConstraint c : unresolved) {
- final Number max = c.getMax();
- final Number min = c.getMin();
-
- if (max instanceof UnresolvedNumber || min instanceof UnresolvedNumber) {
- final Number rMax = max instanceof UnresolvedNumber
- ? ((UnresolvedNumber)max).resolveLength(baseLengthConstraints) : max;
- final Number rMin = min instanceof UnresolvedNumber
- ? ((UnresolvedNumber)min).resolveLength(baseLengthConstraints) : min;
-
- builder.add(BaseConstraints.newLengthConstraint(rMin, rMax, Optional.ofNullable(c.getDescription()),
- Optional.ofNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
- } else {
- builder.add(c);
- }
- }
-
- return builder.build();
- }
+ // Run through alternatives and resolve them against the base type
+ requireNonNull(constraint);
+ final Builder<Integer> builder = ImmutableRangeSet.builder();
+ final Range<Integer> span = baseLengths.getAllowedRanges().span();
- private static List<LengthConstraint> ensureTypedLengths(final List<LengthConstraint> lengths,
- final Class<? extends Number> clazz) {
- for (LengthConstraint c : lengths) {
- if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
- return typedLengths(lengths, clazz);
- }
+ for (ValueRange c : ranges) {
+ builder.add(Range.closed(resolveLength(c.lowerBound(), span), resolveLength(c.upperBound(), span)));
}
- return lengths;
- }
- private static List<LengthConstraint> typedLengths(final List<LengthConstraint> lengths,
- final Class<? extends Number> clazz) {
- final Function<Number, Number> function = NumberUtil.converterTo(clazz);
- Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
-
- final Builder<LengthConstraint> builder = ImmutableList.builder();
-
- for (LengthConstraint c : lengths) {
- if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
- final Number min;
- final Number max;
-
- try {
- min = function.apply(c.getMin());
- max = function.apply(c.getMax());
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException(String.format("Constraint %s does not fit into range of %s",
- c, clazz.getSimpleName()), e);
- }
- builder.add(BaseConstraints.newLengthConstraint(min, max, Optional.ofNullable(c.getDescription()),
- Optional.ofNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
- } else {
- builder.add(c);
+ // Now verify if new ranges are strict subset of base ranges
+ final RangeSet<Integer> allowed = builder.build();
+ final RangeSet<Integer> baseRanges = baseLengths.getAllowedRanges();
+ for (Range<Integer> range : allowed.asRanges()) {
+ if (!baseRanges.encloses(range)) {
+ throw new InvalidLengthConstraintException("Range %s is not a subset of parent constraint %s", range,
+ baseRanges);
}
}
- return builder.build();
+ lengthConstraint = new ResolvedLengthConstraint(constraint, allowed);
+ touch();
}
- private static boolean lengthCovered(final List<LengthConstraint> where,
- final LengthConstraint what) {
- for (LengthConstraint c : where) {
- if (NumberUtil.isRangeCovered(what.getMin(), what.getMax(), c.getMin(), c.getMax())) {
- return true;
- }
- }
-
- return false;
- }
+ abstract T buildType(LengthConstraint lengthConstraint);
@Override
final T buildType() {
- final List<LengthConstraint> baseLengths = findLenghts();
-
- if (lengthAlternatives == null || lengthAlternatives.isEmpty()) {
- return buildType(baseLengths);
- }
-
- // Run through alternatives and resolve them against the base type
- final List<LengthConstraint> resolvedLengths = ensureResolvedLengths(lengthAlternatives, baseLengths);
+ return buildType(lengthConstraint != null ? lengthConstraint : findLenghts());
+ }
- // Next up, ensure the of boundaries match base constraints
- final Class<? extends Number> clazz = baseLengths.get(0).getMin().getClass();
- final List<LengthConstraint> typedLengths = ensureTypedLengths(resolvedLengths, clazz);
+ abstract LengthConstraint typeLengthConstraints();
- // Now verify if new ranges are strict subset of base ranges
- for (LengthConstraint c : typedLengths) {
- if (!lengthCovered(baseLengths, c)) {
- throw new InvalidLengthConstraintException(c,
- "Length constraint %s is not a subset of parent constraints %s", c, baseLengths);
- }
+ private static Integer resolveLength(final Number unresolved, final Range<Integer> span) {
+ if (unresolved instanceof Integer) {
+ return (Integer) unresolved;
+ }
+ if (unresolved instanceof UnresolvedNumber) {
+ return ((UnresolvedNumber)unresolved).resolveLength(span);
}
- return buildType(typedLengths);
+ return Verify.verifyNotNull(NumberUtil.converterTo(Integer.class)).apply(unresolved);
}
- abstract T buildType(List<LengthConstraint> lengthConstraints);
-
- abstract List<LengthConstraint> typeLengthConstraints();
-
- private List<LengthConstraint> findLenghts() {
- List<LengthConstraint> ret = ImmutableList.of();
+ private LengthConstraint findLenghts() {
+ Optional<LengthConstraint> ret = Optional.empty();
T wlk = getBaseType();
- while (wlk != null && ret.isEmpty()) {
- ret = wlk.getLengthConstraints();
+ while (wlk != null && !ret.isPresent()) {
+ ret = wlk.getLengthConstraint();
wlk = wlk.getBaseType();
}
- return ret.isEmpty() ? typeLengthConstraints() : ret;
+ return ret.orElse(typeLengthConstraints());
}
}