*/
package org.opendaylight.yangtools.yang.model.util.type;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import java.util.Collection;
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 org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
+import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
public abstract class LengthRestrictedTypeBuilder<T extends TypeDefinition<T>> extends AbstractRestrictedTypeBuilder<T> {
- private Collection<LengthConstraint> lengthAlternatives;
+ private List<LengthConstraint> lengthAlternatives;
LengthRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
super(Preconditions.checkNotNull(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 = Preconditions.checkNotNull(lengthAlternatives);
+ this.lengthAlternatives = ImmutableList.copyOf(lengthAlternatives);
touch();
}
- final List<LengthConstraint> calculateLenghtConstraints(final List<LengthConstraint> baseLengthConstraints) {
+ 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);
+ }
+ }
+
+ // 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.fromNullable(c.getDescription()),
+ Optional.fromNullable(c.getReference())));
+ } else {
+ builder.add(c);
+ }
+ }
+
+ return builder.build();
+ }
+
+ 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);
+ }
+ }
+
+ 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, 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.fromNullable(c.getDescription()),
+ Optional.fromNullable(c.getReference())));
+ } else {
+ builder.add(c);
+ }
+ }
+
+ return builder.build();
+ }
+
+ 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(List<LengthConstraint> lengthConstraints);
+ abstract List<LengthConstraint> getLengthConstraints(T type);
+ abstract List<LengthConstraint> typeLengthConstraints();
+
+ private List<LengthConstraint> findLenghts() {
+ List<LengthConstraint> ret = ImmutableList.of();
+ T wlk = getBaseType();
+ while (wlk != null && ret.isEmpty()) {
+ ret = getLengthConstraints(wlk);
+ wlk = wlk.getBaseType();
+ }
+
+ return ret.isEmpty() ? typeLengthConstraints() : ret;
+ }
+
+ @Override
+ final T buildType() {
if (lengthAlternatives == null || lengthAlternatives.isEmpty()) {
- return baseLengthConstraints;
+ return buildType(ImmutableList.<LengthConstraint>of());
+ }
+
+ final List<LengthConstraint> baseLengths = findLenghts();
+
+ // Run through alternatives and resolve them against the base type
+ final List<LengthConstraint> resolvedLengths = ensureResolvedLengths(lengthAlternatives, baseLengths);
+
+ // 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);
+
+ // Now verify if new ranges are strict subset of base ranges
+ for (LengthConstraint c : typedLengths) {
+ Preconditions.checkArgument(lengthCovered(baseLengths, c),
+ "Range constraint %s is not a subset of parent constraints %s", c, baseLengths);
}
- // FIXME: calculate lengths
- throw new UnsupportedOperationException();
+ return buildType(typedLengths);
}
}