/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.quercus.program; import com.caucho.quercus.QuercusContext; import com.caucho.quercus.QuercusModuleException; import com.caucho.quercus.QuercusException; import com.caucho.quercus.QuercusRuntimeException; import com.caucho.quercus.annotation.*; import com.caucho.quercus.env.*; import com.caucho.quercus.expr.Expr; import com.caucho.quercus.expr.LiteralExpr; import com.caucho.quercus.function.AbstractFunction; import com.caucho.quercus.marshal.JavaMarshal; import com.caucho.quercus.marshal.Marshal; import com.caucho.quercus.marshal.MarshalFactory; import com.caucho.quercus.module.ModuleContext; import com.caucho.util.L10N; import com.caucho.vfs.WriteStream; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * Represents an introspected Java class. */ public class JavaClassDef extends ClassDef { private final static Logger log = Logger.getLogger(JavaClassDef.class.getName()); private final static L10N L = new L10N(JavaClassDef.class); private final ModuleContext _moduleContext; private final String _name; private final Class _type; private QuercusClass _quercusClass; private HashSet<String> _instanceOfSet; private HashSet<String> _instanceOfSetLowerCase; private final boolean _isAbstract; private final boolean _isInterface; private final boolean _isDelegate; private boolean _isPhpClass; private String _resourceType; private JavaClassDef _componentDef; protected volatile boolean _isInit; private final HashMap<String, Value> _constMap = new HashMap<String, Value>(); private final HashMap<String, Object> _constJavaMap = new HashMap<String, Object>(); private final MethodMap<AbstractJavaMethod> _functionMap = new MethodMap<AbstractJavaMethod>(null, this); private final HashMap<StringValue, AbstractJavaMethod> _getMap = new HashMap<StringValue, AbstractJavaMethod>(); private final HashMap<StringValue, AbstractJavaMethod> _setMap = new HashMap<StringValue, AbstractJavaMethod>(); // _fieldMap stores all public non-static fields // used by getField and setField private final HashMap<StringValue, FieldMarshalPair> _fieldMap = new HashMap<StringValue, FieldMarshalPair> (); private AbstractJavaMethod _cons; private AbstractJavaMethod __construct; private JavaMethod __fieldGet; private JavaMethod __fieldSet; private FunctionArrayDelegate _funArrayDelegate; private ArrayDelegate _arrayDelegate; private JavaMethod __call; private JavaMethod __toString; private Method _printRImpl; private Method _varDumpImpl; private Method _jsonEncode; private Method _entrySet; private TraversableDelegate _traversableDelegate; private CountDelegate _countDelegate; private Method _iteratorMethod; private Marshal _marshal; private String _extension; public JavaClassDef(ModuleContext moduleContext, String name, Class type) { super(null, name, null, new String[] {}); _moduleContext = moduleContext; _name = name; _type = type; _isAbstract = Modifier.isAbstract(type.getModifiers()); _isInterface = type.isInterface(); _isDelegate = type.isAnnotationPresent(ClassImplementation.class); if (type.isArray() && ! isArray()) throw new IllegalStateException( L.l("'{0}' needs to be called with JavaArrayClassDef", type)); } public JavaClassDef(ModuleContext moduleContext, String name, Class type, String extension) { this(moduleContext, name, type); _extension = extension; moduleContext.addExtensionClass(extension, name); } private void fillInstanceOfSet(Class type, boolean isTop) { if (type == null) return; if (isTop && _isDelegate) { _instanceOfSet.add(_name); _instanceOfSetLowerCase.add(_name.toLowerCase(Locale.ENGLISH)); } else { String name = type.getSimpleName(); _instanceOfSet.add(name); _instanceOfSetLowerCase.add(name.toLowerCase(Locale.ENGLISH)); } fillInstanceOfSet(type.getSuperclass(), false); Class []ifaceList = type.getInterfaces(); if (ifaceList != null) { for (Class iface : ifaceList) fillInstanceOfSet(iface, false); } } public static JavaClassDef create(ModuleContext moduleContext, String name, Class<?> type) { if (Double.class.isAssignableFrom(type) || Float.class.isAssignableFrom(type)) return new DoubleClassDef(moduleContext); else if (Long.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type) || Short.class.isAssignableFrom(type) || Byte.class.isAssignableFrom(type)) return new LongClassDef(moduleContext); else if (BigDecimal.class.isAssignableFrom(type)) return new BigDecimalClassDef(moduleContext); else if (BigInteger.class.isAssignableFrom(type)) return new BigIntegerClassDef(moduleContext); else if (String.class.isAssignableFrom(type) || Character.class.isAssignableFrom(type)) return new StringClassDef(moduleContext); else if (Boolean.class.isAssignableFrom(type)) return new BooleanClassDef(moduleContext); else if (Calendar.class.isAssignableFrom(type)) return new CalendarClassDef(moduleContext); else if (Date.class.isAssignableFrom(type)) return new DateClassDef(moduleContext); else if (URL.class.isAssignableFrom(type)) return new URLClassDef(moduleContext); else if (Map.class.isAssignableFrom(type)) return new JavaMapClassDef(moduleContext, name, type); else if (List.class.isAssignableFrom(type)) return new JavaListClassDef(moduleContext, name, type); else if (Collection.class.isAssignableFrom(type) && ! Queue.class.isAssignableFrom(type)) return new JavaCollectionClassDef(moduleContext, name, type); else return null; } /** * Returns the class name. */ @Override public String getName() { return _name; } /** * Returns the class name. */ public String getSimpleName() { return _type.getSimpleName(); } public Class getType() { return _type; } /* * Returns the type of this resource. */ public String getResourceType() { return _resourceType; } protected ModuleContext getModuleContext() { return _moduleContext; } /* * Returns the name of the extension that this class is part of. */ @Override public String getExtension() { return _extension; } @Override public boolean isA(String name) { if (_instanceOfSet == null) { _instanceOfSet = new HashSet<String>(); _instanceOfSetLowerCase = new HashSet<String>(); fillInstanceOfSet(_type, true); } return (_instanceOfSet.contains(name) || _instanceOfSetLowerCase.contains(name.toLowerCase(Locale.ENGLISH))); } /** * Adds the interfaces to the set */ @Override public void addInterfaces(HashSet<String> interfaceSet) { addInterfaces(interfaceSet, _type, true); } protected void addInterfaces(HashSet<String> interfaceSet, Class type, boolean isTop) { if (type == null) return; interfaceSet.add(_name.toLowerCase(Locale.ENGLISH)); interfaceSet.add(type.getSimpleName().toLowerCase(Locale.ENGLISH)); if (type.getInterfaces() != null) { for (Class iface : type.getInterfaces()) { addInterfaces(interfaceSet, iface, false); } } // php/1z21 addInterfaces(interfaceSet, type.getSuperclass(), false); } private boolean hasInterface(String name, Class type) { Class[] interfaces = type.getInterfaces(); if (interfaces != null) { for (Class intfc : interfaces) { if (intfc.getSimpleName().equalsIgnoreCase(name)) return true; if (hasInterface(name, intfc)) return true; } } return false; } @Override public boolean isAbstract() { return _isAbstract; } public boolean isArray() { return false; } @Override public boolean isInterface() { return _isInterface; } public boolean isDelegate() { return _isDelegate; } public void setPhpClass(boolean isPhpClass) { _isPhpClass = isPhpClass; } public boolean isPhpClass() { return _isPhpClass; } public JavaClassDef getComponentDef() { if (_componentDef == null) { Class compType = getType().getComponentType(); _componentDef = _moduleContext.getJavaClassDefinition(compType.getName()); } return _componentDef; } public Value wrap(Env env, Object obj) { if (! _isInit) init(); if (_resourceType != null) return new JavaResourceValue(env, obj, this); else return new JavaValue(env, obj, this); } private int cmpObject(Object lValue, Object rValue) { if (lValue == rValue) return 0; if (lValue == null) return -1; if (rValue == null) return 1; if (lValue instanceof Comparable) { if (!(rValue instanceof Comparable)) return -1; return ((Comparable) lValue).compareTo(rValue); } else if (rValue instanceof Comparable) { return 1; } if (lValue.equals(rValue)) return 0; String lName = lValue.getClass().getName(); String rName = rValue.getClass().getName(); return lName.compareTo(rName); } public int cmpObject(Object lValue, Object rValue, JavaClassDef rClassDef) { int cmp = cmpObject(lValue, rValue); if (cmp != 0) return cmp; // attributes // XX: not sure how to do this, to imitate PHP objects, // should getters be involved as well? for ( Map.Entry<StringValue, FieldMarshalPair> lEntry : _fieldMap.entrySet() ) { StringValue lFieldName = lEntry.getKey(); FieldMarshalPair rFieldPair = rClassDef._fieldMap.get(lFieldName); if (rFieldPair == null) return 1; FieldMarshalPair lFieldPair = lEntry.getValue(); try { Object lResult = lFieldPair._field.get(lValue); Object rResult = rFieldPair._field.get(lValue); int resultCmp = cmpObject(lResult, rResult); if (resultCmp != 0) return resultCmp; } catch (IllegalAccessException e) { log.log(Level.FINE, L.l(e.getMessage()), e); return 0; } } return 0; } /** * Returns the field getter. * * @param name * @return Value attained through invoking getter */ public Value getField(Env env, Value qThis, StringValue name) { AbstractJavaMethod get = _getMap.get(name); if (get != null) { try { return get.callMethod(env, getQuercusClass(), qThis); } catch (Exception e) { log.log(Level.FINE, L.l(e.getMessage()), e); return null; } } FieldMarshalPair fieldPair = _fieldMap.get(name); if (fieldPair != null) { try { Object result = fieldPair._field.get(qThis.toJavaObject()); return fieldPair._marshal.unmarshal(env, result); } catch (Exception e) { log.log(Level.FINE, L.l(e.getMessage()), e); return null; } } AbstractFunction phpGet = qThis.getQuercusClass().getFieldGet(); if (phpGet != null) { return phpGet.callMethod(env, getQuercusClass(), qThis, name); } if (__fieldGet != null) { try { return __fieldGet.callMethod(env, getQuercusClass(), qThis, name); } catch (Exception e) { log.log(Level.FINE, L.l(e.getMessage()), e); return null; } } return null; } public Value putField(Env env, Value qThis, StringValue name, Value value) { AbstractJavaMethod setter = _setMap.get(name); if (setter != null) { try { return setter.callMethod(env, getQuercusClass(), qThis, value); } catch (Exception e) { log.log(Level.FINE, L.l(e.getMessage()), e); return NullValue.NULL; } } FieldMarshalPair fieldPair = _fieldMap.get(name); if (fieldPair != null) { try { Class type = fieldPair._field.getType(); Object marshaledValue = fieldPair._marshal.marshal(env, value, type); fieldPair._field.set(qThis.toJavaObject(), marshaledValue); return value; } catch (Exception e) { log.log(Level.FINE, L.l(e.getMessage()), e); return NullValue.NULL; } } if (! qThis.isFieldInit()) { AbstractFunction phpSet = qThis.getQuercusClass().getFieldSet(); if (phpSet != null) { qThis.setFieldInit(true); try { return phpSet.callMethod(env, getQuercusClass(), qThis, name, value); } finally { qThis.setFieldInit(false); } } } if (__fieldSet != null) { try { return __fieldSet.callMethod(env, getQuercusClass(), qThis, name, value); } catch (Exception e) { log.log(Level.FINE, L.l(e.getMessage()), e); return NullValue.NULL; } } return null; } /** * Returns the marshal instance. */ public Marshal getMarshal() { return _marshal; } /** * Creates a new instance. */ @Override public ObjectValue newInstance(Env env, QuercusClass qClass) { // return newInstance(); return null; } public Value newInstance() { return null; /* try { //Object obj = _type.newInstance(); return new JavaValue(null, _type.newInstance(), this); } catch (Exception e) { throw new QuercusRuntimeException(e); } */ } /** * Eval new */ @Override public Value callNew(Env env, Value []args) { if (_cons != null) { if (__construct != null) { Value value = _cons.call(env, Value.NULL_ARGS); __construct.callMethod(env, __construct.getQuercusClass(), value, args); return value; } else { return _cons.call(env, args); } } else if (__construct != null) return __construct.call(env, args); else return NullValue.NULL; } /** * Returns the __call. */ public AbstractFunction getCallMethod() { return __call; } @Override public AbstractFunction getCall() { return __call; } /** * Eval a method */ public AbstractFunction findFunction(StringValue methodName) { return _functionMap.getRaw(methodName); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash, Value []args) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis, args); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash, Value a1) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis, a1); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash, Value a1, Value a2) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis, a1, a2); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash, Value a1, Value a2, Value a3) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis, a1, a2, a3); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash, Value a1, Value a2, Value a3, Value a4) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis, a1, a2, a3, a4); } /** * Eval a method */ public Value callMethod(Env env, Value qThis, StringValue methodName, int hash, Value a1, Value a2, Value a3, Value a4, Value a5) { AbstractFunction fun = _functionMap.get(methodName, hash); return fun.callMethod(env, getQuercusClass(), qThis, a1, a2, a3, a4, a5); } public Set<? extends Map.Entry<Value,Value>> entrySet(Object obj) { try { if (_entrySet == null) { return null; } return (Set) _entrySet.invoke(obj); } catch (Exception e) { throw new QuercusException(e); } } /** * Initialize the quercus class. */ @Override public void initClass(QuercusClass cl) { init(); if (_cons != null) { cl.setConstructor(_cons); cl.addMethod(new StringBuilderValue("__construct"), _cons); } if (__construct != null) { cl.setConstructor(__construct); cl.addMethod(new StringBuilderValue("__construct"), __construct); } for (AbstractJavaMethod value : _functionMap.values()) { cl.addMethod(new StringBuilderValue(value.getName()), value); } if (__fieldGet != null) cl.setFieldGet(__fieldGet); if (__fieldSet != null) cl.setFieldSet(__fieldSet); if (__call != null) cl.setCall(__call); if (__toString != null) { cl.addMethod(new StringBuilderValue("__toString"), __toString); } if (_arrayDelegate != null) cl.setArrayDelegate(_arrayDelegate); else if (_funArrayDelegate != null) cl.setArrayDelegate(_funArrayDelegate); if (_traversableDelegate != null) cl.setTraversableDelegate(_traversableDelegate); else if (cl.getTraversableDelegate() == null && _iteratorMethod != null) { // adds support for Java classes implementing iterator() // php/ cl.setTraversableDelegate(new JavaTraversableDelegate(_iteratorMethod)); } if (_countDelegate != null) cl.setCountDelegate(_countDelegate); for (Map.Entry<String,Value> entry : _constMap.entrySet()) { cl.addConstant(entry.getKey(), new LiteralExpr(entry.getValue())); } for (Map.Entry<String,Object> entry : _constJavaMap.entrySet()) { cl.addJavaConstant(entry.getKey(), entry.getValue()); } } /** * Finds the matching constant */ public Value findConstant(Env env, String name) { return _constMap.get(name); } /** * Creates a new instance. */ public void initInstance(Env env, Value value) { } /** * Returns the quercus class */ public QuercusClass getQuercusClass() { if (_quercusClass == null) { init(); _quercusClass = new QuercusClass(_moduleContext, this, null); } return _quercusClass; } /** * Returns the constructor */ @Override public AbstractFunction findConstructor() { return null; } @Override public final void init() { if (_isInit) return; synchronized (this) { if (_isInit) return; super.init(); try { initInterfaceList(_type); introspect(); } finally { _isInit = true; } } } private void initInterfaceList(Class type) { Class[] ifaces = type.getInterfaces(); if (ifaces == null) return; for (Class iface : ifaces) { JavaClassDef javaClassDef = _moduleContext.getJavaClassDefinition(iface); if (javaClassDef != null) addInterface(javaClassDef.getName()); // recurse for parent interfaces initInterfaceList(iface); } } /** * Introspects the Java class. */ private void introspect() { introspectConstants(_type); introspectMethods(_moduleContext, _type); introspectFields(_moduleContext, _type); _marshal = new JavaMarshal(this, false); AbstractJavaMethod consMethod = getConsMethod(); if (consMethod != null) { if (consMethod.isStatic()) _cons = consMethod; else __construct = consMethod; } //Method consMethod = getConsMethod(_type); /* if (consMethod != null) { if (Modifier.isStatic(consMethod.getModifiers())) _cons = new JavaMethod(_moduleContext, consMethod); else __construct = new JavaMethod(_moduleContext, consMethod); } */ if (_cons == null) { Constructor []cons = _type.getConstructors(); if (cons.length > 0) { int i; for (i = 0; i < cons.length; i++) { if (cons[i].isAnnotationPresent(Construct.class)) break; } if (i < cons.length) { _cons = new JavaConstructor(_moduleContext, cons[i]); } else { _cons = new JavaConstructor(_moduleContext, cons[0]); for (i = 1; i < cons.length; i++) { _cons = _cons.overload(new JavaConstructor(_moduleContext, cons[i])); } } } else _cons = null; } if (_cons != null) _cons.setConstructor(true); if (__construct != null) __construct.setConstructor(true); introspectAnnotations(_type); } private void introspectAnnotations(Class type) { try { if (type == null) return; // interfaces for (Class<?> iface : type.getInterfaces()) introspectAnnotations(iface); // super-class introspectAnnotations(type.getSuperclass()); // this for (Annotation annotation : type.getAnnotations()) { if (annotation.annotationType() == Delegates.class) { Class[] delegateClasses = ((Delegates) annotation).value(); for (Class cl : delegateClasses) { boolean isDelegate = addDelegate(cl); if (! isDelegate) throw new IllegalArgumentException( L.l("unknown @Delegate class '{0}'", cl)); } } else if (annotation.annotationType() == ResourceType.class) { _resourceType = ((ResourceType) annotation).value(); } } } catch (RuntimeException e) { throw e; } catch (InstantiationException e) { throw new QuercusModuleException(e.getCause()); } catch (Exception e) { throw new QuercusModuleException(e); } } private boolean addDelegate(Class cl) throws InstantiationException, IllegalAccessException { boolean isDelegate = false; if (TraversableDelegate.class.isAssignableFrom(cl)) { _traversableDelegate = (TraversableDelegate) cl.newInstance(); isDelegate = true; } if (ArrayDelegate.class.isAssignableFrom(cl)) { _arrayDelegate = (ArrayDelegate) cl.newInstance(); isDelegate = true; } if (CountDelegate.class.isAssignableFrom(cl)) { _countDelegate = (CountDelegate) cl.newInstance(); isDelegate = true; } return isDelegate; } private <T> boolean addDelegate(Class<T> cl, ArrayList<T> delegates, Class<? extends Object> delegateClass) { if (!cl.isAssignableFrom(delegateClass)) return false; for (T delegate : delegates) { if (delegate.getClass() == delegateClass) { return true; } } try { delegates.add((T) delegateClass.newInstance()); } catch (InstantiationException e) { throw new QuercusModuleException(e); } catch (IllegalAccessException e) { throw new QuercusModuleException(e); } return true; } /* private Method getConsMethod(Class type) { Method []methods = type.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; if (! method.getName().equals("__construct")) continue; if (! Modifier.isPublic(method.getModifiers())) continue; return method; } return null; } */ private AbstractJavaMethod getConsMethod() { for (AbstractJavaMethod method : _functionMap.values()) { if (method.getName().equals("__construct")) return method; } return null; } /** * Introspects the Java class. */ private void introspectFields(ModuleContext moduleContext, Class type) { if (type == null) return; if (! Modifier.isPublic(type.getModifiers())) return; // Introspect getXXX and setXXX // also register whether __get, __getField, __set, __setField exists Method[] methods = type.getMethods(); for (Method method : methods) { if (Modifier.isStatic(method.getModifiers())) continue; if (method.isAnnotationPresent(Hide.class)) continue; String methodName = method.getName(); int length = methodName.length(); if (length > 3) { if (methodName.startsWith("get")) { StringValue quercusName = javaToQuercusConvert(methodName.substring(3, length)); AbstractJavaMethod existingGetter = _getMap.get(quercusName); AbstractJavaMethod newGetter = new JavaMethod(moduleContext, method); if (existingGetter != null) { newGetter = existingGetter.overload(newGetter); } _getMap.put(quercusName, newGetter); } else if (methodName.startsWith("is")) { StringValue quercusName = javaToQuercusConvert(methodName.substring(2, length)); AbstractJavaMethod existingGetter = _getMap.get(quercusName); AbstractJavaMethod newGetter = new JavaMethod(moduleContext, method); if (existingGetter != null) { newGetter = existingGetter.overload(newGetter); } _getMap.put(quercusName, newGetter); } else if (methodName.startsWith("set")) { StringValue quercusName = javaToQuercusConvert(methodName.substring(3, length)); AbstractJavaMethod existingSetter = _setMap.get(quercusName); AbstractJavaMethod newSetter = new JavaMethod(moduleContext, method); if (existingSetter != null) newSetter = existingSetter.overload(newSetter); _setMap.put(quercusName, newSetter); } else if ("__get".equals(methodName)) { if (_funArrayDelegate == null) _funArrayDelegate = new FunctionArrayDelegate(); _funArrayDelegate.setArrayGet(new JavaMethod(moduleContext, method)); } else if ("__set".equals(methodName)) { if (_funArrayDelegate == null) _funArrayDelegate = new FunctionArrayDelegate(); _funArrayDelegate.setArrayPut(new JavaMethod(moduleContext, method)); } else if ("__count".equals(methodName)) { FunctionCountDelegate delegate = new FunctionCountDelegate(); delegate.setCount(new JavaMethod(moduleContext, method)); _countDelegate = delegate; } else if ("__getField".equals(methodName)) { __fieldGet = new JavaMethod(moduleContext, method); } else if ("__setField".equals(methodName)) { __fieldSet = new JavaMethod(moduleContext, method); } else if ("__fieldGet".equals(methodName)) { __fieldGet = new JavaMethod(moduleContext, method); } else if ("__fieldSet".equals(methodName)) { __fieldSet = new JavaMethod(moduleContext, method); } } } // server/2v00 /* if (__fieldGet != null) _getMap.clear(); if (__fieldSet != null) _setMap.clear(); */ // Introspect public non-static fields Field[] fields = type.getFields(); for (Field field : fields) { if (Modifier.isStatic(field.getModifiers())) continue; else if (field.isAnnotationPresent(Hide.class)) continue; MarshalFactory factory = moduleContext.getMarshalFactory(); Marshal marshal = factory.create(field.getType(), false); _fieldMap.put(new ConstStringValue(field.getName()), new FieldMarshalPair(field, marshal)); } // introspectFields(quercus, type.getSuperclass()); } /** * helper for introspectFields * * @param s (eg: Foo, URL) * @return (foo, URL) */ private StringValue javaToQuercusConvert(String s) { if (s.length() == 1) { return new ConstStringValue( new char[]{ Character.toLowerCase(s.charAt(0)) }); } if (Character.isUpperCase(s.charAt(1))) return new ConstStringValue(s); else { StringBuilderValue sb = new StringBuilderValue(); sb.append(Character.toLowerCase(s.charAt(0))); int length = s.length(); for (int i = 1; i < length; i++) { sb.append(s.charAt(i)); } return sb; } } /** * Introspects the Java class. */ private void introspectConstants(Class type) { if (type == null) return; if (! Modifier.isPublic(type.getModifiers())) return; /* not needed because Class.getFields() is recursive Class []ifcs = type.getInterfaces(); for (Class ifc : ifcs) { introspectConstants(ifc); } */ Field []fields = type.getFields(); for (Field field : fields) { if (_constMap.get(field.getName()) != null) continue; else if (_constJavaMap.get(field.getName()) != null) continue; else if (! Modifier.isPublic(field.getModifiers())) continue; else if (! Modifier.isStatic(field.getModifiers())) continue; else if (! Modifier.isFinal(field.getModifiers())) continue; else if (field.isAnnotationPresent(Hide.class)) continue; try { Object obj = field.get(null); Value value = QuercusContext.objectToValue(obj); if (value != null) _constMap.put(field.getName().intern(), value); else _constJavaMap.put(field.getName().intern(), obj); } catch (Throwable e) { log.log(Level.FINEST, e.toString(), e); } } //introspectConstants(type.getSuperclass()); } /** * Introspects the Java class. */ private void introspectMethods(ModuleContext moduleContext, Class<?> type) { if (type == null) return; Method []methods = type.getMethods(); for (Method method : methods) { if (! Modifier.isPublic(method.getModifiers())) continue; if (method.isAnnotationPresent(Hide.class)) continue; if (_isPhpClass && method.getDeclaringClass() == Object.class) continue; if ("iterator".equals(method.getName()) && method.getParameterTypes().length == 0 && Iterator.class.isAssignableFrom(method.getReturnType())) { _iteratorMethod = method; } if ("printRImpl".equals(method.getName())) { _printRImpl = method; } else if ("varDumpImpl".equals(method.getName())) { _varDumpImpl = method; } else if (method.isAnnotationPresent(JsonEncode.class)) { _jsonEncode = method; } else if (method.isAnnotationPresent(EntrySet.class)) { _entrySet = method; } else if ("__call".equals(method.getName())) { __call = new JavaMethod(moduleContext, method); } else if ("__toString".equals(method.getName())) { __toString = new JavaMethod(moduleContext, method); } else { if (method.getName().startsWith("quercus_")) throw new UnsupportedOperationException( L.l("{0}: use @Name instead", method.getName())); JavaMethod newFun = new JavaMethod(moduleContext, method); StringValue funName = new StringBuilderValue(newFun.getName()); AbstractJavaMethod fun = _functionMap.getRaw(funName); if (fun != null) fun = fun.overload(newFun); else fun = newFun; _functionMap.put(funName.toString(), fun); } } /* Class.getMethods() is recursive introspectMethods(moduleContext, type.getSuperclass()); Class []ifcs = type.getInterfaces(); for (Class ifc : ifcs) { introspectMethods(moduleContext, ifc); } */ } public JavaMethod getToString() { return __toString; } public StringValue toString(Env env, JavaValue value) { if (__toString == null) return null; return __toString.callMethod( env, getQuercusClass(), value, new Expr[0]).toStringValue(); } public boolean jsonEncode(Env env, Object obj, StringValue sb) { if (_jsonEncode == null) return false; try { _jsonEncode.invoke(obj, env, sb); return true; } catch (InvocationTargetException e) { throw new QuercusRuntimeException(e); } catch (IllegalAccessException e) { throw new QuercusRuntimeException(e); } } /** * * @return false if printRImpl not implemented * @throws IOException */ public boolean printRImpl(Env env, Object obj, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet) throws IOException { try { if (_printRImpl == null) { return false; } _printRImpl.invoke(obj, env, out, depth, valueSet); return true; } catch (InvocationTargetException e) { throw new QuercusRuntimeException(e); } catch (IllegalAccessException e) { throw new QuercusRuntimeException(e); } } public boolean varDumpImpl(Env env, Value obj, Object javaObj, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet) throws IOException { try { if (_varDumpImpl == null) return false; _varDumpImpl.invoke(javaObj, env, obj, out, depth, valueSet); return true; } catch (InvocationTargetException e) { throw new QuercusRuntimeException(e); } catch (IllegalAccessException e) { throw new QuercusRuntimeException(e); } } private class JavaTraversableDelegate implements TraversableDelegate { private Method _iteratorMethod; public JavaTraversableDelegate(Method iterator) { _iteratorMethod = iterator; } public Iterator<Map.Entry<Value, Value>> getIterator(Env env, ObjectValue qThis) { try { Iterator iterator = (Iterator) _iteratorMethod.invoke(qThis.toJavaObject()); return new JavaIterator(env, iterator); } catch (InvocationTargetException e) { throw new QuercusRuntimeException(e); } catch (IllegalAccessException e) { throw new QuercusRuntimeException(e); } } public Iterator<Value> getKeyIterator(Env env, ObjectValue qThis) { try { Iterator iterator = (Iterator) _iteratorMethod.invoke(qThis.toJavaObject()); return new JavaKeyIterator(iterator); } catch (InvocationTargetException e) { throw new QuercusRuntimeException(e); } catch (IllegalAccessException e) { throw new QuercusRuntimeException(e); } } public Iterator<Value> getValueIterator(Env env, ObjectValue qThis) { try { Iterator iterator = (Iterator) _iteratorMethod.invoke(qThis.toJavaObject()); return new JavaValueIterator(env, iterator); } catch (InvocationTargetException e) { throw new QuercusRuntimeException(e); } catch (IllegalAccessException e) { throw new QuercusRuntimeException(e); } } } private class JavaKeyIterator implements Iterator<Value> { private Iterator _iterator; private int _index; public JavaKeyIterator(Iterator iterator) { _iterator = iterator; } public Value next() { _iterator.next(); return LongValue.create(_index++); } public boolean hasNext() { return _iterator.hasNext(); } public void remove() { throw new UnsupportedOperationException(); } } private class JavaValueIterator implements Iterator<Value> { private Env _env; private Iterator _iterator; public JavaValueIterator(Env env, Iterator iterator) { _env = env; _iterator = iterator; } public Value next() { return _env.wrapJava(_iterator.next()); } public boolean hasNext() { if (_iterator != null) return _iterator.hasNext(); else return false; } public void remove() { throw new UnsupportedOperationException(); } } private class JavaIterator implements Iterator<Map.Entry<Value, Value>> { private Env _env; private Iterator _iterator; private int _index; public JavaIterator(Env env, Iterator iterator) { _env = env; _iterator = iterator; } public Map.Entry<Value, Value> next() { Object next = _iterator.next(); int index = _index++; if (next instanceof Map.Entry) { Map.Entry entry = (Map.Entry) next; if (entry.getKey() instanceof Value && entry.getValue() instanceof Value) { return (Map.Entry<Value, Value>) entry; } else { Value key = _env.wrapJava(entry.getKey()); Value val = _env.wrapJava(entry.getValue()); return new JavaEntry(key, val); } } else { return new JavaEntry(LongValue.create(index), _env.wrapJava(next)); } } public boolean hasNext() { if (_iterator != null) return _iterator.hasNext(); else return false; } public void remove() { throw new UnsupportedOperationException(); } } private class JavaEntry implements Map.Entry<Value, Value> { private Value _key; private Value _value; public JavaEntry(Value key, Value value) { _key = key; _value = value; } public Value getKey() { return _key; } public Value getValue() { return _value; } public Value setValue(Value value) { throw new UnsupportedOperationException(); } } private class MethodMarshalPair { public Method _method; public Marshal _marshal; public MethodMarshalPair(Method method, Marshal marshal) { _method = method; _marshal = marshal; } } private class FieldMarshalPair { public Field _field; public Marshal _marshal; public FieldMarshalPair(Field field, Marshal marshal) { _field = field; _marshal = marshal; } } private static class LongClassDef extends JavaClassDef { LongClassDef(ModuleContext module) { super(module, "Long", Long.class); } @Override public Value wrap(Env env, Object obj) { return LongValue.create(((Number) obj).longValue()); } } private static class DoubleClassDef extends JavaClassDef { DoubleClassDef(ModuleContext module) { super(module, "Double", Double.class); } @Override public Value wrap(Env env, Object obj) { return new DoubleValue(((Number) obj).doubleValue()); } } private static class BigIntegerClassDef extends JavaClassDef { BigIntegerClassDef(ModuleContext module) { super(module, "BigInteger", BigInteger.class); } @Override public Value wrap(Env env, Object obj) { return new BigIntegerValue(env, (BigInteger) obj, this); } } private static class BigDecimalClassDef extends JavaClassDef { BigDecimalClassDef(ModuleContext module) { super(module, "BigDecimal", BigDecimal.class); } @Override public Value wrap(Env env, Object obj) { return new BigDecimalValue(env, (BigDecimal) obj, this); } } private static class StringClassDef extends JavaClassDef { StringClassDef(ModuleContext module) { super(module, "String", String.class); } @Override public Value wrap(Env env, Object obj) { return env.createString((String) obj); } } private static class BooleanClassDef extends JavaClassDef { BooleanClassDef(ModuleContext module) { super(module, "Boolean", Boolean.class); } @Override public Value wrap(Env env, Object obj) { if (Boolean.TRUE.equals(obj)) return BooleanValue.TRUE; else return BooleanValue.FALSE; } } private static class CalendarClassDef extends JavaClassDef { CalendarClassDef(ModuleContext module) { super(module, "Calendar", Calendar.class); } @Override public Value wrap(Env env, Object obj) { return new JavaCalendarValue(env, (Calendar)obj, this); } } private static class DateClassDef extends JavaClassDef { DateClassDef(ModuleContext module) { super(module, "Date", Date.class); } @Override public Value wrap(Env env, Object obj) { return new JavaDateValue(env, (Date)obj, this); } } private static class URLClassDef extends JavaClassDef { URLClassDef(ModuleContext module) { super(module, "URL", URL.class); } @Override public Value wrap(Env env, Object obj) { return new JavaURLValue(env, (URL)obj, this); } } }