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