2 * Copyright (c) 2015 Pantheon Technologies s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.model.util.type;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableList.Builder;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.function.Function;
17 import javax.annotation.Nonnull;
18 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
19 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
20 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
21 import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
22 import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
24 public abstract class LengthRestrictedTypeBuilder<T extends TypeDefinition<T>>
25 extends AbstractRestrictedTypeBuilder<T> {
26 private List<LengthConstraint> lengthAlternatives;
28 LengthRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
29 super(Preconditions.checkNotNull(baseType), path);
32 public final void setLengthAlternatives(@Nonnull final Collection<LengthConstraint> lengthAlternatives) {
33 Preconditions.checkState(this.lengthAlternatives == null, "Range alternatives already defined as %s",
35 this.lengthAlternatives = ImmutableList.copyOf(lengthAlternatives);
39 private static List<LengthConstraint> ensureResolvedLengths(final List<LengthConstraint> unresolved,
40 final List<LengthConstraint> baseRangeConstraints) {
41 // First check if we need to resolve anything at all
42 for (LengthConstraint c : unresolved) {
43 if (c.getMax() instanceof UnresolvedNumber || c.getMin() instanceof UnresolvedNumber) {
44 return resolveLengths(unresolved, baseRangeConstraints);
48 // No need, just return the same list
52 private static List<LengthConstraint> resolveLengths(final List<LengthConstraint> unresolved,
53 final List<LengthConstraint> baseLengthConstraints) {
54 final Builder<LengthConstraint> builder = ImmutableList.builder();
56 for (LengthConstraint c : unresolved) {
57 final Number max = c.getMax();
58 final Number min = c.getMin();
60 if (max instanceof UnresolvedNumber || min instanceof UnresolvedNumber) {
61 final Number rMax = max instanceof UnresolvedNumber
62 ? ((UnresolvedNumber)max).resolveLength(baseLengthConstraints) : max;
63 final Number rMin = min instanceof UnresolvedNumber
64 ? ((UnresolvedNumber)min).resolveLength(baseLengthConstraints) : min;
66 builder.add(BaseConstraints.newLengthConstraint(rMin, rMax, Optional.fromNullable(c.getDescription()),
67 Optional.fromNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
73 return builder.build();
76 private static List<LengthConstraint> ensureTypedLengths(final List<LengthConstraint> lengths,
77 final Class<? extends Number> clazz) {
78 for (LengthConstraint c : lengths) {
79 if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
80 return typedLengths(lengths, clazz);
87 private static List<LengthConstraint> typedLengths(final List<LengthConstraint> lengths,
88 final Class<? extends Number> clazz) {
89 final Function<Number, Number> function = NumberUtil.converterTo(clazz);
90 Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
92 final Builder<LengthConstraint> builder = ImmutableList.builder();
94 for (LengthConstraint c : lengths) {
95 if (!clazz.isInstance(c.getMin()) || !clazz.isInstance(c.getMax())) {
100 min = function.apply(c.getMin());
101 max = function.apply(c.getMax());
102 } catch (NumberFormatException e) {
103 throw new IllegalArgumentException(String.format("Constraint %s does not fit into range of %s",
104 c, clazz.getSimpleName()), e);
106 builder.add(BaseConstraints.newLengthConstraint(min, max, Optional.fromNullable(c.getDescription()),
107 Optional.fromNullable(c.getReference()), c.getErrorAppTag(), c.getErrorMessage()));
113 return builder.build();
116 private static boolean lengthCovered(final List<LengthConstraint> where,
117 final LengthConstraint what) {
118 for (LengthConstraint c : where) {
119 if (NumberUtil.isRangeCovered(what.getMin(), what.getMax(), c.getMin(), c.getMax())) {
128 final T buildType() {
129 final List<LengthConstraint> baseLengths = findLenghts();
131 if (lengthAlternatives == null || lengthAlternatives.isEmpty()) {
132 return buildType(baseLengths);
135 // Run through alternatives and resolve them against the base type
136 final List<LengthConstraint> resolvedLengths = ensureResolvedLengths(lengthAlternatives, baseLengths);
138 // Next up, ensure the of boundaries match base constraints
139 final Class<? extends Number> clazz = baseLengths.get(0).getMin().getClass();
140 final List<LengthConstraint> typedLengths = ensureTypedLengths(resolvedLengths, clazz);
142 // Now verify if new ranges are strict subset of base ranges
143 for (LengthConstraint c : typedLengths) {
144 if (!lengthCovered(baseLengths, c)) {
145 throw new InvalidLengthConstraintException(c,
146 "Length constraint %s is not a subset of parent constraints %s", c, baseLengths);
150 return buildType(typedLengths);
153 abstract T buildType(List<LengthConstraint> lengthConstraints);
155 abstract List<LengthConstraint> getLengthConstraints(T type);
157 abstract List<LengthConstraint> typeLengthConstraints();
159 private List<LengthConstraint> findLenghts() {
160 List<LengthConstraint> ret = ImmutableList.of();
161 T wlk = getBaseType();
162 while (wlk != null && ret.isEmpty()) {
163 ret = getLengthConstraints(wlk);
164 wlk = wlk.getBaseType();
167 return ret.isEmpty() ? typeLengthConstraints() : ret;