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-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Igor Bukanov, igor@fastmail.fm
026: *
027: * Alternatively, the contents of this file may be used under the terms of
028: * the GNU General Public License Version 2 or later (the "GPL"), in which
029: * case the provisions of the GPL are applicable instead of those above. If
030: * you wish to allow use of your version of this file only under the terms of
031: * the GPL and not to allow others to use your version of this file under the
032: * MPL, indicate your decision by deleting the provisions above and replacing
033: * them with the notice and other provisions required by the GPL. If you do
034: * not delete the provisions above, a recipient may use your version of this
035: * file under either the MPL or the GPL.
036: *
037: * ***** END LICENSE BLOCK ***** */
038:
039: package org.mozilla.javascript;
040:
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.io.Reader;
044: import java.lang.reflect.Method;
045: import java.util.Hashtable;
046:
047: /**
048: * Collection of utilities
049: */
050:
051: public class Kit {
052: /**
053: * Reflection of Throwable.initCause(Throwable) from JDK 1.4
054: * or nul if it is not available.
055: */
056: private static Method Throwable_initCause = null;
057:
058: static {
059: // Are we running on a JDK 1.4 or later system?
060: try {
061: Class ThrowableClass = Kit
062: .classOrNull("java.lang.Throwable");
063: Class[] signature = { ThrowableClass };
064: Throwable_initCause = ThrowableClass.getMethod("initCause",
065: signature);
066: } catch (Exception ex) {
067: // Assume any exceptions means the method does not exist.
068: }
069: }
070:
071: public static Class classOrNull(String className) {
072: try {
073: return Class.forName(className);
074: } catch (ClassNotFoundException ex) {
075: } catch (SecurityException ex) {
076: } catch (LinkageError ex) {
077: } catch (IllegalArgumentException e) {
078: // Can be thrown if name has characters that a class name
079: // can not contain
080: }
081: return null;
082: }
083:
084: public static Class classOrNull(ClassLoader loader, String className) {
085: try {
086: return loader.loadClass(className);
087: } catch (ClassNotFoundException ex) {
088: } catch (SecurityException ex) {
089: } catch (LinkageError ex) {
090: } catch (IllegalArgumentException e) {
091: // Can be thrown if name has characters that a class name
092: // can not contain
093: }
094: return null;
095: }
096:
097: static Object newInstanceOrNull(Class cl) {
098: try {
099: return cl.newInstance();
100: } catch (SecurityException x) {
101: } catch (LinkageError ex) {
102: } catch (InstantiationException x) {
103: } catch (IllegalAccessException x) {
104: }
105: return null;
106: }
107:
108: /**
109: * Check that testClass is accesible from the given loader.
110: */
111: static boolean testIfCanLoadRhinoClasses(ClassLoader loader) {
112: Class testClass = ScriptRuntime.ContextFactoryClass;
113: Class x = Kit.classOrNull(loader, testClass.getName());
114: if (x != testClass) {
115: // The check covers the case when x == null =>
116: // loader does not know about testClass or the case
117: // when x != null && x != testClass =>
118: // loader loads a class unrelated to testClass
119: return false;
120: }
121: return true;
122: }
123:
124: /**
125: * If initCause methods exists in Throwable, call
126: * <tt>ex.initCause(cause)</tt> or otherwise do nothing.
127: * @return The <tt>ex</tt> argument.
128: */
129: public static RuntimeException initCause(RuntimeException ex,
130: Throwable cause) {
131: if (Throwable_initCause != null) {
132: Object[] args = { cause };
133: try {
134: Throwable_initCause.invoke(ex, args);
135: } catch (Exception e) {
136: // Ignore any exceptions
137: }
138: }
139: return ex;
140: }
141:
142: /**
143: * Split string into array of strings using semicolon as string terminator
144: * (; after the last string is required).
145: */
146: public static String[] semicolonSplit(String s) {
147: String[] array = null;
148: for (;;) {
149: // loop 2 times: first to count semicolons and then to fill array
150: int count = 0;
151: int cursor = 0;
152: for (;;) {
153: int next = s.indexOf(';', cursor);
154: if (next < 0) {
155: break;
156: }
157: if (array != null) {
158: array[count] = s.substring(cursor, next);
159: }
160: ++count;
161: cursor = next + 1;
162: }
163: // after the last semicolon
164: if (array == null) {
165: // array size counting state:
166: // check for required terminating ';'
167: if (cursor != s.length())
168: throw new IllegalArgumentException();
169: array = new String[count];
170: } else {
171: // array filling state: stop the loop
172: break;
173: }
174: }
175: return array;
176: }
177:
178: /**
179: * If character <tt>c</tt> is a hexadecimal digit, return
180: * <tt>accumulator</tt> * 16 plus corresponding
181: * number. Otherise return -1.
182: */
183: public static int xDigitToInt(int c, int accumulator) {
184: check: {
185: // Use 0..9 < A..Z < a..z
186: if (c <= '9') {
187: c -= '0';
188: if (0 <= c) {
189: break check;
190: }
191: } else if (c <= 'F') {
192: if ('A' <= c) {
193: c -= ('A' - 10);
194: break check;
195: }
196: } else if (c <= 'f') {
197: if ('a' <= c) {
198: c -= ('a' - 10);
199: break check;
200: }
201: }
202: return -1;
203: }
204: return (accumulator << 4) | c;
205: }
206:
207: /**
208: * Add <i>listener</i> to <i>bag</i> of listeners.
209: * The function does not modify <i>bag</i> and return a new collection
210: * containing <i>listener</i> and all listeners from <i>bag</i>.
211: * Bag without listeners always represented as the null value.
212: * <p>
213: * Usage example:
214: * <pre>
215: * private volatile Object changeListeners;
216: *
217: * public void addMyListener(PropertyChangeListener l)
218: * {
219: * synchronized (this) {
220: * changeListeners = Kit.addListener(changeListeners, l);
221: * }
222: * }
223: *
224: * public void removeTextListener(PropertyChangeListener l)
225: * {
226: * synchronized (this) {
227: * changeListeners = Kit.removeListener(changeListeners, l);
228: * }
229: * }
230: *
231: * public void fireChangeEvent(Object oldValue, Object newValue)
232: * {
233: * // Get immune local copy
234: * Object listeners = changeListeners;
235: * if (listeners != null) {
236: * PropertyChangeEvent e = new PropertyChangeEvent(
237: * this, "someProperty" oldValue, newValue);
238: * for (int i = 0; ; ++i) {
239: * Object l = Kit.getListener(listeners, i);
240: * if (l == null)
241: * break;
242: * ((PropertyChangeListener)l).propertyChange(e);
243: * }
244: * }
245: * }
246: * </pre>
247: *
248: * @param listener Listener to add to <i>bag</i>
249: * @param bag Current collection of listeners.
250: * @return A new bag containing all listeners from <i>bag</i> and
251: * <i>listener</i>.
252: * @see #removeListener(Object bag, Object listener)
253: * @see #getListener(Object bag, int index)
254: */
255: public static Object addListener(Object bag, Object listener) {
256: if (listener == null)
257: throw new IllegalArgumentException();
258: if (listener instanceof Object[])
259: throw new IllegalArgumentException();
260:
261: if (bag == null) {
262: bag = listener;
263: } else if (!(bag instanceof Object[])) {
264: bag = new Object[] { bag, listener };
265: } else {
266: Object[] array = (Object[]) bag;
267: int L = array.length;
268: // bag has at least 2 elements if it is array
269: if (L < 2)
270: throw new IllegalArgumentException();
271: Object[] tmp = new Object[L + 1];
272: System.arraycopy(array, 0, tmp, 0, L);
273: tmp[L] = listener;
274: bag = tmp;
275: }
276:
277: return bag;
278: }
279:
280: /**
281: * Remove <i>listener</i> from <i>bag</i> of listeners.
282: * The function does not modify <i>bag</i> and return a new collection
283: * containing all listeners from <i>bag</i> except <i>listener</i>.
284: * If <i>bag</i> does not contain <i>listener</i>, the function returns
285: * <i>bag</i>.
286: * <p>
287: * For usage example, see {@link #addListener(Object bag, Object listener)}.
288: *
289: * @param listener Listener to remove from <i>bag</i>
290: * @param bag Current collection of listeners.
291: * @return A new bag containing all listeners from <i>bag</i> except
292: * <i>listener</i>.
293: * @see #addListener(Object bag, Object listener)
294: * @see #getListener(Object bag, int index)
295: */
296: public static Object removeListener(Object bag, Object listener) {
297: if (listener == null)
298: throw new IllegalArgumentException();
299: if (listener instanceof Object[])
300: throw new IllegalArgumentException();
301:
302: if (bag == listener) {
303: bag = null;
304: } else if (bag instanceof Object[]) {
305: Object[] array = (Object[]) bag;
306: int L = array.length;
307: // bag has at least 2 elements if it is array
308: if (L < 2)
309: throw new IllegalArgumentException();
310: if (L == 2) {
311: if (array[1] == listener) {
312: bag = array[0];
313: } else if (array[0] == listener) {
314: bag = array[1];
315: }
316: } else {
317: int i = L;
318: do {
319: --i;
320: if (array[i] == listener) {
321: Object[] tmp = new Object[L - 1];
322: System.arraycopy(array, 0, tmp, 0, i);
323: System.arraycopy(array, i + 1, tmp, i, L
324: - (i + 1));
325: bag = tmp;
326: break;
327: }
328: } while (i != 0);
329: }
330: }
331:
332: return bag;
333: }
334:
335: /**
336: * Get listener at <i>index</i> position in <i>bag</i> or null if
337: * <i>index</i> equals to number of listeners in <i>bag</i>.
338: * <p>
339: * For usage example, see {@link #addListener(Object bag, Object listener)}.
340: *
341: * @param bag Current collection of listeners.
342: * @param index Index of the listener to access.
343: * @return Listener at the given index or null.
344: * @see #addListener(Object bag, Object listener)
345: * @see #removeListener(Object bag, Object listener)
346: */
347: public static Object getListener(Object bag, int index) {
348: if (index == 0) {
349: if (bag == null)
350: return null;
351: if (!(bag instanceof Object[]))
352: return bag;
353: Object[] array = (Object[]) bag;
354: // bag has at least 2 elements if it is array
355: if (array.length < 2)
356: throw new IllegalArgumentException();
357: return array[0];
358: } else if (index == 1) {
359: if (!(bag instanceof Object[])) {
360: if (bag == null)
361: throw new IllegalArgumentException();
362: return null;
363: }
364: Object[] array = (Object[]) bag;
365: // the array access will check for index on its own
366: return array[1];
367: } else {
368: // bag has to array
369: Object[] array = (Object[]) bag;
370: int L = array.length;
371: if (L < 2)
372: throw new IllegalArgumentException();
373: if (index == L)
374: return null;
375: return array[index];
376: }
377: }
378:
379: static Object initHash(Hashtable h, Object key, Object initialValue) {
380: synchronized (h) {
381: Object current = h.get(key);
382: if (current == null) {
383: h.put(key, initialValue);
384: } else {
385: initialValue = current;
386: }
387: }
388: return initialValue;
389: }
390:
391: private final static class ComplexKey {
392: private Object key1;
393: private Object key2;
394: private int hash;
395:
396: ComplexKey(Object key1, Object key2) {
397: this .key1 = key1;
398: this .key2 = key2;
399: }
400:
401: public boolean equals(Object anotherObj) {
402: if (!(anotherObj instanceof ComplexKey))
403: return false;
404: ComplexKey another = (ComplexKey) anotherObj;
405: return key1.equals(another.key1)
406: && key2.equals(another.key2);
407: }
408:
409: public int hashCode() {
410: if (hash == 0) {
411: hash = key1.hashCode() ^ key2.hashCode();
412: }
413: return hash;
414: }
415: }
416:
417: public static Object makeHashKeyFromPair(Object key1, Object key2) {
418: if (key1 == null)
419: throw new IllegalArgumentException();
420: if (key2 == null)
421: throw new IllegalArgumentException();
422: return new ComplexKey(key1, key2);
423: }
424:
425: public static String readReader(Reader r) throws IOException {
426: char[] buffer = new char[512];
427: int cursor = 0;
428: for (;;) {
429: int n = r.read(buffer, cursor, buffer.length - cursor);
430: if (n < 0) {
431: break;
432: }
433: cursor += n;
434: if (cursor == buffer.length) {
435: char[] tmp = new char[buffer.length * 2];
436: System.arraycopy(buffer, 0, tmp, 0, cursor);
437: buffer = tmp;
438: }
439: }
440: return new String(buffer, 0, cursor);
441: }
442:
443: public static byte[] readStream(InputStream is,
444: int initialBufferCapacity) throws IOException {
445: if (initialBufferCapacity <= 0) {
446: throw new IllegalArgumentException(
447: "Bad initialBufferCapacity: "
448: + initialBufferCapacity);
449: }
450: byte[] buffer = new byte[initialBufferCapacity];
451: int cursor = 0;
452: for (;;) {
453: int n = is.read(buffer, cursor, buffer.length - cursor);
454: if (n < 0) {
455: break;
456: }
457: cursor += n;
458: if (cursor == buffer.length) {
459: byte[] tmp = new byte[buffer.length * 2];
460: System.arraycopy(buffer, 0, tmp, 0, cursor);
461: buffer = tmp;
462: }
463: }
464: if (cursor != buffer.length) {
465: byte[] tmp = new byte[cursor];
466: System.arraycopy(buffer, 0, tmp, 0, cursor);
467: buffer = tmp;
468: }
469: return buffer;
470: }
471:
472: /**
473: * Throws RuntimeException to indicate failed assertion.
474: * The function never returns and its return type is RuntimeException
475: * only to be able to write <tt>throw Kit.codeBug()</tt> if plain
476: * <tt>Kit.codeBug()</tt> triggers unreachable code error.
477: */
478: public static RuntimeException codeBug() throws RuntimeException {
479: RuntimeException ex = new IllegalStateException(
480: "FAILED ASSERTION");
481: // Print stack trace ASAP
482: ex.printStackTrace(System.err);
483: throw ex;
484: }
485: }
|