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