BUG-8043: correct LengthConstraint definition
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / type / LengthRestrictedTypeBuilder.java
1 /*
2  * Copyright (c) 2015 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.model.util.type;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Verify;
14 import com.google.common.collect.ImmutableRangeSet;
15 import com.google.common.collect.ImmutableRangeSet.Builder;
16 import com.google.common.collect.Range;
17 import com.google.common.collect.RangeSet;
18 import java.util.List;
19 import java.util.Optional;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
22 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
23 import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
24 import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
25 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
26 import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
27
28 public abstract class LengthRestrictedTypeBuilder<T extends LengthRestrictedTypeDefinition<T>>
29         extends AbstractRestrictedTypeBuilder<T> {
30     private LengthConstraint lengthConstraint;
31
32     LengthRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
33         super(requireNonNull(baseType), path);
34     }
35
36     /**
37      * Set a new length constraint.
38      *
39      * @param constraint Constraint metadata
40      * @param ranges Allowed ranges
41      * @throws IllegalStateException if the constraint has already been set
42      * @throws InvalidLengthConstraintException if one of the proposed ranges does not overlap with supertype
43      * @throws NullPointerException if any of the arguments is null
44      */
45     public final void setLengthConstraint(final @NonNull ConstraintMetaDefinition constraint,
46             final @NonNull List<ValueRange> ranges) throws InvalidLengthConstraintException {
47         Preconditions.checkState(lengthConstraint == null, "Length alternatives already defined as %s",
48                 lengthConstraint);
49         final LengthConstraint baseLengths = findLenghts();
50         if (ranges.isEmpty()) {
51             lengthConstraint = baseLengths;
52             return;
53         }
54
55         // Run through alternatives and resolve them against the base type
56         requireNonNull(constraint);
57         final Builder<Integer> builder = ImmutableRangeSet.builder();
58         final Range<Integer> span = baseLengths.getAllowedRanges().span();
59
60         for (ValueRange c : ranges) {
61             builder.add(Range.closed(resolveLength(c.lowerBound(), span), resolveLength(c.upperBound(), span)));
62         }
63
64
65         // Now verify if new ranges are strict subset of base ranges
66         final RangeSet<Integer> allowed = builder.build();
67         final RangeSet<Integer> baseRanges = baseLengths.getAllowedRanges();
68         for (Range<Integer> range : allowed.asRanges()) {
69             if (!baseRanges.encloses(range)) {
70                 throw new InvalidLengthConstraintException("Range %s is not a subset of parent constraint %s", range,
71                     baseRanges);
72             }
73         }
74
75         lengthConstraint = new ResolvedLengthConstraint(constraint, allowed);
76         touch();
77     }
78
79     abstract T buildType(LengthConstraint lengthConstraint);
80
81     @Override
82     final T buildType() {
83         return buildType(lengthConstraint != null ? lengthConstraint : findLenghts());
84     }
85
86     abstract LengthConstraint typeLengthConstraints();
87
88     private static Integer resolveLength(final Number unresolved, final Range<Integer> span) {
89         if (unresolved instanceof Integer) {
90             return (Integer) unresolved;
91         }
92         if (unresolved instanceof UnresolvedNumber) {
93             return ((UnresolvedNumber)unresolved).resolveLength(span);
94         }
95
96         return Verify.verifyNotNull(NumberUtil.converterTo(Integer.class)).apply(unresolved);
97     }
98
99     private LengthConstraint findLenghts() {
100         Optional<LengthConstraint> ret = Optional.empty();
101         T wlk = getBaseType();
102         while (wlk != null && !ret.isPresent()) {
103             ret = wlk.getLengthConstraint();
104             wlk = wlk.getBaseType();
105         }
106
107         return ret.orElse(typeLengthConstraints());
108     }
109 }