001/*
002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon)
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 */
009package gololang;
010
011import org.eclipse.golo.runtime.AmbiguousFunctionReferenceException;
012
013import java.io.File;
014import java.io.IOException;
015import java.lang.invoke.MethodHandleProxies;
016import java.lang.invoke.MethodHandles;
017import java.lang.reflect.Method;
018import java.lang.reflect.Parameter;
019import java.math.BigInteger;
020import java.math.BigDecimal;
021import java.nio.charset.Charset;
022import java.nio.file.Files;
023import java.nio.file.Path;
024import java.nio.file.Paths;
025import java.nio.file.StandardOpenOption;
026import java.util.*;
027import java.util.function.Predicate;
028import java.util.stream.Stream;
029import org.objectweb.asm.Type;
030import org.eclipse.golo.runtime.Extractors;
031import org.eclipse.golo.runtime.Loader;
032
033import static java.lang.invoke.MethodHandles.dropArguments;
034import static java.lang.reflect.Modifier.isStatic;
035import static org.eclipse.golo.runtime.DecoratorsHelper.isMethodDecorated;
036import static org.eclipse.golo.runtime.DecoratorsHelper.getDecoratedMethodHandle;
037import static java.util.stream.Collectors.toList;
038
039/**
040 * <code>Predefined</code> provides the module of predefined functions in Golo. The provided module is imported by
041 * default.
042 */
043public final class Predefined {
044
045  private Predefined() {
046    throw new UnsupportedOperationException("Why on earth are you trying to instantiate this class?");
047  }
048
049  // ...................................................................................................................
050  /**
051   * Raises a <code>RuntimeException</code> with a message.
052   *
053   * @param message the exception description.
054   * @throws RuntimeException (always)
055   */
056  public static void raise(Object message) {
057    require(message instanceof String, "raise requires a message as a String");
058    throw new RuntimeException((String) message);
059  }
060
061  /**
062   * Raises a <code>RuntimeException</code> with a message and a cause.
063   *
064   * @param message the exception description.
065   * @param cause   the exception cause.
066   * @throws RuntimeException (always)
067   */
068  public static void raise(Object message, Object cause) {
069    require(message instanceof String, "raise requires a message as a String");
070    require(cause instanceof Throwable, "raise requires a cause as a Throwable");
071    throw new RuntimeException((String) message, (Throwable) cause);
072  }
073
074  // ...................................................................................................................
075  /**
076   * Prints to the standard console.
077   *
078   * @param obj the object to be printed.
079   */
080  public static void print(Object obj) {
081    System.out.print(obj);
082  }
083
084  /**
085   * Prints to the standard console, including a newline.
086   *
087   * @param obj obj the object to be printed.
088   */
089  public static void println(Object obj) {
090    System.out.println(obj);
091  }
092
093  /**
094   * Reads the next line of characters from the console.
095   *
096   * @return a String.
097   */
098  public static String readln() throws IOException {
099    return System.console().readLine();
100  }
101
102  /**
103   * Reads the next line of characters from the console.
104   *
105   * @param message displays a prompt message.
106   * @return a String.
107   */
108  public static String readln(String message) throws IOException {
109    System.out.print(message);
110    return readln();
111  }
112
113  /**
114   * Reads a password from the console with echoing disabled.
115   *
116   * @return a String.
117   */
118  public static String readPassword() throws IOException {
119    return String.valueOf(System.console().readPassword());
120  }
121
122  /**
123   * Reads a password from the console with echoing disabled.
124   *
125   * @param message displays a prompt message.
126   * @return a String.
127   */
128  public static String readPassword(String message) throws IOException {
129    System.out.print(message);
130    return readPassword();
131  }
132
133  /**
134   * Reads a password from the console with echoing disabled, returning an {@code char[]} array.
135   *
136   * @return a character array.
137   */
138  public static char[] secureReadPassword() throws IOException {
139    return System.console().readPassword();
140  }
141
142  /**
143   * Reads a password from the console with echoing disabled, returning an {@code char[]} array.
144   *
145   * @param message displays a prompt message.
146   * @return a character array.
147   */
148  public static char[] secureReadPassword(String message) throws IOException {
149    System.out.print(message);
150    return secureReadPassword();
151  }
152
153  // ...................................................................................................................
154  /**
155   * Requires that an object is not the <code>null</code> reference.
156   *
157   * @param obj the object to test against <code>null</code>.
158   * @throws AssertionError if <code>obj</code> is <code>null</code>.
159   */
160  public static void requireNotNull(Object obj) throws AssertionError {
161    if (obj != null) {
162      return;
163    }
164    throw new AssertionError("null reference encountered");
165  }
166
167  /**
168   * Requires that a condition be <code>true</code>.
169   *
170   * @param condition    the condition, must be a <code>Boolean</code>.
171   * @param errorMessage the error message, must be a <code>String</code>.
172   * @throws IllegalArgumentException if the arguments are of the wrong type.
173   * @throws AssertionError           if <code>condition</code> is <code>false</code>.
174   */
175  public static void require(Object condition, Object errorMessage) throws IllegalArgumentException, AssertionError {
176    requireNotNull(condition);
177    requireNotNull(errorMessage);
178    if ((condition instanceof Boolean) && (errorMessage instanceof String)) {
179      if ((Boolean) condition) {
180        return;
181      }
182      throw new AssertionError(errorMessage);
183    } else {
184      throw new IllegalArgumentException(
185          new StringBuilder()
186          .append("Wrong parameters for require: expected (Boolean, String) but got (")
187          .append(condition.getClass().getName())
188          .append(", ")
189          .append(errorMessage.getClass().getName())
190          .append(")")
191          .toString());
192    }
193  }
194
195  // ...................................................................................................................
196  /**
197   * Makes a new typed JVM array.
198   *
199   * This function simply forwards to {@code java.lang.reflect.Array.newInstance}.
200   *
201   * @param type   the array type.
202   * @param length the array length.
203   * @return a new typed array.
204   */
205  public static Object newTypedArray(Class<?> type, int length) {
206    return java.lang.reflect.Array.newInstance(type, length);
207  }
208
209  // ...................................................................................................................
210  private static void checkRangeTypes(Object value, String name) {
211    require((value instanceof Integer)
212        || (value instanceof Long)
213        || (value instanceof Character)
214        || (value instanceof BigInteger),
215        name + " must either be an Integer, Long, Character or BigInteger");
216  }
217
218  /**
219   * Makes an range object between two bounds. Range objects implement
220   * <code>java.lang.Collection</code> (immutable), so they can be used in Golo <code>foreach</code>
221   * loops.
222   *
223   * @param from the lower-bound (inclusive) as an {@code Integer}, {@code Long}, {@code Character} or {@code BigInteger}.
224   * @param to   the upper-bound (exclusive) as an {@code Integer}, {@code Long}, {@code Character} or {@code BigInteger}.
225   * @return a range object.
226   * @see java.util.Collection
227   */
228  public static Object range(Object from, Object to) {
229    checkRangeTypes(from, "from");
230    checkRangeTypes(to, "to");
231    if ((from instanceof Character && !(to instanceof Character))
232        || (to instanceof Character && !(from instanceof Character))) {
233      throw new IllegalArgumentException("both bounds must be char for a char range");
234    }
235    if (to instanceof Character && from instanceof Character) {
236      return new CharRange((Character) from, (Character) to);
237    }
238    if (to instanceof Integer && from instanceof Integer) {
239      return new IntRange((Integer) from, (Integer) to);
240    }
241    if (to instanceof Long && from instanceof Long) {
242      return new LongRange((Long) from, (Long) to);
243    }
244    if (from instanceof BigInteger || to instanceof BigInteger) {
245      return new BigIntegerRange(bigIntegerValue(from), bigIntegerValue(to));
246    }
247    if (from instanceof Long) {
248      return new LongRange((Long) from, (Integer) to);
249    }
250    return new LongRange((Integer) from, (Long) to);
251  }
252
253  /**
254   * Makes an range object starting from the default value.
255   * <p>
256   * The default value is 0 for numbers and 'A' for chars.
257   *
258   * @param to the upper-bound (exclusive) as an {@code Integer}, {@code Long}, {@code Character} or {@code BigInteger}.
259   * @return a range object.
260   * @see gololang.Predefined#range
261   */
262  public static Object range(Object to) {
263    checkRangeTypes(to, "to");
264    if (to instanceof Integer) {
265      return new IntRange((Integer) to);
266    }
267    if (to instanceof Long) {
268      return new LongRange((Long) to);
269    }
270    if (to instanceof BigInteger) {
271      return new BigIntegerRange((BigInteger) to);
272    }
273    return new CharRange((Character) to);
274  }
275
276  /**
277   * Makes an decreasing range object between two bounds. Range objects implement <code>java.lang.Collection</code>, so
278   * they can be used in Golo <code>foreach</code> loops.
279   *
280   * @param from the upper-bound (inclusive) as an {@code Integer}, {@code Long}, {@code Character} or {@code BigInteger}.
281   * @param to   the lower-bound (exclusive) as an {@code Integer}, {@code Long}, {@code Character} or {@code BigInteger}.
282   * @return a range object.
283   * @see gololang.Predefined#range
284   */
285  public static Object reversedRange(Object from, Object to) {
286    return ((Range<?>) range(from, to)).incrementBy(-1);
287  }
288
289  /**
290   * Makes an decreasing range object up to the default value.
291   * <p>
292   * The default value is 0 for numbers and 'A' for chars.
293   *
294   * @param from the upper-bound (inclusive) as an {@code Integer}, {@code Long}, {@code Character} or {@code BigInteger}.
295   * @return a range object.
296   * @see gololang.Predefined#reversedRange
297   * @see gololang.Predefined#range
298   */
299  public static Object reversedRange(Object from) {
300    return ((Range<?>) range(from)).reversed();
301  }
302
303  // ...................................................................................................................
304  /**
305   * Makes a key / value pair.
306   *
307   * @param key   the key.
308   * @param value the value.
309   * @return an instance of <code>AbstractMap.SimpleEntry</code>
310   * @see java.util.AbstractMap.SimpleEntry
311   */
312  public static Object mapEntry(Object key, Object value) {
313    return new AbstractMap.SimpleEntry<>(key, value);
314  }
315
316  // ...................................................................................................................
317  /**
318   * Turns a function reference into an instance of a single-method interface.
319   *
320   * @param interfaceClass the target single-method interface class.
321   * @param target         the implementation function reference.
322   * @return an instance of {@code interfaceClass}.
323   * @see java.lang.invoke.MethodHandleProxies#asInterfaceInstance(Class, java.lang.invoke.MethodHandle)
324   */
325  public static Object asInterfaceInstance(Object interfaceClass, Object target) {
326    require(interfaceClass instanceof Class, "interfaceClass must be a Class");
327    require(target instanceof FunctionReference, "target must be a FunctionReference");
328    return MethodHandleProxies.asInterfaceInstance((Class<?>) interfaceClass, ((FunctionReference) target).handle());
329  }
330
331  /**
332   * Turns a function reference into an instance of a Java 8 functional interface.
333   *
334   * The implementation delegates to Golo adapter fabrics.
335   *
336   * @param type the functional interface class.
337   * @param func the implementation function.
338   * @return an instance of {@code type}.
339   * @throws Throwable if the adaptation fails.
340   * @see gololang.GoloAdapter
341   */
342  public static Object asFunctionalInterface(Object type, Object func) throws Throwable {
343    require(type instanceof Class, "type must be a Class");
344    require(func instanceof FunctionReference, "func must be a FunctionReference");
345    Class<?> theType = (Class<?>) type;
346    for (Method method : theType.getMethods()) {
347      if (!method.isDefault() && !isStatic(method.getModifiers())) {
348        Map<String, Object> configuration = new HashMap<>();
349        configuration.put("interfaces", new Tuple(theType.getCanonicalName()));
350        Map<String, FunctionReference> implementations = new HashMap<>();
351        implementations.put(
352            method.getName(),
353            new FunctionReference(
354                dropArguments(((FunctionReference) func).handle(), 0, Object.class),
355                Arrays.stream(method.getParameters())
356                .map(Parameter::getName)
357                .toArray(String[]::new)));
358        configuration.put("implements", implementations);
359        return new AdapterFabric().maker(configuration).newInstance();
360      }
361    }
362    throw new RuntimeException("Could not convert " + func + " to a functional interface of type " + type);
363  }
364
365  /**
366   * Test whether an object is a closure or not.
367   *
368   * @param object the object.
369   * @return {@code true} if {@code object} is an instance of {@link gololang.FunctionReference} {@code false} otherwise.
370   */
371  public static boolean isClosure(Object object) {
372    return object instanceof FunctionReference;
373  }
374
375  // ...................................................................................................................
376  /**
377   * Obtains a reference to a function.
378   *
379   * @param name   the function name.
380   * @param module the function enclosing module (a Java class).
381   * @param arity  the function arity, where a negative value means that any arity will do.
382   * @param varargs if the functions has variable arity.
383   * @return a function reference to the matched function.
384   * @throws NoSuchMethodException    if the target function could not be found.
385   * @throws IllegalArgumentException if the argument types are not of types <code>(String, Class, Integer)</code>.
386   * @throws Throwable                if an error occurs.
387   */
388  public static FunctionReference fun(Object name, Object module, Object arity, Object varargs) throws Throwable {
389    require(name instanceof String, "name must be a String");
390    require(module instanceof Class, "module must be a module (e.g., foo.bar.Some.module)");
391    require(arity instanceof Integer, "name must be an Integer");
392    require(varargs instanceof Boolean, "varargs must be a Boolean");
393    final Class<?> moduleClass = (Class<?>) module;
394    final String functionName = (String) name;
395    final int functionArity = (Integer) arity;
396    final boolean isVarargs = (Boolean) varargs;
397    Method targetMethod = null;
398    Predicate<Method> candidate = Extractors.matchFunctionReference(functionName, functionArity, isVarargs);
399    final List<Method> validCandidates = Extractors.getMethods(moduleClass)
400        .filter(candidate)
401        .collect(toList());
402    if (validCandidates.size() == 1) {
403      targetMethod = validCandidates.get(0);
404      // FIXME: only if the defining module is the calling module (see #319)
405      targetMethod.setAccessible(true);
406      return toFunctionReference(targetMethod, functionArity);
407    }
408    if (validCandidates.size() > 1) {
409      // TODO: return the first method, printing a warning
410      throw new AmbiguousFunctionReferenceException(("The reference to " + name + " in " + module
411          + ((functionArity < 0) ? "" : (" with arity " + functionArity))
412          + " is ambiguous"));
413    }
414    Optional<Method> target = getImportedFunctions(moduleClass).filter(candidate).findFirst();
415    if (target.isPresent()) {
416      return toFunctionReference(target.get(), functionArity);
417    }
418    throw new NoSuchMethodException((name + " in " + module
419        + (functionArity < 0 ? "" : (" with arity " + functionArity))));
420  }
421
422  /**
423   * Obtains the first reference to a function.
424   * <p>
425   * This is the same as calling {@code fun(name, module, arity, false)}.
426   *
427   * @see Predefined#fun(Object, Object, Object, Object)
428   */
429  public static FunctionReference fun(Object name, Object module, Object arity) throws Throwable {
430    return fun(name, module, arity, false);
431  }
432
433  /**
434   * Obtains the first reference to a function.
435   * <p>
436   * This is the same as calling {@code fun(name, module, -1)}.
437   *
438   * @see Predefined#fun(Object, Object, Object)
439   */
440  public static FunctionReference fun(Object name, Object module) throws Throwable {
441    return fun(name, module, -1);
442  }
443
444  private static FunctionReference toFunctionReference(Method targetMethod, int functionArity) throws Throwable {
445    String[] parameterNames = Arrays.stream(targetMethod.getParameters())
446        .map(Parameter::getName)
447        .toArray(String[]::new);
448    if (isMethodDecorated(targetMethod)) {
449      return new FunctionReference(getDecoratedMethodHandle(targetMethod, functionArity), parameterNames);
450    }
451    return new FunctionReference(MethodHandles.publicLookup().unreflect(targetMethod), parameterNames);
452  }
453
454  private static Stream<Method> getImportedFunctions(Class<?> source) {
455    return Extractors.getImportedNames(source)
456        .map(Loader.forClass(source))
457        .filter(Objects::nonNull)
458        .flatMap(Extractors::getMethods)
459        .filter(Extractors::isPublic);
460  }
461
462  // ...................................................................................................................
463  /**
464   * Reads the content of a text file.
465   *
466   * @param file     the file to read from as an instance of either {@link String}, {@link File} or {@link Path}.
467   * @param encoding the file encoding as a {@link String} or {@link Charset}.
468   * @return the content as a {@link String}.
469   */
470  public static Object fileToText(Object file, Object encoding) throws Throwable {
471    Charset charset = null;
472    if (encoding instanceof String) {
473      charset = Charset.forName((String) encoding);
474    } else if (encoding instanceof Charset) {
475      charset = (Charset) encoding;
476    } else {
477      throw new IllegalArgumentException("encoding must be either a string or a charset instance");
478    }
479    return new String(Files.readAllBytes(pathFrom(file)), charset);
480  }
481
482  private static Path pathFrom(Object file) {
483    if (file instanceof String) {
484      return Paths.get((String) file);
485    } else if (file instanceof File) {
486      return ((File) file).toPath();
487    } else if (file instanceof Path) {
488      return (Path) file;
489    }
490    throw new IllegalArgumentException("file must be a string, a file or a path");
491  }
492
493  /**
494   * Writes some text to a file.
495   *
496   * The file and parents directories are created if they does not exist. The file is overwritten if it already exists. If the file is {@code "-"}, the content is written to standard output.
497   *
498   * @param text the text to write.
499   * @param file the file to write to as an instance of either {@link String}, {@link File} or {@link Path}.
500   */
501  public static void textToFile(Object text, Object file) throws Throwable {
502    textToFile(text, file, Charset.defaultCharset());
503  }
504
505  /**
506   * Writes some text to a file using the given {@link Charset}.
507   *
508   * The file and parents directories are created if they does not exist. The file is overwritten if it already exists. If the file is {@code "-"}, the content is written to standard output.
509   *
510   * @param text the text to write.
511   * @param file the file to write to as an instance of either {@link String}, {@link File} or {@link Path}.
512   * @param charset the charset to encode the text in.
513   */
514  public static void textToFile(Object text, Object file, Object charset) throws Throwable {
515    require(text instanceof String, "text must be a string");
516    Charset encoding;
517    if (charset instanceof String) {
518      encoding = Charset.forName((String) charset);
519    } else {
520      require(charset instanceof Charset, "not a charset");
521      encoding = (Charset) charset;
522    }
523    String str = (String) text;
524    if ("-".equals(file.toString())) {
525      System.out.write(str.getBytes(encoding));
526    } else {
527      Path path = pathFrom(file);
528      if (path.getParent() != null) {
529        Files.createDirectories(path.getParent());
530      }
531      Files.write(
532          path,
533          str.getBytes(encoding),
534          StandardOpenOption.WRITE,
535          StandardOpenOption.CREATE,
536          StandardOpenOption.TRUNCATE_EXISTING);
537    }
538  }
539
540  /**
541   * Check if a file exists.
542   *
543   * @param file the file to read from as an instance of either {@link String}, {@link File} or {@link Path}.
544   * @return true if the file exists, false if it doesn't
545   */
546  public static boolean fileExists(Object file) {
547    return Files.exists(pathFrom(file));
548  }
549
550  /**
551   * Return current path of execution.
552   *
553   * @return current path of execution
554   */
555  public static String currentDir() throws Throwable {
556    return new File(".").getCanonicalPath();
557  }
558
559  /**
560   * Sleep on the current thread.
561   *
562   * @param ms time in milliseconds.
563   * @throws InterruptedException in case the thread gets interrupted.
564   */
565  public static void sleep(long ms) throws InterruptedException {
566    java.lang.Thread.sleep(ms);
567  }
568
569  /**
570   * Return a universally unique identifier as String.
571   *
572   * @return a universally unique identifier as String
573   */
574  public static String uuid() {
575    return java.util.UUID.randomUUID().toString();
576  }
577
578  // ...................................................................................................................
579  /**
580   * Checks if an object is a (JVM) array or not.
581   *
582   * @param object the object to check.
583   * @return {@code true} if {@code object} is an array, {@code false} otherwise or if {@code null}.
584   */
585  public static boolean isArray(Object object) {
586    return (object != null) && object.getClass().isArray();
587  }
588
589  /**
590   * Function to obtain the {@code Object[].class} reference.
591   *
592   * @return {@code Object[].class}
593   */
594  public static Class<?> objectArrayType() {
595    return Object[].class;
596  }
597
598  /**
599   * Returns an array class given a type class.
600   *
601   * @param klass the array type.
602   * @return the class of the array of type {@code klass}, i.e., {@code klass[]}.
603   * @throws ClassNotFoundException if the type could not be found.
604   */
605  public static Class<?> arrayTypeOf(Object klass) throws ClassNotFoundException {
606    require(klass instanceof Class<?>, "klass must be a class");
607    Class<?> type = (Class<?>) klass;
608    return Class.forName("[" + intertnalDescriptorFormClass(type), true, type.getClassLoader());
609  }
610
611  private static String intertnalDescriptorFormClass(Class<?> clazz) {
612    return Type.getDescriptor(clazz).replaceAll("/", ".");
613  }
614
615  // ...................................................................................................................
616  // These are generated methods, see src/main/ruby/generate_type_conversions.rb
617  /**
618   * Gives the Character value of some number or String object.
619   *
620   * @param obj a boxed number or String value.
621   * @return the Character value.
622   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
623   */
624  public static Object charValue(Object obj) throws IllegalArgumentException {
625    if (obj instanceof Character) {
626      return obj;
627    }
628    if (obj instanceof Integer) {
629      int value = (Integer) obj;
630      return (char) value;
631    }
632    if (obj instanceof Long) {
633      long value = (Long) obj;
634      return (char) value;
635    }
636    if (obj instanceof Double) {
637      double value = (Double) obj;
638      return (char) value;
639    }
640    if (obj instanceof Float) {
641      float value = (Float) obj;
642      return (char) value;
643    }
644    if (obj instanceof Number) {
645      return (char) ((Number) obj).doubleValue();
646    }
647    if (obj instanceof String) {
648      return ((String) obj).charAt(0);
649    }
650    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
651  }
652
653  /**
654   * Gives the Integer value of some number or String object.
655   *
656   * @param obj a boxed number or String value.
657   * @return the Integer value.
658   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
659   */
660  public static Object intValue(Object obj) throws IllegalArgumentException {
661    if (obj instanceof Integer) {
662      return obj;
663    }
664    if (obj instanceof Character) {
665      char value = (Character) obj;
666      return (int) value;
667    }
668    if (obj instanceof Long) {
669      long value = (Long) obj;
670      return (int) value;
671    }
672    if (obj instanceof Double) {
673      double value = (Double) obj;
674      return (int) value;
675    }
676    if (obj instanceof Float) {
677      float value = (Float) obj;
678      return (int) value;
679    }
680    if (obj instanceof Number) {
681      return ((Number) obj).intValue();
682    }
683    if (obj instanceof String) {
684      return Integer.valueOf((String) obj);
685    }
686    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
687  }
688
689  /**
690   * Gives the Long value of some number or String object.
691   *
692   * @param obj a boxed number or String value.
693   * @return the Long value.
694   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
695   */
696  public static Object longValue(Object obj) throws IllegalArgumentException {
697    if (obj instanceof Long) {
698      return obj;
699    }
700    if (obj instanceof Character) {
701      char value = (Character) obj;
702      return (long) value;
703    }
704    if (obj instanceof Integer) {
705      int value = (Integer) obj;
706      return (long) value;
707    }
708    if (obj instanceof Double) {
709      double value = (Double) obj;
710      return (long) value;
711    }
712    if (obj instanceof Float) {
713      float value = (Float) obj;
714      return (long) value;
715    }
716    if (obj instanceof Number) {
717      return ((Number) obj).longValue();
718    }
719    if (obj instanceof String) {
720      return Long.valueOf((String) obj);
721    }
722    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
723  }
724
725  /**
726   * Gives the Double value of some number or String object.
727   *
728   * @param obj a boxed number or String value.
729   * @return the Double value.
730   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
731   */
732  public static Object doubleValue(Object obj) throws IllegalArgumentException {
733    if (obj instanceof Double) {
734      return obj;
735    }
736    if (obj instanceof Character) {
737      char value = (Character) obj;
738      return (double) value;
739    }
740    if (obj instanceof Integer) {
741      int value = (Integer) obj;
742      return (double) value;
743    }
744    if (obj instanceof Long) {
745      long value = (Long) obj;
746      return (double) value;
747    }
748    if (obj instanceof Float) {
749      float value = (Float) obj;
750      return (double) value;
751    }
752    if (obj instanceof Number) {
753      return ((Number) obj).doubleValue();
754    }
755    if (obj instanceof String) {
756      return Double.valueOf((String) obj);
757    }
758    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
759  }
760
761  /**
762   * Gives the Float value of some number or String object.
763   *
764   * @param obj a boxed number or String value.
765   * @return the Float value.
766   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
767   */
768  public static Object floatValue(Object obj) throws IllegalArgumentException {
769    if (obj instanceof Float) {
770      return obj;
771    }
772    if (obj instanceof Character) {
773      char value = (Character) obj;
774      return (float) value;
775    }
776    if (obj instanceof Integer) {
777      int value = (Integer) obj;
778      return (float) value;
779    }
780    if (obj instanceof Long) {
781      long value = (Long) obj;
782      return (float) value;
783    }
784    if (obj instanceof Double) {
785      double value = (Double) obj;
786      return (float) value;
787    }
788    if (obj instanceof Number) {
789      return ((Number) obj).floatValue();
790    }
791    if (obj instanceof String) {
792      return Float.valueOf((String) obj);
793    }
794    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
795  }
796  // END GENERATED .....................................................................................................
797
798  /**
799   * Gives the BigDecimal value of some number or String object.
800   *
801   * @param obj a boxed number or String value.
802   * @return the Float value.
803   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
804   */
805  public static BigDecimal bigDecimalValue(Object obj) throws IllegalArgumentException {
806    if (obj instanceof BigDecimal) {
807      return (BigDecimal) obj;
808    }
809    if (obj instanceof BigInteger) {
810      return new BigDecimal((BigInteger) obj);
811    }
812    if (obj instanceof Number) {
813      return BigDecimal.valueOf(((Number) obj).doubleValue());
814    }
815    if (obj instanceof Character) {
816      char value = (Character) obj;
817      return BigDecimal.valueOf((long) value);
818    }
819    if (obj instanceof String) {
820      return new BigDecimal((String) obj);
821    }
822    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
823  }
824
825  /**
826   * Gives the BigInteger value of some number or String object.
827   *
828   * @param obj a boxed number or String value.
829   * @return the Float value.
830   * @throws IllegalArgumentException if {@code obj} is not a number or a String.
831   */
832  public static BigInteger bigIntegerValue(Object obj) throws IllegalArgumentException {
833    if (obj instanceof BigInteger) {
834      return (BigInteger) obj;
835    }
836    if (obj instanceof BigDecimal) {
837      return ((BigDecimal) obj).toBigInteger();
838    }
839    if (obj instanceof Number) {
840      return BigInteger.valueOf(((Number) obj).longValue());
841    }
842    if (obj instanceof Character) {
843      char value = (Character) obj;
844      return BigInteger.valueOf((long) value);
845    }
846    if (obj instanceof String) {
847      return new BigInteger((String) obj);
848    }
849    throw new IllegalArgumentException("Expected a number or a string, but got: " + obj);
850  }
851
852  // ...................................................................................................................
853  /**
854   * Create a {@code String} by concatenating all arguments.
855   * <p>
856   * For instance:
857   * <pre class="listing"><code class="lang-golo" data-lang="golo">
858   * let s = str("The answer", " is ", 2 * 21)
859   * </code></pre>
860   * This is functionally equivalent to:
861   * <pre class="listing"><code class="lang-golo" data-lang="golo">
862   * let s = ["The answer", " is ", 2 * 21]: join("")
863   * </code></pre>
864   */
865  public static String str(Object... args) {
866    if (args == null || args.length == 0) {
867      return "";
868    }
869    if (args.length == 1) {
870      return String.valueOf(args[0]);
871    }
872    StringBuilder sb = new StringBuilder();
873    for (Object o : args) {
874      sb.append(o);
875    }
876    return sb.toString();
877  }
878
879  /**
880   * Removes an element of a List by index.
881   *
882   * @param lst the list to remove the element from.
883   * @param idx an Integer representing the index of the element to remove.
884   * @return the element that was removed.
885   * @throws IndexOutOfBoundsException if {@code idx}
886   */
887  public static Object removeByIndex(List<?> lst, Integer idx) {
888    return lst.remove(idx.intValue());
889  }
890
891  /**
892   * Returns a box containing the given object.
893   * <p>
894   * Useful whenever an explicit reference is needed, for instance to create a closure with internal
895   * mutable state:
896   * <pre class="listing"><code class="lang-golo" data-lang="golo">
897   *    function counter = |init| {
898   *      let current = box(init)
899   *      return -> current: getAndSet(current: get() + 1)
900   *    }
901   *
902   *    let c = counter(3)
903   *    c() # 3
904   *    c() # 4
905   *    c() # 5
906   * </code></pre>
907   *
908   * @param obj the object to reference.
909   * @return a {@code java.util.concurrent.atomic.AtomicReference} instance wrapping the object
910   */
911  public static Object box(Object obj) {
912    return new java.util.concurrent.atomic.AtomicReference<Object>(obj);
913  }
914
915  // ...................................................................................................................
916  /**
917   * Varargs version of a list constructor.
918   *
919   * @return a list of the given values.
920   */
921  public static List<Object> list(Object... values) {
922    return new LinkedList<Object>(Arrays.asList(values));
923  }
924
925  /**
926   * Varargs version of a set constructor.
927   *
928   * @return a set of the given values.
929   */
930  public static Set<Object> set(Object... values) {
931    return new LinkedHashSet<Object>(Arrays.asList(values));
932  }
933
934  /**
935   * array constructor.
936   *
937   * @return an array of the given values.
938   */
939  public static Object[] array(Object... values) {
940    return values;
941  }
942
943  /**
944   * Varargs version of a vector constructor.
945   *
946   * @return a vector of the give values.
947   */
948  public static List<Object> vector(Object... values) {
949    return new ArrayList<Object>(Arrays.asList(values));
950  }
951
952  /**
953   * Tuple constructor.
954   *
955   * @return a tuple of the given values.
956   */
957  public static Tuple tuple(Object... values) {
958    return new Tuple(values);
959  }
960
961  /**
962   * Varargs version of a map constructor.
963   *
964   * @param items tuples containing the key and the value.
965   * @return a map corresponding to the given key/value pairs.
966   */
967  public static Map<Object, Object> map(Tuple... items) {
968    Map<Object, Object> m = new LinkedHashMap<>();
969    for (Tuple t : items) {
970      m.put(t.get(0), t.get(1));
971    }
972    return m;
973  }
974}