001 /* 002 $Id: InvokerHelper.java 4294 2006-12-02 19:31:27Z blackdrag $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package org.codehaus.groovy.runtime; 047 048 import groovy.lang.*; 049 050 import java.beans.Introspector; 051 import java.io.IOException; 052 import java.io.InputStream; 053 import java.io.InputStreamReader; 054 import java.io.Reader; 055 import java.io.StringWriter; 056 import java.io.Writer; 057 import java.math.BigDecimal; 058 import java.math.BigInteger; 059 import java.util.ArrayList; 060 import java.util.Arrays; 061 import java.util.Collection; 062 import java.util.Collections; 063 import java.util.Enumeration; 064 import java.util.HashMap; 065 import java.util.Iterator; 066 import java.util.List; 067 import java.util.Map; 068 import java.util.regex.Matcher; 069 import java.util.regex.Pattern; 070 071 import org.apache.xml.serialize.OutputFormat; 072 import org.apache.xml.serialize.XMLSerializer; 073 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; 074 import org.codehaus.groovy.runtime.typehandling.IntegerCache; 075 import org.w3c.dom.Element; 076 077 /** 078 * A static helper class to make bytecode generation easier and act as a facade over the Invoker 079 * 080 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 081 * @version $Revision: 4294 $ 082 */ 083 public class InvokerHelper { 084 public static final Object[] EMPTY_ARGS = { 085 }; 086 087 private static final Object[] EMPTY_MAIN_ARGS = new Object[]{new String[0]}; 088 089 private static final Invoker singleton = new Invoker(); 090 091 092 093 public static MetaClass getMetaClass(Object object) { 094 return getInstance().getMetaClass(object); 095 } 096 097 public static void removeClass(Class clazz) { 098 getInstance().removeMetaClass(clazz); 099 Introspector.flushFromCaches(clazz); 100 } 101 102 public static Invoker getInstance() { 103 return singleton; 104 } 105 106 public static Object invokeNoArgumentsMethod(Object object, String methodName) { 107 return getInstance().invokeMethod(object, methodName, EMPTY_ARGS); 108 } 109 110 public static Object invokeMethod(Object object, String methodName, Object arguments) { 111 return getInstance().invokeMethod(object, methodName, arguments); 112 } 113 114 public static Object invokeSuperMethod(Object object, String methodName, Object arguments) { 115 return getInstance().invokeSuperMethod(object, methodName, arguments); 116 } 117 118 public static Object invokeMethodSafe(Object object, String methodName, Object arguments) { 119 if (object != null) { 120 return getInstance().invokeMethod(object, methodName, arguments); 121 } 122 return null; 123 } 124 125 public static Object invokeStaticMethod(Class type, String methodName, Object arguments) { 126 return getInstance().invokeStaticMethod(type, methodName, arguments); 127 } 128 129 public static Object invokeStaticMethod(String klass, String methodName, Object arguments) throws ClassNotFoundException { 130 Class type = InvokerHelper.class.forName(klass); 131 return getInstance().invokeStaticMethod(type, methodName, arguments); 132 } 133 134 135 public static Object invokeStaticNoArgumentsMethod(Class type, String methodName) { 136 return getInstance().invokeStaticMethod(type, methodName, EMPTY_ARGS); 137 } 138 139 public static Object invokeConstructorOf(Class type, Object arguments) { 140 return getInstance().invokeConstructorOf(type, arguments); 141 } 142 143 public static Object invokeConstructorOf(String klass, Object arguments) throws ClassNotFoundException { 144 Class type = InvokerHelper.class.forName(klass); 145 return getInstance().invokeConstructorOf(type, arguments); 146 } 147 148 public static Object invokeNoArgumentsConstructorOf(Class type) { 149 return getInstance().invokeConstructorOf(type, EMPTY_ARGS); 150 } 151 152 public static Object invokeClosure(Object closure, Object arguments) { 153 return getInstance().invokeMethod(closure, "doCall", arguments); 154 } 155 156 public static List asList(Object value) { 157 if (value == null) { 158 return Collections.EMPTY_LIST; 159 } 160 else if (value instanceof List) { 161 return (List) value; 162 } 163 else if (value.getClass().isArray()) { 164 return Arrays.asList((Object[]) value); 165 } 166 else if (value instanceof Enumeration) { 167 List answer = new ArrayList(); 168 for (Enumeration e = (Enumeration) value; e.hasMoreElements();) { 169 answer.add(e.nextElement()); 170 } 171 return answer; 172 } 173 else { 174 // lets assume its a collection of 1 175 return Collections.singletonList(value); 176 } 177 } 178 179 public static String toString(Object arguments) { 180 if (arguments instanceof Object[]) 181 return toArrayString((Object[])arguments); 182 else if (arguments instanceof Collection) 183 return toListString((Collection)arguments); 184 else if (arguments instanceof Map) 185 return toMapString((Map)arguments); 186 else if (arguments instanceof Collection) 187 return format(arguments, true); 188 else 189 return format(arguments, false); 190 } 191 192 public static String inspect(Object self) { 193 return format(self, true); 194 } 195 196 public static Object getAttribute(Object object, String attribute) { 197 return getInstance().getAttribute(object, attribute); 198 } 199 200 public static void setAttribute(Object object, String attribute, Object newValue) { 201 getInstance().setAttribute(object, attribute, newValue); 202 } 203 204 public static Object getProperty(Object object, String property) { 205 return getInstance().getProperty(object, property); 206 } 207 208 public static Object getPropertySafe(Object object, String property) { 209 if (object != null) { 210 return getInstance().getProperty(object, property); 211 } 212 return null; 213 } 214 215 public static void setProperty(Object object, String property, Object newValue) { 216 getInstance().setProperty(object, property, newValue); 217 } 218 219 /** 220 * This is so we don't have to reorder the stack when we call this method. 221 * At some point a better name might be in order. 222 */ 223 public static void setProperty2(Object newValue, Object object, String property) { 224 getInstance().setProperty(object, property, newValue); 225 } 226 227 228 /** 229 * This is so we don't have to reorder the stack when we call this method. 230 * At some point a better name might be in order. 231 */ 232 public static void setGroovyObjectProperty(Object newValue, GroovyObject object, String property) { 233 object.setProperty(property, newValue); 234 } 235 236 public static Object getGroovyObjectProperty(GroovyObject object, String property) { 237 return object.getProperty(property); 238 } 239 240 241 /** 242 * This is so we don't have to reorder the stack when we call this method. 243 * At some point a better name might be in order. 244 */ 245 public static void setPropertySafe2(Object newValue, Object object, String property) { 246 if (object != null) { 247 setProperty2(newValue, object, property); 248 } 249 } 250 251 /** 252 * Returns the method pointer for the given object name 253 */ 254 public static Closure getMethodPointer(Object object, String methodName) { 255 return getInstance().getMethodPointer(object, methodName); 256 } 257 258 public static Object negate(Object value) { 259 if (value instanceof Integer) { 260 Integer number = (Integer) value; 261 return IntegerCache.integerValue(-number.intValue()); 262 } 263 else if (value instanceof Long) { 264 Long number = (Long) value; 265 return new Long(-number.longValue()); 266 } 267 else if (value instanceof BigInteger) { 268 return ((BigInteger) value).negate(); 269 } 270 else if (value instanceof BigDecimal) { 271 return ((BigDecimal) value).negate(); 272 } 273 else if (value instanceof Double) { 274 Double number = (Double) value; 275 return new Double(-number.doubleValue()); 276 } 277 else if (value instanceof Float) { 278 Float number = (Float) value; 279 return new Float(-number.floatValue()); 280 } 281 else if (value instanceof ArrayList) { 282 // value is an list. 283 ArrayList newlist = new ArrayList(); 284 Iterator it = ((ArrayList) value).iterator(); 285 for (; it.hasNext();) { 286 newlist.add(negate(it.next())); 287 } 288 return newlist; 289 } 290 else { 291 throw new GroovyRuntimeException("Cannot negate type " + value.getClass().getName() + ", value " + value); 292 } 293 } 294 295 /** 296 * Find the right hand regex within the left hand string and return a matcher. 297 * 298 * @param left string to compare 299 * @param right regular expression to compare the string to 300 * @return 301 */ 302 public static Matcher findRegex(Object left, Object right) { 303 String stringToCompare; 304 if (left instanceof String) { 305 stringToCompare = (String) left; 306 } 307 else { 308 stringToCompare = toString(left); 309 } 310 String regexToCompareTo; 311 if (right instanceof String) { 312 regexToCompareTo = (String) right; 313 } 314 else if (right instanceof Pattern) { 315 Pattern pattern = (Pattern) right; 316 return pattern.matcher(stringToCompare); 317 } 318 else { 319 regexToCompareTo = toString(right); 320 } 321 Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare); 322 return matcher; 323 } 324 325 326 /** 327 * Find the right hand regex within the left hand string and return a matcher. 328 * 329 * @param left string to compare 330 * @param right regular expression to compare the string to 331 * @return 332 */ 333 public static boolean matchRegex(Object left, Object right) { 334 Pattern pattern; 335 if (right instanceof Pattern) { 336 pattern = (Pattern) right; 337 } 338 else { 339 pattern = Pattern.compile(toString(right)); 340 } 341 String stringToCompare = toString(left); 342 Matcher matcher = pattern.matcher(stringToCompare); 343 RegexSupport.setLastMatcher(matcher); 344 return matcher.matches(); 345 } 346 347 public static Tuple createTuple(Object[] array) { 348 return new Tuple(array); 349 } 350 351 public static SpreadMap spreadMap(Object value) { 352 if (value instanceof Map) { 353 Object[] values = new Object[((Map) value).keySet().size() * 2]; 354 int index = 0; 355 Iterator it = ((Map) value).keySet().iterator(); 356 for (; it.hasNext();) { 357 Object key = it.next(); 358 values[index++] = key; 359 values[index++] = ((Map) value).get(key); 360 } 361 return new SpreadMap(values); 362 } 363 else { 364 throw new SpreadMapEvaluatingException("Cannot spread the map " + value.getClass().getName() + ", value " + value); 365 } 366 } 367 368 public static List createList(Object[] values) { 369 ArrayList answer = new ArrayList(values.length); 370 for (int i = 0; i < values.length; i++) { 371 answer.add(values[i]); 372 } 373 return answer; 374 } 375 376 public static Map createMap(Object[] values) { 377 Map answer = new HashMap(values.length / 2); 378 int i = 0; 379 while (i < values.length - 1) { 380 if ((values[i] instanceof SpreadMap) && (values[i+1] instanceof Map)) { 381 Map smap = (Map) values[i+1]; 382 Iterator iter = smap.keySet().iterator(); 383 for (; iter.hasNext(); ) { 384 Object key = (Object) iter.next(); 385 answer.put(key, smap.get(key)); 386 } 387 i+=2; 388 } 389 else { 390 answer.put(values[i++], values[i++]); 391 } 392 } 393 return answer; 394 } 395 396 public static void assertFailed(Object expression, Object message) { 397 if (message == null || "".equals(message)) { 398 throw new AssertionError("Expression: " + expression); 399 } 400 else { 401 throw new AssertionError("" + message + ". Expression: " + expression); 402 } 403 } 404 405 public static Object runScript(Class scriptClass, String[] args) { 406 Binding context = new Binding(args); 407 Script script = createScript(scriptClass, context); 408 return invokeMethod(script, "run", EMPTY_ARGS); 409 } 410 411 public static Script createScript(Class scriptClass, Binding context) { 412 // for empty scripts 413 if (scriptClass == null) { 414 return new Script() { 415 public Object run() { 416 return null; 417 } 418 }; 419 } 420 try { 421 final GroovyObject object = (GroovyObject) scriptClass.newInstance(); 422 Script script = null; 423 if (object instanceof Script) { 424 script = (Script) object; 425 } 426 else { 427 // it could just be a class, so lets wrap it in a Script wrapper 428 // though the bindings will be ignored 429 script = new Script() { 430 public Object run() { 431 object.invokeMethod("main", EMPTY_MAIN_ARGS); 432 return null; 433 } 434 }; 435 setProperties(object, context.getVariables()); 436 } 437 script.setBinding(context); 438 return script; 439 } 440 catch (Exception e) { 441 throw new GroovyRuntimeException("Failed to create Script instance for class: " + scriptClass + ". Reason: " + e, 442 e); 443 } 444 } 445 446 /** 447 * Sets the properties on the given object 448 * 449 * @param object 450 * @param map 451 */ 452 public static void setProperties(Object object, Map map) { 453 MetaClass mc = getInstance().getMetaClass(object); 454 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 455 Map.Entry entry = (Map.Entry) iter.next(); 456 String key = entry.getKey().toString(); 457 458 Object value = entry.getValue(); 459 try { 460 mc.setProperty(object, key, value); 461 } catch (MissingPropertyException mpe) {} 462 } 463 } 464 465 public static String getVersion() { 466 String version = null; 467 Package p = Package.getPackage("groovy.lang"); 468 if (p != null) { 469 version = p.getImplementationVersion(); 470 } 471 if (version == null) { 472 version = ""; 473 } 474 return version; 475 } 476 477 /** 478 * Writes the given object to the given stream 479 */ 480 public static void write(Writer out, Object object) throws IOException { 481 if (object instanceof String) { 482 out.write((String) object); 483 } 484 else if (object instanceof Object[]) { 485 out.write(toArrayString((Object[]) object)); 486 } 487 else if (object instanceof Map) { 488 out.write(toMapString((Map) object)); 489 } 490 else if (object instanceof Collection) { 491 out.write(toListString((Collection) object)); 492 } 493 else if (object instanceof Writable) { 494 Writable writable = (Writable) object; 495 writable.writeTo(out); 496 } 497 else if (object instanceof InputStream || object instanceof Reader) { 498 // Copy stream to stream 499 Reader reader; 500 if (object instanceof InputStream) { 501 reader = new InputStreamReader((InputStream) object); 502 } 503 else { 504 reader = (Reader) object; 505 } 506 char[] chars = new char[8192]; 507 int i; 508 while ((i = reader.read(chars)) != -1) { 509 out.write(chars, 0, i); 510 } 511 reader.close(); 512 } 513 else { 514 out.write(toString(object)); 515 } 516 } 517 518 public static Iterator asIterator(Object o) { 519 return (Iterator) invokeMethod(o,"iterator",EMPTY_ARGS); 520 } 521 522 protected static String format(Object arguments, boolean verbose) { 523 if (arguments == null) { 524 return "null"; 525 } 526 else if (arguments.getClass().isArray()) { 527 return format(DefaultTypeTransformation.asCollection(arguments), verbose); 528 } 529 else if (arguments instanceof Range) { 530 Range range = (Range) arguments; 531 if (verbose) { 532 return range.inspect(); 533 } 534 else { 535 return range.toString(); 536 } 537 } 538 else if (arguments instanceof List) { 539 List list = (List) arguments; 540 StringBuffer buffer = new StringBuffer("["); 541 boolean first = true; 542 for (Iterator iter = list.iterator(); iter.hasNext();) { 543 if (first) { 544 first = false; 545 } 546 else { 547 buffer.append(", "); 548 } 549 buffer.append(format(iter.next(), verbose)); 550 } 551 buffer.append("]"); 552 return buffer.toString(); 553 } 554 else if (arguments instanceof Map) { 555 Map map = (Map) arguments; 556 if (map.isEmpty()) { 557 return "[:]"; 558 } 559 StringBuffer buffer = new StringBuffer("["); 560 boolean first = true; 561 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 562 if (first) { 563 first = false; 564 } 565 else { 566 buffer.append(", "); 567 } 568 Map.Entry entry = (Map.Entry) iter.next(); 569 buffer.append(format(entry.getKey(), verbose)); 570 buffer.append(":"); 571 if (entry.getValue()==map) { 572 buffer.append("this Map_"); 573 } else { 574 buffer.append(format(entry.getValue(), verbose)); 575 } 576 } 577 buffer.append("]"); 578 return buffer.toString(); 579 } 580 else if (arguments instanceof Element) { 581 Element node = (Element) arguments; 582 OutputFormat format = new OutputFormat(node.getOwnerDocument()); 583 format.setOmitXMLDeclaration(true); 584 format.setIndenting(true); 585 format.setLineWidth(0); 586 format.setPreserveSpace(true); 587 StringWriter sw = new StringWriter(); 588 XMLSerializer serializer = new XMLSerializer(sw, format); 589 try { 590 serializer.asDOMSerializer(); 591 serializer.serialize(node); 592 } 593 catch (IOException e) { 594 } 595 return sw.toString(); 596 } 597 else if (arguments instanceof String) { 598 if (verbose) { 599 String arg = ((String)arguments).replaceAll("\\n", "\\\\n"); // line feed 600 arg = arg.replaceAll("\\r", "\\\\r"); // carriage return 601 arg = arg.replaceAll("\\t", "\\\\t"); // tab 602 arg = arg.replaceAll("\\f", "\\\\f"); // form feed 603 arg = arg.replaceAll("\\\"", "\\\\\""); // double quotation amrk 604 arg = arg.replaceAll("\\\\", "\\\\"); // back slash 605 return "\"" + arg + "\""; 606 } 607 else { 608 return (String) arguments; 609 } 610 } 611 else { 612 return arguments.toString(); 613 } 614 } 615 616 617 /** 618 * A helper method to format the arguments types as a comma-separated list 619 */ 620 public static String toTypeString(Object[] arguments) { 621 if (arguments == null) { 622 return "null"; 623 } 624 StringBuffer argBuf = new StringBuffer(); 625 for (int i = 0; i < arguments.length; i++) { 626 if (i > 0) { 627 argBuf.append(", "); 628 } 629 argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null"); 630 } 631 return argBuf.toString(); 632 } 633 634 /** 635 * A helper method to return the string representation of a map with bracket boundaries "[" and "]". 636 */ 637 public static String toMapString(Map arg) { 638 return format(arg, true); 639 /*if (arg == null) { 640 return "null"; 641 } 642 if (arg.isEmpty()) { 643 return "[:]"; 644 } 645 String sbdry = "["; 646 String ebdry = "]"; 647 StringBuffer buffer = new StringBuffer(sbdry); 648 boolean first = true; 649 for (Iterator iter = arg.entrySet().iterator(); iter.hasNext();) { 650 if (first) 651 first = false; 652 else 653 buffer.append(", "); 654 Map.Entry entry = (Map.Entry) iter.next(); 655 buffer.append(format(entry.getKey(), true)); 656 buffer.append(":"); 657 buffer.append(format(entry.getValue(), true)); 658 } 659 buffer.append(ebdry); 660 return buffer.toString();*/ 661 } 662 663 /** 664 * A helper method to return the string representation of a list with bracket boundaries "[" and "]". 665 */ 666 public static String toListString(Collection arg) { 667 if (arg == null) { 668 return "null"; 669 } 670 if (arg.isEmpty()) { 671 return "[]"; 672 } 673 String sbdry = "["; 674 String ebdry = "]"; 675 StringBuffer buffer = new StringBuffer(sbdry); 676 boolean first = true; 677 for (Iterator iter = arg.iterator(); iter.hasNext();) { 678 if (first) 679 first = false; 680 else 681 buffer.append(", "); 682 Object elem = iter.next(); 683 buffer.append(format(elem, true)); 684 } 685 buffer.append(ebdry); 686 return buffer.toString(); 687 } 688 689 /** 690 * A helper method to return the string representation of an arrray of objects 691 * with brace boundaries "{" and "}". 692 */ 693 public static String toArrayString(Object[] arguments) { 694 if (arguments == null) { 695 return "null"; 696 } 697 String sbdry = "{"; 698 String ebdry = "}"; 699 StringBuffer argBuf = new StringBuffer(sbdry); 700 for (int i = 0; i < arguments.length; i++) { 701 if (i > 0) { 702 argBuf.append(", "); 703 } 704 argBuf.append(format(arguments[i], true)); 705 } 706 argBuf.append(ebdry); 707 return argBuf.toString(); 708 } 709 710 public static List createRange(Object from, Object to, boolean inclusive) { 711 try { 712 return ScriptBytecodeAdapter.createRange(from,to,inclusive); 713 } catch (RuntimeException re) { 714 throw re; 715 } catch (Error e) { 716 throw e; 717 } catch (Throwable t) { 718 throw new RuntimeException(t); 719 } 720 } 721 722 public static Object bitNegate(Object value) { 723 if (value instanceof Integer) { 724 Integer number = (Integer) value; 725 return new Integer(~number.intValue()); 726 } 727 else if (value instanceof Long) { 728 Long number = (Long) value; 729 return new Long(~number.longValue()); 730 } 731 else if (value instanceof BigInteger) { 732 return ((BigInteger) value).not(); 733 734 } 735 else if (value instanceof String) { 736 // value is a regular expression. 737 return DefaultGroovyMethods.negate(value.toString()); 738 } 739 else if (value instanceof GString) { 740 // value is a regular expression. 741 return DefaultGroovyMethods.negate(value.toString()); 742 } 743 else if (value instanceof ArrayList) { 744 // value is an list. 745 ArrayList newlist = new ArrayList(); 746 Iterator it = ((ArrayList) value).iterator(); 747 for (; it.hasNext();) { 748 newlist.add(bitNegate(it.next())); 749 } 750 return newlist; 751 } 752 else { 753 throw new BitwiseNegateEvaluatingException("Cannot bitwise negate type " + value.getClass().getName() + ", value " + value); 754 } 755 756 757 } 758 759 }