1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2007-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 package org.octave; 27 28 import java.lang.reflect.*; 29 30 public class ClassHelper 31 { 32 private static OctClassLoader loader; 33 34 static 35 { 36 ClassLoader l = ClassHelper.class.getClassLoader (); 37 loader = (l instanceof OctClassLoader ? (OctClassLoader) l : 38 new OctClassLoader (l)); 39 } 40 41 42 /** 43 * Add the given path to the classpath. 44 * @param name String - path to add to the classpath 45 * @param append boolean - if true, append path to classpath, otherwise prepend it. 46 * @return boolean - true if the given path exists and was added to the classpath. 47 * @throws Exception if an error occurs 48 */ addClassPath(String name, boolean append)49 public static boolean addClassPath (String name, boolean append) 50 throws Exception 51 { 52 boolean found = false; 53 java.io.File f = new java.io.File (name); 54 if (f.exists ()) 55 { 56 found = true; 57 58 if (append) 59 { 60 loader.addClassPath (name); 61 } 62 else 63 { 64 // create a completely new class loader because java.net.URLClassLoader appears to have no method to prepend directories to the existing classpath 65 66 // FIXME: is there a more efficient way to do this job? 67 68 java.net.URL[] urls = loader.getURLs (); 69 70 ClassLoader l = ClassHelper.class.getClassLoader (); 71 loader = (l instanceof OctClassLoader ? (OctClassLoader) l : 72 new OctClassLoader (l)); 73 74 loader.addClassPath (name); 75 76 for (int i = 0; i < urls.length; i++) 77 { 78 loader.addURL (urls[i]); 79 } 80 } 81 } 82 83 return (found); 84 } 85 86 87 // new -MH- 88 /** 89 * 90 * @param name String - path to remove from classpath. 91 * @return boolean - true if the given path existed in the classpath before it was removed. 92 * @throws Exception if an error occurs 93 */ removeClassPath(String name)94 public static boolean removeClassPath (String name) 95 throws Exception 96 { 97 boolean found = false; 98 java.io.File f = new java.io.File (name); 99 java.net.URL urlToRemove = f.toURI ().toURL (); 100 // save urls in current class path 101 java.net.URL[] urls = loader.getURLs (); 102 103 // create a completely new class loader because java.net.URLClassLoader has no removeClassPath() method 104 ClassLoader l = ClassHelper.class.getClassLoader (); 105 loader = (l instanceof OctClassLoader ? (OctClassLoader) l : 106 new OctClassLoader (l)); 107 108 // add the previous urls back, except for the one 109 for (int i = 0; i < urls.length; i++) 110 { 111 java.net.URL url = urls[i]; 112 if (!url.equals (urlToRemove)) 113 { 114 loader.addURL (url); 115 } 116 else 117 { 118 // path to remove 119 found = true; 120 } 121 } 122 123 return (found); 124 } 125 126 getClassPath()127 public static String getClassPath () 128 { 129 StringBuilder buf = new StringBuilder(); 130 String pathSep = System.getProperty ("path.separator"); 131 java.net.URL[] urls = loader.getURLs (); 132 133 for (int i = 0; i < urls.length; i++) 134 { 135 try 136 { 137 java.io.File f = new java.io.File (urls[i].toURI ()); 138 if (buf.length () > 0) 139 { 140 buf.append (pathSep); 141 } 142 buf.append (f.toString ()); 143 } 144 catch (java.net.URISyntaxException ex) 145 {} 146 } 147 return buf.toString (); 148 } 149 150 151 // new -MH- 152 // return list of methods for given class name getMethods(String classname)153 public static String getMethods (String classname) 154 throws ClassNotFoundException 155 { 156 return (getMethods (Class.forName (classname))); 157 } 158 159 160 // new -MH- 161 // return list of methods for given class instance getMethods(Object obj)162 public static String getMethods (Object obj) 163 throws ClassNotFoundException 164 { 165 return (getMethods (obj.getClass ())); 166 } 167 168 169 // new -MH- 170 // return list of methods for given class getMethods(Class klass)171 public static String getMethods (Class klass) 172 { 173 StringBuilder sb = new StringBuilder(); 174 boolean first = true; 175 176 Constructor theConstructor[] = klass.getConstructors (); 177 for (int i = 0; i < theConstructor.length; i++) 178 { 179 if (first) 180 { 181 first = false; 182 } 183 else 184 { 185 sb.append (";"); 186 } 187 188 sb.append (theConstructor[i].getName ()); 189 sb.append ("("); 190 191 Class theParameter[] = theConstructor[i].getParameterTypes (); 192 for (int j = 0; j < theParameter.length; j++) 193 { 194 if (j > 0) 195 { 196 sb.append (", "); 197 } 198 sb.append (theParameter[j].getCanonicalName ()); 199 } 200 sb.append (")"); 201 202 Class theExceptions[] = theConstructor[i].getExceptionTypes (); 203 if (theExceptions.length > 0) 204 { 205 sb.append (" throws "); 206 for (int j = 0; j < theExceptions.length; j++) 207 { 208 if (j > 0) 209 { 210 sb.append (", "); 211 } 212 sb.append (theExceptions[j].getCanonicalName ()); 213 } 214 } 215 } 216 217 Method theMethod[] = klass.getMethods (); 218 for (int i = 0; i < theMethod.length; i++) 219 { 220 if (first) 221 { 222 first = false; 223 } 224 else 225 { 226 sb.append (";"); 227 } 228 sb.append (theMethod[i].getReturnType ().getCanonicalName ()); 229 sb.append (" "); 230 sb.append (theMethod[i].getName ()); 231 sb.append ("("); 232 233 Class theParameter[] = theMethod[i].getParameterTypes (); 234 for (int j = 0; j < theParameter.length; j++) 235 { 236 if (j > 0) 237 { 238 sb.append (", "); 239 } 240 sb.append (theParameter[j].getCanonicalName ()); 241 } 242 sb.append (")"); 243 244 Class theExceptions[] = theMethod[i].getExceptionTypes (); 245 if (theExceptions.length > 0) 246 { 247 sb.append (" throws "); 248 for (int j = 0; j < theExceptions.length; j++) 249 { 250 if (j > 0) 251 { 252 sb.append (", "); 253 } 254 sb.append (theExceptions[j].getCanonicalName ()); 255 } 256 } 257 } 258 259 return (sb.toString ()); 260 } 261 262 263 // new -MH- 264 // return list of fields for given class getFields(Class klass)265 public static String getFields (Class klass) 266 { 267 StringBuilder sb = new StringBuilder(); 268 269 Field theField[] = klass.getFields (); 270 for (int i = 0; i < theField.length; i++) 271 { 272 if (i > 0) 273 { 274 sb.append (";"); 275 } 276 sb.append (theField[i].getName ()); 277 } 278 279 return (sb.toString ()); 280 } 281 282 283 // new -MH- 284 // return list of fields for given class name getFields(String classname)285 public static String getFields (String classname) 286 throws ClassNotFoundException 287 { 288 return (getFields (Class.forName (classname))); 289 } 290 291 292 // new -MH- 293 // return list of fields for given class instance getFields(Object obj)294 public static String getFields (Object obj) 295 throws ClassNotFoundException 296 { 297 return (getFields (obj.getClass ())); 298 } 299 300 findMethod(Class<?> cls, String name, Class<?>[] argTypes)301 public static Method findMethod (Class<?> cls, String name, Class<?>[] argTypes) 302 { 303 try 304 { 305 return cls.getMethod (name, argTypes); 306 } 307 catch (Exception e) 308 { 309 Method[] mList = cls.getMethods (); 310 Method m; 311 for (int i = 0; i < mList.length; i++) 312 { 313 m = mList[i]; 314 if (m.getName ().equals (name) 315 && m.getParameterTypes ().length == argTypes.length 316 && isCallableFrom (m, argTypes)) 317 { 318 return m; 319 } 320 } 321 return null; 322 } 323 } 324 325 findConstructor(Class<?> cls, Class<?>[] argTypes)326 public static Constructor findConstructor (Class<?> cls, Class<?>[] argTypes) 327 { 328 try 329 { 330 return cls.getConstructor (argTypes); 331 } 332 catch (Exception e) 333 { 334 Constructor[] cList = cls.getConstructors (); 335 //System.out.println("# constructors: " + cList.length); 336 Constructor c; 337 for (int i = 0; i < cList.length; i++) 338 { 339 //System.out.println("Considering constructor: " + cList[i]); 340 c = cList[i]; 341 if (c.getParameterTypes ().length == argTypes.length 342 && isCallableFrom (c, argTypes)) 343 { 344 return c; 345 } 346 } 347 return null; 348 } 349 } 350 351 invokeMethod(Method m, Object target, Object[] args)352 private static Object invokeMethod (Method m, Object target, Object[] args) 353 throws Exception 354 { 355 try 356 { 357 return m.invoke (target, args); 358 } 359 catch (IllegalAccessException ex) 360 { 361 String mName = m.getName (); 362 Class<?>[] pTypes = m.getParameterTypes (); 363 Class<?> currClass = target.getClass (); 364 365 while (currClass != null) 366 { 367 try 368 { 369 Method meth = currClass.getMethod (mName, pTypes); 370 if (!meth.equals (m)) 371 { 372 return meth.invoke (target, args); 373 } 374 } 375 catch (NoSuchMethodException ex2) 376 {} 377 catch (IllegalAccessException ex2) 378 {} 379 380 Class<?>[] ifaceList = currClass.getInterfaces (); 381 for (int i = 0; i < ifaceList.length; i++) 382 { 383 try 384 { 385 Method meth = ifaceList[i].getMethod (mName, pTypes); 386 return meth.invoke (target, args); 387 } 388 catch (NoSuchMethodException ex2) 389 {} 390 catch (IllegalAccessException ex2) 391 {} 392 } 393 394 currClass = currClass.getSuperclass (); 395 } 396 397 throw ex; 398 } 399 } 400 401 invokeMethod(Object target, String name, Object[] args, Class[] argTypes)402 public static Object invokeMethod (Object target, String name, 403 Object[] args, Class[] argTypes) 404 throws Throwable 405 { 406 Method m = findMethod (target.getClass (), name, argTypes); 407 if (m != null) 408 { 409 try 410 { 411 Object result = invokeMethod (m, target, 412 castArguments (args, argTypes, 413 m.getParameterTypes ())); 414 return result; 415 } 416 catch (InvocationTargetException ex) 417 { 418 throw ex.getCause (); 419 } 420 } 421 else 422 { 423 throw new NoSuchMethodException (name); 424 } 425 } 426 427 invokeStaticMethod(String cls, String name, Object[] args, Class[] argTypes)428 public static Object invokeStaticMethod (String cls, String name, 429 Object[] args, Class[] argTypes) 430 throws Throwable 431 { 432 Method m = findMethod (Class.forName (cls, true, loader), name, 433 argTypes); 434 if (m != null) 435 { 436 try 437 { 438 Object result = m.invoke (null, 439 castArguments (args, argTypes, 440 m.getParameterTypes ())); 441 return result; 442 } 443 catch (InvocationTargetException ex) 444 { 445 throw ex.getCause (); 446 } 447 } 448 else 449 { 450 throw new NoSuchMethodException (name); 451 } 452 } 453 454 invokeConstructor(String cls, Object[] args, Class[] argTypes)455 public static Object invokeConstructor (String cls, Object[] args, 456 Class[] argTypes) 457 throws Throwable 458 { 459 Constructor c = findConstructor (Class.forName (cls, true, loader), 460 argTypes); 461 if (c != null) 462 { 463 try 464 { 465 Object result = c.newInstance (castArguments (args, argTypes, 466 c.getParameterTypes ())); 467 return result; 468 } 469 catch (InvocationTargetException ex) 470 { 471 throw ex.getCause (); 472 } 473 } 474 else 475 { 476 throw new NoSuchMethodException (cls); 477 } 478 } 479 480 getField(Object target, String name)481 public static Object getField (Object target, String name) 482 throws Throwable 483 { 484 try 485 { 486 Field f = target.getClass ().getField (name); 487 return f.get (target); 488 } 489 catch (NoSuchFieldException ex) 490 { 491 try 492 { 493 return invokeMethod (target, name, new Object[0], new Class[0]); 494 } 495 catch (NoSuchMethodException ex2) 496 { 497 throw ex; 498 } 499 } 500 } 501 502 getStaticField(String cls, String name)503 public static Object getStaticField (String cls, String name) 504 throws Throwable 505 { 506 try 507 { 508 Field f = Class.forName (cls, true, loader).getField (name); 509 return f.get (null); 510 } 511 catch (NoSuchFieldException ex) 512 { 513 try 514 { 515 return invokeStaticMethod (cls, name, new Object[0], new Class[0]); 516 } 517 catch (NoSuchMethodException ex2) 518 { 519 throw ex; 520 } 521 } 522 } 523 524 setField(Object target, String name, Object value)525 public static void setField (Object target, String name, Object value) 526 throws Exception 527 { 528 Field f = target.getClass ().getField (name); 529 f.set (target, castArgument (value, value.getClass (), f.getType ())); 530 } 531 532 setStaticField(String cls, String name, Object value)533 public static void setStaticField (String cls, String name, Object value) 534 throws Exception 535 { 536 Field f = Class.forName (cls, true, loader).getField (name); 537 f.set (null, castArgument (value, value.getClass (), f.getType ())); 538 } 539 540 isCallableFrom(Method m, Class[] argTypes)541 private static boolean isCallableFrom (Method m, Class[] argTypes) 542 { 543 Class[] expTypes = m.getParameterTypes (); 544 for (int i = 0; i < argTypes.length; i++) 545 { 546 if (!isCallableFrom (expTypes[i], argTypes[i])) 547 { 548 return false; 549 } 550 } 551 return true; 552 } 553 554 isCallableFrom(Constructor c, Class[] argTypes)555 private static boolean isCallableFrom (Constructor c, Class[] argTypes) 556 { 557 Class[] expTypes = c.getParameterTypes (); 558 for (int i = 0; i < argTypes.length; i++) 559 { 560 if (!isCallableFrom (expTypes[i], argTypes[i])) 561 { 562 return false; 563 } 564 } 565 return true; 566 } 567 568 isCallableFrom(Class<?> expCls, Class<?> argCls)569 private static boolean isCallableFrom (Class<?> expCls, Class<?> argCls) 570 { 571 //System.out.println("isCallableFrom: "+expCls.getCanonicalName() + " <=? " + argCls.getCanonicalName()); 572 if (argCls == null) 573 { 574 return!expCls.isPrimitive (); 575 } 576 else if (expCls.isAssignableFrom (argCls)) 577 { 578 return true; 579 } 580 else if ((isNumberClass (expCls) || isBooleanClass (expCls)) 581 && isNumberClass (argCls)) 582 { 583 return true; 584 } 585 else if (isCharClass (expCls) && argCls.equals (Character.class)) 586 { 587 /* 588 modified into a more strict check to avoid char to string matching 589 to avoid matching method signatureslike 590 java_method(char) with octave_call('a String') 591 */ 592 return true; 593 } 594 else if (isStringClass (expCls) && argCls.equals (String.class)) 595 { 596 /* 597 added for strict String to String matching 598 java_method(String) with octave_call('a String') 599 but not 600 java_method(char) with octave_call('a String') 601 */ 602 return true; 603 } 604 else if (expCls.isArray () && argCls.isArray () 605 && isCallableFrom (expCls.getComponentType (), 606 argCls.getComponentType ())) 607 { 608 return true; 609 } 610 else if (expCls.equals (Object.class) && argCls.isPrimitive ()) 611 { 612 return true; 613 } 614 else 615 { 616 return false; 617 } 618 } 619 620 isNumberClass(Class cls)621 private static boolean isNumberClass (Class cls) 622 { 623 return (cls.equals (Integer.TYPE) || cls.equals (Integer.class) 624 || cls.equals (Short.TYPE) || cls.equals (Short.class) 625 || cls.equals (Long.TYPE) || cls.equals (Long.class) 626 || cls.equals (Float.TYPE) || cls.equals (Float.class) 627 || cls.equals (Double.TYPE) || cls.equals (Double.class)); 628 } 629 630 isBooleanClass(Class cls)631 private static boolean isBooleanClass (Class cls) 632 { 633 return (cls.equals (Boolean.class) || cls.equals (Boolean.TYPE)); 634 } 635 636 isCharClass(Class cls)637 private static boolean isCharClass (Class cls) 638 { 639 return (cls.equals (Character.class) || cls.equals (Character.TYPE)); 640 } 641 642 643 /** 644 * Check whether the supplied class is a String class. 645 * 646 * Added for more strict char/string matching of method signatures 647 * @param cls Class - the class to check 648 * @return boolean - true if clas is of class java.lang.String 649 */ isStringClass(Class cls)650 private static boolean isStringClass (Class cls) 651 { 652 return ( 653 cls.equals (String.class) 654 ); 655 } 656 657 castArguments(Object[] args, Class[] argTypes, Class[] expTypes)658 private static Object[] castArguments (Object[] args, Class[] argTypes, 659 Class[] expTypes) 660 { 661 for (int i = 0; i < args.length; i++) 662 { 663 args[i] = castArgument (args[i], argTypes[i], expTypes[i]); 664 } 665 return args; 666 } 667 668 castArgument(Object obj, Class<?> type, Class<?> expType)669 private static Object castArgument (Object obj, Class<?> type, Class<?> expType) 670 { 671 // System.out.println("expType:"+expType.getCanonicalName() + " <= type:" + type.getCanonicalName()); 672 if (type == null || expType.isAssignableFrom (type)) 673 { 674 return obj; 675 } 676 else if (isNumberClass (expType)) 677 { 678 if (expType.equals (Integer.TYPE) || expType.equals (Integer.class)) 679 { 680 return new Integer (((Number) obj).intValue ()); 681 } 682 else if (expType.equals (Double.TYPE) || expType.equals (Double.class)) 683 { 684 return new Double (((Number) obj).doubleValue ()); 685 } 686 else if (expType.equals (Short.TYPE) || expType.equals (Short.class)) 687 { 688 return new Short (((Number) obj).shortValue ()); 689 } 690 else if (expType.equals (Long.TYPE) || expType.equals (Long.class)) 691 { 692 return new Long (((Number) obj).longValue ()); 693 } 694 else if (expType.equals (Float.TYPE) || expType.equals (Float.class)) 695 { 696 return new Float (((Number) obj).floatValue ()); 697 } 698 } 699 else if (isBooleanClass (expType)) 700 { 701 return new Boolean (((Number) obj).intValue () != 0); 702 } 703 else if (isCharClass (expType)) 704 { 705 String s = obj.toString (); 706 if (s.length () != 1) 707 { 708 throw new ClassCastException ("cannot cast " + s + " to character"); 709 } 710 return new Character (s.charAt (0)); 711 } 712 else if (expType.isArray () && type.isArray ()) 713 { 714 return castArray (obj, type.getComponentType (), 715 expType.getComponentType ()); 716 } 717 else if (type.isPrimitive ()) 718 { 719 return obj; 720 } 721 return null; 722 } 723 724 castArray(Object obj, Class elemType, Class elemExpType)725 private static Object castArray (Object obj, Class elemType, 726 Class elemExpType) 727 { 728 int len = Array.getLength (obj); 729 Object result = Array.newInstance (elemExpType, len); 730 for (int i = 0; i < len; i++) 731 { 732 Array.set (result, i, 733 castArgument (Array.get (obj, i), elemType, elemExpType)); 734 } 735 return result; 736 } 737 738 getArrayClassNDims(Class cls)739 private static int getArrayClassNDims (Class cls) 740 { 741 if (cls != null && cls.isArray ()) 742 { 743 return (1 + getArrayClassNDims (cls.getComponentType ())); 744 } 745 else 746 { 747 return 0; 748 } 749 } 750 751 getArrayElemClass(Class cls)752 private static Class getArrayElemClass (Class cls) 753 { 754 if (cls.isArray ()) 755 { 756 return getArrayElemClass (cls.getComponentType ()); 757 } 758 else 759 { 760 return cls; 761 } 762 } 763 764 getArrayElements(Object array, int[][] idx, int offset, int ndims, Class elemType)765 private static Object getArrayElements (Object array, int[][] idx, 766 int offset, 767 int ndims, Class elemType) 768 { 769 if (offset >= ndims) 770 { 771 Object elem = Array.get (array, idx[offset][0]); 772 if (offset < idx.length - 1) 773 { 774 return getArrayElements (elem, idx, offset + 1, ndims, elemType); 775 } 776 else 777 { 778 return elem; 779 } 780 } 781 else 782 { 783 Class compType = elemType.getComponentType (); 784 Object retval = Array.newInstance (compType, idx[offset].length); 785 for (int i = 0; i < idx[offset].length; i++) 786 { 787 Object elem = Array.get (array, idx[offset][i]); 788 if (offset < idx.length - 1) 789 { 790 elem = getArrayElements (elem, idx, offset + 1, ndims, compType); 791 } 792 Array.set (retval, i, elem); 793 } 794 return retval; 795 } 796 } 797 798 arraySubsref(Object obj, int[][] idx)799 public static Object arraySubsref (Object obj, int[][] idx) 800 throws Exception 801 { 802 if (!obj.getClass ().isArray ()) 803 { 804 throw new IllegalArgumentException ("not a Java array"); 805 } 806 807 if (idx.length == 1) 808 { 809 if (idx[0].length == 1) 810 { 811 return Array.get (obj, idx[0][0]); 812 } 813 else 814 { 815 Object retval = Array.newInstance (obj.getClass (). 816 getComponentType (), 817 idx[0].length); 818 for (int i = 0; i < idx[0].length; i++) 819 { 820 Array.set (retval, i, Array.get (obj, idx[0][i])); 821 } 822 return retval; 823 } 824 } 825 else 826 { 827 int[] dims = new int[idx.length]; 828 for (int i = 0; i < idx.length; i++) 829 { 830 dims[i] = idx[i].length; 831 } 832 833 if (dims.length != getArrayClassNDims (obj.getClass ())) 834 { 835 throw new IllegalArgumentException ("index size mismatch"); 836 } 837 838 /* resolve leading singletons */ 839 Object theObj = obj; 840 int offset = 0; 841 while (dims[offset] == 1) 842 { 843 theObj = Array.get (theObj, idx[offset][0]); 844 offset = offset + 1; 845 if (offset >= dims.length) 846 { 847 return theObj; 848 } 849 } 850 if (offset > 0) 851 { 852 int[][] new_idx = new int[idx.length - offset][]; 853 System.arraycopy (idx, offset, new_idx, 0, idx.length - offset); 854 return arraySubsref (theObj, new_idx); 855 } 856 857 /* chop trailing singletons */ 858 int ndims = dims.length; 859 while (ndims > 1 && dims[ndims - 1] == 1) 860 { 861 ndims--; 862 } 863 864 /* create result array */ 865 Class elemClass = theObj.getClass (); 866 for (int i = 0; i <= (dims.length - ndims); i++) 867 { 868 elemClass = elemClass.getComponentType (); 869 } 870 Object retval = Array.newInstance (elemClass, dims[0]); 871 872 /* fill-in array */ 873 for (int i = 0; i < idx[0].length; i++) 874 { 875 Object elem = getArrayElements (Array.get (theObj, idx[0][i]), 876 idx, 1, ndims, elemClass); 877 Array.set (retval, i, elem); 878 } 879 880 return retval; 881 } 882 } 883 884 setArrayElements(Object array, int[][] idx, int offset, int ndims, Object rhs)885 private static Object setArrayElements (Object array, int[][] idx, 886 int offset, int ndims, Object rhs) 887 throws Exception 888 { 889 if (offset >= ndims) 890 { 891 if (offset < idx.length - 1) 892 { 893 setArrayElements (Array.get (array, idx[offset][0]), idx, 894 offset + 1, ndims, rhs); 895 } 896 else 897 { 898 Array.set (array, idx[offset][0], rhs); 899 } 900 return array; 901 } 902 else 903 { 904 for (int i = 0; i < idx[offset].length; i++) 905 { 906 if (offset < idx.length - 1) 907 { 908 setArrayElements (Array.get (array, idx[offset][i]), idx, 909 offset + 1, ndims, Array.get (rhs, i)); 910 } 911 else 912 { 913 Array.set (array, idx[offset][i], Array.get (rhs, i)); 914 } 915 } 916 return array; 917 } 918 } 919 920 arraySubsasgn(Object obj, int[][] idx, Object rhs)921 public static Object arraySubsasgn (Object obj, int[][] idx, Object rhs) 922 throws Exception 923 { 924 if (!obj.getClass ().isArray ()) 925 { 926 throw new IllegalArgumentException ("not a Java array"); 927 } 928 929 if (idx.length == 1) 930 { 931 if (idx[0].length == 1) 932 { 933 Array.set (obj, idx[0][0], rhs); 934 } 935 else 936 { 937 for (int i = 0; i < idx[0].length; i++) 938 { 939 Array.set (obj, idx[0][i], Array.get (rhs, i)); 940 } 941 } 942 return obj; 943 } 944 else 945 { 946 int[] dims = new int[idx.length]; 947 for (int i = 0; i < idx.length; i++) 948 { 949 dims[i] = idx[i].length; 950 } 951 952 if (dims.length != getArrayClassNDims (obj.getClass ())) 953 { 954 throw new IllegalArgumentException ("index size mismatch"); 955 } 956 957 /* resolve leading singletons */ 958 Object theObj = obj; 959 int offset = 0; 960 while (dims[offset] == 1 && offset < (dims.length - 1)) 961 { 962 theObj = Array.get (theObj, idx[offset][0]); 963 offset = offset + 1; 964 } 965 if (offset > 0) 966 { 967 int[][] new_idx = new int[idx.length - offset][]; 968 System.arraycopy (idx, offset, new_idx, 0, idx.length - offset); 969 arraySubsasgn (theObj, new_idx, rhs); 970 return obj; 971 } 972 973 /* chop trailing singletons */ 974 int ndims = dims.length; 975 while (ndims > 1 && dims[ndims - 1] == 1) 976 { 977 ndims--; 978 } 979 980 for (int i = 0; i < idx[0].length; i++) 981 { 982 setArrayElements (Array.get (theObj, idx[0][i]), idx, 1, ndims, 983 Array.get (rhs, i)); 984 } 985 986 return obj; 987 } 988 } 989 990 createArray(Object cls, int[] dims)991 public static Object createArray (Object cls, int[] dims) 992 throws Exception 993 { 994 Class theClass; 995 if (cls instanceof Class) 996 { 997 theClass = (Class) cls; 998 } 999 else if (cls instanceof String) 1000 { 1001 theClass = Class.forName ((String) cls, true, loader); 1002 } 1003 else 1004 { 1005 throw new IllegalArgumentException ("invalid class specification " + 1006 cls); 1007 } 1008 1009 return Array.newInstance (theClass, dims); 1010 } 1011 1012 createArray(Object cls, int length)1013 public static Object createArray (Object cls, int length) 1014 throws Exception 1015 { 1016 return createArray (cls, new int[] {length}); 1017 } 1018 } 1019