1 /***** BEGIN LICENSE BLOCK *****
2 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Common Public
5 * License Version 1.0 (the "License"); you may not use this file
6 * except in compliance with the License. You may obtain a copy of
7 * the License at http://www.eclipse.org/legal/cpl-v10.html
9 * Software distributed under the License is distributed on an "AS
10 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * rights and limitations under the License.
14 * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
15 * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16 * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
17 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
18 * Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
19 * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
20 * Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
21 * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
22 * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the CPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the CPL, the GPL or the LGPL.
35 ***** END LICENSE BLOCK *****/
36 package org
.jruby
.javasupport
;
38 import org
.jruby
.java
.invokers
.StaticFieldGetter
;
39 import org
.jruby
.java
.invokers
.StaticMethodInvoker
;
40 import org
.jruby
.java
.invokers
.InstanceFieldGetter
;
41 import org
.jruby
.java
.invokers
.InstanceFieldSetter
;
42 import org
.jruby
.java
.invokers
.InstanceMethodInvoker
;
43 import org
.jruby
.java
.invokers
.StaticFieldSetter
;
44 import java
.io
.ByteArrayOutputStream
;
45 import java
.io
.InputStream
;
46 import java
.io
.IOException
;
47 import java
.lang
.reflect
.Array
;
48 import java
.lang
.reflect
.Constructor
;
49 import java
.lang
.reflect
.Field
;
50 import java
.lang
.reflect
.Method
;
51 import java
.lang
.reflect
.Modifier
;
52 import java
.util
.ArrayList
;
53 import java
.util
.HashMap
;
54 import java
.util
.Iterator
;
55 import java
.util
.List
;
57 import java
.util
.concurrent
.locks
.ReentrantLock
;
58 import java
.util
.regex
.Matcher
;
59 import java
.util
.regex
.Pattern
;
61 import org
.jruby
.Ruby
;
62 import org
.jruby
.RubyArray
;
63 import org
.jruby
.RubyBoolean
;
64 import org
.jruby
.RubyClass
;
65 import org
.jruby
.RubyFixnum
;
66 import org
.jruby
.RubyInteger
;
67 import org
.jruby
.RubyModule
;
68 import org
.jruby
.RubyString
;
69 import org
.jruby
.RubySymbol
;
70 import org
.jruby
.anno
.JRubyMethod
;
71 import org
.jruby
.anno
.JRubyClass
;
72 import org
.jruby
.common
.IRubyWarnings
.ID
;
73 import org
.jruby
.exceptions
.RaiseException
;
74 import org
.jruby
.internal
.runtime
.methods
.DynamicMethod
;
75 import org
.jruby
.java
.addons
.ArrayJavaAddons
;
76 import org
.jruby
.java
.proxies
.ArrayJavaProxy
;
77 import org
.jruby
.java
.invokers
.ConstructorInvoker
;
78 import org
.jruby
.java
.invokers
.DynalangInstanceInvoker
;
79 import org
.jruby
.javasupport
.util
.RuntimeHelpers
;
80 import org
.jruby
.runtime
.Arity
;
81 import org
.jruby
.runtime
.Block
;
82 import org
.jruby
.runtime
.CallType
;
83 import org
.jruby
.runtime
.ObjectAllocator
;
84 import org
.jruby
.runtime
.ThreadContext
;
85 import org
.jruby
.runtime
.Visibility
;
86 import org
.jruby
.runtime
.builtin
.IRubyObject
;
87 import org
.jruby
.runtime
.callback
.Callback
;
88 import org
.jruby
.util
.ByteList
;
89 import org
.jruby
.util
.IdUtil
;
90 import org
.jruby
.util
.SafePropertyAccessor
;
92 @JRubyClass(name
="Java::JavaClass", parent
="Java::JavaObject")
93 public class JavaClass
extends JavaObject
{
95 // some null objects to simplify later code
96 private static final Class
<?
>[] EMPTY_CLASS_ARRAY
= new Class
<?
>[] {};
97 private static final Method
[] EMPTY_METHOD_ARRAY
= new Method
[] {};
98 private static final Constructor
[] EMPTY_CONSTRUCTOR_ARRAY
= new Constructor
[] {};
99 private static final Field
[] EMPTY_FIELD_ARRAY
= new Field
[] {};
101 private static class AssignedName
{
102 // to override an assigned name, the type must be less than
103 // or equal to the assigned type. so a field name in a subclass
104 // will override an alias in a superclass, but not a method.
105 static final int RESERVED
= 0;
106 static final int METHOD
= 1;
107 static final int FIELD
= 2;
108 static final int PROTECTED_METHOD
= 3;
109 static final int WEAKLY_RESERVED
= 4; // we'll be peeved, but not devastated, if you override
110 static final int ALIAS
= 5;
111 // yes, protected fields are weaker than aliases. many conflicts
112 // in the old AWT code, for example, where you really want 'size'
113 // to mean the public method getSize, not the protected field 'size'.
114 static final int PROTECTED_FIELD
= 6;
118 AssignedName(String name
, int type
) {
124 // TODO: other reserved names?
125 private static final Map
<String
, AssignedName
> RESERVED_NAMES
= new HashMap
<String
, AssignedName
>();
127 RESERVED_NAMES
.put("__id__", new AssignedName("__id__", AssignedName
.RESERVED
));
128 RESERVED_NAMES
.put("__send__", new AssignedName("__send__", AssignedName
.RESERVED
));
129 RESERVED_NAMES
.put("class", new AssignedName("class", AssignedName
.RESERVED
));
130 RESERVED_NAMES
.put("initialize", new AssignedName("initialize", AssignedName
.RESERVED
));
131 RESERVED_NAMES
.put("object_id", new AssignedName("object_id", AssignedName
.RESERVED
));
132 RESERVED_NAMES
.put("private", new AssignedName("private", AssignedName
.RESERVED
));
133 RESERVED_NAMES
.put("protected", new AssignedName("protected", AssignedName
.RESERVED
));
134 RESERVED_NAMES
.put("public", new AssignedName("public", AssignedName
.RESERVED
));
136 // weakly reserved names
137 RESERVED_NAMES
.put("id", new AssignedName("id", AssignedName
.WEAKLY_RESERVED
));
139 private static final Map
<String
, AssignedName
> STATIC_RESERVED_NAMES
= new HashMap
<String
, AssignedName
>(RESERVED_NAMES
);
141 STATIC_RESERVED_NAMES
.put("new", new AssignedName("new", AssignedName
.RESERVED
));
143 private static final Map
<String
, AssignedName
> INSTANCE_RESERVED_NAMES
= new HashMap
<String
, AssignedName
>(RESERVED_NAMES
);
145 private static abstract class NamedInstaller
{
146 static final int STATIC_FIELD
= 1;
147 static final int STATIC_METHOD
= 2;
148 static final int INSTANCE_FIELD
= 3;
149 static final int INSTANCE_METHOD
= 4;
150 static final int CONSTRUCTOR
= 5;
153 Visibility visibility
= Visibility
.PUBLIC
;
156 NamedInstaller (String name
, int type
) {
160 abstract void install(RubyClass proxy
);
161 // small hack to save a cast later on
162 boolean hasLocalMethod() {
166 return visibility
== Visibility
.PUBLIC
;
168 boolean isProtected() {
169 return visibility
== Visibility
.PROTECTED
;
173 private static abstract class FieldInstaller
extends NamedInstaller
{
176 FieldInstaller(String name
, int type
, Field field
) {
182 private static class StaticFieldGetterInstaller
extends FieldInstaller
{
183 StaticFieldGetterInstaller(){}
184 StaticFieldGetterInstaller(String name
, Field field
) {
185 super(name
,STATIC_FIELD
,field
);
187 void install(RubyClass proxy
) {
188 if (Modifier
.isPublic(field
.getModifiers())) {
189 proxy
.getSingletonClass().addMethod(name
, new StaticFieldGetter(name
, proxy
, field
));
194 private static class StaticFieldSetterInstaller
extends FieldInstaller
{
195 StaticFieldSetterInstaller(){}
196 StaticFieldSetterInstaller(String name
, Field field
) {
197 super(name
,STATIC_FIELD
,field
);
199 void install(RubyClass proxy
) {
200 if (Modifier
.isPublic(field
.getModifiers())) {
201 proxy
.getSingletonClass().addMethod(name
, new StaticFieldSetter(name
, proxy
, field
));
206 private static class InstanceFieldGetterInstaller
extends FieldInstaller
{
207 InstanceFieldGetterInstaller(){}
208 InstanceFieldGetterInstaller(String name
, Field field
) {
209 super(name
,INSTANCE_FIELD
,field
);
211 void install(RubyClass proxy
) {
212 if (Modifier
.isPublic(field
.getModifiers())) {
213 proxy
.addMethod(name
, new InstanceFieldGetter(name
, proxy
, field
));
218 private static class InstanceFieldSetterInstaller
extends FieldInstaller
{
219 InstanceFieldSetterInstaller(){}
220 InstanceFieldSetterInstaller(String name
, Field field
) {
221 super(name
,INSTANCE_FIELD
,field
);
223 void install(RubyClass proxy
) {
224 if (Modifier
.isPublic(field
.getModifiers())) {
225 proxy
.addMethod(name
, new InstanceFieldSetter(name
, proxy
, field
));
230 private static abstract class MethodInstaller
extends NamedInstaller
{
231 private boolean haveLocalMethod
;
232 protected List
<Method
> methods
;
233 protected List
<String
> aliases
;
235 MethodInstaller(String name
, int type
) {
239 // called only by initializing thread; no synchronization required
240 void addMethod(Method method
, Class
<?
> javaClass
) {
241 if (methods
== null) {
242 methods
= new ArrayList
<Method
>();
244 if (!Ruby
.isSecurityRestricted()) {
245 method
.setAccessible(true);
248 haveLocalMethod
|= javaClass
== method
.getDeclaringClass();
251 // called only by initializing thread; no synchronization required
252 void addAlias(String alias
) {
253 if (aliases
== null) {
254 aliases
= new ArrayList
<String
>();
256 if (!aliases
.contains(alias
))
260 // modified only by addMethod; no synchronization required
261 boolean hasLocalMethod () {
262 return haveLocalMethod
;
266 private static class ConstructorInvokerInstaller
extends MethodInstaller
{
267 private boolean haveLocalConstructor
;
268 protected List
<Constructor
> constructors
;
270 ConstructorInvokerInstaller(String name
) {
271 super(name
,STATIC_METHOD
);
274 // called only by initializing thread; no synchronization required
275 void addConstructor(Constructor ctor
, Class
<?
> javaClass
) {
276 if (constructors
== null) {
277 constructors
= new ArrayList
<Constructor
>();
279 if (!Ruby
.isSecurityRestricted()) {
280 ctor
.setAccessible(true);
282 constructors
.add(ctor
);
283 haveLocalConstructor
|= javaClass
== ctor
.getDeclaringClass();
286 void install(RubyClass proxy
) {
287 if (haveLocalConstructor
) {
288 DynamicMethod method
= new ConstructorInvoker(proxy
, constructors
);
289 proxy
.addMethod(name
, method
);
294 private static class StaticMethodInvokerInstaller
extends MethodInstaller
{
295 StaticMethodInvokerInstaller(String name
) {
296 super(name
,STATIC_METHOD
);
299 void install(RubyClass proxy
) {
300 if (hasLocalMethod()) {
301 RubyClass singleton
= proxy
.getSingletonClass();
302 DynamicMethod method
= new StaticMethodInvoker(singleton
, methods
);
303 singleton
.addMethod(name
, method
);
304 if (aliases
!= null && isPublic() ) {
305 singleton
.defineAliases(aliases
, this.name
);
312 private static class InstanceMethodInvokerInstaller
extends MethodInstaller
{
313 InstanceMethodInvokerInstaller(String name
) {
314 super(name
,INSTANCE_METHOD
);
316 void install(RubyClass proxy
) {
317 if (hasLocalMethod()) {
318 DynamicMethod method
;
319 if (SafePropertyAccessor
.getBoolean("jruby.dynalang.enabled", false)) {
320 method
= new DynalangInstanceInvoker(proxy
, methods
);
322 method
= new InstanceMethodInvoker(proxy
, methods
);
324 proxy
.addMethod(name
, method
);
325 if (aliases
!= null && isPublic()) {
326 proxy
.defineAliases(aliases
, this.name
);
333 private static class ConstantField
{
334 static final int CONSTANT
= Modifier
.FINAL
| Modifier
.PUBLIC
| Modifier
.STATIC
;
336 ConstantField(Field field
) {
339 void install(final RubyModule proxy
) {
340 if (proxy
.fastGetConstantAt(field
.getName()) == null) {
341 // TODO: catch exception if constant is already set by other
343 if (!Ruby
.isSecurityRestricted()) {
344 field
.setAccessible(true);
347 proxy
.setConstant(field
.getName(), JavaUtil
.convertJavaToUsableRubyObject(proxy
.getRuntime(), field
.get(null)));
348 } catch (IllegalAccessException iae
) {
349 throw proxy
.getRuntime().newTypeError(
350 "illegal access on setting variable: " + iae
.getMessage());
354 static boolean isConstant(final Field field
) {
355 return (field
.getModifiers() & CONSTANT
) == CONSTANT
&&
356 Character
.isUpperCase(field
.getName().charAt(0));
360 private final RubyModule JAVA_UTILITIES
= getRuntime().getJavaSupport().getJavaUtilitiesModule();
362 private Map
<String
, AssignedName
> staticAssignedNames
;
363 private Map
<String
, AssignedName
> instanceAssignedNames
;
364 private Map
<String
, NamedInstaller
> staticInstallers
;
365 private Map
<String
, NamedInstaller
> instanceInstallers
;
366 private ConstructorInvokerInstaller constructorInstaller
;
367 private List
<ConstantField
> constantFields
;
368 // caching constructors, as they're accessed for each new instance
369 private volatile RubyArray constructors
;
371 private volatile ArrayList
<IRubyObject
> proxyExtenders
;
373 // proxy module for interfaces
374 private volatile RubyModule proxyModule
;
376 // proxy class for concrete classes. also used for
377 // "concrete" interfaces, which is why we have two fields
378 private volatile RubyClass proxyClass
;
380 // readable only by thread building proxy, so don't need to be
381 // volatile. used to handle recursive calls to getProxyClass/Module
382 // while proxy is being constructed (usually when a constant
383 // defined by a class is of the same type as that class).
384 private RubyModule unfinishedProxyModule
;
385 private RubyClass unfinishedProxyClass
;
387 private final ReentrantLock proxyLock
= new ReentrantLock();
389 public RubyModule
getProxyModule() {
390 // allow proxy to be read without synchronization. if proxy
391 // is under construction, only the building thread can see it.
393 if ((proxy
= proxyModule
) != null) {
394 // proxy is complete, return it
396 } else if (proxyLock
.isHeldByCurrentThread()) {
397 // proxy is under construction, building thread can
398 // safely read non-volatile value
399 return unfinishedProxyModule
;
404 public RubyClass
getProxyClass() {
405 // allow proxy to be read without synchronization. if proxy
406 // is under construction, only the building thread can see it.
408 if ((proxy
= proxyClass
) != null) {
409 // proxy is complete, return it
411 } else if (proxyLock
.isHeldByCurrentThread()) {
412 // proxy is under construction, building thread can
413 // safely read non-volatile value
414 return unfinishedProxyClass
;
419 public void lockProxy() {
423 public void unlockProxy() {
427 protected Map
<String
, AssignedName
> getStaticAssignedNames() {
428 return staticAssignedNames
;
430 protected Map
<String
, AssignedName
> getInstanceAssignedNames() {
431 return instanceAssignedNames
;
434 private JavaClass(Ruby runtime
, Class
<?
> javaClass
) {
435 super(runtime
, (RubyClass
) runtime
.getJavaSupport().getJavaClassClass(), javaClass
);
436 if (javaClass
.isInterface()) {
437 initializeInterface(javaClass
);
438 } else if (!(javaClass
.isArray() || javaClass
.isPrimitive())) {
439 // TODO: public only?
440 initializeClass(javaClass
);
444 public boolean equals(Object other
) {
445 return other
instanceof JavaClass
&&
446 this.getValue() == ((JavaClass
)other
).getValue();
449 private void initializeInterface(Class
<?
> javaClass
) {
450 Map
<String
, AssignedName
> staticNames
= new HashMap
<String
, AssignedName
>(STATIC_RESERVED_NAMES
);
451 List
<ConstantField
> constantFields
= new ArrayList
<ConstantField
>();
452 Field
[] fields
= EMPTY_FIELD_ARRAY
;
454 fields
= javaClass
.getDeclaredFields();
455 } catch (SecurityException e
) {
457 fields
= javaClass
.getFields();
458 } catch (SecurityException e2
) {
461 for (int i
= fields
.length
; --i
>= 0; ) {
462 Field field
= fields
[i
];
463 if (javaClass
!= field
.getDeclaringClass()) continue;
464 if (ConstantField
.isConstant(field
)) {
465 constantFields
.add(new ConstantField(field
));
468 this.staticAssignedNames
= staticNames
;
469 this.constantFields
= constantFields
;
472 private void initializeClass(Class
<?
> javaClass
) {
473 Class
<?
> superclass
= javaClass
.getSuperclass();
474 Map
<String
, AssignedName
> staticNames
;
475 Map
<String
, AssignedName
> instanceNames
;
476 if (superclass
== null) {
477 staticNames
= new HashMap
<String
, AssignedName
>();
478 instanceNames
= new HashMap
<String
, AssignedName
>();
480 JavaClass superJavaClass
= get(getRuntime(),superclass
);
481 staticNames
= new HashMap
<String
, AssignedName
>(superJavaClass
.getStaticAssignedNames());
482 instanceNames
= new HashMap
<String
, AssignedName
>(superJavaClass
.getInstanceAssignedNames());
484 staticNames
.putAll(STATIC_RESERVED_NAMES
);
485 instanceNames
.putAll(INSTANCE_RESERVED_NAMES
);
486 Map
<String
, NamedInstaller
> staticCallbacks
= new HashMap
<String
, NamedInstaller
>();
487 Map
<String
, NamedInstaller
> instanceCallbacks
= new HashMap
<String
, NamedInstaller
>();
488 List
<ConstantField
> constantFields
= new ArrayList
<ConstantField
>();
489 Field
[] fields
= EMPTY_FIELD_ARRAY
;
491 fields
= javaClass
.getFields();
492 } catch (SecurityException e
) {
494 for (int i
= fields
.length
; --i
>= 0; ) {
495 Field field
= fields
[i
];
496 if (javaClass
!= field
.getDeclaringClass()) continue;
498 if (ConstantField
.isConstant(field
)) {
499 constantFields
.add(new ConstantField(field
));
502 String name
= field
.getName();
503 int modifiers
= field
.getModifiers();
504 if (Modifier
.isStatic(modifiers
)) {
505 AssignedName assignedName
= staticNames
.get(name
);
506 if (assignedName
!= null && assignedName
.type
< AssignedName
.FIELD
)
508 staticNames
.put(name
,new AssignedName(name
,AssignedName
.FIELD
));
509 staticCallbacks
.put(name
,new StaticFieldGetterInstaller(name
,field
));
510 if (!Modifier
.isFinal(modifiers
)) {
511 String setName
= name
+ '=';
512 staticCallbacks
.put(setName
,new StaticFieldSetterInstaller(setName
,field
));
515 AssignedName assignedName
= instanceNames
.get(name
);
516 if (assignedName
!= null && assignedName
.type
< AssignedName
.FIELD
)
518 instanceNames
.put(name
, new AssignedName(name
,AssignedName
.FIELD
));
519 instanceCallbacks
.put(name
, new InstanceFieldGetterInstaller(name
,field
));
520 if (!Modifier
.isFinal(modifiers
)) {
521 String setName
= name
+ '=';
522 instanceCallbacks
.put(setName
, new InstanceFieldSetterInstaller(setName
,field
));
526 // TODO: protected methods. this is going to require a rework
527 // of some of the mechanism.
528 Method
[] methods
= EMPTY_METHOD_ARRAY
;
529 for (Class c
= javaClass
; c
!= null; c
= c
.getSuperclass()) {
531 methods
= javaClass
.getMethods();
533 } catch (SecurityException e
) {
536 for (int i
= methods
.length
; --i
>= 0; ) {
537 // we need to collect all methods, though we'll only
538 // install the ones that are named in this class
539 Method method
= methods
[i
];
540 String name
= method
.getName();
541 if (Modifier
.isStatic(method
.getModifiers())) {
542 AssignedName assignedName
= staticNames
.get(name
);
543 if (assignedName
== null) {
544 staticNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
546 if (assignedName
.type
< AssignedName
.METHOD
)
548 if (assignedName
.type
!= AssignedName
.METHOD
) {
549 staticCallbacks
.remove(name
);
550 staticCallbacks
.remove(name
+'=');
551 staticNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
554 StaticMethodInvokerInstaller invoker
= (StaticMethodInvokerInstaller
)staticCallbacks
.get(name
);
555 if (invoker
== null) {
556 invoker
= new StaticMethodInvokerInstaller(name
);
557 staticCallbacks
.put(name
,invoker
);
559 invoker
.addMethod(method
,javaClass
);
561 AssignedName assignedName
= instanceNames
.get(name
);
562 if (assignedName
== null) {
563 instanceNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
565 if (assignedName
.type
< AssignedName
.METHOD
)
567 if (assignedName
.type
!= AssignedName
.METHOD
) {
568 instanceCallbacks
.remove(name
);
569 instanceCallbacks
.remove(name
+'=');
570 instanceNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
573 InstanceMethodInvokerInstaller invoker
= (InstanceMethodInvokerInstaller
)instanceCallbacks
.get(name
);
574 if (invoker
== null) {
575 invoker
= new InstanceMethodInvokerInstaller(name
);
576 instanceCallbacks
.put(name
,invoker
);
578 invoker
.addMethod(method
,javaClass
);
581 // TODO: protected methods. this is going to require a rework
582 // of some of the mechanism.
583 Constructor
[] constructors
= EMPTY_CONSTRUCTOR_ARRAY
;
585 constructors
= javaClass
.getConstructors();
586 } catch (SecurityException e
) {
588 for (int i
= constructors
.length
; --i
>= 0; ) {
589 // we need to collect all methods, though we'll only
590 // install the ones that are named in this class
591 Constructor ctor
= constructors
[i
];
593 if (constructorInstaller
== null) {
594 constructorInstaller
= new ConstructorInvokerInstaller("__jcreate!");
596 constructorInstaller
.addConstructor(ctor
,javaClass
);
599 this.staticAssignedNames
= staticNames
;
600 this.instanceAssignedNames
= instanceNames
;
601 this.staticInstallers
= staticCallbacks
;
602 this.instanceInstallers
= instanceCallbacks
;
603 this.constantFields
= constantFields
;
606 public void setupProxy(final RubyClass proxy
) {
607 assert proxyLock
.isHeldByCurrentThread();
608 proxy
.defineFastMethod("__jsend!", __jsend_method
);
609 final Class
<?
> javaClass
= javaClass();
610 if (javaClass
.isInterface()) {
611 setupInterfaceProxy(proxy
);
614 assert this.proxyClass
== null;
615 this.unfinishedProxyClass
= proxy
;
616 if (javaClass
.isArray() || javaClass
.isPrimitive()) {
617 // see note below re: 2-field kludge
618 this.proxyClass
= proxy
;
619 this.proxyModule
= proxy
;
623 for (ConstantField field
: constantFields
) {
624 field
.install(proxy
);
626 for (Iterator
<NamedInstaller
> iter
= staticInstallers
.values().iterator(); iter
.hasNext(); ) {
627 NamedInstaller installer
= iter
.next();
628 if (installer
.type
== NamedInstaller
.STATIC_METHOD
&& installer
.hasLocalMethod()) {
629 assignAliases((MethodInstaller
)installer
,staticAssignedNames
);
631 installer
.install(proxy
);
633 for (Iterator
<NamedInstaller
> iter
= instanceInstallers
.values().iterator(); iter
.hasNext(); ) {
634 NamedInstaller installer
= iter
.next();
635 if (installer
.type
== NamedInstaller
.INSTANCE_METHOD
&& installer
.hasLocalMethod()) {
636 assignAliases((MethodInstaller
)installer
,instanceAssignedNames
);
638 installer
.install(proxy
);
641 if (constructorInstaller
!= null) {
642 constructorInstaller
.install(proxy
);
645 // setup constants for public inner classes
646 Class
<?
>[] classes
= EMPTY_CLASS_ARRAY
;
648 classes
= javaClass
.getClasses();
649 } catch (SecurityException e
) {
651 for (int i
= classes
.length
; --i
>= 0; ) {
652 if (javaClass
== classes
[i
].getDeclaringClass()) {
653 Class
<?
> clazz
= classes
[i
];
654 String simpleName
= getSimpleName(clazz
);
656 if (simpleName
.length() == 0) continue;
658 // Ignore bad constant named inner classes pending JRUBY-697
659 if (IdUtil
.isConstant(simpleName
) && proxy
.getConstantAt(simpleName
) == null) {
660 proxy
.setConstant(simpleName
,
661 Java
.get_proxy_class(JAVA_UTILITIES
,get(getRuntime(),clazz
)));
665 // FIXME: bit of a kludge here (non-interface classes assigned to both
666 // class and module fields). simplifies proxy extender code, will go away
667 // when JI is overhauled (and proxy extenders are deprecated).
668 this.proxyClass
= proxy
;
669 this.proxyModule
= proxy
;
671 applyProxyExtenders();
673 // TODO: we can probably release our references to the constantFields
674 // array and static/instance callback hashes at this point.
677 private static void assignAliases(MethodInstaller installer
, Map
<String
, AssignedName
> assignedNames
) {
678 String name
= installer
.name
;
679 String rubyCasedName
= JavaUtil
.getRubyCasedName(name
);
680 addUnassignedAlias(rubyCasedName
,assignedNames
,installer
);
682 String javaPropertyName
= JavaUtil
.getJavaPropertyName(name
);
683 String rubyPropertyName
= null;
685 for (Method method
: installer
.methods
) {
686 Class
<?
>[] argTypes
= method
.getParameterTypes();
687 Class
<?
> resultType
= method
.getReturnType();
688 int argCount
= argTypes
.length
;
690 // Add property name aliases
691 if (javaPropertyName
!= null) {
692 if (rubyCasedName
.startsWith("get_")) {
693 rubyPropertyName
= rubyCasedName
.substring(4);
694 if (argCount
== 0 || // getFoo => foo
695 argCount
== 1 && argTypes
[0] == int.class) { // getFoo(int) => foo(int)
697 addUnassignedAlias(javaPropertyName
,assignedNames
,installer
);
698 addUnassignedAlias(rubyPropertyName
,assignedNames
,installer
);
700 } else if (rubyCasedName
.startsWith("set_")) {
701 rubyPropertyName
= rubyCasedName
.substring(4);
702 if (argCount
== 1 && resultType
== void.class) { // setFoo(Foo) => foo=(Foo)
703 addUnassignedAlias(javaPropertyName
+'=',assignedNames
,installer
);
704 addUnassignedAlias(rubyPropertyName
+'=',assignedNames
,installer
);
706 } else if (rubyCasedName
.startsWith("is_")) {
707 rubyPropertyName
= rubyCasedName
.substring(3);
708 if (resultType
== boolean.class) { // isFoo() => foo, isFoo(*) => foo(*)
709 addUnassignedAlias(javaPropertyName
,assignedNames
,installer
);
710 addUnassignedAlias(rubyPropertyName
,assignedNames
,installer
);
715 // Additionally add ?-postfixed aliases to any boolean methods and properties.
716 if (resultType
== boolean.class) {
717 // is_something?, contains_thing?
718 addUnassignedAlias(rubyCasedName
+'?',assignedNames
,installer
);
719 if (rubyPropertyName
!= null) {
721 addUnassignedAlias(rubyPropertyName
+'?',assignedNames
,installer
);
727 private static void addUnassignedAlias(String name
, Map
<String
, AssignedName
> assignedNames
,
728 MethodInstaller installer
) {
730 AssignedName assignedName
= (AssignedName
)assignedNames
.get(name
);
731 if (assignedName
== null) {
732 installer
.addAlias(name
);
733 assignedNames
.put(name
,new AssignedName(name
,AssignedName
.ALIAS
));
734 } else if (assignedName
.type
== AssignedName
.ALIAS
) {
735 installer
.addAlias(name
);
736 } else if (assignedName
.type
> AssignedName
.ALIAS
) {
737 // TODO: there will be some additional logic in this branch
738 // dealing with conflicting protected fields.
739 installer
.addAlias(name
);
740 assignedNames
.put(name
,new AssignedName(name
,AssignedName
.ALIAS
));
745 // old (quasi-deprecated) interface class
746 private void setupInterfaceProxy(final RubyClass proxy
) {
747 assert javaClass().isInterface();
748 assert proxyLock
.isHeldByCurrentThread();
749 assert this.proxyClass
== null;
750 this.proxyClass
= proxy
;
751 // nothing else to here - the module version will be
752 // included in the class.
755 public void setupInterfaceModule(final RubyModule module
) {
756 assert javaClass().isInterface();
757 assert proxyLock
.isHeldByCurrentThread();
758 assert this.proxyModule
== null;
759 this.unfinishedProxyModule
= module
;
760 Class
<?
> javaClass
= javaClass();
761 for (ConstantField field
: constantFields
) {
762 field
.install(module
);
764 // setup constants for public inner classes
765 Class
<?
>[] classes
= EMPTY_CLASS_ARRAY
;
767 classes
= javaClass
.getClasses();
768 } catch (SecurityException e
) {
770 for (int i
= classes
.length
; --i
>= 0; ) {
771 if (javaClass
== classes
[i
].getDeclaringClass()) {
772 Class
<?
> clazz
= classes
[i
];
773 String simpleName
= getSimpleName(clazz
);
774 if (simpleName
.length() == 0) continue;
776 // Ignore bad constant named inner classes pending JRUBY-697
777 if (IdUtil
.isConstant(simpleName
) && module
.getConstantAt(simpleName
) == null) {
778 module
.const_set(getRuntime().newString(simpleName
),
779 Java
.get_proxy_class(JAVA_UTILITIES
,get(getRuntime(),clazz
)));
784 this.proxyModule
= module
;
785 applyProxyExtenders();
788 public void addProxyExtender(final IRubyObject extender
) {
791 if (!extender
.respondsTo("extend_proxy")) {
792 throw getRuntime().newTypeError("proxy extender must have an extend_proxy method");
794 if (proxyModule
== null) {
795 if (proxyExtenders
== null) {
796 proxyExtenders
= new ArrayList
<IRubyObject
>();
798 proxyExtenders
.add(extender
);
800 getRuntime().getWarnings().warn(ID
.PROXY_EXTENDED_LATE
, " proxy extender added after proxy class created for " + this);
801 extendProxy(extender
);
808 private void applyProxyExtenders() {
809 ArrayList
<IRubyObject
> extenders
;
810 if ((extenders
= proxyExtenders
) != null) {
811 for (IRubyObject extender
: extenders
) {
812 extendProxy(extender
);
814 proxyExtenders
= null;
818 private void extendProxy(IRubyObject extender
) {
819 extender
.callMethod(getRuntime().getCurrentContext(), "extend_proxy", proxyModule
);
822 @JRubyMethod(required
= 1)
823 public IRubyObject
extend_proxy(IRubyObject extender
) {
824 addProxyExtender(extender
);
825 return getRuntime().getNil();
828 public static JavaClass
get(Ruby runtime
, Class
<?
> klass
) {
829 JavaClass javaClass
= runtime
.getJavaSupport().getJavaClassFromCache(klass
);
830 if (javaClass
== null) {
831 javaClass
= createJavaClass(runtime
,klass
);
836 public static RubyArray
getRubyArray(Ruby runtime
, Class
<?
>[] classes
) {
837 IRubyObject
[] javaClasses
= new IRubyObject
[classes
.length
];
838 for (int i
= classes
.length
; --i
>= 0; ) {
839 javaClasses
[i
] = get(runtime
, classes
[i
]);
841 return runtime
.newArrayNoCopy(javaClasses
);
844 private static synchronized JavaClass
createJavaClass(Ruby runtime
, Class
<?
> klass
) {
845 // double-check the cache now that we're synchronized
846 JavaClass javaClass
= runtime
.getJavaSupport().getJavaClassFromCache(klass
);
847 if (javaClass
== null) {
848 javaClass
= new JavaClass(runtime
, klass
);
849 runtime
.getJavaSupport().putJavaClassIntoCache(javaClass
);
854 public static RubyClass
createJavaClassClass(Ruby runtime
, RubyModule javaModule
) {
855 // FIXME: Determine if a real allocator is needed here. Do people want to extend
856 // JavaClass? Do we want them to do that? Can you Class.new(JavaClass)? Should
858 // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here, since we don't intend for people to monkey with
859 // this type and it can't be marshalled. Confirm. JRUBY-415
860 RubyClass result
= javaModule
.defineClassUnder("JavaClass", javaModule
.fastGetClass("JavaObject"), ObjectAllocator
.NOT_ALLOCATABLE_ALLOCATOR
);
862 result
.includeModule(runtime
.fastGetModule("Comparable"));
864 result
.defineAnnotatedMethods(JavaClass
.class);
866 result
.getMetaClass().undefineMethod("new");
867 result
.getMetaClass().undefineMethod("allocate");
872 public static synchronized JavaClass
forNameVerbose(Ruby runtime
, String className
) {
873 Class
<?
> klass
= runtime
.getJavaSupport().loadJavaClassVerbose(className
);
874 return JavaClass
.get(runtime
, klass
);
877 public static synchronized JavaClass
forNameQuiet(Ruby runtime
, String className
) {
878 Class klass
= runtime
.getJavaSupport().loadJavaClassQuiet(className
);
879 return JavaClass
.get(runtime
, klass
);
882 @JRubyMethod(name
= "for_name", required
= 1, meta
= true)
883 public static JavaClass
for_name(IRubyObject recv
, IRubyObject name
) {
884 return forNameVerbose(recv
.getRuntime(), name
.asJavaString());
887 private static final Callback __jsend_method
= new Callback() {
888 public IRubyObject
execute(IRubyObject self
, IRubyObject
[] args
, Block block
) {
889 String name
= args
[0].asJavaString();
891 DynamicMethod method
= self
.getMetaClass().searchMethod(name
);
892 int v
= method
.getArity().getValue();
894 IRubyObject
[] newArgs
= new IRubyObject
[args
.length
- 1];
895 System
.arraycopy(args
, 1, newArgs
, 0, newArgs
.length
);
897 if(v
< 0 || v
== (newArgs
.length
)) {
898 return RuntimeHelpers
.invoke(self
.getRuntime().getCurrentContext(), self
, name
, newArgs
, block
);
900 RubyClass superClass
= self
.getMetaClass().getSuperClass();
901 return RuntimeHelpers
.invokeAs(self
.getRuntime().getCurrentContext(), superClass
, self
, name
, newArgs
, CallType
.SUPER
, block
);
905 public Arity
getArity() {
906 return Arity
.optional();
911 public RubyModule
ruby_class() {
912 // Java.getProxyClass deals with sync issues, so we won't duplicate the logic here
913 return Java
.getProxyClass(getRuntime(), this);
916 @JRubyMethod(name
= "public?")
917 public RubyBoolean
public_p() {
918 return getRuntime().newBoolean(Modifier
.isPublic(javaClass().getModifiers()));
921 @JRubyMethod(name
= "protected?")
922 public RubyBoolean
protected_p() {
923 return getRuntime().newBoolean(Modifier
.isProtected(javaClass().getModifiers()));
926 @JRubyMethod(name
= "private?")
927 public RubyBoolean
private_p() {
928 return getRuntime().newBoolean(Modifier
.isPrivate(javaClass().getModifiers()));
931 public Class
javaClass() {
932 return (Class
) getValue();
935 @JRubyMethod(name
= "final?")
936 public RubyBoolean
final_p() {
937 return getRuntime().newBoolean(Modifier
.isFinal(javaClass().getModifiers()));
940 @JRubyMethod(name
= "interface?")
941 public RubyBoolean
interface_p() {
942 return getRuntime().newBoolean(javaClass().isInterface());
945 @JRubyMethod(name
= "array?")
946 public RubyBoolean
array_p() {
947 return getRuntime().newBoolean(javaClass().isArray());
950 @JRubyMethod(name
= "enum?")
951 public RubyBoolean
enum_p() {
952 return getRuntime().newBoolean(javaClass().isEnum());
955 @JRubyMethod(name
= "annotation?")
956 public RubyBoolean
annotation_p() {
957 return getRuntime().newBoolean(javaClass().isAnnotation());
960 @JRubyMethod(name
= "anonymous_class?")
961 public RubyBoolean
anonymous_class_p() {
962 return getRuntime().newBoolean(javaClass().isAnonymousClass());
965 @JRubyMethod(name
= "local_class?")
966 public RubyBoolean
local_class_p() {
967 return getRuntime().newBoolean(javaClass().isLocalClass());
970 @JRubyMethod(name
= "member_class?")
971 public RubyBoolean
member_class_p() {
972 return getRuntime().newBoolean(javaClass().isMemberClass());
975 @JRubyMethod(name
= "synthetic?")
976 public IRubyObject
synthetic_p() {
977 return getRuntime().newBoolean(javaClass().isSynthetic());
980 @JRubyMethod(name
= {"name", "to_s"})
981 public RubyString
name() {
982 return getRuntime().newString(javaClass().getName());
986 public IRubyObject
canonical_name() {
987 String canonicalName
= javaClass().getCanonicalName();
988 if (canonicalName
!= null) {
989 return getRuntime().newString(canonicalName
);
991 return getRuntime().getNil();
994 @JRubyMethod(name
= "package")
995 public IRubyObject
get_package() {
996 return Java
.getInstance(getRuntime(), javaClass().getPackage());
1000 public IRubyObject
class_loader() {
1001 return Java
.getInstance(getRuntime(), javaClass().getClassLoader());
1005 public IRubyObject
protection_domain() {
1006 return Java
.getInstance(getRuntime(), javaClass().getProtectionDomain());
1009 @JRubyMethod(required
= 1)
1010 public IRubyObject
resource(IRubyObject name
) {
1011 return Java
.getInstance(getRuntime(), javaClass().getResource(name
.asJavaString()));
1014 @JRubyMethod(required
= 1)
1015 public IRubyObject
resource_as_stream(IRubyObject name
) {
1016 return Java
.getInstance(getRuntime(), javaClass().getResourceAsStream(name
.asJavaString()));
1019 @JRubyMethod(required
= 1)
1020 public IRubyObject
resource_as_string(IRubyObject name
) {
1021 InputStream in
= javaClass().getResourceAsStream(name
.asJavaString());
1022 if (in
== null) return getRuntime().getNil();
1023 ByteArrayOutputStream out
= new ByteArrayOutputStream();
1026 byte[] buf
= new byte[4096];
1027 while ((len
= in
.read(buf
)) >= 0) {
1028 out
.write(buf
, 0, len
);
1030 } catch (IOException e
) {
1031 throw getRuntime().newIOErrorFromException(e
);
1033 return getRuntime().newString(new ByteList(out
.toByteArray(), false));
1036 @SuppressWarnings("unchecked")
1037 @JRubyMethod(required
= 1)
1038 public IRubyObject
annotation(IRubyObject annoClass
) {
1039 if (!(annoClass
instanceof JavaClass
)) {
1040 throw getRuntime().newTypeError(annoClass
, getRuntime().getJavaSupport().getJavaClassClass());
1042 return Java
.getInstance(getRuntime(), javaClass().getAnnotation(((JavaClass
)annoClass
).javaClass()));
1046 public IRubyObject
annotations() {
1047 // note: intentionally returning the actual array returned from Java, rather
1048 // than wrapping it in a RubyArray. wave of the future, when java_class will
1049 // return the actual class, rather than a JavaClass wrapper.
1050 return Java
.getInstance(getRuntime(), javaClass().getAnnotations());
1053 @JRubyMethod(name
= "annotations?")
1054 public RubyBoolean
annotations_p() {
1055 return getRuntime().newBoolean(javaClass().getAnnotations().length
> 0);
1059 public IRubyObject
declared_annotations() {
1060 // see note above re: return type
1061 return Java
.getInstance(getRuntime(), javaClass().getDeclaredAnnotations());
1064 @JRubyMethod(name
= "declared_annotations?")
1065 public RubyBoolean
declared_annotations_p() {
1066 return getRuntime().newBoolean(javaClass().getDeclaredAnnotations().length
> 0);
1069 @SuppressWarnings("unchecked")
1070 @JRubyMethod(name
= "annotation_present?", required
= 1)
1071 public IRubyObject
annotation_present_p(IRubyObject annoClass
) {
1072 if (!(annoClass
instanceof JavaClass
)) {
1073 throw getRuntime().newTypeError(annoClass
, getRuntime().getJavaSupport().getJavaClassClass());
1075 return getRuntime().newBoolean(javaClass().isAnnotationPresent(((JavaClass
)annoClass
).javaClass()));
1079 public IRubyObject
modifiers() {
1080 return getRuntime().newFixnum(javaClass().getModifiers());
1084 public IRubyObject
declaring_class() {
1085 Class
<?
> clazz
= javaClass().getDeclaringClass();
1086 if (clazz
!= null) {
1087 return JavaClass
.get(getRuntime(), clazz
);
1089 return getRuntime().getNil();
1093 public IRubyObject
enclosing_class() {
1094 return Java
.getInstance(getRuntime(), javaClass().getEnclosingClass());
1098 public IRubyObject
enclosing_constructor() {
1099 Constructor
<?
> ctor
= javaClass().getEnclosingConstructor();
1101 return new JavaConstructor(getRuntime(), ctor
);
1103 return getRuntime().getNil();
1107 public IRubyObject
enclosing_method() {
1108 Method meth
= javaClass().getEnclosingMethod();
1110 return new JavaMethod(getRuntime(), meth
);
1112 return getRuntime().getNil();
1116 public IRubyObject
enum_constants() {
1117 return Java
.getInstance(getRuntime(), javaClass().getEnumConstants());
1121 public IRubyObject
generic_interfaces() {
1122 return Java
.getInstance(getRuntime(), javaClass().getGenericInterfaces());
1126 public IRubyObject
generic_superclass() {
1127 return Java
.getInstance(getRuntime(), javaClass().getGenericSuperclass());
1131 public IRubyObject
type_parameters() {
1132 return Java
.getInstance(getRuntime(), javaClass().getTypeParameters());
1136 public IRubyObject
signers() {
1137 return Java
.getInstance(getRuntime(), javaClass().getSigners());
1140 private static String
getSimpleName(Class
<?
> clazz
) {
1141 if (clazz
.isArray()) {
1142 return getSimpleName(clazz
.getComponentType()) + "[]";
1145 String className
= clazz
.getName();
1146 int len
= className
.length();
1147 int i
= className
.lastIndexOf('$');
1151 } while (i
< len
&& Character
.isDigit(className
.charAt(i
)));
1152 return className
.substring(i
);
1155 return className
.substring(className
.lastIndexOf('.') + 1);
1159 public RubyString
simple_name() {
1160 return getRuntime().newString(getSimpleName(javaClass()));
1164 public IRubyObject
superclass() {
1165 Class
<?
> superclass
= javaClass().getSuperclass();
1166 if (superclass
== null) {
1167 return getRuntime().getNil();
1169 return JavaClass
.get(getRuntime(), superclass
);
1172 @JRubyMethod(name
= "<=>", required
= 1)
1173 public RubyFixnum
op_cmp(IRubyObject other
) {
1174 if (! (other
instanceof JavaClass
)) {
1175 throw getRuntime().newTypeError("<=> requires JavaClass (" + other
.getType() + " given)");
1177 JavaClass otherClass
= (JavaClass
) other
;
1178 if (this.javaClass() == otherClass
.javaClass()) {
1179 return getRuntime().newFixnum(0);
1181 if (otherClass
.javaClass().isAssignableFrom(this.javaClass())) {
1182 return getRuntime().newFixnum(-1);
1184 return getRuntime().newFixnum(1);
1188 public RubyArray
java_instance_methods() {
1189 return java_methods(javaClass().getMethods(), false);
1193 public RubyArray
declared_instance_methods() {
1194 return java_methods(javaClass().getDeclaredMethods(), false);
1197 private RubyArray
java_methods(Method
[] methods
, boolean isStatic
) {
1198 RubyArray result
= getRuntime().newArray(methods
.length
);
1199 for (int i
= 0; i
< methods
.length
; i
++) {
1200 Method method
= methods
[i
];
1201 if (isStatic
== Modifier
.isStatic(method
.getModifiers())) {
1202 result
.append(JavaMethod
.create(getRuntime(), method
));
1209 public RubyArray
java_class_methods() {
1210 return java_methods(javaClass().getMethods(), true);
1214 public RubyArray
declared_class_methods() {
1215 return java_methods(javaClass().getDeclaredMethods(), true);
1218 @JRubyMethod(required
= 1, rest
= true)
1219 public JavaMethod
java_method(IRubyObject
[] args
) throws ClassNotFoundException
{
1220 String methodName
= args
[0].asJavaString();
1221 Class
<?
>[] argumentTypes
= buildArgumentTypes(args
);
1222 return JavaMethod
.create(getRuntime(), javaClass(), methodName
, argumentTypes
);
1225 @JRubyMethod(required
= 1, rest
= true)
1226 public JavaMethod
declared_method(IRubyObject
[] args
) throws ClassNotFoundException
{
1227 String methodName
= args
[0].asJavaString();
1228 Class
<?
>[] argumentTypes
= buildArgumentTypes(args
);
1229 return JavaMethod
.createDeclared(getRuntime(), javaClass(), methodName
, argumentTypes
);
1232 @JRubyMethod(required
= 1, rest
= true)
1233 public JavaCallable
declared_method_smart(IRubyObject
[] args
) throws ClassNotFoundException
{
1234 String methodName
= args
[0].asJavaString();
1235 Class
<?
>[] argumentTypes
= buildArgumentTypes(args
);
1237 JavaCallable callable
= getMatchingCallable(getRuntime(), javaClass(), methodName
, argumentTypes
);
1239 if (callable
!= null) return callable
;
1241 throw getRuntime().newNameError("undefined method '" + methodName
+ "' for class '" + javaClass().getName() + "'",
1245 public static JavaCallable
getMatchingCallable(Ruby runtime
, Class
<?
> javaClass
, String methodName
, Class
<?
>[] argumentTypes
) {
1246 if ("<init>".equals(methodName
)) {
1247 return JavaConstructor
.getMatchingConstructor(runtime
, javaClass
, argumentTypes
);
1249 // FIXME: do we really want 'declared' methods? includes private/protected, and does _not_
1250 // include superclass methods
1251 return JavaMethod
.getMatchingDeclaredMethod(runtime
, javaClass
, methodName
, argumentTypes
);
1255 private Class
<?
>[] buildArgumentTypes(IRubyObject
[] args
) throws ClassNotFoundException
{
1256 if (args
.length
< 1) {
1257 throw getRuntime().newArgumentError(args
.length
, 1);
1259 Class
<?
>[] argumentTypes
= new Class
[args
.length
- 1];
1260 for (int i
= 1; i
< args
.length
; i
++) {
1262 if (args
[i
] instanceof JavaClass
) {
1263 type
= (JavaClass
)args
[i
];
1264 } else if (args
[i
].respondsTo("java_class")) {
1265 type
= (JavaClass
)args
[i
].callMethod(getRuntime().getCurrentContext(), "java_class");
1267 type
= for_name(this, args
[i
]);
1269 argumentTypes
[i
- 1] = type
.javaClass();
1271 return argumentTypes
;
1275 public RubyArray
constructors() {
1277 if ((ctors
= constructors
) != null) return ctors
;
1278 return constructors
= buildConstructors(javaClass().getConstructors());
1282 public RubyArray
classes() {
1283 return JavaClass
.getRubyArray(getRuntime(), javaClass().getClasses());
1287 public RubyArray
declared_classes() {
1288 Ruby runtime
= getRuntime();
1289 RubyArray result
= runtime
.newArray();
1290 Class
<?
> javaClass
= javaClass();
1292 Class
<?
>[] classes
= javaClass
.getDeclaredClasses();
1293 for (int i
= 0; i
< classes
.length
; i
++) {
1294 if (Modifier
.isPublic(classes
[i
].getModifiers())) {
1295 result
.append(get(runtime
, classes
[i
]));
1298 } catch (SecurityException e
) {
1299 // restrictive security policy; no matter, we only want public
1302 Class
<?
>[] classes
= javaClass
.getClasses();
1303 for (int i
= 0; i
< classes
.length
; i
++) {
1304 if (javaClass
== classes
[i
].getDeclaringClass()) {
1305 result
.append(get(runtime
, classes
[i
]));
1308 } catch (SecurityException e2
) {
1309 // very restrictive policy (disallows Member.PUBLIC)
1310 // we'd never actually get this far in that case
1317 public RubyArray
declared_constructors() {
1318 return buildConstructors(javaClass().getDeclaredConstructors());
1321 private RubyArray
buildConstructors(Constructor
<?
>[] constructors
) {
1322 RubyArray result
= getRuntime().newArray(constructors
.length
);
1323 for (int i
= 0; i
< constructors
.length
; i
++) {
1324 result
.append(new JavaConstructor(getRuntime(), constructors
[i
]));
1329 @JRubyMethod(rest
= true)
1330 public JavaConstructor
constructor(IRubyObject
[] args
) {
1332 Class
<?
>[] parameterTypes
= buildClassArgs(args
);
1333 Constructor
<?
> constructor
= javaClass().getConstructor(parameterTypes
);
1334 return new JavaConstructor(getRuntime(), constructor
);
1335 } catch (NoSuchMethodException nsme
) {
1336 throw getRuntime().newNameError("no matching java constructor", null);
1340 @JRubyMethod(rest
= true)
1341 public JavaConstructor
declared_constructor(IRubyObject
[] args
) {
1343 Class
<?
>[] parameterTypes
= buildClassArgs(args
);
1344 Constructor
<?
> constructor
= javaClass().getDeclaredConstructor (parameterTypes
);
1345 return new JavaConstructor(getRuntime(), constructor
);
1346 } catch (NoSuchMethodException nsme
) {
1347 throw getRuntime().newNameError("no matching java constructor", null);
1351 private Class
<?
>[] buildClassArgs(IRubyObject
[] args
) {
1352 JavaSupport javaSupport
= getRuntime().getJavaSupport();
1353 Class
<?
>[] parameterTypes
= new Class
<?
>[args
.length
];
1354 for (int i
= args
.length
; --i
>= 0; ) {
1355 String name
= args
[i
].asJavaString();
1356 parameterTypes
[i
] = javaSupport
.loadJavaClassVerbose(name
);
1358 return parameterTypes
;
1362 public JavaClass
array_class() {
1363 return JavaClass
.get(getRuntime(), Array
.newInstance(javaClass(), 0).getClass());
1366 @JRubyMethod(required
= 1)
1367 public JavaObject
new_array(IRubyObject lengthArgument
) {
1368 if (lengthArgument
instanceof RubyInteger
) {
1369 // one-dimensional array
1370 int length
= (int) ((RubyInteger
) lengthArgument
).getLongValue();
1371 return new JavaArray(getRuntime(), Array
.newInstance(javaClass(), length
));
1372 } else if (lengthArgument
instanceof RubyArray
) {
1373 // n-dimensional array
1374 List list
= ((RubyArray
)lengthArgument
).getList();
1375 int length
= list
.size();
1377 throw getRuntime().newArgumentError("empty dimensions specifier for java array");
1379 int[] dimensions
= new int[length
];
1380 for (int i
= length
; --i
>= 0; ) {
1381 IRubyObject dimensionLength
= (IRubyObject
)list
.get(i
);
1382 if ( !(dimensionLength
instanceof RubyInteger
) ) {
1384 .newTypeError(dimensionLength
, getRuntime().getInteger());
1386 dimensions
[i
] = (int) ((RubyInteger
) dimensionLength
).getLongValue();
1388 return new JavaArray(getRuntime(), Array
.newInstance(javaClass(), dimensions
));
1390 throw getRuntime().newArgumentError(
1391 "invalid length or dimensions specifier for java array" +
1392 " - must be Integer or Array of Integer");
1396 public IRubyObject
emptyJavaArray(ThreadContext context
) {
1397 JavaArray javaArray
= new JavaArray(getRuntime(), Array
.newInstance(javaClass(), 0));
1398 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1400 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1401 proxy
.dataWrapStruct(javaArray
);
1406 public IRubyObject
javaArraySubarray(ThreadContext context
, JavaArray fromArray
, int index
, int size
) {
1407 int actualLength
= Array
.getLength(fromArray
.getValue());
1408 if (index
>= actualLength
) {
1409 return context
.getRuntime().getNil();
1411 if (index
+ size
> actualLength
) {
1412 size
= actualLength
- index
;
1415 Object newArray
= Array
.newInstance(javaClass(), size
);
1416 JavaArray javaArray
= new JavaArray(getRuntime(), newArray
);
1417 System
.arraycopy(fromArray
.getValue(), index
, newArray
, 0, size
);
1418 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1420 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1421 proxy
.dataWrapStruct(javaArray
);
1428 * Contatenate two Java arrays into a new one. The component type of the
1429 * additional array must be assignable to the component type of the
1437 public IRubyObject
concatArrays(ThreadContext context
, JavaArray original
, JavaArray additional
) {
1438 int oldLength
= (int)original
.length().getLongValue();
1439 int addLength
= (int)additional
.length().getLongValue();
1440 Object newArray
= Array
.newInstance(javaClass(), oldLength
+ addLength
);
1441 JavaArray javaArray
= new JavaArray(getRuntime(), newArray
);
1442 System
.arraycopy(original
.getValue(), 0, newArray
, 0, oldLength
);
1443 System
.arraycopy(additional
.getValue(), 0, newArray
, oldLength
, addLength
);
1444 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1446 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1447 proxy
.dataWrapStruct(javaArray
);
1453 * The slow version for when concatenating a Java array of a different type.
1460 public IRubyObject
concatArrays(ThreadContext context
, JavaArray original
, IRubyObject additional
) {
1461 int oldLength
= (int)original
.length().getLongValue();
1462 int addLength
= (int)((RubyFixnum
)RuntimeHelpers
.invoke(context
, additional
, "length")).getLongValue();
1463 Object newArray
= Array
.newInstance(javaClass(), oldLength
+ addLength
);
1464 JavaArray javaArray
= new JavaArray(getRuntime(), newArray
);
1465 System
.arraycopy(original
.getValue(), 0, newArray
, 0, oldLength
);
1466 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1467 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1468 proxy
.dataWrapStruct(javaArray
);
1470 Ruby runtime
= context
.getRuntime();
1471 for (int i
= 0; i
< addLength
; i
++) {
1472 RuntimeHelpers
.invoke(context
, proxy
, "[]=", runtime
.newFixnum(oldLength
+ i
),
1473 RuntimeHelpers
.invoke(context
, additional
, "[]", runtime
.newFixnum(i
)));
1479 public IRubyObject
javaArrayFromRubyArray(ThreadContext context
, IRubyObject fromArray
) {
1480 Ruby runtime
= context
.getRuntime();
1481 if (!(fromArray
instanceof RubyArray
)) {
1482 throw runtime
.newTypeError(fromArray
, runtime
.getArray());
1484 RubyArray rubyArray
= (RubyArray
)fromArray
;
1485 JavaArray javaArray
= new JavaArray(getRuntime(), Array
.newInstance(javaClass(), rubyArray
.size()));
1486 ArrayJavaAddons
.copyDataToJavaArray(context
, rubyArray
, javaArray
);
1487 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1489 ArrayJavaProxy proxy
= new ArrayJavaProxy(runtime
, proxyClass
);
1490 proxy
.dataWrapStruct(javaArray
);
1496 public RubyArray
fields() {
1497 return buildFieldResults(javaClass().getFields());
1501 public RubyArray
declared_fields() {
1502 return buildFieldResults(javaClass().getDeclaredFields());
1505 private RubyArray
buildFieldResults(Field
[] fields
) {
1506 RubyArray result
= getRuntime().newArray(fields
.length
);
1507 for (int i
= 0; i
< fields
.length
; i
++) {
1508 result
.append(new JavaField(getRuntime(), fields
[i
]));
1513 @JRubyMethod(required
= 1)
1514 public JavaField
field(IRubyObject name
) {
1515 String stringName
= name
.asJavaString();
1518 field
= javaClass().getField(stringName
);
1519 return new JavaField(getRuntime(), field
);
1520 } catch (NoSuchFieldException nsfe
) {
1521 String newName
= JavaUtil
.getJavaCasedName(stringName
);
1522 if(newName
!= null) {
1524 field
= javaClass().getField(newName
);
1525 return new JavaField(getRuntime(), field
);
1526 } catch (NoSuchFieldException nsfe2
) {}
1528 throw undefinedFieldError(stringName
);
1532 @JRubyMethod(required
= 1)
1533 public JavaField
declared_field(IRubyObject name
) {
1534 String stringName
= name
.asJavaString();
1537 field
= javaClass().getDeclaredField(stringName
);
1538 return new JavaField(getRuntime(), field
);
1539 } catch (NoSuchFieldException nsfe
) {
1540 String newName
= JavaUtil
.getJavaCasedName(stringName
);
1541 if(newName
!= null) {
1543 field
= javaClass().getDeclaredField(newName
);
1544 return new JavaField(getRuntime(), field
);
1545 } catch (NoSuchFieldException nsfe2
) {}
1547 throw undefinedFieldError(stringName
);
1551 private RaiseException
undefinedFieldError(String name
) {
1552 return getRuntime().newNameError("undefined field '" + name
+ "' for class '" + javaClass().getName() + "'", name
);
1556 public RubyArray
interfaces() {
1557 return JavaClass
.getRubyArray(getRuntime(), javaClass().getInterfaces());
1560 @JRubyMethod(name
= "primitive?")
1561 public RubyBoolean
primitive_p() {
1562 return getRuntime().newBoolean(isPrimitive());
1565 @JRubyMethod(name
= "assignable_from?", required
= 1)
1566 public RubyBoolean
assignable_from_p(IRubyObject other
) {
1567 if (! (other
instanceof JavaClass
)) {
1568 throw getRuntime().newTypeError("assignable_from requires JavaClass (" + other
.getType() + " given)");
1571 Class
<?
> otherClass
= ((JavaClass
) other
).javaClass();
1572 return assignable(javaClass(), otherClass
) ?
getRuntime().getTrue() : getRuntime().getFalse();
1575 static boolean assignable(Class
<?
> thisClass
, Class
<?
> otherClass
) {
1576 if(!thisClass
.isPrimitive() && otherClass
== Void
.TYPE
||
1577 thisClass
.isAssignableFrom(otherClass
)) {
1581 otherClass
= JavaUtil
.primitiveToWrapper(otherClass
);
1582 thisClass
= JavaUtil
.primitiveToWrapper(thisClass
);
1584 if(thisClass
.isAssignableFrom(otherClass
)) {
1587 if(Number
.class.isAssignableFrom(thisClass
)) {
1588 if(Number
.class.isAssignableFrom(otherClass
)) {
1591 if(otherClass
.equals(Character
.class)) {
1595 if(thisClass
.equals(Character
.class)) {
1596 if(Number
.class.isAssignableFrom(otherClass
)) {
1603 private boolean isPrimitive() {
1604 return javaClass().isPrimitive();
1608 public JavaClass
component_type() {
1609 if (! javaClass().isArray()) {
1610 throw getRuntime().newTypeError("not a java array-class");
1612 return JavaClass
.get(getRuntime(), javaClass().getComponentType());