Populate data/ hierarchy
[yangtools.git] / model / yang-model-ri / src / main / java / org / opendaylight / yangtools / yang / model / ri / 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.ri.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.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
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 QName qname) {
33         super(requireNonNull(baseType), qname);
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 constraint already defined as %s", lengthConstraint);
48         final LengthConstraint baseLengths = findLenghts();
49         if (ranges.isEmpty()) {
50             lengthConstraint = baseLengths;
51             return;
52         }
53
54         // Run through alternatives and resolve them against the base type
55         requireNonNull(constraint);
56         final Builder<Integer> builder = ImmutableRangeSet.builder();
57         final Range<Integer> span = baseLengths.getAllowedRanges().span();
58
59         for (ValueRange c : ranges) {
60             builder.add(Range.closed(resolveLength(c.lowerBound(), span), resolveLength(c.upperBound(), span)));
61         }
62
63
64         // Now verify if new ranges are strict subset of base ranges
65         final RangeSet<Integer> allowed = builder.build();
66         final RangeSet<Integer> baseRanges = baseLengths.getAllowedRanges();
67         for (Range<Integer> range : allowed.asRanges()) {
68             if (!baseRanges.encloses(range)) {
69                 throw new InvalidLengthConstraintException("Range %s is not a subset of parent constraint %s", range,
70                     baseRanges);
71             }
72         }
73
74         lengthConstraint = new ResolvedLengthConstraint(constraint, allowed);
75         touch();
76     }
77
78     abstract @NonNull T buildType(LengthConstraint constraint);
79
80     @Override
81     final T buildType() {
82         return buildType(lengthConstraint != null ? lengthConstraint : findLenghts());
83     }
84
85     abstract LengthConstraint typeLengthConstraints();
86
87     private static Integer resolveLength(final Number unresolved, final Range<Integer> span) {
88         if (unresolved instanceof Integer) {
89             return (Integer) unresolved;
90         }
91         if (unresolved instanceof UnresolvedNumber) {
92             return ((UnresolvedNumber)unresolved).resolveLength(span);
93         }
94
95         return Verify.verifyNotNull(NumberUtil.converterTo(Integer.class)).apply(unresolved);
96     }
97
98     private LengthConstraint findLenghts() {
99         Optional<LengthConstraint> ret = Optional.empty();
100         T wlk = getBaseType();
101         while (wlk != null && ret.isEmpty()) {
102             ret = wlk.getLengthConstraint();
103             wlk = wlk.getBaseType();
104         }
105
106         return ret.orElse(typeLengthConstraints());
107     }
108 }