import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.common.Decimal64;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
public final class DecimalTypeBuilder extends RangeRestrictedTypeBuilder<DecimalTypeDefinition, Decimal64> {
}
@Override
- DecimalTypeDefinition buildType() {
- checkState(fractionDigits != null, "Fraction digits not defined");
- return new BaseDecimalType(getQName(), getUnknownSchemaNodes(), fractionDigits,
- calculateRangeConstraint(BaseDecimalType.constraintsForDigits(fractionDigits)));
+ DecimalTypeDefinition buildConstrainedType(final ConstraintMetaDefinition constraint,
+ final ImmutableList<ValueRange> ranges) {
+ final int scale = scale();
+ return new BaseDecimalType(getQName(), getUnknownSchemaNodes(), scale,
+ new ResolvedRangeConstraint<>(constraint, ensureResolvedScale(
+ calculateRanges(BaseDecimalType.constraintsForDigits(scale), ranges), scale)));
+ }
+
+ @Override
+ DecimalTypeDefinition buildUnconstrainedType() {
+ final int scale = scale();
+ return new BaseDecimalType(getQName(), getUnknownSchemaNodes(), scale,
+ BaseDecimalType.constraintsForDigits(scale));
+ }
+
+ private int scale() {
+ final var local = fractionDigits;
+ checkState(local != null, "Fraction digits not defined");
+ return local;
+ }
+
+ private static @NonNull RangeSet<Decimal64> ensureResolvedScale(final RangeSet<Decimal64> ranges, final int scale) {
+ // Check if we need to resolve anything at all
+ final var rangeSet = ranges.asRanges();
+ for (final var range : rangeSet) {
+ if (range.lowerEndpoint().scale() != scale || range.upperEndpoint().scale() != scale) {
+ return resolveScale(rangeSet, scale);
+ }
+ }
+
+ // Everything is good - return same ranges
+ return ranges;
+ }
+
+ private static @NonNull RangeSet<Decimal64> resolveScale(final Set<Range<Decimal64>> ranges, final int scale) {
+ final var builder = ImmutableRangeSet.<Decimal64>builder();
+ for (final var range : ranges) {
+ boolean reuse = true;
+
+ var lower = range.lowerEndpoint();
+ if (lower.scale() != scale) {
+ lower = lower.scaleTo(scale);
+ reuse = false;
+ }
+ var upper = range.upperEndpoint();
+ if (upper.scale() != scale) {
+ upper = upper.scaleTo(scale);
+ reuse = false;
+ }
+ builder.add(reuse ? range : Range.closed(lower, upper));
+ }
+
+ return builder.build();
}
}
*/
package org.opendaylight.yangtools.yang.model.ri.type;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
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.ImmutableRangeSet;
import com.google.common.collect.ImmutableRangeSet.Builder;
touch();
}
- final RangeConstraint<N> calculateRangeConstraint(final RangeConstraint<N> baseRangeConstraint) {
- if (ranges == null) {
- return baseRangeConstraint;
- }
+ @Override
+ final T buildType() {
+ final var localRanges = ranges;
+ return localRanges == null ? buildUnconstrainedType()
+ : buildConstrainedType(verifyNotNull(constraint), localRanges);
+ }
+
+ abstract @NonNull T buildConstrainedType(@NonNull ConstraintMetaDefinition constraint,
+ @NonNull ImmutableList<ValueRange> ranges);
+ abstract @NonNull T buildUnconstrainedType();
+
+ final RangeSet<N> calculateRanges(final @NonNull RangeConstraint<N> baseConstraint,
+ final @NonNull ImmutableList<ValueRange> contraintRanges) {
// 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 RangeSet<N> baseRangeSet = baseConstraint.getAllowedRanges();
+ verify(!baseRangeSet.isEmpty(), "Base type %s does not define constraints", getBaseType());
final Range<N> baseRange = baseRangeSet.span();
- final List<ValueRange> resolvedRanges = ensureResolvedRanges(ranges, baseRange);
+ final List<ValueRange> resolvedRanges = ensureResolvedRanges(contraintRanges, baseRange);
// Next up, ensure the of boundaries match base constraints
final RangeSet<N> typedRanges = ensureTypedRanges(resolvedRanges, baseRange.lowerEndpoint().getClass());
throw new InvalidRangeConstraintException(typedRanges,
"Range constraint %s is not a subset of parent constraint %s", typedRanges, baseRangeSet);
}
-
- return new ResolvedRangeConstraint<>(constraint, typedRanges);
+ return typedRanges;
}
private static <C extends Number & Comparable<C>> List<ValueRange> ensureResolvedRanges(
@SuppressWarnings("unchecked")
private static <T extends Number & Comparable<T>> RangeSet<T> ensureTypedRanges(final List<ValueRange> ranges,
final Class<? extends Number> clazz) {
- final Builder<T> builder = ImmutableRangeSet.builder();
+ final Builder<T> builder = ImmutableRangeSet.<T>builder();
for (ValueRange range : ranges) {
if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
return typedRanges(ranges, clazz);
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);
+ checkArgument(function != null, "Unsupported range class %s", clazz);
final Builder<T> builder = ImmutableRangeSet.builder();
import static java.util.Objects.requireNonNull;
+import com.google.common.collect.ImmutableList;
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.ValueRange;
import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
}
@Override
- final T buildType() {
- return buildType(calculateRangeConstraint(getBaseType().getRangeConstraint().get()));
+ final T buildConstrainedType(final ConstraintMetaDefinition constraint,
+ final ImmutableList<ValueRange> ranges) {
+ return buildType(new ResolvedRangeConstraint<>(constraint, calculateRanges(getBaseRangeConstraint(), ranges)));
+ }
+
+ @Override
+ final T buildUnconstrainedType() {
+ return buildType(getBaseRangeConstraint());
}
abstract @NonNull T buildType(RangeConstraint<N> rangeConstraints);
+
+ private RangeConstraint<N> getBaseRangeConstraint() {
+ return getBaseType().getRangeConstraint().orElseThrow();
+ }
}
final DecimalTypeBuilder builder = BaseTypes.decimalTypeBuilder(stmt.argumentAsTypeQName());
for (final EffectiveStatement<?, ?> subStmt : substatements) {
- if (subStmt instanceof FractionDigitsEffectiveStatement) {
- builder.setFractionDigits(((FractionDigitsEffectiveStatement) subStmt).argument());
+ if (subStmt instanceof FractionDigitsEffectiveStatement fracDigits) {
+ builder.setFractionDigits(fracDigits.argument());
}
- if (subStmt instanceof RangeEffectiveStatement) {
- final RangeEffectiveStatement range = (RangeEffectiveStatement) subStmt;
+ if (subStmt instanceof RangeEffectiveStatement range) {
builder.setRangeConstraint(range, range.argument());
}
}
- return new TypeEffectiveStatementImpl<>(stmt.declared(), substatements, builder);
+ try {
+ return new TypeEffectiveStatementImpl<>(stmt.declared(), substatements, builder);
+ } catch (ArithmeticException e) {
+ throw new SourceException("Range constraint does not match fraction-digits: " + e.getMessage(), stmt, e);
+ }
}
private static SourceException noFracDigits(final CommonStmtCtx stmt) {
assertTrue(type instanceof DecimalTypeDefinition);
final DecimalTypeDefinition decType = (DecimalTypeDefinition) type;
+ assertEquals(4, decType.getFractionDigits());
- final Set<? extends Range<?>> decRangeConstraints = decType.getRangeConstraint().get().getAllowedRanges()
- .asRanges();
+ final Set<? extends Range<Decimal64>> decRangeConstraints = decType.getRangeConstraint().get()
+ .getAllowedRanges().asRanges();
assertEquals(1, decRangeConstraints.size());
- final Range<?> range = decRangeConstraints.iterator().next();
- assertEquals(Decimal64.valueOf("1.5"), range.lowerEndpoint());
- assertEquals(Decimal64.valueOf("5.5"), range.upperEndpoint());
+ final Range<Decimal64> range = decRangeConstraints.iterator().next();
+ assertEquals(Decimal64.of(4, 15000), range.lowerEndpoint());
+ assertEquals(4, range.lowerEndpoint().scale());
+ assertEquals(Decimal64.of(4, 55000), range.upperEndpoint());
+ assertEquals(4, range.upperEndpoint().scale());
assertEquals(TypeDefinitions.DECIMAL64.bindTo(leafDecimal.getQName().getModule()), decType.getQName());
assertNull(decType.getBaseType());
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, 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.stmt;
+
+import static org.hamcrest.CoreMatchers.startsWith;
+
+import org.junit.Test;
+
+public class YT1441Test extends AbstractYangTest {
+ @Test
+ public void testInvalidRange() {
+ assertSourceException(startsWith(
+ "Range constraint does not match fraction-digits: Decreasing scale of 2.345 to 2 requires rounding [at "),
+ "/bugs/YT1441/foo.yang");
+ }
+}
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ typedef foo {
+ type decimal64 {
+ fraction-digits 2;
+ range 1..2.345;
+ }
+ }
+}