/*
* 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.env;
import com.caucho.quercus.expr.Expr;
import com.caucho.quercus.expr.LiteralStringExpr;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.util.Primes;
import com.caucho.util.Alarm;
import com.caucho.vfs.WriteStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* Represents a PHP object value.
*/
@SuppressWarnings("serial")
public class ObjectExtValue extends ObjectValue
implements Serializable
{
private static final int DEFAULT_SIZE = 16;
private static final int DEFAULT_PRIME = Primes.getBiggestPrime(DEFAULT_SIZE);
private MethodMap<AbstractFunction> _methodMap;
private Entry []_entries;
private int _prime;
private int _size;
private boolean _isFieldInit;
public ObjectExtValue(QuercusClass cl)
{
super(cl);
_methodMap = cl.getMethodMap();
_entries = new Entry[DEFAULT_SIZE];
_prime = DEFAULT_PRIME;
}
public ObjectExtValue(Env env, ObjectExtValue copy, CopyRoot root)
{
super(copy.getQuercusClass());
root.putCopy(copy, this);
_methodMap = copy._methodMap;
_size = copy._size;
_isFieldInit = copy._isFieldInit;
Entry []copyEntries = copy._entries;
_entries = new Entry[copyEntries.length];
_prime = copy._prime;
int len = copyEntries.length;
for (int i = 0; i < len; i++) {
Entry entry = copyEntries[i];
for (; entry != null; entry = entry._next) {
Entry entryCopy = entry.copyTree(env, root);
entryCopy._next = _entries[i];
if (_entries[i] != null)
_entries[i]._prev = entryCopy;
_entries[i] = entryCopy;
}
}
_incompleteObjectName = copy._incompleteObjectName;
}
public ObjectExtValue(Env env,
IdentityHashMap<Value,Value> copyMap,
ObjectExtValue copy)
{
super(copy.getQuercusClass());
_methodMap = copy._methodMap;
_size = copy._size;
_isFieldInit = copy._isFieldInit;
Entry []copyEntries = copy._entries;
_entries = new Entry[copyEntries.length];
_prime = copy._prime;
int len = copyEntries.length;
for (int i = 0; i < len; i++) {
Entry entry = copyEntries[i];
for (; entry != null; entry = entry._next) {
Entry entryCopy = new Entry(env, copyMap, entry);
entryCopy._next = _entries[i];
if (_entries[i] != null)
_entries[i]._prev = entryCopy;
_entries[i] = entryCopy;
}
}
_incompleteObjectName = copy._incompleteObjectName;
}
private void init()
{
_entries = new Entry[DEFAULT_SIZE];
_prime = DEFAULT_PRIME;
_size = 0;
}
@Override
protected void setQuercusClass(QuercusClass cl)
{
super.setQuercusClass(cl);
_methodMap = cl.getMethodMap();
}
/*
* Initializes the incomplete class.
*/
@Override
public void initObject(Env env, QuercusClass cls)
{
setQuercusClass(cls);
_incompleteObjectName = null;
Entry []existingEntries = _entries;
_entries = new Entry[DEFAULT_SIZE];
_prime = DEFAULT_PRIME;
_size = 0;
cls.initObject(env, this);
EntryIterator iter = new EntryIterator(existingEntries);
while (iter.hasNext()) {
Entry newField = iter.next();
Entry entry = getThisEntry(newField._key);
if (entry != null)
entry._value = newField._value;
else
initField(newField._key, newField._value, newField._visibility);
}
}
/**
* Returns the number of entries.
*/
@Override
public int getSize()
{
return _size;
}
/**
* Gets a field value.
*/
@Override
public final Value getField(Env env, StringValue name)
{
Value returnValue = getFieldExt(env, name);
if(returnValue == UnsetValue.UNSET)
{
// __get didn't work, lets look in the class itself
int hash = (name.hashCode() & 0x7fffffff) % _prime;
for (Entry entry = _entries[hash]; entry != null; entry = entry._next) {
StringValue entryKey = entry._key;
if (name == entryKey || name.equals(entryKey)) {
// php/09ks vs php/091m
returnValue = entry._value.toValue();
}
}
}
return returnValue;
}
/**
* Gets a field value.
*/
@Override
public Value getThisField(Env env, StringValue name)
{
Entry entry = getThisEntry(name);
if (entry != null) {
return entry._value.toValue();
}
return getFieldExt(env, name);
}
/**
* Returns fields not explicitly specified by this value.
*/
protected Value getFieldExt(Env env, StringValue name)
{
Entry e = this.getEntry(env, name);
if(e != null && e._value != NullValue.NULL && e._value != UnsetValue.UNSET)
return e._value;
return _quercusClass.getField(env, this, name);
}
/**
* Returns the array ref.
*/
@Override
public Var getFieldVar(Env env, StringValue name)
{
Entry entry = getEntry(env, name);
if (entry != null) {
Value value = entry._value;
if (value instanceof Var)
return (Var) value;
Var var = new Var(value);
entry._value = var;
return var;
}
Value value = getFieldExt(env, name);
if (value != UnsetValue.UNSET) {
if (value instanceof Var)
return (Var) value;
else
return new Var(value);
}
// php/3d28
entry = createEntry(name, FieldVisibility.PUBLIC);
value = entry._value;
if (value instanceof Var)
return (Var) value;
Var var = new Var(value);
entry.setValue(var);
return var;
}
/**
* Returns the array ref.
*/
@Override
public Var getThisFieldVar(Env env, StringValue name)
{
Entry entry = getThisEntry(name);
if (entry != null) {
Value value = entry._value;
if (value instanceof Var)
return (Var) value;
Var var = new Var(value);
entry._value = var;
return var;
}
Value value = getFieldExt(env, name);
if (value != UnsetValue.UNSET) {
if (value instanceof Var)
return (Var) value;
else
return new Var(value);
}
entry = createEntry(name, FieldVisibility.PUBLIC);
value = entry._value;
if (value instanceof Var)
return (Var) value;
Var var = new Var(value);
entry.setValue(var);
return var;
}
/**
* Returns the value as an argument which may be a reference.
*/
@Override
public Value getFieldArg(Env env, StringValue name, boolean isTop)
{
Entry entry = getEntry(env, name);
if (entry != null) {
Value value = entry.getValue();
if (isTop || ! value.isset())
return entry.toArg();
else
return value;
}
Value value = getFieldExt(env, name);
if (value != UnsetValue.UNSET)
return value;
return new ArgGetFieldValue(env, this, name);
}
/**
* Returns the value as an argument which may be a reference.
*/
@Override
public Value getThisFieldArg(Env env, StringValue name)
{
Entry entry = getThisEntry(name);
if (entry != null)
return entry.toArg();
Value value = getFieldExt(env, name);
if (value != UnsetValue.UNSET)
return value;
return new ArgGetFieldValue(env, this, name);
}
/**
* Returns the value as an argument which may be a reference.
*/
@Override
public Value getFieldArgRef(Env env, StringValue name)
{
Entry entry = getEntry(env, name);
if (entry != null)
return entry.toArg();
Value value = getFieldExt(env, name);
if (value != UnsetValue.UNSET)
return value;
return new ArgGetFieldValue(env, this, name);
}
/**
* Returns the value as an argument which may be a reference.
*/
@Override
public Value getThisFieldArgRef(Env env, StringValue name)
{
Entry entry = getThisEntry(name);
if (entry != null)
return entry.toArg();
Value value = getFieldExt(env, name);
if (value != UnsetValue.UNSET)
return value;
return new ArgGetFieldValue(env, this, name);
}
/**
* Adds a new value.
*/
@Override
public Value putField(Env env, StringValue name, Value value)
{
Entry entry = getEntry(env, name);
// XXX: php/09ks, need visibility check
if (entry == null) {
Value oldValue = putFieldExt(env, name, value);
if (oldValue != null)
return oldValue;
if (! _isFieldInit) {
AbstractFunction fieldSet = _quercusClass.getFieldSet();
if (fieldSet != null) {
_isFieldInit = true;
Value retVal = _quercusClass.setField(env, this, name, value);
_isFieldInit = false;
if(retVal != UnsetValue.UNSET)
return retVal;
}
}
entry = createEntry(name, FieldVisibility.PUBLIC);
}
Value oldValue = entry._value;
if (value instanceof Var) {
Var var = (Var) value;
// for function return optimization
// var.setReference();
entry._value = var;
}
else if (oldValue instanceof Var) {
oldValue.set(value);
}
else {
entry._value = value;
}
return value;
}
/**
* Sets/adds field to this object.
*/
@Override
public Value putThisField(Env env, StringValue name, Value value)
{
Entry entry = getThisEntry(name);
if (entry == null) {
Value oldValue = putFieldExt(env, name, value);
if (oldValue != null)
return oldValue;
if (! _isFieldInit) {
AbstractFunction fieldSet = _quercusClass.getFieldSet();
if (fieldSet != null) {
//php/09k7
_isFieldInit = true;
Value retValue = NullValue.NULL;
try {
retValue = fieldSet.callMethod(env,
_quercusClass,
this,
name,
value);
} finally {
_isFieldInit = false;
}
return retValue;
}
}
}
entry = createEntry(name, FieldVisibility.PUBLIC);
Value oldValue = entry._value;
if (value instanceof Var) {
Var var = (Var) value;
// for function return optimization
// var.setReference();
entry._value = var;
}
else if (oldValue instanceof Var) {
oldValue.set(value);
}
else {
entry._value = value;
}
return value;
}
protected Value putFieldExt(Env env, StringValue name, Value value)
{
return null;
}
@Override
public void setFieldInit(boolean isInit)
{
_isFieldInit = isInit;
}
/**
* Returns true if the object is in a __set() method call.
* Prevents infinite recursion.
*/
@Override
public boolean isFieldInit()
{
return _isFieldInit;
}
/**
* Adds a new value to the object.
*/
@Override
public void initField(StringValue key,
Value value,
FieldVisibility visibility)
{
Entry entry = createEntry(key, visibility);
entry._value = value;
}
/**
* Removes a value.
*/
@Override
public void unsetField(StringValue name)
{
Value returnValue = _quercusClass.unsetField(Env.getCurrent(),this,name);
if(returnValue == UnsetValue.UNSET || returnValue == NullValue.NULL)
{
// __unset didn't work, lets look in the class itself
int hash = (name.hashCode() & 0x7fffffff) % _prime;
for (Entry entry = _entries[hash];
entry != null;
entry = entry._next) {
if (name.equals(entry.getKey())) {
Entry prev = entry._prev;
Entry next = entry._next;
if (prev != null)
prev._next = next;
else
_entries[hash] = next;
if (next != null)
next._prev = prev;
_size--;
return;
}
}
}
return;
}
/**
* Removes the field array ref.
*/
@Override
public void unsetArray(Env env, StringValue name, Value index)
{
// php/022b
if (_quercusClass.getFieldGet() != null)
return;
Entry entry = createEntry(name, FieldVisibility.PUBLIC);
// XXX
//if (entry._visibility == FieldVisibility.PRIVATE)
//return;
entry.toValue().remove(index);
}
/**
* Removes the field array ref.
*/
public void unsetThisArray(Env env, StringValue name, Value index)
{
if (_quercusClass.getFieldGet() != null)
return;
Entry entry = createEntry(name, FieldVisibility.PUBLIC);
entry.toValue().remove(index);
}
/**
* Gets a new value.
*/
private Entry getEntry(Env env, StringValue name)
{
int hash = (name.hashCode() & 0x7fffffff) % _prime;
for (Entry entry = _entries[hash]; entry != null; entry = entry._next) {
StringValue entryKey = entry._key;
if (name == entryKey || name.equals(entryKey)) {
if (entry._visibility == FieldVisibility.PRIVATE) {
QuercusClass cls = env.getCallingClass();
// XXX: this really only checks access from outside of class scope
// php/091m
if (cls != _quercusClass) {
env.notice(L.l("Can't access private field '{0}::${1}'",
_quercusClass.getName(), name));
return null;
}
} else if (entry._visibility == FieldVisibility.PROTECTED)
{
QuercusClass cls = env.getCallingClass();
if (cls == null || (cls != _quercusClass && !cls.isA(_quercusClass.getName())))
{
env.notice(L.l("Can't access protected field '{0}::${1}'",
_quercusClass.getName(), name
));
return null;
}
}
return entry;
}
}
return null;
}
/**
* Gets a new value.
*/
private Entry getThisEntry(StringValue name)
{
int hash = (name.hashCode() & 0x7fffffff) % _prime;
for (Entry entry = _entries[hash]; entry != null; entry = entry._next) {
StringValue entryKey = entry._key;
if (name == entryKey || name.equals(entryKey))
return entry;
}
return null;
}
/**
* Creates the entry for a key.
*/
private Entry createEntry(StringValue name, FieldVisibility visibility)
{
int hash = (name.hashCode() & 0x7fffffff) % _prime;
for (Entry entry = _entries[hash];
entry != null;
entry = entry._next) {
if (name.equals(entry._key))
return entry;
}
_size++;
Entry newEntry = new Entry(name, visibility);
Entry next = _entries[hash];
if (next != null) {
newEntry._next = next;
next._prev = newEntry;
}
_entries[hash] = newEntry;
// XXX: possibly resize
return newEntry;
}
//
// Foreach/Traversable functions
//
/**
* Returns an iterator for the key => value pairs.
*/
@Override
public Iterator<Map.Entry<Value, Value>> getIterator(Env env)
{
TraversableDelegate delegate = _quercusClass.getTraversableDelegate();
if (delegate != null)
return delegate.getIterator(env, this);
else
return getBaseIterator(env);
}
/**
* Returns an iterator for the key => value pairs.
*/
@Override
public Iterator<Map.Entry<Value, Value>> getBaseIterator(Env env)
{
return new KeyValueIterator(_entries);
}
/**
* Returns an iterator for the keys.
*/
@Override
public Iterator<Value> getKeyIterator(Env env)
{
TraversableDelegate delegate = _quercusClass.getTraversableDelegate();
if (delegate != null)
return delegate.getKeyIterator(env, this);
return new KeyIterator(_entries);
}
/**
* Returns an iterator for the values.
*/
@Override
public Iterator<Value> getValueIterator(Env env)
{
TraversableDelegate delegate = _quercusClass.getTraversableDelegate();
if (delegate != null)
return delegate.getValueIterator(env, this);
return new ValueIterator(_entries);
}
//
// method calls
//
/**
* Finds the method name.
*/
@Override
public AbstractFunction findFunction(String methodName)
{
return _quercusClass.findFunction(methodName);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value []args)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this, args);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this, a1);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethod(Env env, StringValue methodName, int hash,
Value a1, Value a2)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this, a1, a2);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this, a1, a2, a3);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this, a1, a2, a3, a4);
}
/**
* calls the function.
*/
@Override
public Value callMethod(Env env,
StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4, Value a5)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethod(env, _quercusClass, this, a1, a2, a3, a4, a5);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value []args)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this, args);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this, a1);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this, a1, a2);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this, a1, a2, a3);
}
/**
* Evaluates a method.
*/
@Override
public Value callMethodRef(Env env, StringValue methodName, int hash,
Value a1, Value a2, Value a3, Value a4)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this, 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)
{
AbstractFunction fun = _methodMap.get(methodName, hash);
return fun.callMethodRef(env, _quercusClass, this, a1, a2, a3, a4, a5);
}
/**
* Evaluates a method.
*/
/*
@Override
public Value callClassMethod(Env env, AbstractFunction fun, Value []args)
{
return fun.callMethod(env, this, args);
}
*/
/**
* Returns the value for the variable, creating an object if the var
* is unset.
*/
@Override
public Value getObject(Env env)
{
return this;
}
/*
@Override
public Value getObject(Env env, Value index)
{
// php/3d92
env.error(L.l("Can't use object '{0}' as array", getName()));
return NullValue.NULL;
}
*/
/**
* Copy for assignment.
*/
@Override
public Value copy()
{
return this;
}
/**
* Copy for serialization
*/
@Override
public Value copy(Env env, IdentityHashMap<Value,Value> map)
{
Value oldValue = map.get(this);
if (oldValue != null)
return oldValue;
// php/4048 - needs to be deep copy
return new ObjectExtValue(env, map, this);
}
/**
* Copy for serialization
*/
@Override
public Value copyTree(Env env, CopyRoot root)
{
// php/420c
Value copy = root.getCopy(this);
if (copy != null)
return copy;
else
return new CopyObjectExtValue(env, this, root);
}
/**
* Clone the object
*/
@Override
public Value clone(Env env)
{
ObjectExtValue newObject = new ObjectExtValue(_quercusClass);
Iterator<Entry> iter = new EntryIterator(_entries);
while (iter.hasNext()) {
Entry entry = iter.next();
Entry copy = newObject.createEntry(entry.getKey(),
entry.getVisibility());
copy.setValue(entry.getValue().copy());
}
return newObject;
}
// XXX: need to check the other copy, e.g. for sessions
/*
* Serializes the value.
*
* @param sb holds result of serialization
* @param serializeMap holds reference indexes
*/
@Override
public void serialize(Env env,
StringBuilder sb, SerializeMap serializeMap)
{
Integer index = serializeMap.get(this);
if (index != null) {
sb.append("r:");
sb.append(index);
sb.append(";");
return;
}
serializeMap.put(this);
serializeMap.incrementIndex();
sb.append("O:");
sb.append(_className.length());
sb.append(":\"");
sb.append(_className);
sb.append("\":");
sb.append(getSize());
sb.append(":{");
Iterator<Entry> iter = new EntryIterator(_entries);
while (iter.hasNext()) {
Entry entry = iter.next();
sb.append("s:");
Value key = entry.getKey();
int len = key.length();
if (entry._visibility == FieldVisibility.PROTECTED) {
sb.append(len + 3);
sb.append(":\"");
sb.append("\u0000*\u0000");
}
else if (entry._visibility == FieldVisibility.PRIVATE) {
sb.append(len + 3);
sb.append(":\"");
sb.append("\u0000A\u0000");
}
else {
sb.append(len);
sb.append(":\"");
}
sb.append(key);
sb.append("\";");
Value value = ((Entry) entry).getRawValue();
value.serialize(env, sb, serializeMap);
}
sb.append("}");
}
/**
* Exports the value.
*/
@Override
public void varExport(StringBuilder sb)
{
sb.append(getName());
sb.append("::__set_state(array(\n");
for (Map.Entry<Value,Value> entry : entrySet()) {
sb.append(" ");
entry.getKey().varExport(sb);
sb.append(" => ");
entry.getValue().varExport(sb);
sb.append(",\n");
}
sb.append("))");
}
/**
* Append to a string builder.
*/
public StringValue appendTo(UnicodeBuilderValue sb)
{
return sb.append(toString(Env.getInstance()));
}
/**
* Append to a binary builder.
*/
public StringValue appendTo(StringBuilderValue sb)
{
return sb.append(toString(Env.getInstance()));
}
/**
* Append to a binary builder.
*/
public StringValue appendTo(BinaryBuilderValue sb)
{
return sb.appendBytes(toString(Env.getInstance()));
}
/**
* Append to a binary builder.
*/
public StringValue appendTo(LargeStringBuilderValue sb)
{
return sb.append(toString(Env.getInstance()));
}
/**
* Converts to a string builder
*/
@Override
public StringValue toStringBuilder(Env env)
{
return toString(env).toStringBuilder(env);
}
/**
* Converts to a java String object.
*/
public String toJavaString()
{
return toString(Env.getInstance()).toString();
}
/**
* Converts to a string.
* @param env
*/
@Override
public StringValue toString(Env env)
{
AbstractFunction toString = _quercusClass.getToString();
if (toString != null)
return toString.callMethod(env, _quercusClass, this).toStringValue();
else
return env.createString(_className + "[]");
}
/**
* Converts to a string.
* @param env
*/
@Override
public void print(Env env)
{
env.print(toString(env));
}
/**
* Converts to an array.
*/
@Override
public Value toArray()
{
ArrayValue array = new ArrayValueImpl();
for (Map.Entry<Value,Value> entry : entrySet()) {
array.put(entry.getKey(), entry.getValue());
}
return array;
}
/**
* Converts to an object.
*/
@Override
public Value toObject(Env env)
{
return this;
}
/**
* Converts to an object.
*/
@Override
public Object toJavaObject()
{
return this;
}
@Override
public Set<? extends Map.Entry<Value,Value>> entrySet()
{
return new EntrySet();
}
/**
* Returns a Set of entries, sorted by key.
*/
public Set<? extends Map.Entry<Value,Value>> sortedEntrySet()
{
return new TreeSet<Map.Entry<Value, Value>>(entrySet());
}
//
// debugging
//
//XXX: push up to super, and use varDumpObject
public void varDumpImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException
{
int size = getSize();
if (isIncompleteObject())
size++;
out.println("object(" + getName() + ") (" + size + ") {");
if (isIncompleteObject()) {
printDepth(out, 2 * (depth + 1));
out.println("[\"__Quercus_Incomplete_Class_name\"]=>");
printDepth(out, 2 * (depth + 1));
Value value = env.createString(getIncompleteObjectName());
value.varDump(env, out, depth + 1, valueSet);
out.println();
}
for (Map.Entry<Value,Value> mapEntry : sortedEntrySet()) {
ObjectExtValue.Entry entry = (ObjectExtValue.Entry) mapEntry;
entry.varDumpImpl(env, out, depth + 1, valueSet);
}
printDepth(out, 2 * depth);
out.print("}");
}
@Override
protected void printRImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException
{
out.print(getName());
out.print(' ');
out.println("Object");
printDepth(out, 4 * depth);
out.println("(");
for (Map.Entry<Value,Value> mapEntry : sortedEntrySet()) {
ObjectExtValue.Entry entry = (ObjectExtValue.Entry) mapEntry;
entry.printRImpl(env, out, depth + 1, valueSet);
}
printDepth(out, 4 * depth);
out.println(")");
}
//
// Java Serialization
//
private void writeObject(ObjectOutputStream out)
throws IOException
{
out.writeObject(_className);
out.writeInt(_size);
for (Map.Entry<Value,Value> entry : entrySet()) {
out.writeObject(entry.getKey());
out.writeObject(entry.getValue());
}
}
/**
* Encodes the value in JSON.
*/
@Override
public void jsonEncode(Env env, StringValue sb)
{
sb.append('{');
int length = 0;
Iterator<Entry> iter = new EntryIterator(_entries);
while (iter.hasNext()) {
Entry entry = iter.next();
if (entry.getVisibility() != FieldVisibility.PUBLIC)
continue;
if (length > 0)
sb.append(',');
entry.getKey().toStringValue().jsonEncode(env, sb);
sb.append(':');
entry.getValue().jsonEncode(env, sb);
length++;
}
sb.append('}');
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException
{
Env env = Env.getInstance();
String name = (String) in.readObject();
QuercusClass cl = env.findClass(name);
init();
if (cl != null) {
setQuercusClass(cl);
}
else {
cl = env.getQuercus().getStdClass();
setQuercusClass(cl);
setIncompleteObjectName(name);
}
int size = in.readInt();
for (int i = 0; i < size; i++) {
putThisField(env,
(StringValue) in.readObject(),
(Value) in.readObject());
}
}
public void cleanup(Env env)
{
QuercusClass qClass = getQuercusClass();
AbstractFunction fun = qClass.getDestructor();
if (fun != null)
fun.callMethod(env, qClass, this);
}
private static String toMethod(char []key, int keyLength)
{
return new String(key, 0, keyLength);
}
@Override
public boolean issetField( StringValue name) {
Value returnValue = _quercusClass.issetField(Env.getCurrent(),this,name);
if(returnValue == UnsetValue.UNSET)
{
// setter didn't work, lets look in the class itself
int hash = (name.hashCode() & 0x7fffffff) % _prime;
for (Entry entry = _entries[hash]; entry != null; entry = entry._next) {
StringValue entryKey = entry._key;
if ((name == entryKey || name.equals(entryKey)) && entry._value != NullValue.NULL ) {
// php/09ks vs php/091m
return true;
}
}
if(this.isA("arrayaccess"))
{
// TODO: This should probably be in ArrayAccessDelegate
Env _env = Env.getCurrent();
Value v = this.getObject(_env).getArray().get(name);
if( v != null && v != NullValue.NULL && v != UnsetValue.UNSET)
return true;
return false;
}
}
return returnValue.toBoolean();
}
@Override
public String toString()
{
if (Alarm.isTest())
return getClass().getSimpleName() + "[" + _className + "]";
else
return getClass().getSimpleName()
+ "@" + System.identityHashCode(this)
+ "[" + _className + "]";
}
public class EntrySet extends AbstractSet<Map.Entry<Value,Value>> {
EntrySet()
{
}
@Override
public int size()
{
return ObjectExtValue.this.getSize();
}
@Override
public Iterator<Map.Entry<Value,Value>> iterator()
{
return new KeyValueIterator(ObjectExtValue.this._entries);
}
}
public static class EntryIterator
implements Iterator<Entry>
{
private final Entry []_list;
private int _index;
private Entry _entry;
EntryIterator(Entry []list)
{
_list = list;
}
public boolean hasNext()
{
if (_entry != null)
return true;
for (; _index < _list.length && _list[_index] == null; _index++) {
}
return _index < _list.length;
}
public Entry next()
{
if (_entry != null) {
Entry entry = _entry;
_entry = entry._next;
return entry;
}
for (; _index < _list.length && _list[_index] == null; _index++) {
}
if (_list.length <= _index)
return null;
Entry entry = _list[_index++];
_entry = entry._next;
return entry;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
public static class KeyValueIterator
implements Iterator<Map.Entry<Value,Value>>
{
private final Entry []_list;
private int _index;
private Entry _entry;
KeyValueIterator(Entry []list)
{
_list = list;
}
public boolean hasNext()
{
if (_entry != null)
return true;
for (; _index < _list.length && _list[_index] == null; _index++) {
}
return _index < _list.length;
}
public Map.Entry<Value,Value> next()
{
if (_entry != null) {
Entry entry = _entry;
_entry = entry._next;
return entry;
}
for (; _index < _list.length && _list[_index] == null; _index++) {
}
if (_list.length <= _index)
return null;
Entry entry = _list[_index++];
_entry = entry._next;
return entry;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
public static class ValueIterator
implements Iterator<Value>
{
private final Entry []_list;
private int _index;
private Entry _entry;
ValueIterator(Entry []list)
{
_list = list;
}
public boolean hasNext()
{
if (_entry != null)
return true;
for (; _index < _list.length && _list[_index] == null; _index++) {
}
return _index < _list.length;
}
public Value next()
{
if (_entry != null) {
Entry entry = _entry;
_entry = entry._next;
return entry._value;
}
for (; _index < _list.length && _list[_index] == null; _index++) {
}
if (_list.length <= _index)
return null;
Entry entry = _list[_index++];
_entry = entry._next;
return entry._value;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
public static class KeyIterator
implements Iterator<Value>
{
private final Entry []_list;
private int _index;
private Entry _entry;
KeyIterator(Entry []list)
{
_list = list;
}
public boolean hasNext()
{
if (_entry != null)
return true;
for (; _index < _list.length && _list[_index] == null; _index++) {
}
return _index < _list.length;
}
public Value next()
{
if (_entry != null) {
Entry entry = _entry;
_entry = entry._next;
return entry._key;
}
for (; _index < _list.length && _list[_index] == null; _index++) {
}
if (_list.length <= _index)
return null;
Entry entry = _list[_index++];
_entry = entry._next;
return entry._key;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
public final static class Entry
implements Map.Entry<Value,Value>,
Comparable<Map.Entry<Value, Value>>
{
private final StringValue _key;
private final FieldVisibility _visibility;
private Value _value;
private Entry _prev;
private Entry _next;
public Entry(StringValue key)
{
_key = key;
_visibility = FieldVisibility.PUBLIC;
_value = NullValue.NULL;
}
public Entry(StringValue key, FieldVisibility visibility)
{
_key = key;
_visibility = visibility;
_value = NullValue.NULL;
}
public Entry(StringValue key, Value value)
{
_key = key;
_visibility = FieldVisibility.PUBLIC;
_value = value;
}
public Entry(StringValue key, Value value, FieldVisibility visibility)
{
_key = key;
_visibility = visibility;
_value = value;
}
public Entry(Env env, IdentityHashMap<Value,Value> map, Entry entry)
{
_key = entry._key;
_visibility = entry._visibility;
_value = entry._value.copy(env, map);
}
public Value getValue()
{
return _value.toValue();
}
public Value getRawValue()
{
return _value;
}
public StringValue getKey()
{
return _key;
}
public Entry getNext()
{
return _next;
}
public void setNext(Entry next)
{
_next = next;
}
public FieldVisibility getVisibility()
{
return _visibility;
}
public final boolean isPrivate()
{
return _visibility == FieldVisibility.PRIVATE;
}
public Value toValue()
{
// The value may be a var
// XXX: need test
return _value.toValue();
}
/**
* Argument used/declared as a ref.
*/
public Var toRefVar()
{
Var var = _value.toLocalVarDeclAsRef();
_value = var;
return var;
}
/**
* Converts to an argument value.
*/
public Value toArgValue()
{
return _value.toValue();
}
public Value setValue(Value value)
{
Value oldValue = toValue();
_value = value;
return oldValue;
}
/**
* Converts to a variable reference (for function arguments)
*/
public Value toRef()
{
Value value = _value;
if (value instanceof Var)
return new ArgRef((Var) value);
else {
Var var = new Var(_value);
_value = var;
return new ArgRef(var);
}
}
/**
* Converts to a variable reference (for function arguments)
*/
public Value toArgRef()
{
Value value = _value;
if (value instanceof Var)
return new ArgRef((Var) value);
else {
Var var = new Var(_value);
_value = var;
return new ArgRef(var);
}
}
public Value toArg()
{
Value value = _value;
if (value instanceof Var)
return value;
else {
Var var = new Var(_value);
_value = var;
return var;
}
}
Entry copyTree(Env env, CopyRoot root)
{
Value copy = root.getCopy(_value);
if (copy == null)
copy = _value.copyTree(env, root);
return new Entry(_key, copy, _visibility);
}
public int compareTo(Map.Entry<Value, Value> other)
{
if (other == null)
return 1;
Value thisKey = getKey();
Value otherKey = other.getKey();
if (thisKey == null)
return otherKey == null ? 0 : -1;
if (otherKey == null)
return 1;
return thisKey.cmp(otherKey);
}
public void varDumpImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException
{
String suffix = "";
if (_visibility == FieldVisibility.PROTECTED)
suffix = ":protected";
else if (_visibility == FieldVisibility.PRIVATE)
suffix = ":private";
printDepth(out, 2 * depth);
out.println("[\"" + getKey() + suffix + "\"]=>");
printDepth(out, 2 * depth);
_value.varDump(env, out, depth, valueSet);
out.println();
}
protected void printRImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException
{
String suffix = "";
if (_visibility == FieldVisibility.PROTECTED)
suffix = ":protected";
else if (_visibility == FieldVisibility.PRIVATE)
suffix = ":private";
printDepth(out, 4 * depth);
out.print("[" + getKey() + suffix + "] => ");
_value.printR(env, out, depth + 1, valueSet);
out.println();
}
private void printDepth(WriteStream out, int depth)
throws java.io.IOException
{
for (int i = 0; i < depth; i++)
out.print(' ');
}
@Override
public String toString()
{
return "ObjectExtValue.Entry[" + getKey() + "]";
}
}
}