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