View Javadoc
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 }