--- /dev/null
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.util.type;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Map;
+
+final class NumberUtil {
+ private static final Map<Class<? extends Number>, Function<Number, Number>> CONVERTERS;
+ static {
+ final ImmutableMap.Builder<Class<? extends Number>, Function<Number, Number>> b = ImmutableMap.builder();
+ b.put(Byte.class, new Function<Number, Number>() {
+ @Override
+ public Number apply(final Number input) {
+ if (input instanceof Byte) {
+ return input;
+ }
+
+ return Byte.valueOf(input.toString());
+ }
+ });
+ b.put(Short.class, new Function<Number, Number>() {
+ @Override
+ public Number apply(final Number input) {
+ if (input instanceof Short) {
+ return input;
+ }
+ if (input instanceof Byte) {
+ return input.shortValue();
+ }
+
+ return Short.valueOf(input.toString());
+ }
+ });
+ b.put(Integer.class, new Function<Number, Number>() {
+ @Override
+ public Number apply(final Number input) {
+ if (input instanceof Integer) {
+ return input;
+ }
+ if (input instanceof Byte || input instanceof Short) {
+ return input.intValue();
+ }
+
+ return Integer.valueOf(input.toString());
+ }
+ });
+ b.put(Long.class, new Function<Number, Number>() {
+ @Override
+ public Number apply(final Number input) {
+ if (input instanceof Long) {
+ return input;
+ }
+ if (input instanceof Byte || input instanceof Short || input instanceof Integer) {
+ return input.longValue();
+ }
+
+ return Long.valueOf(input.toString());
+ }
+ });
+ b.put(BigDecimal.class, new Function<Number, Number>() {
+ @Override
+ public Number apply(final Number input) {
+ if (input instanceof BigDecimal) {
+ return input;
+ }
+ if (input instanceof Byte || input instanceof Short ||
+ input instanceof Integer || input instanceof Long) {
+ return BigDecimal.valueOf(input.longValue());
+ }
+
+ return new BigDecimal(input.toString());
+ }
+ });
+ b.put(BigInteger.class, new Function<Number, Number>() {
+ @Override
+ public Number apply(final Number input) {
+ if (input instanceof BigInteger) {
+ return input;
+ }
+ if (input instanceof Byte || input instanceof Short ||
+ input instanceof Integer || input instanceof Long) {
+ return BigInteger.valueOf(input.longValue());
+ }
+
+ return new BigInteger(input.toString());
+ }
+ });
+ CONVERTERS = b.build();
+ }
+
+ private NumberUtil() {
+ throw new UnsupportedOperationException();
+ }
+
+ static Function<Number, Number> converterTo(final Class<? extends Number> clazz) {
+ return CONVERTERS.get(clazz);
+ }
+}
*/
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.base.Verify;
+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.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
+import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
public abstract class RangeRestrictedTypeBuilder<T extends TypeDefinition<T>> extends AbstractRestrictedTypeBuilder<T> {
- private Collection<RangeConstraint> rangeAlternatives;
+ private List<RangeConstraint> rangeAlternatives;
RangeRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
super(Preconditions.checkNotNull(baseType), path);
}
- public void setRangeAlternatives(@Nonnull final Collection<RangeConstraint> rangeAlternatives) {
+ public final void setRangeAlternatives(@Nonnull final Collection<RangeConstraint> rangeAlternatives) {
Preconditions.checkState(this.rangeAlternatives == null, "Range alternatives already defined as %s",
this.rangeAlternatives);
- this.rangeAlternatives = Preconditions.checkNotNull(rangeAlternatives);
+ this.rangeAlternatives = ImmutableList.copyOf(rangeAlternatives);
touch();
}
+ private static List<RangeConstraint> ensureResolvedRanges(final List<RangeConstraint> unresolved,
+ final List<RangeConstraint> baseRangeConstraints) {
+ // 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);
+ }
+ }
+
+ // No need, just return the same list
+ 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();
+
+ 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())));
+ } else {
+ builder.add(c);
+ }
+
+ }
+
+ return builder.build();
+ }
+
+ private static List<RangeConstraint> ensureTypedRanges(final List<RangeConstraint> ranges,
+ final Class<? extends Number> clazz) {
+ for (RangeConstraint c : ranges) {
+ if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
+ return typedRanges(ranges, clazz);
+ }
+ }
+
+ return ranges;
+ }
+
+ private static List<RangeConstraint> typedRanges(final List<RangeConstraint> ranges, final Class<? extends Number> clazz) {
+ final Function<Number, Number> function = NumberUtil.converterTo(clazz);
+ Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
+
+ final Builder<RangeConstraint> builder = ImmutableList.builder();
+
+ for (RangeConstraint c : ranges) {
+ 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.newRangeConstraint(min, max, Optional.fromNullable(c.getDescription()),
+ Optional.fromNullable(c.getReference())));
+ } else {
+ builder.add(c);
+ }
+ }
+
+ return builder.build();
+ }
+
+ private static boolean rangeCovered(final List<RangeConstraint> where,
+ final RangeConstraint what) {
+ // We have ensured the types match, and we are sure each of those types implements comparable
+ @SuppressWarnings("unchecked")
+ final Comparable<Object> min = (Comparable<Object>) what.getMin();
+ @SuppressWarnings("unchecked")
+ final Comparable<Object> max = (Comparable<Object>) what.getMax();
+
+ for (RangeConstraint c : where) {
+ if (min.compareTo(c.getMin()) >= 0 && max.compareTo(c.getMax()) <= 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
final List<RangeConstraint> calculateRangeConstraints(final List<RangeConstraint> baseRangeConstraints) {
if (rangeAlternatives == null || rangeAlternatives.isEmpty()) {
return baseRangeConstraints;
}
- // FIXME: calculate ranges
- throw new UnsupportedOperationException();
+ // 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;
}
}