Improve Decimal64 range support
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / AbstractPrimitiveRangeGenerator.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.mdsal.binding.java.api.generator;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.Range;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.function.Function;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.yangtools.yang.binding.CodeHelpers;
20 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 abstract class AbstractPrimitiveRangeGenerator<T extends Number & Comparable<T>> extends AbstractRangeGenerator<T> {
25     private static final Logger LOG = LoggerFactory.getLogger(AbstractPrimitiveRangeGenerator.class);
26     private final @NonNull String primitiveName;
27     private final @NonNull T minValue;
28     private final @NonNull T maxValue;
29
30     AbstractPrimitiveRangeGenerator(final Class<T> typeClass, final String primitiveName, final T minValue,
31             final T maxValue) {
32         super(typeClass);
33         this.primitiveName = requireNonNull(primitiveName);
34         this.minValue = requireNonNull(minValue);
35         this.maxValue = requireNonNull(maxValue);
36     }
37
38     /**
39      * Return the name of the primitive type, as known by the Java language.
40      *
41      * @return Primitive type name
42      */
43     protected final @NonNull String getPrimitiveName() {
44         return primitiveName;
45     }
46
47     private boolean needsMaximumEnforcement(final T maxToEnforce) {
48         return maxValue.compareTo(maxToEnforce) > 0;
49     }
50
51     private boolean needsMinimumEnforcement(final T minToEnforce) {
52         return minValue.compareTo(minToEnforce) < 0;
53     }
54
55     private Collection<String> createExpressions(final RangeConstraint<?> constraint,
56             final Function<Class<?>, String> classImporter) {
57         final Set<? extends Range<? extends Number>> constraints = constraint.getAllowedRanges().asRanges();
58         final Collection<String> ret = new ArrayList<>(constraints.size());
59
60         for (Range<? extends Number> r : constraints) {
61             final T min = getValue(r.lowerEndpoint());
62             final boolean needMin = needsMinimumEnforcement(min);
63
64             final T max = getValue(r.upperEndpoint());
65             final boolean needMax = needsMaximumEnforcement(max);
66
67             if (!needMin && !needMax) {
68                 LOG.debug("Type {} indicates [{}, {}] does not require enforcement", getTypeName(), min, max);
69                 continue;
70             }
71
72             final StringBuilder sb = new StringBuilder();
73             if (needMin) {
74                 appendMinCheck(sb, min, classImporter);
75             }
76             if (needMax) {
77                 if (needMin) {
78                     sb.append(" && ");
79                 }
80                 appendMaxCheck(sb, max, classImporter);
81             }
82
83             ret.add(sb.toString());
84         }
85
86         return ret;
87     }
88
89     void appendMaxCheck(final StringBuilder sb, final T max, final Function<Class<?>, String> classImporter) {
90         sb.append("value <= ").append(format(max));
91     }
92
93     void appendMinCheck(final StringBuilder sb, final T min, final Function<Class<?>, String> classImporter) {
94         sb.append("value >= ").append(format(min));
95     }
96
97     /**
98      * Format a value into a Java-compilable expression which results in the appropriate
99      * type.
100      *
101      * @param value Number value
102      * @return Java language string representation
103      */
104     protected abstract @NonNull String format(T value);
105
106     String codeHelpersThrow() {
107         return "throwInvalidRange";
108     }
109
110     private String createRangeString(final RangeConstraint<?> constraint) {
111         final Set<? extends Range<? extends Number>> constraints = constraint.getAllowedRanges().asRanges();
112         final List<Range<T>> ranges = new ArrayList<>(constraints.size());
113
114         for (Range<? extends Number> c : constraints) {
115             ranges.add(Range.closed(getValue(c.lowerEndpoint()), getValue(c.upperEndpoint())));
116         }
117
118         return ranges.toString();
119     }
120
121     @Override
122     protected final String generateRangeCheckerImplementation(final String checkerName,
123             final RangeConstraint<?> constraints, final Function<Class<?>, String> classImporter) {
124         final StringBuilder sb = new StringBuilder();
125         final Collection<String> expressions = createExpressions(constraints, classImporter);
126
127         sb.append("private static void ").append(checkerName).append("(final ").append(primitiveName)
128             .append(" value) {\n");
129
130         if (!expressions.isEmpty()) {
131             for (String exp : expressions) {
132                 sb.append("    if (").append(exp).append(") {\n");
133                 sb.append("        return;\n");
134                 sb.append("    }\n");
135             }
136
137             sb.append("    ").append(classImporter.apply(CodeHelpers.class)).append('.').append(codeHelpersThrow())
138             .append("(\"").append(createRangeString(constraints)).append("\", value);\n");
139         }
140
141         return sb.append("}\n").toString();
142     }
143 }