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