Remove SchemaPath from TypeDefinition implementations
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / type / RangeRestrictedTypeBuilder.java
index 434b4adbacb33acdc898bedb166be0bb569d3e73..a7286400436b23c135c9e1f79813e5f9e95c7184 100644 (file)
@@ -7,41 +7,76 @@
  */
 package org.opendaylight.yangtools.yang.model.util.type;
 
-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 java.util.function.Function;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+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);
             }
         }
 
@@ -49,101 +84,69 @@ public abstract class RangeRestrictedTypeBuilder<T extends TypeDefinition<T>> ex
         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).resolveRange(baseRangeConstraints) : max;
-                final Number rMin = min instanceof UnresolvedNumber ?
-                        ((UnresolvedNumber)min).resolveRange(baseRangeConstraints) : min;
-
-                builder.add(BaseConstraints.newRangeConstraint(rMin, rMax, Optional.fromNullable(c.getDescription()),
-                    Optional.fromNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
+                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()), c.getErrorAppTag(), c.getErrorMessage()));
+
+                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) {
-            if (!rangeCovered(baseRangeConstraints, c)) {
-                throw new InvalidRangeConstraintException(c, "Range constraint %s is not a subset of parent constraints %s",
-                    c, baseRangeConstraints);
-            }
-        }
-
-        return typedRanges;
-    }
 }