001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Norris Boyd
026: * Igor Bukanov
027: * Frank Mitchell
028: * Mike Shaver
029: * Kemal Bayram
030: *
031: * Alternatively, the contents of this file may be used under the terms of
032: * the GNU General Public License Version 2 or later (the "GPL"), in which
033: * case the provisions of the GPL are applicable instead of those above. If
034: * you wish to allow use of your version of this file only under the terms of
035: * the GPL and not to allow others to use your version of this file under the
036: * MPL, indicate your decision by deleting the provisions above and replacing
037: * them with the notice and other provisions required by the GPL. If you do
038: * not delete the provisions above, a recipient may use your version of this
039: * file under either the MPL or the GPL.
040: *
041: * ***** END LICENSE BLOCK ***** */
042:
043: package org.mozilla.javascript;
044:
045: import java.io.*;
046: import java.lang.reflect.*;
047: import java.util.Hashtable;
048: import java.util.Date;
049:
050: /**
051: * This class reflects non-Array Java objects into the JavaScript environment. It
052: * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly
053: * overloaded) methods.<p>
054: *
055: * @author Mike Shaver
056: * @see NativeJavaArray
057: * @see NativeJavaPackage
058: * @see NativeJavaClass
059: */
060:
061: public class NativeJavaObject implements Scriptable, Wrapper,
062: Serializable {
063: static final long serialVersionUID = -6948590651130498591L;
064:
065: public NativeJavaObject() {
066: }
067:
068: public NativeJavaObject(Scriptable scope, Object javaObject,
069: Class staticType) {
070: this (scope, javaObject, staticType, false);
071: }
072:
073: public NativeJavaObject(Scriptable scope, Object javaObject,
074: Class staticType, boolean isAdapter) {
075: this .parent = scope;
076: this .javaObject = javaObject;
077: this .staticType = staticType;
078: this .isAdapter = isAdapter;
079: initMembers();
080: }
081:
082: protected void initMembers() {
083: Class dynamicType;
084: if (javaObject != null) {
085: dynamicType = javaObject.getClass();
086: } else {
087: dynamicType = staticType;
088: }
089: members = JavaMembers.lookupClass(parent, dynamicType,
090: staticType, isAdapter);
091: fieldAndMethods = members.getFieldAndMethodsObjects(this ,
092: javaObject, false);
093: }
094:
095: public boolean has(String name, Scriptable start) {
096: return members.has(name, false);
097: }
098:
099: public boolean has(int index, Scriptable start) {
100: return false;
101: }
102:
103: public Object get(String name, Scriptable start) {
104: if (fieldAndMethods != null) {
105: Object result = fieldAndMethods.get(name);
106: if (result != null) {
107: return result;
108: }
109: }
110: // TODO: passing 'this' as the scope is bogus since it has
111: // no parent scope
112: return members.get(this , name, javaObject, false);
113: }
114:
115: public Object get(int index, Scriptable start) {
116: throw members.reportMemberNotFound(Integer.toString(index));
117: }
118:
119: public void put(String name, Scriptable start, Object value) {
120: // We could be asked to modify the value of a property in the
121: // prototype. Since we can't add a property to a Java object,
122: // we modify it in the prototype rather than copy it down.
123: if (prototype == null || members.has(name, false))
124: members.put(this , name, javaObject, value, false);
125: else
126: prototype.put(name, prototype, value);
127: }
128:
129: public void put(int index, Scriptable start, Object value) {
130: throw members.reportMemberNotFound(Integer.toString(index));
131: }
132:
133: public boolean hasInstance(Scriptable value) {
134: // This is an instance of a Java class, so always return false
135: return false;
136: }
137:
138: public void delete(String name) {
139: }
140:
141: public void delete(int index) {
142: }
143:
144: public Scriptable getPrototype() {
145: if (prototype == null && javaObject instanceof String) {
146: return ScriptableObject.getClassPrototype(parent, "String");
147: }
148: return prototype;
149: }
150:
151: /**
152: * Sets the prototype of the object.
153: */
154: public void setPrototype(Scriptable m) {
155: prototype = m;
156: }
157:
158: /**
159: * Returns the parent (enclosing) scope of the object.
160: */
161: public Scriptable getParentScope() {
162: return parent;
163: }
164:
165: /**
166: * Sets the parent (enclosing) scope of the object.
167: */
168: public void setParentScope(Scriptable m) {
169: parent = m;
170: }
171:
172: public Object[] getIds() {
173: return members.getIds(false);
174: }
175:
176: /**
177: @deprecated Use {@link Context#getWrapFactory()} together with calling {@link
178: WrapFactory#wrap(Context, Scriptable, Object, Class)}
179: */
180: public static Object wrap(Scriptable scope, Object obj,
181: Class staticType) {
182:
183: Context cx = Context.getContext();
184: return cx.getWrapFactory().wrap(cx, scope, obj, staticType);
185: }
186:
187: public Object unwrap() {
188: return javaObject;
189: }
190:
191: public String getClassName() {
192: return "JavaObject";
193: }
194:
195: public Object getDefaultValue(Class hint) {
196: Object value;
197: if (hint == null) {
198: if (javaObject instanceof Boolean) {
199: hint = ScriptRuntime.BooleanClass;
200: }
201: }
202: if (hint == null || hint == ScriptRuntime.StringClass) {
203: value = javaObject.toString();
204: } else {
205: String converterName;
206: if (hint == ScriptRuntime.BooleanClass) {
207: converterName = "booleanValue";
208: } else if (hint == ScriptRuntime.NumberClass) {
209: converterName = "doubleValue";
210: } else {
211: throw Context.reportRuntimeError0("msg.default.value");
212: }
213: Object converterObject = get(converterName, this );
214: if (converterObject instanceof Function) {
215: Function f = (Function) converterObject;
216: value = f.call(Context.getContext(),
217: f.getParentScope(), this ,
218: ScriptRuntime.emptyArgs);
219: } else {
220: if (hint == ScriptRuntime.NumberClass
221: && javaObject instanceof Boolean) {
222: boolean b = ((Boolean) javaObject).booleanValue();
223: value = ScriptRuntime.wrapNumber(b ? 1.0 : 0.0);
224: } else {
225: value = javaObject.toString();
226: }
227: }
228: }
229: return value;
230: }
231:
232: /**
233: * Determine whether we can/should convert between the given type and the
234: * desired one. This should be superceded by a conversion-cost calculation
235: * function, but for now I'll hide behind precedent.
236: */
237: public static boolean canConvert(Object fromObj, Class to) {
238: int weight = getConversionWeight(fromObj, to);
239:
240: return (weight < CONVERSION_NONE);
241: }
242:
243: private static final int JSTYPE_UNDEFINED = 0; // undefined type
244: private static final int JSTYPE_NULL = 1; // null
245: private static final int JSTYPE_BOOLEAN = 2; // boolean
246: private static final int JSTYPE_NUMBER = 3; // number
247: private static final int JSTYPE_STRING = 4; // string
248: private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass
249: private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject
250: private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray
251: private static final int JSTYPE_OBJECT = 8; // Scriptable
252:
253: static final byte CONVERSION_TRIVIAL = 1;
254: static final byte CONVERSION_NONTRIVIAL = 0;
255: static final byte CONVERSION_NONE = 99;
256:
257: /**
258: * Derive a ranking based on how "natural" the conversion is.
259: * The special value CONVERSION_NONE means no conversion is possible,
260: * and CONVERSION_NONTRIVIAL signals that more type conformance testing
261: * is required.
262: * Based on
263: * <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">
264: * "preferred method conversions" from Live Connect 3</a>
265: */
266: static int getConversionWeight(Object fromObj, Class to) {
267: int fromCode = getJSTypeCode(fromObj);
268:
269: switch (fromCode) {
270:
271: case JSTYPE_UNDEFINED:
272: if (to == ScriptRuntime.StringClass
273: || to == ScriptRuntime.ObjectClass) {
274: return 1;
275: }
276: break;
277:
278: case JSTYPE_NULL:
279: if (!to.isPrimitive()) {
280: return 1;
281: }
282: break;
283:
284: case JSTYPE_BOOLEAN:
285: // "boolean" is #1
286: if (to == Boolean.TYPE) {
287: return 1;
288: } else if (to == ScriptRuntime.BooleanClass) {
289: return 2;
290: } else if (to == ScriptRuntime.ObjectClass) {
291: return 3;
292: } else if (to == ScriptRuntime.StringClass) {
293: return 4;
294: }
295: break;
296:
297: case JSTYPE_NUMBER:
298: if (to.isPrimitive()) {
299: if (to == Double.TYPE) {
300: return 1;
301: } else if (to != Boolean.TYPE) {
302: return 1 + getSizeRank(to);
303: }
304: } else {
305: if (to == ScriptRuntime.StringClass) {
306: // native numbers are #1-8
307: return 9;
308: } else if (to == ScriptRuntime.ObjectClass) {
309: return 10;
310: } else if (ScriptRuntime.NumberClass
311: .isAssignableFrom(to)) {
312: // "double" is #1
313: return 2;
314: }
315: }
316: break;
317:
318: case JSTYPE_STRING:
319: if (to == ScriptRuntime.StringClass) {
320: return 1;
321: } else if (to.isInstance(fromObj)) {
322: return 2;
323: } else if (to.isPrimitive()) {
324: if (to == Character.TYPE) {
325: return 3;
326: } else if (to != Boolean.TYPE) {
327: return 4;
328: }
329: }
330: break;
331:
332: case JSTYPE_JAVA_CLASS:
333: if (to == ScriptRuntime.ClassClass) {
334: return 1;
335: } else if (to == ScriptRuntime.ObjectClass) {
336: return 3;
337: } else if (to == ScriptRuntime.StringClass) {
338: return 4;
339: }
340: break;
341:
342: case JSTYPE_JAVA_OBJECT:
343: case JSTYPE_JAVA_ARRAY:
344: Object javaObj = fromObj;
345: if (javaObj instanceof Wrapper) {
346: javaObj = ((Wrapper) javaObj).unwrap();
347: }
348: if (to.isInstance(javaObj)) {
349: return CONVERSION_NONTRIVIAL;
350: }
351: if (to == ScriptRuntime.StringClass) {
352: return 2;
353: } else if (to.isPrimitive() && to != Boolean.TYPE) {
354: return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE
355: : 2 + getSizeRank(to);
356: }
357: break;
358:
359: case JSTYPE_OBJECT:
360: // Other objects takes #1-#3 spots
361: if (to == fromObj.getClass()) {
362: // No conversion required
363: return 1;
364: }
365: if (to.isArray()) {
366: if (fromObj instanceof NativeArray) {
367: // This is a native array conversion to a java array
368: // Array conversions are all equal, and preferable to object
369: // and string conversion, per LC3.
370: return 1;
371: }
372: } else if (to == ScriptRuntime.ObjectClass) {
373: return 2;
374: } else if (to == ScriptRuntime.StringClass) {
375: return 3;
376: } else if (to == ScriptRuntime.DateClass) {
377: if (fromObj instanceof NativeDate) {
378: // This is a native date to java date conversion
379: return 1;
380: }
381: } else if (to.isInterface()) {
382: if (fromObj instanceof Function) {
383: // See comments in coerceType
384: if (to.getMethods().length == 1) {
385: return 1;
386: }
387: }
388: return 11;
389: } else if (to.isPrimitive() && to != Boolean.TYPE) {
390: return 3 + getSizeRank(to);
391: }
392: break;
393: }
394:
395: return CONVERSION_NONE;
396: }
397:
398: static int getSizeRank(Class aType) {
399: if (aType == Double.TYPE) {
400: return 1;
401: } else if (aType == Float.TYPE) {
402: return 2;
403: } else if (aType == Long.TYPE) {
404: return 3;
405: } else if (aType == Integer.TYPE) {
406: return 4;
407: } else if (aType == Short.TYPE) {
408: return 5;
409: } else if (aType == Character.TYPE) {
410: return 6;
411: } else if (aType == Byte.TYPE) {
412: return 7;
413: } else if (aType == Boolean.TYPE) {
414: return CONVERSION_NONE;
415: } else {
416: return 8;
417: }
418: }
419:
420: private static int getJSTypeCode(Object value) {
421: if (value == null) {
422: return JSTYPE_NULL;
423: } else if (value == Undefined.instance) {
424: return JSTYPE_UNDEFINED;
425: } else if (value instanceof String) {
426: return JSTYPE_STRING;
427: } else if (value instanceof Number) {
428: return JSTYPE_NUMBER;
429: } else if (value instanceof Boolean) {
430: return JSTYPE_BOOLEAN;
431: } else if (value instanceof Scriptable) {
432: if (value instanceof NativeJavaClass) {
433: return JSTYPE_JAVA_CLASS;
434: } else if (value instanceof NativeJavaArray) {
435: return JSTYPE_JAVA_ARRAY;
436: } else if (value instanceof Wrapper) {
437: return JSTYPE_JAVA_OBJECT;
438: } else {
439: return JSTYPE_OBJECT;
440: }
441: } else if (value instanceof Class) {
442: return JSTYPE_JAVA_CLASS;
443: } else {
444: Class valueClass = value.getClass();
445: if (valueClass.isArray()) {
446: return JSTYPE_JAVA_ARRAY;
447: } else {
448: return JSTYPE_JAVA_OBJECT;
449: }
450: }
451: }
452:
453: /**
454: * Not intended for public use. Callers should use the
455: * public API Context.toType.
456: * @deprecated as of 1.5 Release 4
457: * @see org.mozilla.javascript.Context#jsToJava(Object, Class)
458: */
459: public static Object coerceType(Class type, Object value) {
460: return coerceTypeImpl(type, value);
461: }
462:
463: /**
464: * Type-munging for field setting and method invocation.
465: * Conforms to LC3 specification
466: */
467: static Object coerceTypeImpl(Class type, Object value) {
468: if (value != null && value.getClass() == type) {
469: return value;
470: }
471:
472: switch (getJSTypeCode(value)) {
473:
474: case JSTYPE_NULL:
475: // raise error if type.isPrimitive()
476: if (type.isPrimitive()) {
477: reportConversionError(value, type);
478: }
479: return null;
480:
481: case JSTYPE_UNDEFINED:
482: if (type == ScriptRuntime.StringClass
483: || type == ScriptRuntime.ObjectClass) {
484: return "undefined";
485: } else {
486: reportConversionError("undefined", type);
487: }
488: break;
489:
490: case JSTYPE_BOOLEAN:
491: // Under LC3, only JS Booleans can be coerced into a Boolean value
492: if (type == Boolean.TYPE
493: || type == ScriptRuntime.BooleanClass
494: || type == ScriptRuntime.ObjectClass) {
495: return value;
496: } else if (type == ScriptRuntime.StringClass) {
497: return value.toString();
498: } else {
499: reportConversionError(value, type);
500: }
501: break;
502:
503: case JSTYPE_NUMBER:
504: if (type == ScriptRuntime.StringClass) {
505: return ScriptRuntime.toString(value);
506: } else if (type == ScriptRuntime.ObjectClass) {
507: return coerceToNumber(Double.TYPE, value);
508: } else if ((type.isPrimitive() && type != Boolean.TYPE)
509: || ScriptRuntime.NumberClass.isAssignableFrom(type)) {
510: return coerceToNumber(type, value);
511: } else {
512: reportConversionError(value, type);
513: }
514: break;
515:
516: case JSTYPE_STRING:
517: if (type == ScriptRuntime.StringClass
518: || type.isInstance(value)) {
519: return value;
520: } else if (type == Character.TYPE
521: || type == ScriptRuntime.CharacterClass) {
522: // Special case for converting a single char string to a
523: // character
524: // Placed here because it applies *only* to JS strings,
525: // not other JS objects converted to strings
526: if (((String) value).length() == 1) {
527: return new Character(((String) value).charAt(0));
528: } else {
529: return coerceToNumber(type, value);
530: }
531: } else if ((type.isPrimitive() && type != Boolean.TYPE)
532: || ScriptRuntime.NumberClass.isAssignableFrom(type)) {
533: return coerceToNumber(type, value);
534: } else {
535: reportConversionError(value, type);
536: }
537: break;
538:
539: case JSTYPE_JAVA_CLASS:
540: if (value instanceof Wrapper) {
541: value = ((Wrapper) value).unwrap();
542: }
543:
544: if (type == ScriptRuntime.ClassClass
545: || type == ScriptRuntime.ObjectClass) {
546: return value;
547: } else if (type == ScriptRuntime.StringClass) {
548: return value.toString();
549: } else {
550: reportConversionError(value, type);
551: }
552: break;
553:
554: case JSTYPE_JAVA_OBJECT:
555: case JSTYPE_JAVA_ARRAY:
556: if (value instanceof Wrapper) {
557: value = ((Wrapper) value).unwrap();
558: }
559: if (type.isPrimitive()) {
560: if (type == Boolean.TYPE) {
561: reportConversionError(value, type);
562: }
563: return coerceToNumber(type, value);
564: } else {
565: if (type == ScriptRuntime.StringClass) {
566: return value.toString();
567: } else {
568: if (type.isInstance(value)) {
569: return value;
570: } else {
571: reportConversionError(value, type);
572: }
573: }
574: }
575: break;
576:
577: case JSTYPE_OBJECT:
578: if (type == ScriptRuntime.StringClass) {
579: return ScriptRuntime.toString(value);
580: } else if (type.isPrimitive()) {
581: if (type == Boolean.TYPE) {
582: reportConversionError(value, type);
583: }
584: return coerceToNumber(type, value);
585: } else if (type.isInstance(value)) {
586: return value;
587: } else if (type == ScriptRuntime.DateClass
588: && value instanceof NativeDate) {
589: double time = ((NativeDate) value).getJSTimeValue();
590: // XXX: This will replace NaN by 0
591: return new Date((long) time);
592: } else if (type.isArray() && value instanceof NativeArray) {
593: // Make a new java array, and coerce the JS array components
594: // to the target (component) type.
595: NativeArray array = (NativeArray) value;
596: long length = array.getLength();
597: Class arrayType = type.getComponentType();
598: Object Result = Array.newInstance(arrayType,
599: (int) length);
600: for (int i = 0; i < length; ++i) {
601: try {
602: Array.set(Result, i, coerceType(arrayType,
603: array.get(i, array)));
604: } catch (EvaluatorException ee) {
605: reportConversionError(value, type);
606: }
607: }
608:
609: return Result;
610: } else if (value instanceof Wrapper) {
611: value = ((Wrapper) value).unwrap();
612: if (type.isInstance(value))
613: return value;
614: reportConversionError(value, type);
615: } else if (type.isInterface() && value instanceof Callable) {
616: // Try to use function as implementation of Java interface.
617: //
618: // XXX: Curently only instances of ScriptableObject are
619: // supported since the resulting interface proxies should
620: // be reused next time conversion is made and generic
621: // Callable has no storage for it. Weak references can
622: // address it but for now use this restriction.
623: if (value instanceof ScriptableObject) {
624: ScriptableObject so = (ScriptableObject) value;
625: Object key = Kit.makeHashKeyFromPair(
626: COERCED_INTERFACE_KEY, type);
627: Object old = so.getAssociatedValue(key);
628: if (old != null) {
629: // Function was already wrapped
630: return old;
631: }
632: Context cx = Context.getContext();
633: Object glue = InterfaceAdapter.create(cx, type,
634: (Callable) value);
635: // Store for later retrival
636: glue = so.associateValue(key, glue);
637: return glue;
638: }
639: reportConversionError(value, type);
640: } else {
641: reportConversionError(value, type);
642: }
643: break;
644: }
645:
646: return value;
647: }
648:
649: private static Object coerceToNumber(Class type, Object value) {
650: Class valueClass = value.getClass();
651:
652: // Character
653: if (type == Character.TYPE
654: || type == ScriptRuntime.CharacterClass) {
655: if (valueClass == ScriptRuntime.CharacterClass) {
656: return value;
657: }
658: return new Character((char) toInteger(value,
659: ScriptRuntime.CharacterClass, Character.MIN_VALUE,
660: Character.MAX_VALUE));
661: }
662:
663: // Double, Float
664: if (type == ScriptRuntime.ObjectClass
665: || type == ScriptRuntime.DoubleClass
666: || type == Double.TYPE) {
667: return valueClass == ScriptRuntime.DoubleClass ? value
668: : new Double(toDouble(value));
669: }
670:
671: if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
672: if (valueClass == ScriptRuntime.FloatClass) {
673: return value;
674: } else {
675: double number = toDouble(value);
676: if (Double.isInfinite(number) || Double.isNaN(number)
677: || number == 0.0) {
678: return new Float((float) number);
679: } else {
680: double absNumber = Math.abs(number);
681: if (absNumber < Float.MIN_VALUE) {
682: return new Float((number > 0.0) ? +0.0 : -0.0);
683: } else if (absNumber > Float.MAX_VALUE) {
684: return new Float(
685: (number > 0.0) ? Float.POSITIVE_INFINITY
686: : Float.NEGATIVE_INFINITY);
687: } else {
688: return new Float((float) number);
689: }
690: }
691: }
692: }
693:
694: // Integer, Long, Short, Byte
695: if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) {
696: if (valueClass == ScriptRuntime.IntegerClass) {
697: return value;
698: } else {
699: return new Integer((int) toInteger(value,
700: ScriptRuntime.IntegerClass, Integer.MIN_VALUE,
701: Integer.MAX_VALUE));
702: }
703: }
704:
705: if (type == ScriptRuntime.LongClass || type == Long.TYPE) {
706: if (valueClass == ScriptRuntime.LongClass) {
707: return value;
708: } else {
709: /* Long values cannot be expressed exactly in doubles.
710: * We thus use the largest and smallest double value that
711: * has a value expressible as a long value. We build these
712: * numerical values from their hexidecimal representations
713: * to avoid any problems caused by attempting to parse a
714: * decimal representation.
715: */
716: final double max = Double
717: .longBitsToDouble(0x43dfffffffffffffL);
718: final double min = Double
719: .longBitsToDouble(0xc3e0000000000000L);
720: return new Long(toInteger(value,
721: ScriptRuntime.LongClass, min, max));
722: }
723: }
724:
725: if (type == ScriptRuntime.ShortClass || type == Short.TYPE) {
726: if (valueClass == ScriptRuntime.ShortClass) {
727: return value;
728: } else {
729: return new Short((short) toInteger(value,
730: ScriptRuntime.ShortClass, Short.MIN_VALUE,
731: Short.MAX_VALUE));
732: }
733: }
734:
735: if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) {
736: if (valueClass == ScriptRuntime.ByteClass) {
737: return value;
738: } else {
739: return new Byte((byte) toInteger(value,
740: ScriptRuntime.ByteClass, Byte.MIN_VALUE,
741: Byte.MAX_VALUE));
742: }
743: }
744:
745: return new Double(toDouble(value));
746: }
747:
748: private static double toDouble(Object value) {
749: if (value instanceof Number) {
750: return ((Number) value).doubleValue();
751: } else if (value instanceof String) {
752: return ScriptRuntime.toNumber((String) value);
753: } else if (value instanceof Scriptable) {
754: if (value instanceof Wrapper) {
755: // XXX: optimize tail-recursion?
756: return toDouble(((Wrapper) value).unwrap());
757: } else {
758: return ScriptRuntime.toNumber(value);
759: }
760: } else {
761: Method meth;
762: try {
763: meth = value.getClass().getMethod("doubleValue",
764: (Class[]) null);
765: } catch (NoSuchMethodException e) {
766: meth = null;
767: } catch (SecurityException e) {
768: meth = null;
769: }
770: if (meth != null) {
771: try {
772: return ((Number) meth
773: .invoke(value, (Object[]) null))
774: .doubleValue();
775: } catch (IllegalAccessException e) {
776: // XXX: ignore, or error message?
777: reportConversionError(value, Double.TYPE);
778: } catch (InvocationTargetException e) {
779: // XXX: ignore, or error message?
780: reportConversionError(value, Double.TYPE);
781: }
782: }
783: return ScriptRuntime.toNumber(value.toString());
784: }
785: }
786:
787: private static long toInteger(Object value, Class type, double min,
788: double max) {
789: double d = toDouble(value);
790:
791: if (Double.isInfinite(d) || Double.isNaN(d)) {
792: // Convert to string first, for more readable message
793: reportConversionError(ScriptRuntime.toString(value), type);
794: }
795:
796: if (d > 0.0) {
797: d = Math.floor(d);
798: } else {
799: d = Math.ceil(d);
800: }
801:
802: if (d < min || d > max) {
803: // Convert to string first, for more readable message
804: reportConversionError(ScriptRuntime.toString(value), type);
805: }
806: return (long) d;
807: }
808:
809: static void reportConversionError(Object value, Class type) {
810: // It uses String.valueOf(value), not value.toString() since
811: // value can be null, bug 282447.
812: throw Context.reportRuntimeError2("msg.conversion.not.allowed",
813: String.valueOf(value), JavaMembers.javaSignature(type));
814: }
815:
816: private void writeObject(ObjectOutputStream out) throws IOException {
817: out.defaultWriteObject();
818:
819: out.writeBoolean(isAdapter);
820: if (isAdapter) {
821: if (adapter_writeAdapterObject == null) {
822: throw new IOException();
823: }
824: Object[] args = { javaObject, out };
825: try {
826: adapter_writeAdapterObject.invoke(null, args);
827: } catch (Exception ex) {
828: throw new IOException();
829: }
830: } else {
831: out.writeObject(javaObject);
832: }
833:
834: if (staticType != null) {
835: out.writeObject(staticType.getClass().getName());
836: } else {
837: out.writeObject(null);
838: }
839: }
840:
841: private void readObject(ObjectInputStream in) throws IOException,
842: ClassNotFoundException {
843: in.defaultReadObject();
844:
845: isAdapter = in.readBoolean();
846: if (isAdapter) {
847: if (adapter_readAdapterObject == null)
848: throw new ClassNotFoundException();
849: Object[] args = { this , in };
850: try {
851: javaObject = adapter_readAdapterObject.invoke(null,
852: args);
853: } catch (Exception ex) {
854: throw new IOException();
855: }
856: } else {
857: javaObject = in.readObject();
858: }
859:
860: String className = (String) in.readObject();
861: if (className != null) {
862: staticType = Class.forName(className);
863: } else {
864: staticType = null;
865: }
866:
867: initMembers();
868: }
869:
870: /**
871: * The prototype of this object.
872: */
873: protected Scriptable prototype;
874:
875: /**
876: * The parent scope of this object.
877: */
878: protected Scriptable parent;
879:
880: protected transient Object javaObject;
881:
882: protected transient Class staticType;
883: protected transient JavaMembers members;
884: private transient Hashtable fieldAndMethods;
885: private transient boolean isAdapter;
886:
887: private static final Object COERCED_INTERFACE_KEY = new Object();
888: private static Method adapter_writeAdapterObject;
889: private static Method adapter_readAdapterObject;
890:
891: static {
892: // Reflection in java is verbose
893: Class[] sig2 = new Class[2];
894: Class cl = Kit
895: .classOrNull("org.mozilla.javascript.JavaAdapter");
896: if (cl != null) {
897: try {
898: sig2[0] = ScriptRuntime.ObjectClass;
899: sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream");
900: adapter_writeAdapterObject = cl.getMethod(
901: "writeAdapterObject", sig2);
902:
903: sig2[0] = ScriptRuntime.ScriptableClass;
904: sig2[1] = Kit.classOrNull("java.io.ObjectInputStream");
905: adapter_readAdapterObject = cl.getMethod(
906: "readAdapterObject", sig2);
907:
908: } catch (Exception ex) {
909: adapter_writeAdapterObject = null;
910: adapter_readAdapterObject = null;
911: }
912: }
913: }
914:
915: }
|