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 * @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 * @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}