f7
f7 is a spreadsheet formula execution library
git clone https://git.vogt.world/f7.git
Log | Files | README.md | LICENSE.md
← All files
name: src/main/java/io/protobase/f7/utils/Converters.java
-rw-r--r--
9615
  1package io.protobase.f7.utils;
  2
  3import io.protobase.f7.errors.F7Exception;
  4import io.protobase.f7.errors.ValueException;
  5import io.protobase.f7.models.Grid;
  6
  7import java.text.DecimalFormat;
  8import java.util.Objects;
  9
 10public class Converters {
 11
 12  /**
 13   * Compare two objects using their type precedence first, and then defaulting to their default class compare methods
 14   * if they are of the same type. If one is null, use a default depending on type.
 15   *
 16   * @param first  - object to compare. Should be Double, Boolean, String, or null.
 17   * @param second - second object to compare.  Should be Double, Boolean, String, or null.
 18   * @return ComparisonResult indicating less-than, greater-than, equal-to.
 19   */
 20  public static ComparisonResult compare(Object first, Object second) {
 21    Integer firstTypePrecedence = Converters.getTypePrecedence(first);
 22    Integer secondTypePrecedence = Converters.getTypePrecedence(second);
 23    if (firstTypePrecedence.equals(secondTypePrecedence)) {
 24      if (Objects.isNull(first)) {
 25        return ComparisonResult.EQUAL;
 26      }
 27      if (first instanceof Double) {
 28        Double firstAsDouble = Converters.castAsDouble(first);
 29        Double secondAsDouble = Converters.castAsDouble(second);
 30        return ComparisonResult.fromComparison(firstAsDouble.compareTo(secondAsDouble));
 31      }
 32      if (first instanceof Boolean) {
 33        Boolean firstAsBoolean = Converters.castAsBoolean(first);
 34        Boolean secondAsBoolean = Converters.castAsBoolean(second);
 35        return ComparisonResult.fromComparison(firstAsBoolean.compareTo(secondAsBoolean));
 36      }
 37      if (first instanceof String) {
 38        String firstAsString = Converters.castAsString(first).toLowerCase();
 39        String secondAsString = Converters.castAsString(second).toLowerCase();
 40        return ComparisonResult.fromComparison(firstAsString.compareTo(secondAsString));
 41      }
 42    }
 43    if (Objects.isNull(first)) {
 44      if (second instanceof Double) {
 45        Double firstAsDouble = 0.0;
 46        Double secondAsDouble = Converters.toPositiveZero(Converters.castAsDouble(second));
 47        return ComparisonResult.fromComparison(firstAsDouble.compareTo(secondAsDouble));
 48      }
 49      if (second instanceof Boolean) {
 50        Boolean firstAsBoolean = false;
 51        Boolean secondAsBoolean = Converters.castAsBoolean(second);
 52        return ComparisonResult.fromComparison(firstAsBoolean.compareTo(secondAsBoolean));
 53      }
 54      if (second instanceof String) {
 55        String firstAsString = "";
 56        String secondAsString = Converters.castAsString(second).toLowerCase();
 57        return ComparisonResult.fromComparison(firstAsString.compareTo(secondAsString));
 58      }
 59    }
 60    if (Objects.isNull(second)) {
 61      if (first instanceof Double) {
 62        Double firstAsDouble = Converters.toPositiveZero(Converters.castAsDouble(first));
 63        Double secondAsDouble = 0.0;
 64        return ComparisonResult.fromComparison(firstAsDouble.compareTo(secondAsDouble));
 65      }
 66      if (first instanceof Boolean) {
 67        Boolean firstAsBoolean = Converters.castAsBoolean(first);
 68        Boolean secondAsBoolean = false;
 69        return ComparisonResult.fromComparison(firstAsBoolean.compareTo(secondAsBoolean));
 70      }
 71      if (first instanceof String) {
 72        String firstAsString = Converters.castAsString(first).toLowerCase();
 73        String secondAsString = "";
 74        return ComparisonResult.fromComparison(firstAsString.compareTo(secondAsString));
 75      }
 76    }
 77    return ComparisonResult.fromComparison(firstTypePrecedence.compareTo(secondTypePrecedence));
 78  }
 79
 80  protected static Object textToZero(Object value) {
 81    if (value instanceof Grid) {
 82      Grid grid = Converters.castAsDataGrid(value);
 83      if (grid.getColumnSize() == 1 && grid.getRowSize() == 1) {
 84        return textToZero(grid.get(0, 0));
 85      } else {
 86        throw new ValueException("Grid value could not be found.");
 87      }
 88    }
 89    if (value instanceof String) {
 90      return 0.0;
 91    }
 92    return value;
 93  }
 94
 95  /**
 96   * Convert double to +0.0, only if it is -0.0.
 97   *
 98   * @param value to ensure is positive zero.
 99   * @return value or +0.0 if value is -0.0.
100   */
101  public static Double toPositiveZero(double value) {
102    if (value == -0.0) {
103      return 0.0;
104    }
105    return value;
106  }
107
108  /**
109   * Convert a Double, String or Boolean to a Double. If F7Exception is the value, then throw it. For all other classes,
110   * a {@link ValueException} will be thrown. If the value is null, 0.0 is returned.
111   *
112   * @param value - to convert to Double.
113   * @return Double for sure, or exception.
114   */
115  public static Double toDouble(Object value) {
116    if (value instanceof Double) {
117      return Converters.toPositiveZero(Converters.castAsDouble(value));
118    }
119    if (value instanceof String) {
120      try {
121        return Double.parseDouble(Converters.castAsString(value));
122      } catch (NumberFormatException e) {
123        throw new ValueException("Cannot coerce to number");
124      }
125    }
126    if (value instanceof Boolean) {
127      return Converters.castAsBoolean(value) ? 1.0 : 0.0;
128    }
129    if (value instanceof F7Exception) {
130      throw Converters.castAsF7Error(value);
131    }
132    if (value instanceof Grid) {
133      Grid grid = Converters.castAsDataGrid(value);
134      if (grid.getColumnSize() == 1 && grid.getRowSize() == 1) {
135        return toDouble(grid.get(0, 0));
136      } else {
137        return 0.0;
138      }
139    }
140    if (Objects.isNull(value)) {
141      return 0.0;
142    }
143    throw new ValueException("Cannot coerce to number");
144  }
145
146  /**
147   * Convert a value to a string.
148   *
149   * @param value - to convert to text.
150   * @return String for sure.
151   */
152  public static String toText(Object value) {
153    if (value instanceof Boolean) {
154      return Converters.castAsBoolean(value) ? "TRUE" : "FALSE";
155    }
156    if (value instanceof String) {
157      return value.toString();
158    }
159    if (value instanceof Double) {
160      Double number = Converters.castAsDouble(value);
161      double remainder = number % 1;
162      if (Converters.toPositiveZero(remainder).equals(0.0)) {
163        DecimalFormat format = new DecimalFormat();
164        format.setMaximumFractionDigits(0);
165        format.setGroupingUsed(false);
166        return format.format(number);
167      }
168      return value.toString();
169    }
170    if (value instanceof F7Exception) {
171      return Converters.castAsF7Error(value).toString();
172    }
173    if (value instanceof Grid) {
174      Grid grid = Converters.castAsDataGrid(value);
175      if (grid.getColumnSize() == 1 && grid.getRowSize() == 1) {
176        return toText(grid.get(0, 0));
177      } else {
178        return "";
179      }
180    }
181    if (Objects.isNull(value)) {
182      return "";
183    }
184    throw new ValueException("Cannot coerce to text");
185  }
186
187  /**
188   * Convert a value to a boolean.
189   *
190   * @param value - to convert to a boolean.
191   * @return Boolean for sure, or throw ValueException.
192   */
193  public static Boolean toBoolean(Object value) {
194    if (value instanceof Boolean) {
195      return Converters.castAsBoolean(value);
196    }
197    if (value instanceof String) {
198      String rawStringValue = ((String) value);
199      if (rawStringValue.toLowerCase().equals("true")) {
200        return true;
201      }
202      if (rawStringValue.toLowerCase().equals("false")) {
203        return false;
204      }
205      if (rawStringValue.equals("")) {
206        return false;
207      }
208      throw new ValueException("Cannot coerce to boolean");
209    }
210    if (value instanceof F7Exception) {
211      throw Converters.castAsF7Error(value);
212    }
213    if (value instanceof Grid) {
214      Grid grid = Converters.castAsDataGrid(value);
215      if (grid.getColumnSize() == 1 && grid.getRowSize() == 1) {
216        return toBoolean(grid.get(0, 0));
217      } else {
218        return false;
219      }
220    }
221    if (value instanceof Double) {
222      return !(value).equals(0.0);
223    }
224    throw new ValueException("Cannot coerce to boolean");
225  }
226
227  /**
228   * Get the first value, if it's a grid, or just pass-through.
229   *
230   * @param value - to get first of.
231   * @return - single value.
232   */
233  public static Object first(Object value) {
234    if (value instanceof Grid) {
235      return ((Grid) value).get(0, 0);
236    }
237    return value;
238  }
239
240  /**
241   * Cast value to Boolean.
242   *
243   * @param value - to cast.
244   * @return Boolean.
245   */
246  public static Boolean castAsBoolean(Object value) {
247    return (Boolean) value;
248  }
249
250  /**
251   * Cast value to Double.
252   *
253   * @param value - to cast.
254   * @return Double.
255   */
256  public static Double castAsDouble(Object value) {
257    return (Double) value;
258  }
259
260  /**
261   * Cast value to String.
262   *
263   * @param value - to cast.
264   * @return String.
265   */
266  public static String castAsString(Object value) {
267    return (String) value;
268  }
269
270  /**
271   * Cast value to F7Exception.
272   *
273   * @param value - to cast.
274   * @return F7Exception.
275   */
276  public static F7Exception castAsF7Error(Object value) {
277    return (F7Exception) value;
278  }
279
280  /**
281   * Cast value to Grid.
282   *
283   * @param value - to cast.
284   * @return Grid.
285   */
286  public static Grid<Object> castAsDataGrid(Object value) {
287    return (Grid<Object>) value;
288  }
289
290  /**
291   * Strings, Numbers, and Booleans have type precedence that is used when comparing across types.
292   *
293   * @param value to get precedence of.
294   * @return {@code 1} if Number,{@code 2} if String, {@code 3} if Boolean.
295   */
296  public static Integer getTypePrecedence(Object value) {
297    if (Objects.isNull(value)) {
298      return 0;
299    }
300    if (value instanceof Number) {
301      return 1;
302    }
303    if (value instanceof String) {
304      return 2;
305    }
306    return 3;
307  }
308}