0001: /*
0002: * Runtime.java
0003: *
0004: * Copyright (c) 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: *
0006: * See the file "LICENSE.txt" for information on usage and redistribution
0007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0008: */
0009: package pnuts.lang;
0010:
0011: import java.io.File;
0012: import java.io.FileNotFoundException;
0013: import java.io.IOException;
0014: import java.io.InputStream;
0015: import java.io.InputStreamReader;
0016: import java.io.PrintWriter;
0017: import java.io.Reader;
0018: import java.io.StringReader;
0019: import java.io.Serializable;
0020: import java.io.ObjectInputStream;
0021: import java.io.ObjectOutputStream;
0022: import java.io.UnsupportedEncodingException;
0023: import java.lang.reflect.Array;
0024: import java.lang.reflect.Constructor;
0025: import java.lang.reflect.Field;
0026: import java.lang.reflect.InvocationTargetException;
0027: import java.lang.reflect.Method;
0028: import java.lang.reflect.Modifier;
0029: import java.math.BigDecimal;
0030: import java.math.BigInteger;
0031: import java.net.URL;
0032: import java.net.URLConnection;
0033: import java.security.AccessController;
0034: import java.security.PrivilegedAction;
0035: import java.security.PrivilegedActionException;
0036: import java.security.PrivilegedExceptionAction;
0037: import java.text.DateFormat;
0038: import java.text.MessageFormat;
0039: import java.util.Calendar;
0040: import java.util.Enumeration;
0041: import java.util.HashMap;
0042: import java.util.HashSet;
0043: import java.util.Hashtable;
0044: import java.util.Map;
0045: import java.util.WeakHashMap;
0046: import java.util.List;
0047: import java.util.ArrayList;
0048: import java.util.MissingResourceException;
0049: import java.util.Properties;
0050: import java.util.ResourceBundle;
0051: import java.util.Set;
0052: import java.util.Iterator;
0053: import java.beans.PropertyChangeListener;
0054: import java.beans.PropertyChangeEvent;
0055:
0056: import org.pnuts.lang.UnparseVisitor;
0057: import org.pnuts.lang.NodeUtil;
0058: import org.pnuts.lang.*;
0059: import org.pnuts.util.Stack;
0060: import org.pnuts.util.*;
0061:
0062: /**
0063: * This class provides runtime supports for Pnuts compiler/interpreter. Most of
0064: * the methods are protected static, so that only subclasses can access them.
0065: */
0066: public class Runtime implements Executable {
0067:
0068: private final static boolean DEBUG = false;
0069:
0070: private final static Object[] noarg = new Object[] {};
0071:
0072: protected static final String INT_SYMBOL = "int".intern();
0073:
0074: protected static final String SHORT_SYMBOL = "short".intern();
0075:
0076: protected static final String CHAR_SYMBOL = "char".intern();
0077:
0078: protected static final String BYTE_SYMBOL = "byte".intern();
0079:
0080: protected static final String LONG_SYMBOL = "long".intern();
0081:
0082: protected static final String FLOAT_SYMBOL = "float".intern();
0083:
0084: protected static final String DOUBLE_SYMBOL = "double".intern();
0085:
0086: protected static final String BOOLEAN_SYMBOL = "boolean".intern();
0087:
0088: protected static final String VOID_SYMBOL = "void".intern();
0089:
0090: protected static final String CLONE = "clone".intern();
0091:
0092: protected static final String EXCEPTOIN_FIELD_SYMBOL = "$exception$"
0093: .intern();
0094:
0095: private final static String DEFAULT_PROPERTY_FILE = "runtime.properties";
0096:
0097: private final static Integer ZERO = new Integer(0);
0098:
0099: protected final static Object[] NO_PARAM = new Object[0];
0100:
0101: private static Properties defaultProperties;
0102:
0103: /**/
0104: static {
0105: try {
0106: AccessController.doPrivileged(new PrivilegedAction() {
0107: public Object run() {
0108: InputStream in = Runtime.class
0109: .getResourceAsStream(DEFAULT_PROPERTY_FILE);
0110: if (in != null) {
0111: Properties prop = new Properties();
0112: try {
0113: prop.load(in);
0114: defaultProperties = prop;
0115: } catch (IOException ioe) {
0116: // ignore
0117: }
0118: }
0119: return null;
0120: }
0121: });
0122: } catch (Exception e) {
0123: // ignore
0124: }
0125: }
0126:
0127: /**/
0128:
0129: public static boolean getBoolean(final String key) {
0130: Boolean b = (Boolean) AccessController
0131: .doPrivileged(new PrivilegedAction() {
0132: public Object run() {
0133: String sval = Runtime.getProperty(key);
0134: if (sval != null
0135: && sval.equalsIgnoreCase("true")) {
0136: return Boolean.TRUE;
0137: } else {
0138: return Boolean.FALSE;
0139: }
0140: }
0141: });
0142: return b.booleanValue();
0143: }
0144:
0145: protected Runtime() {
0146: }
0147:
0148: public static Runtime getDefaultRuntime() {
0149: if (getBoolean("pnuts.compiler.useDynamicProxy")) {
0150: return new pnuts.compiler.DynamicRuntime();
0151: } else {
0152: return new Runtime();
0153: }
0154: }
0155:
0156: /**
0157: * Executes a compiled script. Exceptions are checked if an exception
0158: * handler is registered with the catch() function. Output stream of the
0159: * specified context is flushed after the script is executed or an exception
0160: * is thrown.
0161: *
0162: * @param context
0163: * the context in which this object is executed
0164: * @return the result of the execution
0165: */
0166: public Object run(Context context) {
0167: if (DEBUG) {
0168: System.out.println("runtime is "
0169: + this .getClass().getSuperclass());
0170: }
0171: context.runtime = this ;
0172: try {
0173: return exec(context);
0174: } catch (Throwable t) {
0175: checkException(context, t);
0176: return null;
0177: } finally {
0178: PrintWriter pw = context.getWriter();
0179: if (pw != null) {
0180: pw.flush();
0181: }
0182: }
0183: }
0184:
0185: /**
0186: * Executes a compiled script. Exceptions are checked if an exception
0187: * handler is registered with the catch() function. Output stream of the
0188: * specified context is flushed after the script is executed or an exception
0189: * is thrown.
0190: *
0191: * @param context
0192: * the context in which this object is executed
0193: * @return the result of the execution
0194: *
0195: * @deprecated replaced by run(Context)
0196: */
0197: public Object execute(Context context) {
0198: return run(context);
0199: }
0200:
0201: /**
0202: * This method is overrided by classes generated by the compiler.
0203: *
0204: * @param context
0205: * the context in which this object is executed
0206: * @return the result of the execution
0207: */
0208: protected Object exec(Context context) {
0209: return null;
0210: }
0211:
0212: /**
0213: * Call a method
0214: *
0215: * @param context
0216: * the context in which the method is called
0217: * @param c
0218: * the class of method
0219: * @param name
0220: * the method name
0221: * @param args
0222: * the paramters
0223: * @param types
0224: * the types of the paramters
0225: * @param target
0226: * the target object
0227: * @return the return value of the call
0228: */
0229: public static Object callMethod(Context context, Class c,
0230: String name, Object args[], Class types[], Object target) {
0231: return context.config.callMethod(context, c, name, args, types,
0232: target);
0233: }
0234:
0235: /**
0236: * Call a constructor
0237: *
0238: * @param context
0239: * the context in which the constructor is called
0240: * @param c
0241: * the class of method
0242: * @param args
0243: * the paramters
0244: * @param types
0245: * the types of the formal arguments
0246: * @return the created instance
0247: */
0248: public static Object callConstructor(Context context, Class c,
0249: Object args[], Class types[]) {
0250: return context.config.callConstructor(context, c, args, types);
0251: }
0252:
0253: /**
0254: * Call a method
0255: *
0256: * @param context
0257: * the context
0258: * @param c
0259: * the class of method
0260: * @param name
0261: * the method name
0262: * @param args
0263: * the paramters
0264: * @param types
0265: * the types of the formal arguments
0266: * @param target
0267: * the target object
0268: * @return the return value
0269: */
0270: protected Object _callMethod(Context context, Class c, String name,
0271: Object args[], Class types[], Object target) {
0272: Method method = null;
0273: boolean _static = (target == null);
0274:
0275: if (name == CLONE && args.length == 0) {
0276: if (target instanceof Object[]) {
0277: return ((Object[]) target).clone();
0278: } else if (target instanceof int[]) {
0279: return ((int[]) target).clone();
0280: } else if (target instanceof byte[]) {
0281: return ((byte[]) target).clone();
0282: } else if (target instanceof short[]) {
0283: return ((short[]) target).clone();
0284: } else if (target instanceof char[]) {
0285: return ((char[]) target).clone();
0286: } else if (target instanceof long[]) {
0287: return ((long[]) target).clone();
0288: } else if (target instanceof float[]) {
0289: return ((float[]) target).clone();
0290: } else if (target instanceof double[]) {
0291: return ((double[]) target).clone();
0292: } else if (target instanceof boolean[]) {
0293: return ((boolean[]) target).clone();
0294: }
0295: }
0296: Method m[] = context.config._getMethods(c, name);
0297:
0298: try {
0299: int count = 0;
0300: int min = Integer.MAX_VALUE;
0301: Stack methods = new Stack();
0302: cand: for (int i = 0; i < m.length; i++) {
0303: Method mi = m[i];
0304: Class p[] = mi.getParameterTypes();
0305: if (p.length != args.length) {
0306: continue;
0307: }
0308: count = 0;
0309: for (int j = 0; j < p.length; j++) {
0310: Class pj = p[j];
0311: if (types != null) {
0312: Class tj = types[j];
0313: if (tj != null && pj != tj
0314: && !pj.isAssignableFrom(tj)) {
0315: continue cand;
0316: }
0317: }
0318: int t = matchType(pj, args[j]);
0319: if (t < 0) {
0320: continue cand;
0321: }
0322: count += t;
0323: }
0324: if (count > min) {
0325: continue;
0326: }
0327:
0328: boolean st = Modifier.isStatic(mi.getModifiers());
0329: if (st != _static) {
0330: continue;
0331: }
0332:
0333: if (count < min) {
0334: methods.removeAllElements();
0335: methods.push(mi);
0336: min = count;
0337: } else if (count == min) {
0338: methods.push(mi);
0339: }
0340: }
0341: Class clazz = c;
0342: out: while (clazz != null) {
0343: int size = methods.size();
0344: for (int i = 0; i < size; i++) {
0345: method = (Method) methods.pop();
0346: if (!_static && method.getDeclaringClass() == clazz) {
0347: break out;
0348: }
0349: }
0350: clazz = clazz.getSuperclass();
0351: }
0352:
0353: if (method != null) {
0354: if (args.length > 0) {
0355: Class p[] = method.getParameterTypes();
0356: for (int j = 0; j < p.length; j++) {
0357: Class pj = p[j];
0358: if (args[j] != null
0359: && isArray(args[j])
0360: && (pj.isArray() || List.class
0361: .isAssignableFrom(pj))) {
0362: if (!pj.isInstance(args[j])) {
0363: args[j] = transform(pj, args[j],
0364: context);
0365: }
0366: }
0367: }
0368: }
0369: try {
0370: return method.invoke(target, args);
0371: } catch (InvocationTargetException ita) {
0372: Throwable t = ita.getTargetException();
0373: if (t instanceof PnutsException) {
0374: throw (PnutsException) t;
0375: } else {
0376: throw new PnutsException(t, context);
0377: }
0378: }
0379: } else {
0380: if (target instanceof Class) {
0381: Class cls = (Class) target;
0382: return _callMethod(context, (Class) target, name,
0383: args, types, null);
0384: }
0385:
0386: throw new PnutsException("method.notFound",
0387: new Object[] { name,
0388: "" + (target == null ? c : target),
0389: "" + Pnuts.format(args) }, context);
0390: }
0391: } catch (IllegalAccessException pe) {
0392: Class cls = method.getDeclaringClass();
0393: try {
0394: if (!Modifier.isPublic(cls.getModifiers())) {
0395: Method _m = findCallableMethod(cls, name, method
0396: .getParameterTypes());
0397: if (_m != null) {
0398: for (int i = 0; i < m.length; i++) {
0399: if (m[i] == method) {
0400: if (DEBUG) {
0401: System.out.println(_m + " <- "
0402: + method);
0403: }
0404: m[i] = _m;
0405: break;
0406: }
0407: }
0408: return _m.invoke(target, args);
0409: }
0410: }
0411: return Configuration.normalConfiguration.reInvoke(pe,
0412: method, target, args);
0413: } catch (IllegalAccessException iae) {
0414: throw new PnutsException(iae, context);
0415: } catch (InvocationTargetException ita) {
0416: Throwable t = ita.getTargetException();
0417: if (t instanceof PnutsException) {
0418: throw (PnutsException) t;
0419: } else {
0420: throw new PnutsException(t, context);
0421: }
0422: }
0423: } catch (PnutsException pe) {
0424: throw pe;
0425: } catch (Exception e) {
0426: throw new PnutsException(e, context);
0427: }
0428: }
0429:
0430: protected static Method findCallableMethod(Class clazz,
0431: String name, Class args[]) {
0432: while (clazz != null) {
0433: Method method;
0434: if (Modifier.isPublic(clazz.getModifiers())) {
0435: try {
0436: method = clazz.getMethod(name, args);
0437: if (method != null
0438: && Modifier
0439: .isPublic(method
0440: .getDeclaringClass()
0441: .getModifiers())) {
0442: return method;
0443: }
0444: } catch (NoSuchMethodException nme) {
0445: }
0446: }
0447: Class it[] = clazz.getInterfaces();
0448: for (int i = 0; i < it.length; i++) {
0449: Method m = findCallableMethod(it[i], name, args);
0450: if (m != null) {
0451: return m;
0452: }
0453: }
0454: clazz = clazz.getSuperclass();
0455: }
0456: return null;
0457: }
0458:
0459: /**
0460: * Call a constructor
0461: *
0462: * @param context
0463: * the context in which the constructor is called
0464: * @param c
0465: * the class of method
0466: * @param args
0467: * the paramters
0468: * @param types
0469: * the types of the formal arguments
0470: * @return the created instance
0471: */
0472: protected Object _callConstructor(Context context, Class c,
0473: Object args[], Class types[]) {
0474: try {
0475: Constructor cs[] = context.config._getConstructors(c);
0476:
0477: Constructor cons = null;
0478: int count = 0;
0479: int min = Integer.MAX_VALUE;
0480: cand: for (int i = 0; i < cs.length; i++) {
0481: Class p[] = cs[i].getParameterTypes();
0482: if (p.length != args.length) {
0483: continue;
0484: }
0485: count = 0;
0486: for (int j = 0; j < p.length; j++) {
0487: Class pj = p[j];
0488: if (types != null) {
0489: Class tj = types[j];
0490: if (tj != null && pj != tj
0491: && !pj.isAssignableFrom(tj)) {
0492: continue cand;
0493: }
0494: }
0495: int t = matchType(pj, args[j]);
0496: if (t < 0) {
0497: continue cand;
0498: }
0499: count += t;
0500: }
0501: if (count < min) {
0502: min = count;
0503: cons = cs[i];
0504: }
0505: }
0506: if (cons != null) {
0507: Class p[] = cons.getParameterTypes();
0508: for (int j = 0; j < p.length; j++) {
0509: Class pj = p[j];
0510: if (args[j] != null
0511: && isArray(args[j])
0512: && (pj.isArray() || List.class
0513: .isAssignableFrom(pj))) {
0514: if (!pj.isInstance(args[j])) {
0515: args[j] = transform(pj, args[j], context);
0516: }
0517: }
0518: }
0519: try {
0520: return cons.newInstance(args);
0521: } catch (InvocationTargetException ita) {
0522: Throwable t = ita.getTargetException();
0523: if (t instanceof PnutsException) {
0524: throw (PnutsException) t;
0525: } else {
0526: throw new PnutsException(t, context);
0527: }
0528: }
0529: } else {
0530: throw new IllegalArgumentException();
0531: }
0532: } catch (IllegalArgumentException e2) {
0533: throw new PnutsException("constructor.notFound",
0534: new Object[] { c, Pnuts.format(args) }, context);
0535: } catch (PnutsException e3) {
0536: throw e3;
0537: } catch (Throwable e4) {
0538: throw new PnutsException(e4, context);
0539: }
0540: }
0541:
0542: /**
0543: * Assign an object to a static field.
0544: *
0545: * @param context
0546: * the context in which the field is accessed
0547: * @param clazz
0548: * the class in which the static field is defined
0549: * @param name
0550: * the name of the static field
0551: * @param expr
0552: * the value to be assigned
0553: */
0554: public static void putStaticField(Context context, Class clazz,
0555: String name, Object expr) {
0556: context.config.putStaticField(context, clazz, name, expr);
0557: }
0558:
0559: /**
0560: * Get the value of a static field.
0561: *
0562: * @param context
0563: * the context in which the field is accessed
0564: * @param clazz
0565: * the class in which the static field is defined
0566: * @param name
0567: * the name of the static field
0568: * @return the value
0569: */
0570: public static Object getStaticField(Context context, Class clazz,
0571: String name) {
0572: return context.config.getStaticField(context, clazz, name);
0573: }
0574:
0575: /**
0576: * Assign an object to a instance field.
0577: *
0578: * @param context
0579: * the context in which the field is accessed
0580: * @param target
0581: * the target object of the field
0582: * @param name
0583: * the name of the field
0584: * @param expr
0585: * the value to be assigned
0586: */
0587: public static void putField(Context context, Object target,
0588: String name, Object expr) {
0589: context.config.putField(context, target, name, expr);
0590: }
0591:
0592: /**
0593: * Get the value of a instance field.
0594: *
0595: * @param context
0596: * the context in which the field is accessed
0597: * @param target
0598: * the target object of the field
0599: * @param name
0600: * the name of the field
0601: * @return the value
0602: */
0603: public static Object getField(Context context, Object target,
0604: String name) {
0605: return context.config.getField(context, target, name);
0606: }
0607:
0608: protected Object _getField(Context context, Class c, String name,
0609: Object target) {
0610: try {
0611: Field f = getField(target.getClass(), name);
0612: if (f == null) {
0613: throw new PnutsException("field.notFound",
0614: new Object[] { name, target }, context);
0615: }
0616: return f.get(target);
0617: } catch (PnutsException pe) {
0618: throw pe;
0619: } catch (Exception e) {
0620: throw new PnutsException(e, context);
0621: }
0622: }
0623:
0624: protected void _putField(Context context, Class cls, String name,
0625: Object target, Object value) {
0626: try {
0627: Field f = getField(target.getClass(), name);
0628: if (f == null) {
0629: throw new PnutsException("field.notFound",
0630: new Object[] { name, target }, context);
0631: }
0632: f.set(target, value);
0633: } catch (PnutsException pe) {
0634: throw pe;
0635: } catch (Exception e) {
0636: throw new PnutsException(e, context);
0637: }
0638: }
0639:
0640: /**
0641: * Get true component type from an array type. e.g. int[][] ==> int,
0642: * String[] ==> String
0643: *
0644: * @param clazz
0645: * An array type to be examined
0646: * @return The component type of the array type.
0647: */
0648: public static Class getBottomType(Class clazz) {
0649: while (clazz.isArray()) {
0650: clazz = clazz.getComponentType();
0651: }
0652: return clazz;
0653: }
0654:
0655: /**
0656: * Creates an array type
0657: *
0658: * @param c
0659: * the component type
0660: * @param dim
0661: * the number of dimensions
0662: */
0663: public static Class arrayType(Class c, int dim) {
0664: if (dim == 0) {
0665: return c;
0666: } else {
0667: return arrayType(Array.newInstance(c, 0).getClass(),
0668: dim - 1);
0669: }
0670: }
0671:
0672: protected static int arraydim(Object o) {
0673: if (isArray(o)) {
0674: int len = getArrayLength(o);
0675: int maxDim = 0;
0676: for (int i = 0; i < len; i++) {
0677: int dim = arraydim(Array.get(o, i));
0678: if (dim > maxDim) {
0679: maxDim = dim;
0680: }
0681: }
0682: return maxDim + 1;
0683: } else {
0684: return 0;
0685: }
0686: }
0687:
0688: public static Object transform(Class type, Object obj) {
0689: return transform(type, obj, null);
0690: }
0691:
0692: public static Object transform(Class type, Object obj,
0693: Context context) {
0694: if (type.isArray()) {
0695: boolean isList = (obj instanceof List);
0696: boolean isArray = isArray(obj);
0697: if (obj != null && !(isList || isArray)) {
0698: return obj;
0699: }
0700: Class componentType = type.getComponentType();
0701: int len;
0702: if (isArray) {
0703: len = getArrayLength(obj);
0704: } else if (isList) {
0705: len = ((List) obj).size();
0706: } else {
0707: return obj;
0708: }
0709: Object result = Array.newInstance(componentType, len);
0710: for (int i = 0; i < len; i++) {
0711: Object elem = null;
0712: if (context != null) {
0713: elem = context.config.getElement(context, obj,
0714: new Integer(i));
0715: } else {
0716: if (isArray) {
0717: elem = Array.get(obj, i);
0718: } else if (isList) {
0719: elem = ((List) obj).get(i);
0720: }
0721: }
0722: Array.set(result, i, transform(componentType, elem,
0723: context));
0724: }
0725: return result;
0726: } else {
0727: // TODO
0728: if (type == byte.class) {
0729: if (!(obj instanceof Byte) && obj instanceof Number) {
0730: obj = new Byte(((Number) obj).byteValue());
0731: }
0732: } else if (type == short.class) {
0733: if (!(obj instanceof Short) && obj instanceof Number) {
0734: obj = new Short(((Number) obj).shortValue());
0735: }
0736: } else if (type == char.class) {
0737: if (obj instanceof Number) {
0738: obj = new Character((char) ((Number) obj)
0739: .intValue());
0740: }
0741: } else if (List.class.isAssignableFrom(type)) {
0742: if (isArray(obj)) {
0743: int len = getArrayLength(obj);
0744: try {
0745: List list;
0746: if (type.isInterface()) {
0747: list = new ArrayList();
0748: if (!type.isInstance(list)) {
0749: throw new IllegalArgumentException();
0750: }
0751: } else {
0752: list = (List) type.newInstance();
0753: }
0754: for (int i = 0; i < len; i++) {
0755: list.add(Array.get(obj, i));
0756: }
0757: return list;
0758: } catch (Exception e) {
0759: throw new IllegalArgumentException(e);
0760: }
0761: }
0762: }
0763: return obj;
0764: }
0765: }
0766:
0767: protected static int matchType(Class type, Object obj) {
0768: if (obj == null) {
0769: if (type.isPrimitive()) {
0770: return -1;
0771: }
0772: return 0;
0773: }
0774: Class clazz = obj.getClass();
0775: if (clazz == type) {
0776: return 0;
0777: }
0778: if (type == boolean.class) {
0779: return (clazz == Boolean.class) ? 0 : -1;
0780: }
0781: if (type == byte.class) {
0782: return distance(0, clazz);
0783: }
0784: if (type == char.class) {
0785: return distance(6, clazz);
0786: }
0787: if (type == short.class) {
0788: return distance(1, clazz);
0789: }
0790: if (type == int.class) {
0791: return distance(2, clazz);
0792: }
0793: if (type == long.class) {
0794: return distance(3, clazz);
0795: }
0796: if (type == float.class) {
0797: return distance(4, clazz);
0798: }
0799: if (type == double.class) {
0800: return distance(5, clazz);
0801: }
0802: if (type.isAssignableFrom(clazz)) {
0803: return 1;
0804: } else if (clazz.isArray()) {
0805: if (type.isArray()) {
0806: int j = 1;
0807: for (int i = 0; i < getArrayLength(obj); i++) {
0808: int s = matchType(type.getComponentType(), Array
0809: .get(obj, i));
0810: if (s < 0) {
0811: return -1;
0812: } else {
0813: j += s;
0814: }
0815: }
0816: return j;
0817: } else if (List.class.isAssignableFrom(type)) {
0818: return 1;
0819: }
0820: } else if (type.isArray()) {
0821: if (obj instanceof List) {
0822: int j = 1;
0823: List list = (List) obj;
0824: for (int i = 0; i < list.size(); i++) {
0825: int s = matchType(type.getComponentType(), list
0826: .get(i));
0827: if (s < 0) {
0828: return -1;
0829: } else {
0830: j += s;
0831: }
0832: }
0833: return j;
0834: }
0835: }
0836: return -1;
0837: }
0838:
0839: private final static int[][] distance_table = {
0840: { 0, 1, 2, 3, 4, 5, 1 }, { 1, 0, 1, 2, 3, 4, 2 },
0841: { 2, 2, 0, 1, 2, 3, 2 }, { 3, 3, 3, 0, 1, 2, -1 },
0842: { 4, 4, 4, 4, 0, 1, -1 }, { 5, 5, 5, 5, 5, 0, -1 },
0843: { 1, 2, 2, -1, -1, -1, 0 }, };
0844:
0845: private static int distance(int pos, Class clazz) {
0846: if (clazz == Double.class) {
0847: return distance_table[5][pos];
0848: } else if (clazz == Float.class) {
0849: return distance_table[4][pos];
0850: } else if (clazz == Long.class) {
0851: return distance_table[3][pos];
0852: } else if (clazz == Integer.class) {
0853: return distance_table[2][pos];
0854: } else if (clazz == Short.class) {
0855: return distance_table[1][pos];
0856: } else if (clazz == Character.class) {
0857: return distance_table[6][pos];
0858: } else if (clazz == Byte.class) {
0859: return distance_table[0][pos];
0860: } else {
0861: return -1;
0862: }
0863: }
0864:
0865: /*
0866: * class <-> public methods
0867: */
0868: protected static Method[] getMethods(Context context, Class cls) {
0869: return context.config.getMethods(cls);
0870: }
0871:
0872: /*
0873: * class <-> Constructors
0874: */
0875: protected static Constructor[] getConstructors(Context context,
0876: Class cls) {
0877: return context.config._getConstructors(cls);
0878: }
0879:
0880: /**
0881: * Parse an integer.
0882: *
0883: * @return an array [Number number, int offset_of_unit_symbol]
0884: */
0885: public static Object[] parseInt(String str) throws ParseException {
0886: char c1 = str.charAt(0);
0887: if (c1 == '#') {
0888: return parseInt(str.substring(1), 16, true);
0889: } else if (c1 == '0') {
0890: if (str.length() > 1) {
0891: char c2 = str.charAt(1);
0892: if (c2 == 'x' || c2 == 'X') {
0893: return parseInt(str.substring(2), 16, false);
0894: } else {
0895: return parseInt(str, 8, false);
0896: }
0897: } else {
0898: return new Object[] { new Integer(0), null };
0899: }
0900: } else {
0901: return parseInt(str, 10, false);
0902: }
0903: }
0904:
0905: static Object[] parseInt(String str, int radix, boolean shrink)
0906: throws ParseException {
0907: boolean overflow = false;
0908: long value = 0;
0909: int len = str.length();
0910: int i = 0;
0911: out: for (i = 0; i < len; i++) {
0912: int ch = str.charAt(i);
0913: switch (ch) {
0914: case '0':
0915: case '1':
0916: case '2':
0917: case '3':
0918: case '4':
0919: case '5':
0920: case '6':
0921: case '7':
0922: if (radix == 8) {
0923: value = (value << 3)
0924: + Character.toLowerCase((char) ch) - '0';
0925: if (value < 0) {
0926: overflow = true;
0927: break out;
0928: }
0929: break;
0930: }
0931: case '8':
0932: case '9':
0933: if (radix == 10) {
0934: int c = ch - '0';
0935: value = (value * 10) + c;
0936: if (value < 0) {
0937: overflow = true;
0938: break out;
0939: }
0940: break;
0941: } else if (radix == 16) {
0942: value = (value << 4)
0943: + Character.toLowerCase((char) ch) - '0';
0944: if (value < 0) {
0945: overflow = true;
0946: break out;
0947: }
0948: break;
0949: } else if (radix == 8) {
0950: throw new ParseException();
0951: }
0952: case 'a':
0953: case 'A':
0954: case 'b':
0955: case 'B':
0956: case 'c':
0957: case 'C':
0958: case 'd':
0959: case 'D':
0960: case 'e':
0961: case 'E':
0962: case 'f':
0963: case 'F':
0964: if (radix == 10) {
0965: break out;
0966: } else if (radix == 8) {
0967: throw new ParseException();
0968: }
0969: value = (value << 4) + 10
0970: + Character.toLowerCase((char) ch) - 'a';
0971: if (value < 0) {
0972: overflow = true;
0973: break out;
0974: }
0975: break;
0976: default:
0977: break out;
0978: }
0979: }
0980:
0981: out2: for (; i < len; i++) {
0982: int ch = str.charAt(i);
0983: switch (ch) {
0984: case '0':
0985: case '1':
0986: case '2':
0987: case '3':
0988: case '4':
0989: case '5':
0990: case '6':
0991: case '7':
0992: case '8':
0993: case '9':
0994: break;
0995: case 'a':
0996: case 'A':
0997: case 'b':
0998: case 'B':
0999: case 'c':
1000: case 'C':
1001: case 'd':
1002: case 'D':
1003: case 'e':
1004: case 'E':
1005: case 'f':
1006: case 'F':
1007: if (radix == 10) {
1008: break out2;
1009: }
1010: break;
1011: default:
1012: break out2;
1013: }
1014: }
1015:
1016: Number number = null;
1017:
1018: if (overflow) {
1019: String s = str;
1020: if (str.length() > i) {
1021: s = str.substring(0, i);
1022: }
1023: number = decimalNumber(s, radix);
1024: } else {
1025: if (value >= 0 && value <= 255 && radix == 16) {
1026: if (shrink) {
1027: number = new Byte((byte) value);
1028: } else {
1029: number = new Integer((int) value);
1030: }
1031: } else if (value <= Integer.MIN_VALUE
1032: || value >= Integer.MAX_VALUE) {
1033: number = new Long(value);
1034: } else {
1035: number = new Integer((int) value);
1036: }
1037: }
1038: if (str.length() > i) {
1039: if (radix == 16) {
1040: i += 2;
1041: }
1042: return new Object[] { number, new int[] { i } };
1043: } else {
1044: return new Object[] { number, null };
1045: }
1046: }
1047:
1048: /**
1049: * Parse a floating point number.
1050: *
1051: * @return an array [Number number, int offset_of_unit_symbol]
1052: */
1053: public static Object[] parseFloat(String str) {
1054: int i = 0;
1055: int len = str.length();
1056: out1: for (i = 0; i < len; i++) {
1057: int ch = str.charAt(i);
1058: switch (ch) {
1059: case '0':
1060: case '1':
1061: case '2':
1062: case '3':
1063: case '4':
1064: case '5':
1065: case '6':
1066: case '7':
1067: case '8':
1068: case '9':
1069: case '.':
1070: break;
1071: default:
1072: break out1;
1073: }
1074: }
1075: if (i < len) {
1076: int ch = str.charAt(i);
1077: if (ch == 'e' || ch == 'E') {
1078: i++;
1079: out2: for (; i < len; i++) {
1080: ch = str.charAt(i);
1081: switch (ch) {
1082: case '+':
1083: case '-':
1084: case '0':
1085: case '1':
1086: case '2':
1087: case '3':
1088: case '4':
1089: case '5':
1090: case '6':
1091: case '7':
1092: case '8':
1093: case '9':
1094: break;
1095: default:
1096: break out2;
1097: }
1098: }
1099: }
1100: }
1101: String s = str;
1102: if (i < len) {
1103: s = str.substring(0, i);
1104: }
1105: Number n = null;
1106: if (s.length() > 16) {
1107: n = decimalNumber(s, 10);
1108: } else {
1109: n = Double.valueOf(s);
1110: }
1111: if (i < len) {
1112: return new Object[] { n, new int[] { i } };
1113: } else {
1114: return new Object[] { n, null };
1115: }
1116: }
1117:
1118: /**
1119: * Parse a string literal.
1120: *
1121: * @return the value
1122: */
1123: public static String parseString(String str, int offset)
1124: throws ParseException {
1125: StringBuffer buf = new StringBuffer();
1126: int length = str.length();
1127: for (int i = offset; i < length - offset; i++) {
1128: char ch = str.charAt(i);
1129: if (ch == '\\') {
1130: i++;
1131: switch (str.charAt(i)) {
1132: case '"':
1133: buf.append('"');
1134: break;
1135: case 'b':
1136: buf.append('\b');
1137: break;
1138: case 'f':
1139: buf.append('\f');
1140: break;
1141: case 't':
1142: buf.append('\t');
1143: break;
1144: case 'r':
1145: buf.append('\r');
1146: break;
1147: case 'n':
1148: buf.append('\n');
1149: break;
1150: case '0':
1151: buf.append('\0');
1152: break;
1153: case '\\':
1154: buf.append('\\');
1155: break;
1156: case 'u':
1157: case 'U': {
1158: if (i + 6 > length) {
1159: throw new ParseException();
1160: }
1161: int value = 0;
1162: for (int j = 0; j < 4; j++) {
1163: int c = str.charAt(++i);
1164: switch (c) {
1165: case '0':
1166: case '1':
1167: case '2':
1168: case '3':
1169: case '4':
1170: case '5':
1171: case '6':
1172: case '7':
1173: case '8':
1174: case '9':
1175: value = (value << 4) + (c - '0');
1176: break;
1177: case 'a':
1178: case 'A':
1179: case 'b':
1180: case 'B':
1181: case 'c':
1182: case 'C':
1183: case 'd':
1184: case 'D':
1185: case 'e':
1186: case 'E':
1187: case 'f':
1188: case 'F':
1189: value = (value << 4) + 10
1190: + Character.toLowerCase((char) c)
1191: - 'a';
1192: break;
1193: default:
1194: throw new ParseException();
1195: }
1196: }
1197: buf.append((char) value);
1198: break;
1199: }
1200: default:
1201: throw new ParseException();
1202: }
1203: } else {
1204: buf.append(ch);
1205: }
1206: }
1207: return buf.toString();
1208: }
1209:
1210: /**
1211: * Parse a character literal.
1212: *
1213: * @return the value
1214: */
1215: public static Character parseChar(String str) throws ParseException {
1216: if (str.charAt(1) == '\\') {
1217: switch (str.charAt(2)) {
1218: case '\'':
1219: return new Character('\'');
1220: case 'b':
1221: return new Character('\b');
1222: case 'f':
1223: return new Character('\f');
1224: case 't':
1225: return new Character('\t');
1226: case 'r':
1227: return new Character('\r');
1228: case 'n':
1229: return new Character('\n');
1230: case '0':
1231: return new Character('\0');
1232: case '\\':
1233: return new Character('\\');
1234: case 'u':
1235: case 'U': {
1236: int len = str.length();
1237: if (len != 8) {
1238: throw new ParseException();
1239: }
1240: int value = 0;
1241: for (int p = 3; p < 7; p++) {
1242: int ch = str.charAt(p);
1243: switch (ch) {
1244: case '0':
1245: case '1':
1246: case '2':
1247: case '3':
1248: case '4':
1249: case '5':
1250: case '6':
1251: case '7':
1252: case '8':
1253: case '9':
1254: value = (value << 4) + (ch - '0');
1255: break;
1256: case 'a':
1257: case 'A':
1258: case 'b':
1259: case 'B':
1260: case 'c':
1261: case 'C':
1262: case 'd':
1263: case 'D':
1264: case 'e':
1265: case 'E':
1266: case 'f':
1267: case 'F':
1268: value = (value << 4) + 10
1269: + Character.toLowerCase((char) ch)
1270: - 'a';
1271: break;
1272: default:
1273: throw new ParseException();
1274: }
1275: }
1276: return new Character((char) value);
1277: }
1278: default:
1279: throw new ParseException();
1280: }
1281: } else {
1282: if (str.length() > 3) {
1283: throw new ParseException();
1284: }
1285: return new Character(str.charAt(1));
1286: }
1287: }
1288:
1289: /**
1290: * Creates an object from a number literal and a unit symbol
1291: *
1292: * @param number
1293: * a number object
1294: * @param numberString
1295: * a symbol of the number literal
1296: * @param unit
1297: * a unit symbol
1298: * @param context
1299: * a context in which the quantity is created
1300: */
1301: public static Object quantity(Number number, String numberString,
1302: String unit, Context context) {
1303: Hashtable units = context.unitTable;
1304: if (units != null) {
1305: QuantityFactory factory = (QuantityFactory) units.get(unit);
1306: if (factory != null) {
1307: return factory.make(number, unit);
1308: }
1309: }
1310: if ("f".equals(unit) || "F".equals(unit)) {
1311: return new Float(number.floatValue());
1312: } else if ("d".equals(unit) || "D".equals(unit)) {
1313: return new Double(number.doubleValue());
1314: } else if ("l".equals(unit) || "L".equals(unit)) {
1315: return new Long(number.longValue());
1316: } else if ("b".equals(unit) || "B".equals(unit)) {
1317: return decimalNumber(numberString, 10);
1318: }
1319: throw new PnutsException("unitName.notDefined",
1320: new Object[] { unit }, context);
1321: }
1322:
1323: /**
1324: * This method is called by the syntax "primitiveType(object)" and
1325: * "(primitiveType)object"
1326: *
1327: * @param context
1328: * the context
1329: * @param primitiveType
1330: * a primitive type
1331: * @param param
1332: * the parameter
1333: * @param flag
1334: * string <->number conversion
1335: */
1336: public static Object primitive(Context context,
1337: Class primitiveType, Object param, boolean flag) {
1338: return primitive(primitiveType, param, flag, 10);
1339: }
1340:
1341: static Object primitive(Class primitiveType, Object param,
1342: boolean flag, int radix) {
1343: if (primitiveType == int.class) {
1344: if (param instanceof Byte) {
1345: byte b = ((Byte) param).byteValue();
1346: return new Integer((int) b);
1347: } else if (param instanceof Character) {
1348: return new Integer((int) ((Character) param)
1349: .charValue());
1350: } else if (param instanceof Number) {
1351: return new Integer(((Number) param).intValue());
1352: } else if (flag) {
1353: if (param == null) {
1354: return null;
1355: } else {
1356: return new Integer(Integer.parseInt(param
1357: .toString().trim(), radix));
1358: }
1359: }
1360: } else if (primitiveType == byte.class) {
1361: if (param instanceof Character) {
1362: return new Byte((byte) ((Character) param).charValue());
1363: } else if (param instanceof Number) {
1364: return new Byte(((Number) param).byteValue());
1365: } else if (flag) {
1366: if (param == null) {
1367: return null;
1368: } else {
1369: return new Byte(Byte.parseByte(param.toString()
1370: .trim(), radix));
1371: }
1372: }
1373: } else if (primitiveType == char.class) {
1374: if (param instanceof Number) {
1375: int i = ((Number) param).intValue();
1376: return new Character((char) (i & 0xffff));
1377: } else if (param instanceof Character) {
1378: return param;
1379: }
1380: } else if (primitiveType == long.class) {
1381: if (param instanceof Number) {
1382: return new Long(((Number) param).longValue());
1383: } else if (flag) {
1384: if (param == null) {
1385: return null;
1386: } else {
1387: return new Long(Long.parseLong(param.toString()
1388: .trim(), radix));
1389: }
1390: }
1391: } else if (primitiveType == short.class) {
1392: if (param instanceof Character) {
1393: return new Short((short) ((Character) param)
1394: .charValue());
1395: } else if (param instanceof Number) {
1396: return new Short(((Number) param).shortValue());
1397: } else if (flag) {
1398: if (param == null) {
1399: return null;
1400: } else {
1401: return new Short(Short.parseShort(param.toString()
1402: .trim(), radix));
1403: }
1404: }
1405: } else if (primitiveType == double.class) {
1406: if (param instanceof Number) {
1407: return new Double(((Number) param).doubleValue());
1408: } else if (flag) {
1409: if (param == null) {
1410: return null;
1411: } else {
1412: return Double.valueOf(param.toString().trim());
1413: }
1414: }
1415: } else if (primitiveType == float.class) {
1416: if (param instanceof Number) {
1417: return new Float(((Number) param).floatValue());
1418: } else if (flag) {
1419: if (param == null) {
1420: return null;
1421: } else {
1422: return Float.valueOf(param.toString().trim());
1423: }
1424: }
1425: } else if (primitiveType == boolean.class) {
1426: if (flag) {
1427: return toBoolean(param);
1428: } else {
1429: if (param instanceof Boolean) {
1430: return param;
1431: } else if (param == null) {
1432: return Boolean.FALSE;
1433: }
1434: }
1435: }
1436: throw new ClassCastException("(" + primitiveType.getName()
1437: + ")" + Pnuts.format(param));
1438: }
1439:
1440: /**
1441: * Convert a given object to a boolean value
1442: */
1443: public static Boolean toBoolean(Object param) {
1444: if (param instanceof Boolean) {
1445: return (Boolean) param;
1446: } else if (param == null) {
1447: return Boolean.FALSE;
1448: } else if (param instanceof Double) {
1449: double value = ((Double) param).doubleValue();
1450: return Boolean
1451: .valueOf(value != 0.0 && !Double.isNaN(value));
1452: } else if (param instanceof Float) {
1453: float value = ((Float) param).floatValue();
1454: return Boolean
1455: .valueOf(value != 0.0f && !Float.isNaN(value));
1456: } else if (param instanceof Number) {
1457: return eq(ZERO, param) ? Boolean.FALSE : Boolean.TRUE;
1458: } else if (param instanceof String) {
1459: return ((String) param).length() > 0 ? Boolean.TRUE
1460: : Boolean.FALSE;
1461: } else {
1462: return Boolean.TRUE;
1463: }
1464: }
1465:
1466: /**
1467: * This method is called by the syntax "(Class)object"
1468: *
1469: * @param context
1470: * the context
1471: * @param type
1472: * the type
1473: * @param flag
1474: * object_array <->primitive_array conversion
1475: */
1476: public static Object cast(Context context, Class type,
1477: Object object, boolean flag) {
1478: if (type.isPrimitive()) {
1479: return primitive(context, type, object, false);
1480: }
1481: if (object == null) {
1482: return null;
1483: } else if (type.isInstance(object)) {
1484: return object;
1485: } else if (type.isArray()) {
1486: if (flag) {
1487: try {
1488: return transform(type, object, context);
1489: } catch (IllegalArgumentException e) {
1490: throw new ClassCastException("("
1491: + getClassName(type) + ")"
1492: + Pnuts.format(object));
1493: }
1494: } else {
1495: return object;
1496: }
1497: }
1498: throw new ClassCastException("(" + getClassName(type) + ")"
1499: + Pnuts.format(object));
1500: }
1501:
1502: /**
1503: * Check if the parameter is an array
1504: *
1505: * @param obj
1506: * the object to be checked
1507: * @return true if the obj is an array, false otherwise.
1508: */
1509: public final static boolean isArray(Object obj) {
1510: return (obj instanceof Object[] || obj instanceof int[]
1511: || obj instanceof char[] || obj instanceof boolean[]
1512: || obj instanceof byte[] || obj instanceof double[]
1513: || obj instanceof long[] || obj instanceof short[] || obj instanceof float[]);
1514: }
1515:
1516: /**
1517: * Gets an array's length
1518: *
1519: * @param array
1520: * the array
1521: * @return the length
1522: */
1523: public final static int getArrayLength(Object array) {
1524: if (array instanceof Object[]) {
1525: return ((Object[]) array).length;
1526: } else if (array instanceof int[]) {
1527: return ((int[]) array).length;
1528: } else if (array instanceof byte[]) {
1529: return ((byte[]) array).length;
1530: } else if (array instanceof char[]) {
1531: return ((char[]) array).length;
1532: } else if (array instanceof float[]) {
1533: return ((float[]) array).length;
1534: } else if (array instanceof double[]) {
1535: return ((double[]) array).length;
1536: } else if (array instanceof boolean[]) {
1537: return ((boolean[]) array).length;
1538: } else if (array instanceof long[]) {
1539: return ((long[]) array).length;
1540: } else if (array instanceof short[]) {
1541: return ((short[]) array).length;
1542: } else {
1543: throw new IllegalArgumentException(String.valueOf(array));
1544: }
1545: }
1546:
1547: /**
1548: * Range expression 'target[idx1..idx2]'.
1549: *
1550: * @return the intersection of the whole range of the target object and the
1551: * range [idx1..idx2]. This method never throw
1552: * ArrayIndexOutOfBoundsException.
1553: */
1554: public static Object getRange(Object target, Object idx1,
1555: Object idx2, Context context) {
1556: return context.config.getRange(context, target, idx1, idx2);
1557: }
1558:
1559: /**
1560: * This method is called by the syntax "id[from..to] = sth"
1561: */
1562: public static Object setRange(Object target, Object idx1,
1563: Object idx2, Object expr, Context context) {
1564: return context.config.setRange(context, target, idx1, idx2,
1565: expr);
1566: }
1567:
1568: public static String replaceChar(String str, Number n, Object expr) {
1569: int len = str.length();
1570: int idx = ((Number) n).intValue();
1571: if (idx < 0 && idx >= -len) {
1572: idx += len;
1573: }
1574: StringBuffer sbuf = new StringBuffer(str);
1575: if (expr instanceof Character) {
1576: sbuf.setCharAt(idx, ((Character) expr).charValue());
1577: } else if (expr instanceof String) {
1578: sbuf.replace(idx, idx + 1, expr.toString());
1579: } else if (expr instanceof char[]) {
1580: sbuf.replace(idx, idx + 1, new String((char[]) expr));
1581: } else {
1582: throw new IllegalArgumentException(String.valueOf(expr));
1583: }
1584: return sbuf.toString();
1585: }
1586:
1587: protected static void checkException(Context context,
1588: Throwable throwable) {
1589: if (throwable instanceof ParseException) {
1590: context.beginLine = ((ParseException) throwable)
1591: .getErrorLine();
1592: }
1593: StackFrame stackFrame = context.stackFrame;
1594: if (stackFrame != null) {
1595: StackFrame frame = stackFrame;
1596: if (stackFrame.parent != null) {
1597: NamedValue b = frame
1598: .lookup(Context.exceptionHandlerTableSymbol);
1599: if (b != null) {
1600: checkException(context, throwable, (TypeMap) b
1601: .get());
1602: return;
1603: }
1604: }
1605: }
1606: TypeMap tmap = (TypeMap) context
1607: .resolveSymbol(Context.exceptionHandlerTableSymbol);
1608: if (tmap != null) {
1609: checkException(context, throwable, tmap);
1610: return;
1611: }
1612: if (throwable instanceof Escape) {
1613: throw (Escape) throwable;
1614: } else if (throwable instanceof PnutsException) {
1615: throw (PnutsException) throwable;
1616: } else {
1617: throw new PnutsException(throwable, context);
1618: }
1619: }
1620:
1621: /**
1622: * Check if any exception handler is registered to the specified exception.
1623: * If any an exception handler is executed. If not, the exception is thrown.
1624: *
1625: * @param context
1626: * the Context in which the exception is checked
1627: * @param throwable
1628: * the exception
1629: * @param tmap
1630: * the exception handler table
1631: */
1632: protected static void checkException(Context context,
1633: Throwable throwable, TypeMap tmap) {
1634: if (DEBUG) {
1635: System.out.println("checkException " + throwable + ", "
1636: + tmap);
1637: }
1638: Throwable e = throwable;
1639: if (e instanceof PnutsException) {
1640: throwable = ((PnutsException) e).getThrowable();
1641: }
1642: if (throwable instanceof Escape) {
1643: if (DEBUG) {
1644: System.out.println(throwable);
1645: }
1646: throw (Escape) throwable;
1647: }
1648: if (throwable instanceof ThreadDeath) {
1649: throw (ThreadDeath) throwable;
1650: }
1651: PnutsFunction f = null;
1652: while (tmap != null) {
1653: Class type = tmap.type;
1654: if (type == null || type.isInstance(throwable)) {
1655: f = (PnutsFunction) tmap.value;
1656: break;
1657: }
1658: tmap = tmap.next;
1659: }
1660: if (f == null) {
1661: if (e instanceof PnutsException) {
1662: throw (PnutsException) e;
1663: } else {
1664: throw new PnutsException(throwable, context);
1665: }
1666: } else {
1667: Object ret = f.exec(new Object[] { throwable }, context);
1668: PrintWriter pw = context.getWriter();
1669: if (pw != null) {
1670: pw.flush();
1671: }
1672: throw new Jump(ret);
1673: }
1674: }
1675:
1676: /**
1677: * This method is called when catch() function is called in a
1678: * package(non-local) scope
1679: *
1680: * @param type
1681: * the exception type of which an exception handler is registered
1682: * @param func
1683: * the function to be registered as an exception handler
1684: * @param context
1685: * the context in which the exception handler is registered
1686: */
1687: protected static void catchException(Class type,
1688: PnutsFunction func, Context context) {
1689: if (DEBUG) {
1690: System.out.println("catchException " + type.getName()
1691: + ": " + func.unparse(1));
1692: }
1693: Package pkg = context.getCurrentPackage();
1694: TypeMap typemap = null;
1695: if (type != Throwable.class) {
1696: typemap = (TypeMap) pkg.get(
1697: Context.exceptionHandlerTableSymbol, context);
1698: }
1699: pkg.set(Context.exceptionHandlerTableSymbol, new TypeMap(type,
1700: func, typemap), context);
1701: }
1702:
1703: public static void throwException(Object arg, Context context) {
1704: if (arg instanceof Throwable) {
1705: throw new PnutsException((Throwable) arg, context);
1706: } else {
1707: throw new PnutsException(String.valueOf(arg));
1708: }
1709: }
1710:
1711: public static void setExitHook(Context context,
1712: final PnutsFunction func) {
1713: context.setExitHook(new Executable() {
1714: public Object run(Context ctx) {
1715: return func.call(new Object[] {}, ctx);
1716: }
1717: });
1718: }
1719:
1720: /**
1721: * This method is called by the syntax "target[key]"
1722: */
1723: public static Object getElement(Object target, Object key,
1724: Context context) {
1725: return context.config.getElement(context, target, key);
1726: }
1727:
1728: public static Object getElementAt(Object target, int idx,
1729: Context context) {
1730: try {
1731: if (target == null) {
1732: return null;
1733: } else {
1734: return context.config.getElement(context, target,
1735: new Integer(idx));
1736: }
1737: } catch (IndexOutOfBoundsException e) {
1738: return null;
1739: }
1740: }
1741:
1742: /**
1743: * This method is called by the syntax "target[key] = value"
1744: */
1745: public static void setElement(Object target, Object key,
1746: Object value, Context context) {
1747: context.config.setElement(context, target, key, value);
1748: }
1749:
1750: /*
1751: * Converts an object to Enumeration This method is called by foreach
1752: * statements.
1753: *
1754: * @param target the target object to be converted to an Enumeration @param
1755: * context the context in which the target object is converted to an
1756: * Enumeration @return an Enumeration object converted from the target
1757: * object. null if the target object could not be converted to an
1758: * Enumeration.
1759: */
1760: public static Enumeration toEnumeration(Object target,
1761: Context context) {
1762: return context.config.toEnumeration(target);
1763: }
1764:
1765: /**
1766: * Call a function
1767: *
1768: * @param context
1769: * the context in which the function is called
1770: * @param func
1771: * the function to be called
1772: * @param args
1773: * the arguments
1774: */
1775: protected final static Object callFunction(Context context,
1776: PnutsFunction func, Object[] args) {
1777: return func.exec(args, context);
1778: }
1779:
1780: /**
1781: * This method is called by the syntax "funcOrClass(args...)"
1782: */
1783: public final static Object call(Context context, Object target,
1784: Object[] args, Class[] casts) {
1785: if (target instanceof PnutsFunction) {
1786: return ((PnutsFunction) target).exec(args, context);
1787: } else if (target instanceof Class) {
1788: Class c = (Class) target;
1789: if (c.isPrimitive()) {
1790: try {
1791: int nargs = args.length;
1792: switch (nargs) {
1793: case 1:
1794: return primitive(context, c, args[0], true);
1795: case 2:
1796: int radix = ((Integer) args[1]).intValue();
1797: return primitive(c, args[0], true, radix);
1798: default:
1799: throw new IllegalArgumentException(Pnuts
1800: .format(args));
1801: }
1802: } catch (NumberFormatException e) {
1803: return null;
1804: }
1805: } else if (c.isArray()) {
1806: return cast(context, c, args[0], true);
1807: } else {
1808: return context.config.callConstructor(context, c, args,
1809: casts);
1810: }
1811: } else if (target instanceof Callable) {
1812: return ((Callable) target).call(args, context);
1813: }
1814: throw new PnutsException("funcOrType.expected",
1815: new Object[] { Pnuts.format(target) }, context);
1816: }
1817:
1818: public final static Object call(Context context, Object target,
1819: Object[] args, Class[] casts, int line, int column) {
1820: Object ret;
1821: if (target instanceof PnutsFunction) {
1822: Function caller = context.frame;
1823: PnutsFunction f = (PnutsFunction) target;
1824: try {
1825: ret = f.exec(args, context);
1826: } catch (PnutsException p) {
1827: if (caller != null) {
1828: p.backtrace(new PnutsException.TraceInfo(f.name,
1829: args, caller.file, line, column));
1830: } else {
1831: p.backtrace(new PnutsException.TraceInfo(f.name,
1832: args, null, line, column));
1833: }
1834: throw p;
1835: }
1836: } else if (target instanceof Class) {
1837: Class c = (Class) target;
1838: if (c.isPrimitive()) {
1839: try {
1840: int nargs = args.length;
1841: switch (nargs) {
1842: case 1:
1843: ret = primitive(context, c, args[0], true);
1844: break;
1845: case 2:
1846: int radix = ((Integer) args[1]).intValue();
1847: ret = primitive(c, args[0], true, radix);
1848: break;
1849: default:
1850: throw new IllegalArgumentException(Pnuts
1851: .format(args));
1852: }
1853: } catch (NumberFormatException e) {
1854: context.updateLine(null, line, column);
1855: return null;
1856: }
1857: } else if (c.isArray()) {
1858: ret = cast(context, c, args[0], true);
1859: } else {
1860: ret = context.config.callConstructor(context, c, args,
1861: casts);
1862: }
1863: } else if (target instanceof Callable) {
1864: return ((Callable) target).call(args, context);
1865: } else {
1866: Callable c = context.config.toCallable(target);
1867: if (c != null) {
1868: return c.call(args, context);
1869: } else {
1870: throw new PnutsException("funcOrType.expected",
1871: new Object[] { Pnuts.format(target) }, context);
1872: }
1873: }
1874: context.updateLine(null, line, column);
1875: return ret;
1876: }
1877:
1878: protected static Object newInstance(Context context, Class c,
1879: Object[] args, Class[] casts) {
1880: return context.config.callConstructor(context, c, args, casts);
1881: }
1882:
1883: protected static Object makeArray(Object[] parameters,
1884: Context context) {
1885: return context.config.makeArray(parameters, context);
1886: }
1887:
1888: protected static Map createMap(int size, Context context) {
1889: return context.config.createMap(size, context);
1890: }
1891:
1892: protected static List createList(Context context) {
1893: return context.config.createList();
1894: }
1895:
1896: protected static void jump(Object v) {
1897: throw new Jump(v);
1898: }
1899:
1900: protected static void escape(Object v) {
1901: throw new Escape(v);
1902: }
1903:
1904: /**
1905: * Set line number information for error reporting
1906: *
1907: * @deprecated
1908: */
1909: protected static void setLine(Context context, int beginLine,
1910: int beginColumn) {
1911: context.updateLine(null, beginLine, beginColumn);
1912: }
1913:
1914: public static void setLine(Context context, int line) {
1915: context.updateLine(line);
1916: }
1917:
1918: protected static int getBeginLine(Context context) {
1919: return context.beginLine;
1920: }
1921:
1922: protected static int getBeginColumn(Context context) {
1923: return context.beginColumn;
1924: }
1925:
1926: protected static int getEndLine(Context context) {
1927: return context.endLine;
1928: }
1929:
1930: protected static Function getFunction(PnutsFunction pf, int nargs) {
1931: return pf.get(nargs);
1932: }
1933:
1934: protected static Enumeration getFunctions(PnutsFunction pf) {
1935: return pf.elements();
1936: }
1937:
1938: protected static Runtime getRuntime(Context context) {
1939: return context.runtime;
1940: }
1941:
1942: protected static Object getScriptSource(Context context) {
1943: return context.getScriptSource();
1944: }
1945:
1946: protected static Function getFunction(Context context) {
1947: return context.frame;
1948: }
1949:
1950: protected static void setPackage(Package pkg, Context context) {
1951: pkg.init(context);
1952: }
1953:
1954: private static char[] hexDigit = { '0', '1', '2', '3', '4', '5',
1955: '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
1956:
1957: private final static int DEFAULT_FORMAT_MAX_LENGTH = 512;
1958:
1959: /**
1960: * Get the String representation of the specified object.
1961: *
1962: * @param object
1963: * the target object.
1964: * @param maxArrayLength
1965: * When the target object is an array and maxArrayLength is
1966: * greater than zero, only the first maxArrayLength elements are
1967: * printed and the rest of the elements are omitted as "...".
1968: */
1969: public static String format(Object object, int maxArrayLength) {
1970: return format(object, maxArrayLength, DEFAULT_FORMAT_MAX_LENGTH);
1971: }
1972:
1973: private final static char[] defaultParen = { '[', ']' };
1974:
1975: public static String format(Object object, int maxArrayLength,
1976: int maxFormatSize) {
1977: return format(object, maxArrayLength, maxFormatSize,
1978: defaultParen);
1979: }
1980:
1981: public static String format(Object object, int maxArrayLength,
1982: int maxFormatSize, char[] paren) {
1983: StringBuffer sb = new StringBuffer();
1984: try {
1985: format(object, maxArrayLength, maxFormatSize, paren,
1986: new HashSet(), sb);
1987: } catch (TooLongFormat toolong) {
1988: sb.append("....");
1989: }
1990: return sb.toString();
1991: }
1992:
1993: static class TooLongFormat extends RuntimeException {
1994: }
1995:
1996: static void format(Object object, int maxArrayLength,
1997: int maxFormatSize, char[] paren, Set formattedObjects,
1998: StringBuffer sb) {
1999: if (maxFormatSize < sb.length()) {
2000: throw new TooLongFormat();
2001: }
2002: if (object == null) {
2003: sb.append("null");
2004: return;
2005: } else if (!(isArray(object))) {
2006: if (object instanceof String) {
2007: String str = (String) object;
2008: int len = str.length();
2009: sb.append('"');
2010: for (int i = 0; i < len; i++) {
2011: char ch = str.charAt(i);
2012: switch (ch) {
2013: case '\\':
2014: sb.append("\\\\");
2015: break;
2016: case '\b':
2017: sb.append("\\b");
2018: break;
2019: case '\f':
2020: sb.append("\\f");
2021: break;
2022: case '\t':
2023: sb.append("\\t");
2024: break;
2025: case '\n':
2026: sb.append("\\n");
2027: break;
2028: case '\r':
2029: sb.append("\\r");
2030: break;
2031: case '"':
2032: sb.append("\\\"");
2033: break;
2034: default:
2035: char[] cbuf = new char[1];
2036: cbuf[0] = ch;
2037: byte[] conv = new String(cbuf).getBytes();
2038: if (ch == '?'
2039: || !Character.isISOControl(ch)
2040: && !(conv.length == 1 && conv[0] == (byte) '?')) {
2041: sb.append(ch);
2042: } else {
2043: sb.append("\\u"
2044: + hexDigit[(ch >> 12) & 0xF]
2045: + hexDigit[(ch >> 8) & 0xF]
2046: + hexDigit[(ch >> 4) & 0xF]
2047: + hexDigit[(ch >> 0) & 0xF]);
2048: }
2049: break;
2050: }
2051: }
2052: sb.append('"');
2053: return;
2054: } else if (object instanceof Character) {
2055: String s;
2056: char ch = ((Character) object).charValue();
2057: switch (ch) {
2058: case '\\':
2059: s = "'\\'";
2060: case '\b':
2061: s = "'\\b'";
2062: case '\f':
2063: s = "'\\f'";
2064: case '\t':
2065: s = "'\\t'";
2066: case '\n':
2067: s = "'\\n'";
2068: case '\r':
2069: s = "'\\r'";
2070: case '\'':
2071: s = "'\\''";
2072: case '?':
2073: s = "'?'";
2074: sb.append(s);
2075: return;
2076: default:
2077: char[] buf = new char[1];
2078: buf[0] = ch;
2079: byte[] conv = new String(buf).getBytes();
2080: if (!Character.isISOControl(ch)
2081: && !(conv.length == 1 && conv[0] == (byte) '?')) {
2082: sb.append('\'');
2083: sb.append(ch);
2084: sb.append('\'');
2085: return;
2086: }
2087: sb.append("'\\u" + hexDigit[(ch >> 12) & 0xF]
2088: + hexDigit[(ch >> 8) & 0xF]
2089: + hexDigit[(ch >> 4) & 0xF]
2090: + hexDigit[(ch >> 0) & 0xF] + "'");
2091: return;
2092: }
2093: } else if (object instanceof Class) {
2094: Class c = (Class) object;
2095: String suffix = null;
2096: if (c.isInterface()) {
2097: suffix = " interface";
2098: } else if (c.isPrimitive()) {
2099: suffix = " type";
2100: } else {
2101: suffix = " class";
2102: }
2103: sb.append(getClassName(c) + suffix);
2104: return;
2105: } else if (object instanceof Byte) {
2106: byte value = ((Byte) object).byteValue();
2107: sb.append("#" + hexDigit[(value >> 4) & 0x0f]
2108: + hexDigit[value & 0x0f]);
2109: return;
2110: } else if (object instanceof Float) {
2111: sb.append(object.toString() + "f");
2112: return;
2113: } else if (object instanceof Calendar) {
2114: Calendar cal = (Calendar) object;
2115: DateFormat f = DateFormat.getDateTimeInstance();
2116: f.setTimeZone(cal.getTimeZone());
2117: sb.append(f.format(cal.getTime()));
2118: return;
2119: } else {
2120: sb.append(object.toString());
2121: return;
2122: }
2123: }
2124: int length = getArrayLength(object);
2125: int last = length;
2126: sb.append(paren[0]);
2127:
2128: if (length > 0) {
2129: Object obj = Array.get(object, 0);
2130: if (obj != object) {
2131: formatElement(obj, maxArrayLength, maxFormatSize,
2132: paren, formattedObjects, sb);
2133: } else {
2134: sb.append(String.valueOf(obj));
2135: }
2136: }
2137: if (maxArrayLength > 0 && maxArrayLength < length) {
2138: last = maxArrayLength;
2139: }
2140: for (int j = 1; j < last; j++) {
2141: Object obj = Array.get(object, j);
2142: sb.append(", ");
2143: if (obj != object) {
2144: formatElement(obj, maxArrayLength, maxFormatSize,
2145: paren, formattedObjects, sb);
2146: } else {
2147: sb.append(String.valueOf(obj));
2148: }
2149: }
2150: if (maxArrayLength > 0 && maxArrayLength < length) {
2151: sb.append(", ...");
2152: }
2153: sb.append(paren[1]);
2154: }
2155:
2156: static void formatElement(Object obj, int maxArrayLength,
2157: int maxFormatSize, char[] paren, Set formattedObjects,
2158: StringBuffer sb) {
2159: if (formattedObjects.contains(obj)) {
2160: if (obj == null) {
2161: sb.append("null");
2162: } else if (!(obj instanceof Object[])) {
2163: sb.append(obj.toString());
2164: } else {
2165: sb.append(obj.getClass().getName() + "@"
2166: + obj.hashCode());
2167: }
2168: } else {
2169: formattedObjects.add(obj);
2170: format(obj, maxArrayLength, maxFormatSize, paren,
2171: formattedObjects, sb);
2172: formattedObjects.remove(obj);
2173: }
2174: }
2175:
2176: static String getClassName(Class cls) {
2177: if (cls.isArray()) {
2178: StringBuffer buf = new StringBuffer();
2179: for (; cls.isArray(); cls = cls.getComponentType()) {
2180: buf.append("[]");
2181: }
2182: buf.insert(0, cls.getName());
2183: return buf.toString();
2184: } else {
2185: return cls.getName();
2186: }
2187: }
2188:
2189: /**
2190: * Add 1 to an object (integer)
2191: */
2192: public final static Object add1(Object n) {
2193: return UnaryOperator.Add1.instance.operateOn(n);
2194: }
2195:
2196: protected final static Object add1(Object n, Context context) {
2197: return context._add1.operateOn(n);
2198: }
2199:
2200: /**
2201: * Subtracts 1 from a object (integer)
2202: */
2203: public final static Object subtract1(Object n) {
2204: return UnaryOperator.Subtract1.instance.operateOn(n);
2205: }
2206:
2207: protected final static Object subtract1(Object n, Context context) {
2208: return context._subtract1.operateOn(n);
2209: }
2210:
2211: /**
2212: * Negates an number
2213: */
2214: public final static Object negate(Object n) {
2215: return UnaryOperator.Negate.instance.operateOn(n);
2216: }
2217:
2218: protected final static Object negate(Object n, Context context) {
2219: return context._negate.operateOn(n);
2220: }
2221:
2222: public final static Object not(Object n) {
2223: return UnaryOperator.Not.instance.operateOn(n);
2224: }
2225:
2226: protected final static Object not(Object n, Context context) {
2227: return context._not.operateOn(n);
2228: }
2229:
2230: /**
2231: * + operation
2232: */
2233: public final static Object add(Object n1, Object n2) {
2234: return BinaryOperator.Add.instance.operateOn(n1, n2);
2235: }
2236:
2237: protected final static Object add(Object n1, Object n2,
2238: Context context) {
2239: return context._add.operateOn(n1, n2);
2240: }
2241:
2242: /**
2243: * - operation
2244: */
2245: public final static Object subtract(Object n1, Object n2) {
2246: return BinaryOperator.Subtract.instance.operateOn(n1, n2);
2247: }
2248:
2249: protected final static Object subtract(Object n1, Object n2,
2250: Context context) {
2251: return context._subtract.operateOn(n1, n2);
2252: }
2253:
2254: /**
2255: * * operation
2256: */
2257: public final static Object multiply(Object n1, Object n2) {
2258: return BinaryOperator.Multiply.instance.operateOn(n1, n2);
2259: }
2260:
2261: protected final static Object multiply(Object n1, Object n2,
2262: Context context) {
2263: return context._multiply.operateOn(n1, n2);
2264: }
2265:
2266: /**
2267: * / operation
2268: */
2269: public final static Object divide(Object n1, Object n2) {
2270: return BinaryOperator.Divide.instance.operateOn(n1, n2);
2271: }
2272:
2273: protected final static Object divide(Object n1, Object n2,
2274: Context context) {
2275: try {
2276: return context._divide.operateOn(n1, n2);
2277: } catch (Exception e) {
2278: throw new PnutsException(e, context);
2279: }
2280: }
2281:
2282: /**
2283: * % operation
2284: */
2285: public final static Object mod(Object n1, Object n2) {
2286: return BinaryOperator.Mod.instance.operateOn(n1, n2);
2287: }
2288:
2289: protected final static Object mod(Object n1, Object n2,
2290: Context context) {
2291: return context._mod.operateOn(n1, n2);
2292: }
2293:
2294: /**
2295: * < < operation
2296: */
2297: public final static Object shiftLeft(Object n1, Object n2) {
2298: return BinaryOperator.ShiftLeft.instance.operateOn(n1, n2);
2299: }
2300:
2301: protected final static Object shiftLeft(Object n1, Object n2,
2302: Context context) {
2303: return context._shiftLeft.operateOn(n1, n2);
2304: }
2305:
2306: /**
2307: * >> operation
2308: */
2309: public final static Object shiftRight(Object n1, Object n2) {
2310: return BinaryOperator.ShiftRight.instance.operateOn(n1, n2);
2311: }
2312:
2313: protected final static Object shiftRight(Object n1, Object n2,
2314: Context context) {
2315: return context._shiftRight.operateOn(n1, n2);
2316: }
2317:
2318: /**
2319: * >>> operation
2320: */
2321: public static Object shiftArithmetic(Object n1, Object n2) {
2322: return BinaryOperator.ShiftArithmetic.instance
2323: .operateOn(n1, n2);
2324: }
2325:
2326: protected final static Object shiftArithmetic(Object n1, Object n2,
2327: Context context) {
2328: return context._shiftArithmetic.operateOn(n1, n2);
2329: }
2330:
2331: /**
2332: * | operation
2333: */
2334: public final static Object or(Object n1, Object n2) {
2335: return BinaryOperator.Or.instance.operateOn(n1, n2);
2336: }
2337:
2338: protected final static Object or(Object n1, Object n2,
2339: Context context) {
2340: return context._or.operateOn(n1, n2);
2341: }
2342:
2343: /**
2344: * & operation
2345: */
2346: public final static Object and(Object n1, Object n2) {
2347: return BinaryOperator.And.instance.operateOn(n1, n2);
2348: }
2349:
2350: protected final static Object and(Object n1, Object n2,
2351: Context context) {
2352: return context._and.operateOn(n1, n2);
2353: }
2354:
2355: /**
2356: * ^ operation
2357: */
2358: public final static Object xor(Object n1, Object n2) {
2359: return BinaryOperator.Xor.instance.operateOn(n1, n2);
2360: }
2361:
2362: protected final static Object xor(Object n1, Object n2,
2363: Context context) {
2364: return context._xor.operateOn(n1, n2);
2365: }
2366:
2367: /**
2368: * < operation
2369: */
2370: public final static boolean lt(Object n1, Object n2) {
2371: return BooleanOperator.LT.instance.operateOn(n1, n2);
2372: }
2373:
2374: protected final static boolean lt(Object n1, Object n2,
2375: Context context) {
2376: return context._lt.operateOn(n1, n2);
2377: }
2378:
2379: /**
2380: * > operation
2381: */
2382: public final static boolean gt(Object n1, Object n2) {
2383: return BooleanOperator.GT.instance.operateOn(n1, n2);
2384: }
2385:
2386: protected final static boolean gt(Object n1, Object n2,
2387: Context context) {
2388: return context._gt.operateOn(n1, n2);
2389: }
2390:
2391: /**
2392: * >= operation
2393: */
2394: public final static boolean ge(Object n1, Object n2) {
2395: return BooleanOperator.GE.instance.operateOn(n1, n2);
2396: }
2397:
2398: protected final static boolean ge(Object n1, Object n2,
2399: Context context) {
2400: return context._ge.operateOn(n1, n2);
2401: }
2402:
2403: /**
2404: * <= operation
2405: */
2406: public final static boolean le(Object n1, Object n2) {
2407: return BooleanOperator.LE.instance.operateOn(n1, n2);
2408: }
2409:
2410: protected final static boolean le(Object n1, Object n2,
2411: Context context) {
2412: return context._le.operateOn(n1, n2);
2413: }
2414:
2415: /**
2416: * == operation
2417: */
2418: public final static boolean eq(Object n1, Object n2) {
2419: return BooleanOperator.EQ.instance.operateOn(n1, n2);
2420: }
2421:
2422: protected final static boolean eq(Object n1, Object n2,
2423: Context context) {
2424: return context._eq.operateOn(n1, n2);
2425: }
2426:
2427: /**
2428: * != operation
2429: */
2430: public final static boolean ne(Object n1, Object n2) {
2431: return !BooleanOperator.EQ.instance.operateOn(n1, n2);
2432: }
2433:
2434: protected final static boolean ne(Object n1, Object n2,
2435: Context context) {
2436: return !context._eq.operateOn(n1, n2);
2437: }
2438:
2439: /**
2440: * Compares n1 with n2
2441: */
2442: public final static int compareTo(Object n1, Object n2) {
2443: if (gt(n1, n2)) {
2444: return 1;
2445: } else if (lt(n1, n2)) {
2446: return -1;
2447: } else {
2448: return 0;
2449: }
2450: }
2451:
2452: protected final static int compareTo(Object n1, Object n2,
2453: Context context) {
2454: if (gt(n1, n2, context)) {
2455: return 1;
2456: } else if (lt(n1, n2, context)) {
2457: return -1;
2458: } else {
2459: return 0;
2460: }
2461: }
2462:
2463: /**
2464: * Compare two objects
2465: *
2466: * Elements of List and array are recursively compared.
2467: */
2468: public static int compareObjects(Object e1, Object e2) {
2469: if (e1 == e2) {
2470: return 0;
2471: }
2472: if (e1 instanceof List) {
2473: if (e2 instanceof List) {
2474: return compareLists((List) e1, (List) e2);
2475: } else if (Runtime.isArray(e2)) {
2476: return compareListToArray((List) e1, e2);
2477: }
2478: } else if (Runtime.isArray(e1)) {
2479: if (e2 instanceof List) {
2480: return -compareListToArray((List) e2, e1);
2481: } else if (Runtime.isArray(e2)) {
2482: return compareArrays(e1, e2);
2483: }
2484: }
2485: if (e1 instanceof Comparable) {
2486: return ((Comparable) e1).compareTo(e2);
2487: } else if (e2 instanceof Comparable) {
2488: return -((Comparable) e2).compareTo(e1);
2489: } else {
2490: throw new IllegalArgumentException();
2491: }
2492: }
2493:
2494: static int compareLists(List ol1, List ol2) {
2495: int sz2 = ol2.size();
2496: int sz1 = ol1.size();
2497: int min = (sz2 > sz1) ? sz1 : sz2;
2498: for (int i = 0; i < min; i++) {
2499: Object e1 = ol1.get(i);
2500: Object e2 = ol2.get(i);
2501: int c = compareObjects(e1, e2);
2502: if (c != 0) {
2503: return c;
2504: }
2505: }
2506: if (sz1 == sz2) {
2507: return 0;
2508: } else {
2509: return (sz1 > sz2) ? 1 : -1;
2510: }
2511: }
2512:
2513: static int compareListToArray(List ol, Object array) {
2514: int len = Array.getLength(array);
2515: int sz = ol.size();
2516: int min = (len > sz) ? sz : len;
2517: for (int i = 0; i < min; i++) {
2518: Object e1 = ol.get(i);
2519: Object e2 = Array.get(array, i);
2520: int c = compareObjects(e1, e2);
2521: if (c != 0) {
2522: return c;
2523: }
2524: }
2525: if (sz == len) {
2526: return 0;
2527: } else {
2528: return (sz > len) ? 1 : -1;
2529: }
2530: }
2531:
2532: static int compareArrays(Object a1, Object a2) {
2533: int len = Array.getLength(a1);
2534: int sz = Array.getLength(a2);
2535: int min = (len > sz) ? sz : len;
2536: for (int i = 0; i < min; i++) {
2537: Object e1 = Array.get(a1, i);
2538: Object e2 = Array.get(a2, i);
2539: int c = compareObjects(e1, e2);
2540: if (c != 0) {
2541: return c;
2542: }
2543: }
2544: if (sz == len) {
2545: return 0;
2546: } else {
2547: return (sz > len) ? 1 : -1;
2548: }
2549: }
2550:
2551: /**
2552: * Returns a URL of a script
2553: */
2554: public static URL getScriptURL(String name, Context context) {
2555: URL url = null;
2556: // try {
2557: url = Pnuts.getResource(name, context);
2558: if (url == null) {
2559: url = Pnuts.getResource("/" + name, context);
2560: }
2561: if (url == null)
2562: return null;
2563: // URLConnection conn = url.openConnection();
2564: // conn.connect();
2565: /*
2566: if (context.verbose) {
2567: PrintWriter out = context.getTerminalWriter();
2568: if (out != null) {
2569: out.println("[loading " + url + "]");
2570: out.flush();
2571: }
2572: }
2573: */
2574:
2575: // } catch (IOException ioe) {
2576: // return null;
2577: // }
2578: return url;
2579: }
2580:
2581: /**
2582: * This method is called by Pnuts.load() when the property
2583: * "pnuts.compiled.script.prefix" is defined, to load pre-compiled scripts.
2584: *
2585: * @param name
2586: * the script name
2587: * @param context
2588: * the context in which the class is loaded.
2589: * @return pnuts.lang.Runtime object if the class is found, otherwise null.
2590: */
2591: public static Executable getCompiledScript(String name,
2592: Context context) {
2593: String prefix = Pnuts.getCompiledClassPrefix();
2594: Object rt = null;
2595: try {
2596: String cname = name.replace('/', '.').replace('-', '_');
2597: if (prefix != null) {
2598: cname = prefix + cname;
2599: }
2600: Class cls = Pnuts.loadClass(cname, context);
2601: rt = cls.newInstance();
2602: if (rt instanceof Executable) {
2603: return (Executable) rt;
2604: }
2605: } catch (ClassNotFoundException e0) { /* ignore */
2606: } catch (InstantiationException e1) { /* ignore */
2607: } catch (IllegalAccessException e2) { /* ignore */
2608: }
2609: return null;
2610: }
2611:
2612: /**
2613: * Gets a URL from a File
2614: *
2615: * @param file
2616: * the File object
2617: * @return the resulting URL object
2618: */
2619: public static URL fileToURL(File file) throws IOException {
2620: String path = new File(file.getCanonicalPath())
2621: .getAbsolutePath();
2622: if (File.separatorChar != '/') {
2623: path = path.replace(File.separatorChar, '/');
2624: }
2625: if (!path.startsWith("/")) {
2626: path = "/" + path;
2627: }
2628: if (!path.endsWith("/") && file.isDirectory()) {
2629: path = path + "/";
2630: }
2631: return new URL("file", "", path);
2632: }
2633:
2634: /**
2635: * Gets a Reader to read script files
2636: *
2637: * If context.getScriptEncoding() is non-null, it would be used as the
2638: * encoding. Otherwise, the platform encoding is used.
2639: *
2640: * @param in
2641: * the input stream
2642: * @param context
2643: * the executing context
2644: * @return the resulting reader object
2645: */
2646: public static Reader getScriptReader(InputStream in, Context context) {
2647: String encoding = context.encoding;
2648: if (encoding == null) {
2649: return new InputStreamReader(in);
2650: } else {
2651: try {
2652: return new InputStreamReader(in, encoding);
2653: } catch (UnsupportedEncodingException e) {
2654: throw new PnutsException(e, context);
2655: }
2656: }
2657: }
2658:
2659: public static void printError(Throwable t, Context context) {
2660: context.onError(t);
2661: PrintWriter err = context.getErrorWriter();
2662: if (err != null) {
2663: if (context.verbose) {
2664: t.printStackTrace(err);
2665: } else {
2666: err.println(t);
2667: }
2668: err.flush();
2669: } else {
2670: t.printStackTrace();
2671: }
2672: }
2673:
2674: protected static String getMessage(String bundleName, String key,
2675: Object a[]) {
2676: try {
2677: ResourceBundle bundle = ResourceBundle
2678: .getBundle(bundleName);
2679: String fmt = bundle.getString(key);
2680: if (fmt == null) {
2681: return null;
2682: }
2683: return MessageFormat.format(fmt, a);
2684: } catch (MissingResourceException e) {
2685: return bundleName + ":" + key + Runtime.format(a, 64);
2686: }
2687: }
2688:
2689: static Number compress(Number n) {
2690: if (n instanceof BigInteger) {
2691: BigInteger b = (BigInteger) n;
2692: if (b.compareTo(BinaryOperator.maxLong) > 0
2693: || b.compareTo(BinaryOperator.minLong) < 0) {
2694: return n;
2695: } else {
2696: long l = b.longValue();
2697: if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) {
2698: return new Integer((int) l);
2699: } else {
2700: return new Long(l);
2701: }
2702: }
2703: } else if (n instanceof BigDecimal) {
2704: int j = 0;
2705: String f = n.toString();
2706: int p = f.indexOf((int) '.');
2707: if (p > 0) {
2708: char ca[] = f.toCharArray();
2709: for (int i = 0; i < ca.length - 2
2710: && i < ca.length - p - 2; i++) {
2711: if (ca[ca.length - i - 1] == '0') {
2712: j++;
2713: } else {
2714: break;
2715: }
2716: }
2717: f = f.substring(0, ca.length - j);
2718: }
2719: if (f.length() < 16) {
2720: return new Double(n.doubleValue());
2721: }
2722: if (j > 0) {
2723: return new BigDecimal(f);
2724: } else {
2725: return n;
2726: }
2727: } else if (n instanceof Long) {
2728: long l = ((Long) n).longValue();
2729: if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) {
2730: return new Integer((int) l);
2731: } else {
2732: return new Long(l);
2733: }
2734: } else {
2735: return n;
2736: }
2737: }
2738:
2739: static Number decimalNumber(String s, int radix) {
2740: if (radix == 16 || radix == 8) {
2741: return new BigInteger(s, radix);
2742: } else {
2743: return new BigDecimal(deNormalize(s));
2744: }
2745: }
2746:
2747: private static String deNormalize(String fmt) {
2748: int pt = fmt.indexOf((int) '.');
2749: int exp = fmt.indexOf((int) 'E');
2750: if (exp < 0) {
2751: return fmt;
2752: }
2753: int e = Integer.parseInt(fmt.substring(exp + 1));
2754: int i = 0;
2755: int k = 0;
2756: char b[];
2757: if (e < 0) {
2758: b = new char[exp - e];
2759: i = 0;
2760: if (fmt.charAt(k) == '-') {
2761: b[i++] = '-';
2762: k++;
2763: }
2764: b[i++] = '0';
2765: b[i++] = '.';
2766: for (int j = 0; j < -e - 1; j++) {
2767: b[i++] = '0';
2768: }
2769: b[i++] = fmt.charAt(k++);
2770: k++;
2771: for (int j = 0; j < exp - pt - 1; j++) {
2772: b[i++] = fmt.charAt(k++);
2773: }
2774: } else {
2775: if (exp - pt - 1 > e) {
2776: b = new char[exp];
2777: i = 0;
2778: if (fmt.charAt(k) == '-') {
2779: b[i++] = '-';
2780: k++;
2781: }
2782: b[i++] = fmt.charAt(k++);
2783: k++;
2784: for (int j = 0; j < e; j++) {
2785: b[i++] = fmt.charAt(k++);
2786: }
2787: b[i++] = '.';
2788: for (int j = 0; j < exp - pt - 1 - e; j++) {
2789: b[i++] = fmt.charAt(k++);
2790: }
2791: } else {
2792: b = new char[exp - 1 + (e - (exp - pt - 1))];
2793: i = 0;
2794: if (fmt.charAt(k) == '-') {
2795: b[i++] = '-';
2796: k++;
2797: }
2798: b[i++] = fmt.charAt(k++);
2799: k++;
2800: for (int j = 0; j < exp - pt - 1; j++) {
2801: b[i++] = fmt.charAt(k++);
2802: }
2803: for (int j = 0; j < e - (exp - pt - 1); j++) {
2804: b[i++] = '0';
2805: }
2806: }
2807: }
2808: return new String(b);
2809: }
2810:
2811: static class AutoloadScript implements AutoloadHook, Serializable {
2812: String file;
2813:
2814: ModuleList moduleList;
2815:
2816: Package pkg;
2817:
2818: String encoding;
2819:
2820: Configuration config;
2821:
2822: AutoloadScript(String file, Context context) {
2823: this .file = file.intern();
2824: this .pkg = context.rootPackage;
2825: this .moduleList = context.moduleList;
2826: this .encoding = context.encoding;
2827: this .config = context.config;
2828: }
2829:
2830: public synchronized void load(String name, Context context) {
2831: try {
2832: Context ctx = (Context) context.clone();
2833: ctx.moduleList = this .moduleList;
2834: ctx.setCurrentPackage(this .pkg);
2835: ctx.encoding = this .encoding;
2836: ctx.config = this .config;
2837: Pnuts.require(file, ctx);
2838: } catch (Escape esc) {
2839: throw esc;
2840: } catch (FileNotFoundException e) {
2841: throw new PnutsException(e, context);
2842: }
2843: }
2844: }
2845:
2846: protected static class TypeMap {
2847: Class type;
2848:
2849: Object value;
2850:
2851: TypeMap next;
2852:
2853: public TypeMap(Class type, Object value, TypeMap next) {
2854: this .type = type;
2855: this .value = value;
2856: this .next = next;
2857: }
2858: }
2859:
2860: protected static class Accessor {
2861: protected Class beanClass;
2862:
2863: protected Class stopClass;
2864:
2865: HashMap readMethods;
2866:
2867: HashMap writeMethods;
2868:
2869: HashMap types;
2870:
2871: protected Accessor(Class cls, Class stopClass) {
2872: init(cls, stopClass);
2873: }
2874:
2875: void init(Class cls, Class stopClass) {
2876: this .beanClass = cls;
2877: this .stopClass = stopClass;
2878: this .readMethods = new HashMap();
2879: this .writeMethods = new HashMap();
2880: this .types = new HashMap();
2881:
2882: ObjectDescFactory.getDefault().create(cls, stopClass)
2883: .handleProperties(new PropertyHandler() {
2884: public void handle(String propertyName,
2885: Class type, Method readMethod,
2886: Method writeMethod) {
2887: if (readMethod != null) {
2888: addReadMethod(propertyName, readMethod);
2889: }
2890: if (writeMethod != null) {
2891: addWriteMethod(propertyName,
2892: writeMethod);
2893: }
2894: types.put(propertyName, type);
2895: }
2896: });
2897: }
2898:
2899: public void addReadMethod(String name, Object m) {
2900: readMethods.put(name, m);
2901: }
2902:
2903: public void addWriteMethod(String name, Object m) {
2904: writeMethods.put(name, m);
2905: }
2906:
2907: public Object findReadMethod(String name) {
2908: return readMethods.get(name);
2909: }
2910:
2911: public Object findWriteMethod(String name) {
2912: return writeMethods.get(name);
2913: }
2914:
2915: public Class getType(String name) {
2916: return (Class) types.get(name);
2917: }
2918: }
2919:
2920: public static String getProperty(final String key) {
2921: Properties p = Pnuts.defaultSettings;
2922: if (p != null) {
2923: String v = p.getProperty(key);
2924: if (v != null) {
2925: return v;
2926: }
2927: }
2928: try {
2929: String v = (String) AccessController
2930: .doPrivileged(new PrivilegedAction() {
2931: public Object run() {
2932: return System.getProperty(key);
2933: }
2934: });
2935: if (v != null) {
2936: return v;
2937: }
2938: } catch (Exception e) {
2939: // skip
2940: }
2941: if (defaultProperties != null) {
2942: return defaultProperties.getProperty(key);
2943: } else {
2944: return null;
2945: }
2946: }
2947:
2948: private Map beanAccessors = createWeakMap();
2949:
2950: static class BeanInfoParam {
2951: Class targetClass;
2952:
2953: Class stopClass;
2954:
2955: BeanInfoParam(Class targetClass, Class stopClass) {
2956: this .targetClass = targetClass;
2957: this .stopClass = stopClass;
2958: }
2959:
2960: public int hashCode() {
2961: return targetClass.hashCode() ^ stopClass.hashCode();
2962: }
2963:
2964: public boolean equals(Object that) {
2965: if (that instanceof BeanInfoParam) {
2966: BeanInfoParam p = (BeanInfoParam) that;
2967: return p.targetClass == this .targetClass
2968: && p.stopClass == this .stopClass;
2969: }
2970: return false;
2971: }
2972: }
2973:
2974: private Accessor getAccessor(Class cls, Class stopClass) {
2975: Object key;
2976: if (stopClass == null) {
2977: key = cls;
2978: } else {
2979: key = new BeanInfoParam(cls, stopClass);
2980: }
2981: java.lang.ref.SoftReference ref = (java.lang.ref.SoftReference) beanAccessors
2982: .get(key);
2983: if (ref == null) {
2984: Accessor a = createBeanAccessor(cls, stopClass);
2985: beanAccessors.put(key, new java.lang.ref.SoftReference(a));
2986: return a;
2987: } else {
2988: Accessor a = (Accessor) ref.get();
2989: if (a == null) {
2990: a = createBeanAccessor(cls, stopClass);
2991: beanAccessors.put(key, new java.lang.ref.SoftReference(
2992: a));
2993: }
2994: return a;
2995: }
2996: }
2997:
2998: private Accessor createBeanAccessor(Class cls, Class stopClass) {
2999: return new Accessor(cls, stopClass);
3000: }
3001:
3002: /**
3003: * Sets a Bean property of the specified bean.
3004: *
3005: * @param context
3006: * the context
3007: * @param target
3008: * the target bean
3009: * @param name
3010: * the Bean property name
3011: * @param value
3012: * the value of the Bean property
3013: */
3014: public static void setBeanProperty(Context context, Object target,
3015: String name, Object value) {
3016: try {
3017: context.runtime.setBeanProperty(target, name, value, null);
3018: } catch (IllegalAccessException e1) {
3019: throw new PnutsException(e1, context);
3020: } catch (InvocationTargetException e2) {
3021: throw new PnutsException(e2.getTargetException(), context);
3022: }
3023: }
3024:
3025: /**
3026: * Gets a Bean property of the specified bean.
3027: *
3028: * @param context
3029: * the context
3030: * @param target
3031: * the target bean
3032: * @param name
3033: * the Bean property name
3034: */
3035: public static Object getBeanProperty(Context context,
3036: Object target, String name) {
3037: try {
3038: return context.runtime.getBeanProperty(target, name, null);
3039: } catch (IllegalAccessException e1) {
3040: throw new PnutsException(e1, context);
3041: } catch (InvocationTargetException e2) {
3042: throw new PnutsException(e2.getTargetException(), context);
3043: }
3044: }
3045:
3046: /**
3047: * Gets a Bean property of the specified bean.
3048: *
3049: * @param target
3050: * the target bean
3051: * @param name
3052: * the Bean property name
3053: */
3054: public Object getBeanProperty(Object target, String name)
3055: throws IllegalAccessException, InvocationTargetException {
3056: return getBeanProperty(target, name, null);
3057: }
3058:
3059: /**
3060: * Gets a Bean property of the specified bean.
3061: *
3062: * @param target
3063: * the target bean
3064: * @param name
3065: * the Bean property name
3066: * @param stopClass
3067: * the Introspector's "stopClass"
3068: */
3069: protected Object getBeanProperty(Object target, String name,
3070: Class stopClass) throws IllegalAccessException,
3071: InvocationTargetException {
3072: Accessor a = getAccessor(target.getClass(), stopClass);
3073: Method readMethod = (Method) a.findReadMethod(name);
3074: if (readMethod != null) {
3075: try {
3076: return readMethod.invoke(target, noarg);
3077: } catch (IllegalAccessException e1) {
3078: Class cls = readMethod.getDeclaringClass();
3079: if (!Modifier.isPublic(cls.getModifiers())) {
3080: Method _m = findCallableMethod(cls, readMethod
3081: .getName(), readMethod.getParameterTypes());
3082: if (_m != null) {
3083: a.addReadMethod(name, _m);
3084: if (DEBUG) {
3085: System.out.println("addReadMethod " + _m);
3086: }
3087: try {
3088: return _m.invoke(target, noarg);
3089: } catch (IllegalAccessException iae) {
3090: }
3091: }
3092: }
3093: return Configuration.normalConfiguration.reInvoke(e1,
3094: readMethod, target, noarg);
3095: }
3096: } else {
3097: if (target instanceof Class) {
3098: Class cls = (Class) target;
3099: Field field = getField(cls, name);
3100: if (field != null
3101: && Modifier.isStatic(field.getModifiers())) {
3102: return field.get(null);
3103: }
3104: }
3105: if (a.findWriteMethod(name) == null) {
3106: Class cls = target.getClass();
3107: Field field = getField(cls, name);
3108: if (field != null
3109: && !Modifier.isStatic(field.getModifiers())) {
3110: return field.get(target);
3111: }
3112: }
3113: throw new IllegalArgumentException(
3114: "not readable property: target=" + target
3115: + ", fieldName=" + name);
3116: }
3117: }
3118:
3119: protected Cache fieldCache = createCache();
3120:
3121: protected Field getField(final Class cls, final String name) {
3122: Cache fc = (Cache) fieldCache.get(cls);
3123: if (fc == null) {
3124: fc = createCache();
3125: fieldCache.put(cls, fc);
3126: }
3127: Field field = (Field) fc.get(name);
3128: if (field == null) {
3129: try {
3130: field = (Field) AccessController
3131: .doPrivileged(new PrivilegedExceptionAction() {
3132: public Object run() throws Exception {
3133: return cls.getField(name);
3134: }
3135: });
3136: fc.put(name, field);
3137: } catch (PrivilegedActionException e) {
3138: return null;
3139: }
3140: }
3141: return field;
3142: }
3143:
3144: /**
3145: * Sets a Bean property of the specified bean.
3146: *
3147: * @param target
3148: * the target bean
3149: * @param name
3150: * the Bean property name
3151: * @param value
3152: * the new property value
3153: */
3154: public void setBeanProperty(Object target, String name, Object value)
3155: throws IllegalAccessException, InvocationTargetException {
3156: setBeanProperty(target, name, value, null);
3157: }
3158:
3159: /**
3160: * Sets a Bean property of the specified bean.
3161: *
3162: * @param target
3163: * the target bean
3164: * @param name
3165: * the Bean property name
3166: * @param value
3167: * the new property value
3168: * @param stopClass
3169: * the Introspector's "stopClass"
3170: */
3171: protected void setBeanProperty(Object target, String name,
3172: Object value, Class stopClass)
3173: throws IllegalAccessException, InvocationTargetException {
3174: Accessor a = getAccessor(target.getClass(), stopClass);
3175: Method writeMethod = (Method) a.findWriteMethod(name);
3176: if (writeMethod != null) {
3177: Class type = writeMethod.getParameterTypes()[0];
3178: Object[] arg = null;
3179: try {
3180: if (type.isArray() && !type.isInstance(value)) {
3181: value = transform(type, value, null);
3182: } else if (type.isPrimitive()) {
3183: value = primitive(type, value, false, 10);
3184: } else if (List.class.isAssignableFrom(type)) {
3185: value = transform(type, value, null);
3186: }
3187: arg = new Object[] { value };
3188: writeMethod.invoke(target, arg);
3189: return;
3190: } catch (IllegalArgumentException iae) {
3191: String msg = Runtime.getMessage("pnuts.lang.pnuts",
3192: "type.mismatch",
3193: new Object[] { type, target, name, value,
3194: value.getClass().getName() });
3195: throw new IllegalArgumentException(msg);
3196: } catch (ClassCastException cce) {
3197: String msg = Runtime.getMessage("pnuts.lang.pnuts",
3198: "type.mismatch",
3199: new Object[] { type, target, name, value,
3200: value.getClass().getName() });
3201: throw new IllegalArgumentException(msg);
3202: } catch (IllegalAccessException e1) {
3203: Class cls = writeMethod.getDeclaringClass();
3204: if (!Modifier.isPublic(cls.getModifiers())) {
3205: Method _m = findCallableMethod(cls, writeMethod
3206: .getName(), writeMethod.getParameterTypes());
3207: if (_m != null) {
3208: a.addWriteMethod(name, _m);
3209: if (DEBUG) {
3210: System.out.println("addWriteMethod " + _m);
3211: }
3212: try {
3213: _m.invoke(target, arg);
3214: return;
3215: } catch (IllegalAccessException iae) {
3216: }
3217: }
3218: }
3219: Configuration.normalConfiguration.reInvoke(e1,
3220: writeMethod, target, arg);
3221: return;
3222: }
3223: } else {
3224: if (target instanceof Class) {
3225: Class cls = (Class) target;
3226: Field field = getField(cls, name);
3227: if (field != null
3228: && Modifier.isStatic(field.getModifiers())) {
3229: field.set(null, value);
3230: return;
3231: }
3232: }
3233: if (a.findReadMethod(name) == null) {
3234: Class cls = target.getClass();
3235: Field field = getField(cls, name);
3236: if (field != null
3237: && !Modifier.isStatic(field.getModifiers())) {
3238: field.set(target, value);
3239: return;
3240: }
3241: }
3242: throw new IllegalArgumentException(
3243: "not writable property: target=" + target
3244: + ", fieldName=" + name);
3245: }
3246: }
3247:
3248: /**
3249: * Gets the type of a bean property
3250: *
3251: * @param cls
3252: * the class of the bean
3253: * @param name
3254: * the property name of the bean property
3255: * @return the type of the property
3256: */
3257: public Class getBeanPropertyType(Class cls, String name) {
3258: return getAccessor(cls, null).getType(name);
3259: }
3260:
3261: public static class Break extends RuntimeException {
3262: Object value;
3263:
3264: public Break(Object value) {
3265: this .value = value;
3266: }
3267:
3268: public Object getValue() {
3269: return value;
3270: }
3271: }
3272:
3273: public static class Continue extends RuntimeException {
3274: }
3275:
3276: public static boolean isGenerator(SimpleNode node) {
3277: int n = node.jjtGetNumChildren();
3278: for (int i = 0; i < n; i++) {
3279: SimpleNode c = node.jjtGetChild(i);
3280: if (c.id == PnutsParserTreeConstants.JJTFUNCTIONSTATEMENT) {
3281: continue;
3282: }
3283: if (c.id == PnutsParserTreeConstants.JJTYIELD) {
3284: return true;
3285: } else {
3286: if (isGenerator(c)) {
3287: return true;
3288: }
3289: }
3290: }
3291: return false;
3292: }
3293:
3294: static class RangeGenerator extends Generator {
3295: Generator gen;
3296:
3297: int pos = 0;
3298:
3299: int start, end;
3300:
3301: RangeGenerator(Generator gen, int start) {
3302: this (gen, start, -1);
3303: }
3304:
3305: RangeGenerator(Generator gen, int start, int end) {
3306: this .gen = gen;
3307: this .start = start;
3308: this .end = end;
3309: }
3310:
3311: public Object apply(final PnutsFunction closure, Context context) {
3312: return gen.apply(new PnutsFunction() {
3313: protected Object exec(Object[] args, Context c) {
3314: if (pos < start) {
3315: pos++;
3316: return null;
3317: } else if (start < 0 || end >= 0 && pos > end) {
3318: throw new Generator.Break(null);
3319: } else {
3320: pos++;
3321: return closure.call(args, c);
3322: }
3323: }
3324: }, context);
3325: }
3326: }
3327:
3328: public static Object applyGenerator(Generator g,
3329: PnutsFunction closure, Context context) {
3330: try {
3331: return g.apply(closure, context);
3332: } catch (Generator.Break brk) {
3333: return brk.getValue();
3334: }
3335: }
3336:
3337: public static void addImport(Context context, String name) {
3338: if (name.endsWith(".*")) {
3339: name = name.substring(0, name.length() - 2);
3340: context.addPackageToImport(name);
3341: } else if ("*".equals(name)) {
3342: context.addPackageToImport("");
3343: } else {
3344: context.addClassToImport(name);
3345: }
3346: }
3347:
3348: public static void addStaticMembers(Context context, String name,
3349: boolean wildcard) {
3350: context.addStaticMembers(name, wildcard);
3351: }
3352:
3353: protected static PnutsFunction defineUnboundFunction(Function f,
3354: String symbol, Package pkg) {
3355: Value v = pkg.lookup(symbol);
3356: if (v != null) {
3357: Object o = v.get();
3358: if (o instanceof PnutsFunction) {
3359: return f.register((PnutsFunction) o, true);
3360: }
3361: }
3362: return f.register(null);
3363: }
3364:
3365: static Object lookupTopLevelValue(String symbol, Context context) {
3366: Value v;
3367: ModuleList moduleList = context.moduleList;
3368: if (moduleList != null) {
3369: v = moduleList.resolve(symbol, context);
3370: if (v != null) {
3371: return v.get();
3372: }
3373: }
3374: Package parent = context.currentPackage.getParent();
3375: while (parent != null) {
3376: v = parent.lookup(symbol);
3377: if (v != null) {
3378: return v.get();
3379: }
3380: parent = parent.getParent();
3381: }
3382: return null;
3383: }
3384:
3385: protected static PnutsFunction defineTopLevelFunction(Function f,
3386: String symbol, Package pkg, Context context) {
3387: Value value = pkg.lookup(symbol);
3388: Object o = null;
3389: if (value != null) {
3390: o = value.get();
3391: }
3392: PnutsFunction pf;
3393: if (o instanceof PnutsFunction) {
3394: pf = f.register((PnutsFunction) o);
3395: } else {
3396: o = lookupTopLevelValue(symbol, context);
3397: if (o instanceof PnutsFunction) {
3398: pf = f.register((PnutsFunction) o, true);
3399: } else {
3400: pf = f.register(null);
3401: }
3402: }
3403: pkg.set(symbol, pf, context);
3404: return pf;
3405: }
3406:
3407: public static String unparse(SimpleNode node, Context context) {
3408: StringBuffer sbuf = new StringBuffer();
3409: Visitor unparseVisitor = new UnparseVisitor(sbuf);
3410: node.accept(unparseVisitor, context);
3411: return sbuf.toString();
3412: }
3413:
3414: static void recoverParseError(PnutsParser parser, int tokenType) {
3415: try {
3416: Token t;
3417: do {
3418: t = parser.getNextToken();
3419: } while (t.kind != tokenType
3420: && t.kind != PnutsParserConstants.EOF);
3421: } catch (Exception e) {
3422: /* skip */
3423: }
3424: }
3425:
3426: static class ThreadLocalContext extends ThreadLocal {
3427: }
3428:
3429: static ThreadLocal threadContext = new ThreadLocalContext();
3430:
3431: /**
3432: * Gets the context bound to the current thread
3433: *
3434: * @param context the context
3435: */
3436: public static void setThreadContext(Context context) {
3437: threadContext.set(context);
3438: }
3439:
3440: /**
3441: * Sets the context bound to the current thread
3442: *
3443: * @return the context
3444: */
3445: public static Context getThreadContext() {
3446: return (Context) threadContext.get();
3447: }
3448:
3449: static ImportEnv getDefaultImports(Context context) {
3450: ImportEnv importEnv = new ImportEnv();
3451: String[] array = context.config.getDefaultImports();
3452: for (int i = 0; i < array.length; i++) {
3453: String name = array[i];
3454: if (name.startsWith("static ")) {
3455: if (name.endsWith(".*")) {
3456: name = name.substring(0, name.length() - 2);
3457: importEnv.addStaticMembers(name, true, context);
3458: } else {
3459: importEnv.addStaticMembers(name, false, context);
3460: }
3461: } else {
3462: if (name.endsWith(".*")) {
3463: name = name.substring(0, name.length() - 2);
3464: importEnv.addPackage(name);
3465: } else if ("*".equals(name)) {
3466: importEnv.addPackage("");
3467: } else {
3468: importEnv.addClass(name);
3469: }
3470: }
3471: }
3472: return importEnv;
3473: }
3474:
3475: public static interface FunctionSerializer {
3476: void serialize(PnutsFunction pnutsFunction, ObjectOutputStream s)
3477: throws IOException;
3478:
3479: void deserialize(PnutsFunction pnutsFunction,
3480: ObjectInputStream s) throws IOException,
3481: ClassNotFoundException;
3482: }
3483:
3484: static FunctionSerializer functionSerializer;
3485: static {
3486: try {
3487: Class cls = Class
3488: .forName("pnuts.lang.DefaultFunctionSerializer");
3489: functionSerializer = (FunctionSerializer) cls.newInstance();
3490: } catch (Exception e) {
3491: try {
3492: Class cls = Class
3493: .forName("pnuts.lang.SimpleFunctionSerializer");
3494: functionSerializer = (FunctionSerializer) cls
3495: .newInstance();
3496: } catch (Exception e2) {
3497: // skip
3498: }
3499: }
3500: }
3501:
3502: static void serializePnutsFunction(PnutsFunction pnutsFunction,
3503: ObjectOutputStream s) throws IOException {
3504: functionSerializer.serialize(pnutsFunction, s);
3505: }
3506:
3507: static void deserializePnutsFunction(PnutsFunction pnutsFunction,
3508: ObjectInputStream s) throws IOException,
3509: ClassNotFoundException {
3510: functionSerializer.deserialize(pnutsFunction, s);
3511: }
3512:
3513: public static String saveNode(SimpleNode node) {
3514: return NodeUtil.saveNode(node);
3515: }
3516:
3517: public static SimpleNode loadNode(String str) {
3518: return NodeUtil.loadNode(str);
3519: }
3520:
3521: private static boolean useCacheCleanerThread;
3522: static {
3523: String prop = getProperty("pnuts.lang.useCacheCleanerThread");
3524: useCacheCleanerThread = (prop == null || !prop.toLowerCase()
3525: .equals("false"));
3526: }
3527:
3528: public static Map createWeakMap() {
3529: if (useCacheCleanerThread) {
3530: return new org.pnuts.util.RefMap();
3531: } else {
3532: return new WeakHashMap();
3533: }
3534: }
3535:
3536: public static Cache createCache() {
3537: if (useCacheCleanerThread) {
3538: return new MemoryCache(createWeakMap());
3539: } else {
3540: return new MemoryCache();
3541: }
3542: }
3543:
3544: /**
3545: public static String unparseNode(SimpleNode node){
3546: StringBuffer sbuf = new StringBuffer();
3547: node.accept(new org.pnuts.lang.UnparseVisitor(sbuf), null);
3548: return sbuf.toString();
3549: }
3550: **/
3551:
3552: /* experimental BIND feature */
3553:
3554: protected static void setupPropertyChangeListeners(Map table,
3555: Context context) {
3556: for (Iterator it = table.entrySet().iterator(); it.hasNext();) {
3557: Map.Entry memberTargetEntry = (Map.Entry) it.next();
3558: Object memberTarget = memberTargetEntry.getKey();
3559: Map memberNameTable = (Map) memberTargetEntry.getValue(); // memberName -> list of [obj, property, rhs]
3560: setupPropertyChangeListeners(memberTarget, memberNameTable,
3561: context);
3562:
3563: }
3564: }
3565:
3566: private static void setupPropertyChangeListeners(
3567: Object memberTarget, Map memberNameTable, Context context) {
3568: PropertyChangeListener listener = new PropertyWatcher(
3569: memberNameTable, context);
3570: addPropertyChangeListener(memberTarget, listener, context);
3571: }
3572:
3573: private static void addPropertyChangeListener(Object obj,
3574: PropertyChangeListener listener, Context context) {
3575: /*
3576: Class cls = obj.getClass();
3577: try {
3578: Method addMethod = cls.getMethod("addPropertyChangeListener", new Class[]{PropertyChangeListener.class});
3579: addMethod.invoke(obj, new Object[]{listener});
3580: } catch (Exception e){
3581: throw new PnutsException(e, context);
3582: }
3583: */
3584: callMethod(context, obj.getClass(),
3585: "addPropertyChangeListener", new Object[] { listener },
3586: new Class[] { PropertyChangeListener.class }, obj);
3587: }
3588:
3589: /*
3590: * Whenever memberTarget.memberName changes, rhs.accept(this, context) is evaluated and assigned to obj.property
3591: *
3592: * table = Map of (memberTarget -> Map of (memberName -> [obj, property, rhs])) is maintained somewhere,
3593: * and this method does the followings.
3594: * 1. if (table.memberTarget) == null, then table.memberTareget = map()
3595: * 2. if (table.memberTareget.memberName == null), then table.memberTareget.memberName = list()
3596: * 3. table.memberTareget.memberName.add([obj, property, rhs])
3597: *
3598: */
3599: protected static void watchProperty(Map table, Object obj,
3600: String property, Object memberTarget, String memberName,
3601: Callable rhs) {
3602: if (canAcceptPropertyChangeListener(memberTarget)) {
3603: Map memberNameTable = (Map) table.get(memberTarget);
3604: if (memberNameTable == null) {
3605: memberNameTable = new HashMap();
3606: table.put(memberTarget, memberNameTable);
3607: }
3608: List tupleList = (List) memberNameTable.get(memberName);
3609: if (tupleList == null) {
3610: tupleList = new ArrayList();
3611: memberNameTable.put(memberName, tupleList);
3612: }
3613: tupleList.add(new Object[] { obj, property, rhs });
3614: }
3615: }
3616:
3617: private static boolean canAcceptPropertyChangeListener(Object obj) {
3618: try {
3619: Method addMethod = obj.getClass().getMethod(
3620: "addPropertyChangeListener",
3621: new Class[] { PropertyChangeListener.class });
3622: return true;
3623: } catch (Exception e) {
3624: }
3625: return false;
3626: }
3627:
3628: protected static SimpleNode toFunctionNode(SimpleNode body) {
3629: SimpleNode bodyClone = loadNode(saveNode(body));
3630: SimpleNode f = new SimpleNode(
3631: PnutsParserTreeConstants.JJTFUNCTIONSTATEMENT);
3632: SimpleNode param = new SimpleNode(
3633: PnutsParserTreeConstants.JJTLISTELEMENTS);
3634: f.jjtAddChild(bodyClone, 1);
3635: f.jjtAddChild(param, 0);
3636: bodyClone.jjtSetParent(f);
3637: param.jjtSetParent(f);
3638: return f;
3639: }
3640:
3641: static class PropertyWatcher implements PropertyChangeListener {
3642: private Map memberNameTable;
3643: private Context context;
3644:
3645: PropertyWatcher(Map memberNameTable, Context context) {
3646: this .memberNameTable = memberNameTable;
3647: this .context = context;
3648: }
3649:
3650: public void propertyChange(PropertyChangeEvent e) {
3651: String name = e.getPropertyName();
3652: List tuples = (List) memberNameTable.get(name);
3653: if (tuples != null) {
3654: int size = tuples.size();
3655: for (int i = 0; i < size; i++) {
3656: Object[] tuple = (Object[]) tuples.get(i);
3657: Object target = tuple[0];
3658: String targetProperty = (String) tuple[1];
3659: Callable rhs = (Callable) tuple[2];
3660: Object value = rhs != null ? rhs.call(NO_PARAM,
3661: context) : e.getNewValue();
3662: context.config.putField(context, target,
3663: targetProperty, value);
3664: }
3665: }
3666: }
3667: }
3668: }
|