/*
* Copyright (c) 1998-2010 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.env;
import com.caucho.quercus.UnimplementedException;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.quercus.marshal.Marshal;
import com.caucho.quercus.marshal.MarshalFactory;
import com.caucho.quercus.program.JavaClassDef;
import com.caucho.vfs.WriteStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Interface for marshalled Java data structures.
*/
abstract public class JavaAdapter extends ArrayValue
implements Serializable {
private static final Logger log = Logger.getLogger(JavaAdapter.class.getName());
private WeakReference<Env> _envRef;
private Object _object;
private JavaClassDef _classDef;
protected JavaAdapter(Object object, JavaClassDef def) {
_object = object;
_classDef = def;
}
public JavaClassDef getClassDef() {
return _classDef;
}
public Env getEnv() {
return Env.getCurrent();
}
public Value wrapJava(Object obj) {
return getEnv().wrapJava(obj);
}
/**
* Converts to an object.
*/
@Override
public Object toObject() {
return null;
}
/**
* Converts to a Java object.
*/
@Override
public Object toJavaObject() {
return _object;
}
/**
* Converts to a java object.
*/
@Override
public Object toJavaObjectNotNull(Env env, Class type) {
if (type.isAssignableFrom(_object.getClass())) {
return _object;
} else {
env.warning(L.l("Can't assign {0} to {1}",
_object.getClass().getName(), type.getName()));
return null;
}
}
//
// Conversions
//
/**
* Converts to an object.
*/
@Override
public Value toObject(Env env) {
Value obj = env.createObject();
for (Map.Entry<Value, Value> entry : entrySet()) {
Value key = entry.getKey();
if (key instanceof StringValue) {
// TODO: intern?
obj.putField(env, key.toString(), entry.getValue());
}
}
return obj;
}
/**
* Converts to a java List object.
*/
@Override
public Collection toJavaCollection(Env env, Class type) {
Collection coll = null;
if (type.isAssignableFrom(HashSet.class)) {
coll = new HashSet();
} else if (type.isAssignableFrom(TreeSet.class)) {
coll = new TreeSet();
} else {
try {
coll = (Collection) type.newInstance();
} catch (Throwable e) {
log.log(Level.FINE, e.toString(), e);
env.warning(L.l("Can't assign array to {0}", type.getName()));
return null;
}
}
for (Map.Entry entry : objectEntrySet()) {
coll.add(entry.getValue());
}
return coll;
}
/**
* Converts to a java List object.
*/
@Override
public List toJavaList(Env env, Class type) {
List list = null;
if (type.isAssignableFrom(ArrayList.class)) {
list = new ArrayList();
} else if (type.isAssignableFrom(LinkedList.class)) {
list = new LinkedList();
} else if (type.isAssignableFrom(Vector.class)) {
list = new Vector();
} else {
try {
list = (List) type.newInstance();
} catch (Throwable e) {
log.log(Level.FINE, e.toString(), e);
env.warning(L.l("Can't assign array to {0}", type.getName()));
return null;
}
}
for (Map.Entry entry : objectEntrySet()) {
list.add(entry.getValue());
}
return list;
}
/**
* Converts to a java object.
*/
@Override
public Map toJavaMap(Env env, Class type) {
Map map = null;
if (type.isAssignableFrom(TreeMap.class)) {
map = new TreeMap();
} else if (type.isAssignableFrom(LinkedHashMap.class)) {
map = new LinkedHashMap();
} else {
try {
map = (Map) type.newInstance();
} catch (Throwable e) {
log.log(Level.FINE, e.toString(), e);
env.warning(L.l("Can't assign array to {0}", type.getName()));
return null;
}
}
for (Map.Entry entry : objectEntrySet()) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
/**
* Copy for assignment.
*/
@Override
abstract public Value copy();
/**
* Copy for serialization
*/
@Override
abstract public Value copy(Env env, IdentityHashMap<Value, Value> map);
/**
* Returns the size.
*/
@Override
abstract public int getSize();
/**
* Clears the array
*/
@Override
abstract public void clear();
/**
* Adds a new value.
*/
@Override
public final Value put(Value value) {
return put(createTailKey(), value);
}
/**
* Adds a new value.
*/
@Override
public final Value put(Value key, Value value) {
return putImpl(key, value);
}
/**
* Adds a new value.
*/
abstract public Value putImpl(Value key, Value value);
/**
* Add to front.
*/
@Override
public ArrayValue unshift(Value value) {
throw new UnsupportedOperationException();
}
/**
* Splices.
*/
@Override
public ArrayValue splice(int begin, int end, ArrayValue replace) {
throw new UnsupportedOperationException();
}
/**
* Returns the value as an argument which may be a reference.
*/
@Override
public Value getArg(Value index, boolean isTop) {
return get(index);
}
/**
* Sets the array ref.
*/
@Override
public Var putVar() {
throw new UnsupportedOperationException();
}
/**
* Creatse a tail index.
*/
@Override
abstract public Value createTailKey();
/**
* Returns the field values.
*/
public Collection<Value> getIndices() {
throw new UnsupportedOperationException();
}
/**
* Gets a new value.
*/
@Override
abstract public Value get(Value key);
/**
* Removes a value.
*/
@Override
abstract public Value remove(Value key);
/**
* Returns the array ref.
*/
@Override
public Var getVar(Value index) {
// php/0ceg - Since Java does not support references, the adapter
// just creates a new Var, but modifying the var will not modify
// the field
Var var = new Var(new JavaAdapterVar(this, index));
return var;
}
/**
* Returns an iterator of the entries.
*/
@Override
public Set<Value> keySet() {
return new KeySet(getEnv());
}
/**
* Returns a set of all the entries.
*/
@Override
abstract public Set<Map.Entry<Value, Value>> entrySet();
/**
* Returns a java object set of all the entries.
*/
abstract public Set<Map.Entry<Object, Object>> objectEntrySet();
/**
* Returns a collection of the values.
*/
@Override
public Collection<Value> values() {
throw new UnimplementedException();
}
/**
* Appends as an argument - only called from compiled code
*
* XXX: change name to appendArg
*/
@Override
public ArrayValue append(Value key, Value value) {
put(key, value);
return this;
}
/**
* Pops the top value.
*/
@Override
public Value pop(Env env) {
throw new UnsupportedOperationException();
}
/**
* Shuffles the array
*/
@Override
public Value shuffle() {
throw new UnsupportedOperationException();
}
/**
* Returns the head.
*/
@Override
public Entry getHead() {
throw new UnsupportedOperationException();
}
/**
* Returns the tail.
*/
@Override
protected Entry getTail() {
throw new UnsupportedOperationException();
}
/**
* Returns the current value.
*/
@Override
public Value current() {
throw new UnsupportedOperationException();
}
/**
* Returns the current key
*/
@Override
public Value key() {
throw new UnsupportedOperationException();
}
/**
* Returns true if there are more elements.
*/
@Override
public boolean hasCurrent() {
throw new UnsupportedOperationException();
}
/**
* Returns the next value.
*/
@Override
public Value next() {
throw new UnsupportedOperationException();
}
/**
* Returns the previous value.
*/
@Override
public Value prev() {
throw new UnsupportedOperationException();
}
/**
* The each iterator
*/
@Override
public Value each() {
throw new UnsupportedOperationException();
}
/**
* Returns the first value.
*/
@Override
public Value reset() {
return BooleanValue.FALSE;
}
/**
* Returns the last value.
*/
@Override
public Value end() {
return BooleanValue.FALSE;
}
/**
* Returns the corresponding key if this array contains the given value
*
* @param value to search for in the array
*
* @return the key if it is found in the array, NULL otherwise
*
* @throws NullPointerException
*/
@Override
public Value contains(Value value) {
for (Map.Entry<Value, Value> entry : entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return NullValue.NULL;
}
/**
* Returns the corresponding key if this array contains the given value
*
* @param value to search for in the array
*
* @return the key if it is found in the array, NULL otherwise
*/
@Override
public Value containsStrict(Value value) {
for (Map.Entry<Value, Value> entry : entrySet()) {
if (entry.getValue().eql(value)) {
return entry.getKey();
}
}
return NullValue.NULL;
}
/**
* Returns the corresponding valeu if this array contains the given key
*
* @param key to search for in the array
*
* @return the value if it is found in the array, NULL otherwise
*/
@Override
public Value containsKey(Value key) {
throw new UnsupportedOperationException(getClass().getName());
}
/**
* Returns an object array of this array. This is a copy of this object's
* backing structure. Null elements are not included.
*
* @return an object array of this array
*/
@Override
public Map.Entry<Value, Value>[] toEntryArray() {
throw new UnsupportedOperationException();
}
/**
* Sorts this array based using the passed Comparator
*
* @param comparator the comparator for sorting the array
* @param resetKeys true if the keys should not be preserved
* @param strict true if alphabetic keys should not be preserved
*/
@Override
public void sort(Comparator<Map.Entry<Value, Value>> comparator,
boolean resetKeys, boolean strict) {
Map.Entry<Value, Value>[] entries = new Map.Entry[getSize()];
int i = 0;
for (Map.Entry<Value, Value> entry : entrySet()) {
entries[i++] = entry;
}
Arrays.sort(entries, comparator);
clear();
long base = 0;
if (!resetKeys) {
strict = false;
}
for (int j = 0; j < entries.length; j++) {
Value key = entries[j].getKey();
if (resetKeys && (!(key instanceof StringValue) || strict)) {
put(LongValue.create(base++), entries[j].getValue());
} else {
put(entries[j].getKey(), entries[j].getValue());
}
}
}
/**
* Serializes the value.
*/
@Override
public void serialize(Env env, StringBuilder sb) {
throw new UnsupportedOperationException();
}
/**
* Exports the value.
*/
@Override
public void varExport(StringBuilder sb) {
throw new UnsupportedOperationException();
}
/**
* Resets all numerical keys with the first index as base
*
* @param base the initial index
* @param strict if true, string keys are also reset
*/
@Override
public boolean keyReset(long base, boolean strict) {
throw new UnsupportedOperationException();
}
/**
* Takes the values of this array and puts them in a java array
*/
@Override
public Value[] valuesToArray() {
Value[] values = new Value[getSize()];
int i = 0;
for (Map.Entry<Value, Value> entry : entrySet()) {
values[i++] = entry.getValue();
}
return values;
}
/**
* Takes the values of this array, unmarshalls them to objects of type
* <i>elementType</i>, and puts them in a java array.
*/
@Override
public Object valuesToArray(Env env, Class elementType) {
int size = getSize();
Object array = Array.newInstance(elementType, size);
MarshalFactory factory = env.getModuleContext().getMarshalFactory();
Marshal elementMarshal = factory.create(elementType);
int i = 0;
for (Map.Entry<Value, Value> entry : entrySet()) {
Array.set(array, i++, elementMarshal.marshal(env,
entry.getValue(),
elementType));
}
return array;
}
@Override
public Value getField(Env env, StringValue name) {
return _classDef.getField(env, this, name);
}
@Override
public Value putField(Env env,
StringValue name,
Value value) {
return _classDef.putField(env, this, name, value);
}
/**
* Returns the class name.
*/
public String getName() {
return _classDef.getName();
}
@Override
public boolean isA(String name) {
return _classDef.isA(name);
}
/**
* Returns the method.
*/
public AbstractFunction findFunction(StringValue methodName) {
return _classDef.findFunction(methodName);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value[] args) {
return _classDef.callMethod(env, this,
methodName, hash,
args);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash) {
return _classDef.callMethod(env, this, methodName, hash);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1) {
return _classDef.callMethod(env, this, methodName, hash,
a1);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1, Value a2) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2, a3);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2, a3, a4);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4, Value a5) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2, a3, a4, a5);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value[] args) {
return _classDef.callMethod(env, this, methodName, hash, args);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash) {
return _classDef.callMethod(env, this, methodName, hash);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env,
StringValue methodName, int hash,
Value a1) {
return _classDef.callMethod(env, this,
methodName, hash,
a1);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2, a3);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2, a3, a4);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4, Value a5) {
return _classDef.callMethod(env, this, methodName, hash,
a1, a2, a3, a4, a5);
}
@Override
public void varDumpImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException {
out.println("array(" + getSize() + ") {");
int nestedDepth = depth + 1;
for (Map.Entry<Value, Value> mapEntry : entrySet()) {
printDepth(out, nestedDepth * 2);
out.print("[");
Value key = mapEntry.getKey();
if (key.isString()) {
out.print("\"" + key + "\"");
} else {
out.print(key);
}
out.println("]=>");
printDepth(out, nestedDepth * 2);
mapEntry.getValue().varDump(env, out, nestedDepth, valueSet);
out.println();
}
printDepth(out, 2 * depth);
out.print("}");
}
@Override
protected void printRImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException {
out.println("Array");
printDepth(out, 8 * depth);
out.println("(");
for (Map.Entry<Value, Value> mapEntry : entrySet()) {
printDepth(out, 8 * depth);
out.print(" [");
out.print(mapEntry.getKey());
out.print("] => ");
Value value = mapEntry.getValue();
if (value != null) {
value.printR(env, out, depth + 1, valueSet);
}
out.println();
}
printDepth(out, 8 * depth);
out.println(")");
}
//
// Java Serialization
//
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeObject(_object);
out.writeObject(_classDef.getName());
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
_envRef = new WeakReference<Env>(Env.getInstance());
_object = in.readObject();
_classDef = getEnv().getJavaClassDefinition((String) in.readObject());
}
/**
* Converts to a string.
*/
@Override
public String toString() {
return String.valueOf(_object);
}
public class KeySet extends AbstractSet<Value> {
Env _env;
KeySet(Env env) {
_env = env;
}
@Override
public int size() {
return getSize();
}
@Override
public Iterator<Value> iterator() {
return getKeyIterator(_env);
}
}
}