1 /*******************************************************************************
2 * jArduino: Arduino C++ Code Generation From Java
3 * Copyright 2020 Tony Washer
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ******************************************************************************/
17 package net.sourceforge.jarduino.message;
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import net.sourceforge.jarduino.message.ArduinoParser.ArduinoParserException;
23
24 /**
25 * Attribute constraints.
26 */
27 public interface ArduinoAttrConstraints {
28 /**
29 * Check valid value.
30 * @param pValue the value.
31 * @return corrected value
32 * @throws ArduinoParserException on error
33 */
34 Object checkValue(Object pValue) throws ArduinoParserException;
35
36 /**
37 * Value Constraints.
38 */
39 class ArduinoAttrValueConstraints
40 implements ArduinoAttrConstraints {
41 /**
42 * The minimum value.
43 */
44 private Number theMinimum;
45
46 /**
47 * The maximum value.
48 */
49 private Number theMaximum;
50
51 /**
52 * Constructor.
53 * @param pMinimum the minimum
54 * @param pMaximum the maximum
55 */
56 ArduinoAttrValueConstraints(final Number pMinimum,
57 final Number pMaximum) {
58 /* Store values */
59 theMinimum = pMinimum;
60 theMaximum = pMaximum;
61 }
62
63 /**
64 * Obtain the minimum.
65 * @return the minimum
66 */
67 Number getMinimum() {
68 return theMinimum;
69 }
70
71 /**
72 * Obtain the maximum.
73 * @return the maximum
74 */
75 Number getMaximum() {
76 return theMaximum;
77 }
78
79 @Override
80 public Object checkValue(final Object pValue) throws ArduinoParserException {
81 /* Must be a Number */
82 final Number myValue = (Number) pValue;
83
84 /* Check min and max values */
85 final boolean invalid = theMinimum instanceof Double
86 ? myValue.doubleValue() < theMinimum.doubleValue()
87 || myValue.doubleValue() > theMaximum.doubleValue()
88 : myValue.longValue() < theMinimum.longValue()
89 || myValue.longValue() > theMaximum.longValue();
90
91 /* throw exception if not valid */
92 if (invalid) {
93 throw new ArduinoParserException("Attribute breaks constraints", myValue.toString());
94 }
95
96 /* Just return the value */
97 return pValue;
98 }
99
100 /**
101 * parse the constraints.
102 * @param pAttr the attribute
103 * @param pConstDef the constraints definition
104 * @return the parsed constraints (null if unbounded)
105 * @throws ArduinoParserException on error
106 */
107 static ArduinoAttrValueConstraints parseConstraints(final ArduinoAttribute pAttr,
108 final String pConstDef) throws ArduinoParserException {
109 /* Minimum is first token, Maximum second */
110 final String myMinDef = ArduinoParser.nextToken(pConstDef);
111 final String myMaxDef = ArduinoParser.stripToken(pConstDef, myMinDef);
112
113 /* Parse the values */
114 Number myMin = ArduinoParser.parseNumber(myMinDef);
115 Number myMax = ArduinoParser.parseNumber(myMaxDef);
116
117 /* Make sure that if either value is double, both are */
118 if (myMin.getClass() != myMax.getClass()) {
119 /* Convert longs to doubles */
120 if (myMin instanceof Long) {
121 myMin = myMin.doubleValue();
122 }
123 if (myMax instanceof Long) {
124 myMax = myMax.doubleValue();
125 }
126 }
127
128 /* Return constraints */
129 return ArduinoSignalRange.isZero(myMin) && ArduinoSignalRange.isZero(myMax)
130 ? null
131 : new ArduinoAttrValueConstraints(myMin, myMax);
132 }
133 }
134
135 /**
136 * Enum Constraints.
137 */
138 class ArduinoAttrEnumConstraints
139 implements ArduinoAttrConstraints {
140 /**
141 * The list of enum values.
142 */
143 private final List<String> theValues;
144
145 /**
146 * Constructor.
147 * @param pValues teh values
148 */
149 ArduinoAttrEnumConstraints(final List<String> pValues) {
150 theValues = pValues;
151 }
152
153 /**
154 * Obtain the values.
155 * @return the values
156 */
157 List<String> getValues() {
158 return theValues;
159 }
160
161 @Override
162 public Object checkValue(final Object pValue) throws ArduinoParserException {
163 /* If the value is a number */
164 if (pValue instanceof Long) {
165 final long myValue = (Long) pValue;
166 if (myValue < 0 || myValue >= theValues.size()) {
167 throw new ArduinoParserException("Attribute out of range for enum", pValue.toString());
168 }
169 return theValues.get((int) myValue);
170 }
171
172 /* Value must be a string */
173 final String myValue = (String) pValue;
174
175 /* Check that this is a valid enum */
176 if (!theValues.contains(myValue)) {
177 /* If the value is empty, try for the first enum */
178 if (myValue.length() == 0 && !theValues.isEmpty()) {
179 return theValues.get(0);
180 }
181 throw new ArduinoParserException("Attribute not valid enum", myValue);
182 }
183
184 /* Just return the value */
185 return pValue;
186 }
187
188 /**
189 * parse the constraints.
190 * @param pConstDef the constraints definition
191 * @return the parsed constraints
192 * @throws ArduinoParserException on error
193 */
194 static ArduinoAttrConstraints parseConstraints(final String pConstDef) throws ArduinoParserException {
195 /* Create the value list */
196 final List<String> myEnums = new ArrayList<>();
197
198 /* Loop through the tokens */
199 String myValues = pConstDef;
200 while (myValues.length() > 0) {
201 /* Split on comma separator */
202 final int myIndex = myValues.indexOf(ArduinoChar.COMMA);
203 if (myIndex == -1) {
204 myEnums.add(ArduinoParser.nextQuotedToken(myValues));
205 return new ArduinoAttrEnumConstraints(myEnums);
206 }
207
208 final String myValue = myValues.substring(0, myIndex).trim();
209 myEnums.add(ArduinoParser.nextQuotedToken(myValue));
210 myValues = myValues.substring(myIndex + 1);
211 }
212 return new ArduinoAttrEnumConstraints(myEnums);
213 }
214 }
215
216 /**
217 * parse the constraints.
218 * @param pAttr the attribute
219 * @param pConstDef the constraints definition
220 * @return the parsed constraints (null if unbounded)
221 * @throws ArduinoParserException on error
222 */
223 static ArduinoAttrConstraints parseConstraints(final ArduinoAttribute pAttr,
224 final String pConstDef) throws ArduinoParserException {
225 /* Switch on attribute type */
226 switch (pAttr.getAttrType()) {
227 case INT:
228 case HEX:
229 case FLOAT:
230 return ArduinoAttrValueConstraints.parseConstraints(pAttr, pConstDef);
231 case ENUM:
232 return ArduinoAttrEnumConstraints.parseConstraints(pConstDef);
233 default:
234 return null;
235 }
236 }
237 }