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