Package com.caucho.quercus.env

Source Code of com.caucho.quercus.env.ArrayValue

/*
* Copyright (c) 1998-2008 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.marshal.Marshal;
import com.caucho.quercus.marshal.MarshalFactory;
import com.caucho.vfs.WriteStream;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Represents a PHP array value.
*/
abstract public class ArrayValue extends Value {
  private static final Logger log
    = Logger.getLogger(ArrayValue.class.getName());

  protected static final StringValue KEY = new ConstStringValue("key");
  protected static final StringValue VALUE = new ConstStringValue("value");

  public static final GetKey GET_KEY = new GetKey();
  public static final GetValue GET_VALUE = new GetValue();

  protected Entry _current;

  protected ArrayValue()
  {
  }

  /**
   * Returns the type.
   */
  @Override
  public String getType()
  {
    return "array";
  }

  /**
   * Returns the ValueType.
   */
  @Override
  public ValueType getValueType()
  {
    return ValueType.ARRAY;
  }

  /**
   * Converts to a boolean.
   */
  @Override
  public boolean toBoolean()
  {
    return getSize() != 0;
  }
 
  /**
   * Converts to a long.
   */
  @Override
  public long toLong()
  {
    if (getSize() > 0)
      return 1;
    else
      return 0;
  }
 
  /**
   * Converts to a double.
   */
  @Override
  public double toDouble()
  {
    return toLong();
  }

  /**
   * Converts to a string.
   */
  @Override
  public String toString()
  {
    return "Array";
  }

  /**
   * Converts to an object.
   */
  public Object toObject()
  {
    return null;
  }

  /**
   * Converts to a java object.
   */
  @Override
  public Object toJavaObject()
  {
    return this;
  }

  //
  // Conversions
  //
 
  /**
   * Converts to an object.
   */
  @Override
  public Value toArray()
  {
    return this;
  }
 
  /**
   * Converts to an array value
   */
  @Override
  public ArrayValue toArrayValue(Env env)
  {
    return this;
  }

  /**
   * Converts to an object.
   */
  @Override
  public Value toObject(Env env)
  {
    Value obj = env.createObject();

    for (Entry entry = getHead(); entry != null; entry = entry._next) {
      Value key = entry.getKey();

      // php/03oe
      obj.putField(env, key.toString(), entry.getValue());
    }

    return obj;
  }

  /**
   * Converts to a java List object.
   */
  @Override
  @SuppressWarnings("unchecked")
  public <T> Collection<T> toJavaCollection(Env env, Class<? extends Collection<T>> type)
  {
    Collection<T> coll = null;
   
    if (type.isAssignableFrom(HashSet.class)) {
      coll = new HashSet<T>();
    }
    else if (type.isAssignableFrom(TreeSet.class)) {
      coll = new TreeSet<T>();
    }
    else {
      try {
        coll = 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 (Entry entry = getHead(); entry != null; entry = entry._next) {
      coll.add((T)entry.getValue().toJavaObject());
    }

    return coll;
  }
 
  /**
   * Converts to a java List object.
   */
  @SuppressWarnings("unchecked")
  @Override
  public <T> List<T> toJavaList(Env env, Class<? extends List<T>> type)
  {
    List<T> list = null;
   
    if (type.isAssignableFrom(ArrayList.class)) {
      list = new ArrayList<T>();
    }
    else if (type.isAssignableFrom(LinkedList.class)) {
      list = new LinkedList<T>();
    }
    else if (type.isAssignableFrom(Vector.class)) {
      list = new Vector<T>();
    }
    else {
      try {
        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 (Entry entry = getHead(); entry != null; entry = entry._next) {
      list.add((T)entry.getValue().toJavaObject());
    }

    return list;
  }
 
  /**
   * Converts to a java object.
   */
  @SuppressWarnings("unchecked")
  @Override
  public <K,V> Map<K,V> toJavaMap(Env env, Class<? extends Map<K,V>> type)
  {
    Map<K,V> 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 (Entry entry = getHead(); entry != null; entry = entry._next) {
      map.put((K)entry.getKey().toJavaObject(),
                  (V)entry.getValue().toJavaObject());
    }

    return map;
  }

  /**
   * Returns true for an array.
   */
  @Override
  public boolean isArray()
  {
    return true;
  }

  /**
   * Copy as a return value
   */
  @Override
  public Value copyReturn()
  {
    return copy(); // php/3a5e
  }

  /**
   * 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();

  /**
   * Returns the count().
   */
  @Override
  public int getCount(Env env)
  {
    return getSize();
  }

  /**
   * Returns the count().
   */
  @Override
  public int getCountRecursive(Env env)
  {
    env.stub("recursive count of array unimplemented");

    return getSize();
  }

  /**
   * Returns true if the value is empty
   */
  @Override
  public boolean isEmpty()
  {
    return getSize() == 0;
  }

  /**
   * Clears the array
   */
  abstract public void clear();

  @Override
  public int cmp(Value rValue)
  {
    return cmpImpl(rValue, 1);
  }

  private int cmpImpl(Value rValue, int resultIfKeyMissing)
  {
    // "if key from operand 1 is not found in operand 2 then
    // arrays are uncomparable, otherwise - compare value by value"

    // php/335h

    if (!rValue.isArray())
      return 1;

    int lSize =  getSize();
    int rSize = rValue.toArray().getSize();
   
    if (lSize != rSize)
      return lSize < rSize ? -1 : 1;

    for (Map.Entry<Value,Value> entry : entrySet()) {
      Value lElementValue = entry.getValue();
      Value rElementValue = rValue.get(entry.getKey());

      if (!rElementValue.isset())
        return resultIfKeyMissing;

      int cmp = lElementValue.cmp(rElementValue);

      if (cmp != 0)
        return cmp;
    }

    return 0;
  }

  /**
   * Returns true for less than
   */
  @Override
  public boolean lt(Value rValue)
  {
    // php/335h
    return cmpImpl(rValue, 1) < 0;
  }

  /**
   * Returns true for less than or equal to
   */
  @Override
  public boolean leq(Value rValue)
  {
    // php/335h
    return cmpImpl(rValue, 1) <= 0;
  }

  /**
   * Returns true for greater than
   */
  @Override
  public boolean gt(Value rValue)
  {
    // php/335h
    return cmpImpl(rValue, -1) > 0;
  }

  /**
   * Returns true for greater than or equal to
   */
  @Override
  public boolean geq(Value rValue)
  {
    // php/335h
    return cmpImpl(rValue, -1) >= 0;
  }

  /**
   * Adds a new value.
   */
  @Override
  public Value put(Value key, Value value)
  {
    append(key, value);
   
    return value;
  }

  /**
   * Add
   */
  @Override
  abstract public Value put(Value value);

  /**
   * Add to front.
   */
  abstract public ArrayValue unshift(Value value);

  /**
   * Splices.
   */
  abstract public ArrayValue splice(int begin, int end, ArrayValue replace);

  /**
   * Slices.
   */
  public ArrayValue slice(Env env, int start, int end, boolean isPreserveKeys)
  {
    ArrayValueImpl array = new ArrayValueImpl();
   
    Iterator<Map.Entry<Value,Value>> iter = array.getIterator(env);
   
    for (int i = 0; i < end && iter.hasNext(); i++) {
      Map.Entry<Value,Value> entry = iter.next();
     
      if (start <= i) {
        Value key = entry.getKey();

        Value value = entry.getValue();

        if ((key.isString()) || isPreserveKeys)
          array.put(key, value);
        else
          array.put(value);
      }
    }

    return array;
  }
 
  /**
   * Returns the value as an array.
   */
  @Override
  public Value getArray(Value index)
  {
    Value value = get(index);

    Value array = value.toAutoArray();
   
    if (value != array) {
      value = array;

      put(index, value);
    }

    return value;
  }

  /**
   * Returns the value as an argument which may be a reference.
   */
  @Override
  abstract public Value getArg(Value index, boolean isTop);

  /**
   * Returns the field value, creating an object if it's unset.
   */
  @Override
  public Value getObject(Env env, Value fieldName)
  {
    Value value = get(fieldName);

    Value object = value.toAutoObject(env);
    if (value != object) {
      value = object;

      put(fieldName, value);
    }

    return value;
  }

  /**
   * Sets the array ref.
   */
  @Override
  abstract public Var putRef();

  /**
   * Creatse a tail index.
   */
  abstract public Value createTailKey();

  /**
   * Returns a union of this array and the rValue as array.
   * If the rValue is not an array, the returned union contains the elements
   * of this array only.
   *
   * To append a value to this ArrayValue use the {@link #put(Value)} method.
   */
  @Override
  public Value add(Value rValue)
  {
    rValue = rValue.toValue();

    if (! rValue.isArray())
      return copy();
   
    ArrayValue result = new ArrayValueImpl(this);
   
    Iterator<Map.Entry<Value,Value>> iter
      = ((ArrayValue) rValue).getIterator(Env.getInstance());

    while (iter.hasNext()) {
      Map.Entry<Value,Value> entry = iter.next();
     
      Value key = entry.getKey();
     
      if (result.get(key) == UnsetValue.UNSET)
        result.put(key, entry.getValue());
    }

    return result;
  }

  @Override
  public Iterator<Map.Entry<Value, Value>> getIterator(Env env)
  {
    return new EntryIterator(getHead());
  }
 
  public Iterator<Map.Entry<Value, Value>> getIterator()
  {
    return new EntryIterator(getHead());
  }

  @Override
  public Iterator<Value> getKeyIterator(Env env)
  {
    return new KeyIterator(getHead());
  }

  @Override
  public Iterator<Value> getValueIterator(Env env)
  {
    return new ValueIterator(getHead());
  }

  /**
   * Gets a new value.
   */
  @Override
  abstract public Value get(Value key);

  /**
   * Returns the value in the array as-is.
   * (i.e. without calling toValue() on it).
   */
  public Value getRaw(Value key)
  {
    return get(key);
  }
 
  /**
   * Removes a value.
   */
  @Override
  abstract public Value remove(Value key);

  /**
   * Returns the array ref.
   */
  @Override
  abstract public Var getRef(Value index);

  /**
   * Returns an iterator of the entries.
   */
  public Set<Value> keySet()
  {
    return new KeySet();
  }

  /**
   * Returns a set of all the of the entries.
   */
  public Set<Map.Entry<Value,Value>> entrySet()
  {
    return new EntrySet();
  }

  /**
   * Returns a collection of the values.
   */
  public Collection<Value> values()
  {
    return new ValueCollection();
  }
 
  /**
   * Convenience for lib.
   */
  public void put(String key, String value)
  {
    // XXX: this needs an Env arg because of i18n
    // XXX: but some  modules have arrays that are static constants
    put(StringValue.create(key), StringValue.create(value));
  }

  /**
   * Convenience for lib.
   */
  public void put(Env env, String key, String value)
  {
    put(env.createStringOld(key), env.createStringOld(value));
  }

  /**
   * Convenience for lib.
   */
  public void put(String key, char value)
  {
    // XXX: this needs an Env arg because of i18n
    put(StringValue.create(key), StringValue.create(value));
  }

  /**
   * Convenience for lib.
   */
  public void put(String key, long value)
  {
    // XXX: this needs an Env arg because of i18n
    put(StringValue.create(key), LongValue.create(value));
  }

  /**
   * Convenience for lib.
   */
  public void put(Env env, String key, long value)
  {
    put(env.createStringOld(key), LongValue.create(value));
  }
 
  /**
   * Convenience for lib.
   */
  public void put(String key, double value)
  {
    // XXX: this needs an Env arg because of i18n
    put(StringValue.create(key), new DoubleValue(value));
  }

  /**
   * Convenience for lib.
   */
  public void put(String key, boolean value)
  {
    // XXX: this needs an Env arg because of i18n
    put(StringValue.create(key), value ? BooleanValue.TRUE : BooleanValue.FALSE);
  }

  /**
   * Convenience for lib.
   */
  public void put(Env env, String key, boolean value)
  {
    put(env.createStringOld(key),
        value ? BooleanValue.TRUE : BooleanValue.FALSE);
  }

  /**
   * Convenience for lib.
   */
  public void put(String value)
  {
    // XXX: this needs an Env arg because of i18n
    put(StringValue.create(value));
  }

  /**
   * Convenience for lib.
   */
  public void put(long value)
  {
    put(LongValue.create(value));
  }

  /**
   * Appends as an argument - only called from compiled code
   *
   * XXX: change name to appendArg
   */
  abstract public ArrayValue append(Value key, Value value);

  /**
   * Appends as an argument - only called from compiled code
   *
   * XXX: change name to appendArg
   */
  public ArrayValue append(Value value)
  {
    put(value);

    return this;
  }

  /**
   * Puts all of the arg elements into this array.
   */
  public void putAll(ArrayValue array)
  {
    for (Map.Entry<Value, Value> entry : array.entrySet())
      put(entry.getKey(), entry.getValue());
  }

  /**
   * Convert to an array.
   */
  public static Value toArray(Value value)
  {
    value = value.toValue();

    if (value instanceof ArrayValue)
      return value;
    else
      return new ArrayValueImpl().put(value);
  }


  /**
   * Prints the value.
   * @param env
   */
  @Override
  public void print(Env env)
  {
    env.print("Array");
  }

  /**
   * Pops the top value.
   */
  abstract public Value pop();

  /**
   * Shuffles the array
   */
  abstract public void shuffle();

  /**
   * Returns the head.
   */
  abstract protected Entry getHead();

  /**
   * Returns the tail.
   */
  abstract protected Entry getTail();

  /**
   * Returns the current value.
   */
  @Override
  public Value current()
  {
    if (_current != null)
      return _current.getValue();
    else
      return BooleanValue.FALSE;
  }

  /**
   * Returns the current key
   */
  @Override
  public Value key()
  {
    if (_current != null)
      return _current.getKey();
    else
      return NullValue.NULL;
  }

  /**
   * Returns true if there are more elements.
   */
  @Override
  public boolean hasCurrent()
  {
    return _current != null;
  }

  /**
   * Returns the next value.
   */
  @Override
  public Value next()
  {
    if (_current != null)
      _current = _current._next;

    return current();
  }

  /**
   * Returns the previous value.
   */
  public Value prev()
  {
    if (_current != null)
      _current = _current._prev;

    return current();
  }

  /**
   * The each iterator
   */
  public Value each()
  {
    if (_current == null)
      return NullValue.NULL;

    ArrayValue result = new ArrayValueImpl();

    result.put(LongValue.ZERO, _current.getKey());
    result.put(KEY, _current.getKey());

    result.put(LongValue.ONE, _current.getValue());
    result.put(VALUE, _current.getValue());

    _current = _current._next;

    return result;
  }

  /**
   * Returns the first value.
   */
  public Value reset()
  {
    _current = getHead();

    return current();
  }

  /**
   * Returns the last value.
   */
  public Value end()
  {
    _current = getTail();

    return current();
  }

  /**
   * 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
   */
  abstract public Value contains(Value value);

  /**
   * 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
   */
  abstract public Value containsStrict(Value value);

  /**
   * Returns the corresponding value 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
   */
  abstract public Value containsKey(Value key);

  /**
   * 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
   */
  public Map.Entry<Value, Value>[] toEntryArray()
  {
    ArrayList<Map.Entry<Value, Value>> array
      = new ArrayList<Map.Entry<Value, Value>>(getSize());

    for (Entry entry = getHead(); entry != null; entry = entry._next)
      array.add(entry);

    Map.Entry<Value, Value>[]result = new Entry[array.size()];

    return array.toArray(result);
  }

  /**
   * 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
   */
  public void sort(Comparator<Map.Entry<Value, Value>> comparator,
                   boolean resetKeys, boolean strict)
  {
    Entry []entries;

    entries = new Entry[getSize()];

    int i = 0;
    for (Entry entry = getHead(); entry != null; entry = entry._next) {
      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.
   *
   * @param sb holds result of serialization
   * @param serializeMap holds reference indexes
   */
  @Override
  public void serialize(Env env, StringBuilder sb, SerializeMap serializeMap)
  {
    sb.append("a:");
    sb.append(getSize());
    sb.append(":{");
   
    serializeMap.incrementIndex();

    for (Entry entry = getHead(); entry != null; entry = entry._next) {
      entry.getKey().serialize(env, sb);
      entry.getRawValue().serialize(env, sb, serializeMap);
    }

    sb.append("}");
  }

  /**
   * Exports the value.
   */
  @Override
  public void varExport(StringBuilder sb)
  {
    sb.append("array (");
    sb.append("\n");

    //boolean isFirst = true;
    for (Entry entry = getHead(); entry != null; entry = entry._next) {
      sb.append("  ");
      entry.getKey().varExport(sb);
      sb.append(" => ");
      entry.getValue().varExport(sb);
      sb.append(",\n");
    }

    sb.append(")");
  }

  /**
   * Resets all numerical keys with the first index as base
   *
   * @param base  the initial index
   * @param strict  if true, string keys are also reset
   */
  public boolean keyReset(long base, boolean strict)
  {
    Entry []entries;

    entries = new Entry[getSize()];

    int i = 0;
    for (Entry entry = getHead(); entry != null; entry = entry._next) {
      entries[i++] = entry;
    }

    clear();

    for (int j = 0; j < entries.length; j++) {
      Value key = entries[j].getKey();

      if (! (key instanceof StringValue) || strict)
        put(LongValue.create(base++), entries[j].getValue());
      else
        put(entries[j].getKey(), entries[j].getValue());
    }

    return true;
  }

  /**
   * Test for equality
   *
   * @param rValue rhs ArrayValue to compare to
   *
   * @return true if this is equal to rValue, false otherwise
   */
  @Override
  public boolean eq(Value rValue)
  {
    if (rValue == null)
      return false;

    for (Map.Entry<Value, Value> entry: entrySet()) {
      Value entryValue = entry.getValue();

      Value entryKey = entry.getKey();

      Value rEntryValue = rValue.get(entryKey);

      if ((rEntryValue instanceof ArrayValue) &&
          ! entryValue.eq((ArrayValue) rEntryValue))
        return false;

      if (! entryValue.eq(rEntryValue))
        return false;
    }

    return true;
  }

  /**
   * Test for ===
   *
   * @param rValue rhs ArrayValue to compare to
   *
   * @return true if this is equal to rValue, false otherwise
   */
  @Override
  public boolean eql(Value rValue)
  {
    if (rValue == null)
      return false;
    else if (getSize() != rValue.getSize())
      return false;

    rValue = rValue.toValue();

    if (! (rValue instanceof ArrayValue))
      return false;

    ArrayValue rArray = (ArrayValue) rValue;

    Iterator<Map.Entry<Value,Value>> iterA = entrySet().iterator();
    Iterator<Map.Entry<Value,Value>> iterB = rArray.entrySet().iterator();

    while (iterA.hasNext() && iterB.hasNext()) {
      Map.Entry<Value,Value> entryA = iterA.next();
      Map.Entry<Value,Value> entryB = iterB.next();

      if (! entryA.getKey().eql(entryB.getKey()))
        return false;

      if (! entryA.getValue().eql(entryB.getValue()))
        return false;
    }

    if (iterA.hasNext() || iterB.hasNext())
      return false;
    else
      return true;
  }

  @Override
  public void varDumpImpl(Env env,
                          WriteStream out,
                          int depth,
                          IdentityHashMap<Value, String> valueSet)
    throws IOException
  {
    out.println("array(" + getSize() + ") {");

    for (Map.Entry<Value,Value> mapEntry : entrySet()) {
      varDumpEntry(env, out, depth + 1, valueSet, mapEntry);

      out.println();
    }

    printDepth(out, 2 * depth);

    out.print("}");
  }

  protected void varDumpEntry(Env env,
                              WriteStream out,
                              int depth,
                              IdentityHashMap<Value, String> valueSet,
                              Map.Entry<Value, Value> mapEntry)
    throws IOException
  {
    ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry;

    entry.varDumpImpl(env, out, depth, valueSet);
  }
 
  @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()) {
      ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry;
     
      entry.printRImpl(env, out, depth, valueSet);
    }

    printDepth(out, 8 * depth);
    out.println(")");
  }
 
  protected void printREntry(Env env,
                             WriteStream out,
                             int depth,
                             IdentityHashMap<Value, String> valueSet,
                             Map.Entry<Value, Value> mapEntry)
    throws IOException
  {
    ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry;

    entry.printRImpl(env, out, depth, valueSet);
  }

  public static final class Entry
    implements Map.Entry<Value,Value>, Serializable 
  {
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    final Value _key;
   
    Value _value;
    Var _var;

    Entry _prev;
    Entry _next;

    Entry _nextHash;

    public Entry(Value key)
    {
      _key = key;
      _value = NullValue.NULL;
    }

    public Entry(Value key, Value value)
    {
      _key = key;
      _value = value;
    }

    public Entry(Entry entry)
    {
      _key = entry._key;

      if (entry._var != null)
        _var = entry._var;
      else
        _value = entry._value.copyArrayItem();
    }

    public Entry getNext()
    {
      return _next;
    }

    public Value getRawValue()
    {
      return _var != null ? _var : _value;
    }

    public Value getValue()
    {
      return _var != null ? _var.toValue() : _value;
    }

    public Value getKey()
    {
      return _key;
    }

    public Value toValue()
    {
      // The value may be a var
      // XXX: need test
      return _var != null ? _var.toValue() : _value;
    }

    /**
     * Argument used/declared as a ref.
     */
    public Var toRefVar()
    {
      // php/376a

      if (_var != null)
        return _var;
      else {
        _var = new Var(_value);

        return _var;
      }
    }

    /**
     * Converts to an argument value.
     */
    public Value toArgValue()
    {
      return _var != null ? _var.toValue() : _value;
    }

    public Value setValue(Value value)
    {
      Value oldValue = _value;

      _value = value;
      _var = null;

      return oldValue;
    }

    public Value set(Value value)
    {
      Value oldValue = _value;

      if (value instanceof Var)
        _var = (Var) value;
      else if (_var != null)
        _var.set(value);
      else
        _value = value;

      return oldValue;
    }

    /**
     * Converts to a variable reference (for function  arguments)
     */
    public Value toRef()
    {
      if (_var == null)
        _var = new Var(_value);
     
      return new RefVar(_var);
    }

    /**
     * Converts to a variable reference (for function  arguments)
     */
    public Value toArgRef()
    {
      if (_var == null)
        _var = new Var(_value);
     
      return new RefVar(_var);
    }

    public Value toArg()
    {
      if (_var == null)
        _var = new Var(_value);
     
      return _var;
    }

    public void varDumpImpl(Env env,
                            WriteStream out,
                            int depth,
                            IdentityHashMap<Value, String> valueSet)
      throws IOException
    {
      printDepth(out, 2 * depth);
      out.print("[");

      if (_key instanceof StringValue)
        out.print("\"" + _key + "\"");
      else
        out.print(_key);

      out.println("]=>");

      printDepth(out, 2 * depth);

      getRawValue().varDump(env, out, depth, valueSet);
    }

    protected void printRImpl(Env env,
                              WriteStream out,
                              int depth,
                              IdentityHashMap<Value, String> valueSet)
      throws IOException
    {
      printDepth(out, 8 * depth);
      out.print("    [");
      out.print(_key);
      out.print("] => ");
      if (getRawValue() != null)
        getRawValue().printR(env, out, depth + 1, valueSet);
      out.println();
    }

    private void printDepth(WriteStream out, int depth)
      throws java.io.IOException
    {
      for (int i = depth; i > 0; i--)
        out.print(' ');
    }

    @Override
    public String toString()
    {
      return "ArrayValue.Entry[" + getKey() + "]";
    }
  }

  /**
   * Returns the field keys.
   */
  public Value []getKeyArray(Env env)
  {
    int len = getSize();
    Value []keys = new Value[len];

    Iterator<Value> iter = getKeyIterator(env);
   
    for (int i = 0; i < len; i++) {
      keys[i] = iter.next();
    }
   
    return keys;
  }
 
  /**
   * Returns the field values.
   */
  public Value []getValueArray(Env env)
  {
    int len = getSize();
    Value []values = new Value[len];

    Iterator<Value> iter = getValueIterator(env);
   
    for (int i = 0; i < len; i++) {
      values[i] = iter.next();
    }
   
    return values;
  }
 
  /**
   * Takes the values of this array and puts them in a java array
   */
  public Value[] keysToArray()
  {
    Value[] values = new Value[getSize()];

    int i = 0;
    for (Entry ptr = getHead(); ptr != null; ptr = ptr.getNext()) {
      values[i++] = ptr.getKey();
    }

    return values;
  }
 
  /**
   * Takes the values of this array and puts them in a java array
   */
  public Value[] valuesToArray()
  {
    Value[] values = new Value[getSize()];

    int i = 0;
    for (Entry ptr = getHead(); ptr != null; ptr = ptr.getNext()) {
      values[i++] = ptr.getValue();
    }

    return values;
  }
 
  /**
   * Returns the keys.
   */
  public Value getKeys()
  {
    return new ArrayValueImpl(keysToArray());
  }

  /**
   * Returns the keys.
   */
  public Value getValues()
  {
    return new ArrayValueImpl(valuesToArray());
  }
 
  /**
   * Takes the values of this array, unmarshals them to objects of type
   * <i>elementType</i>, and puts them in a java array.
   */
  @Override
  public <T> T[] valuesToArray(Env env, Class<T> elementType)
  {
    int size = getSize();

    T[] array = T[].class.cast(Array.newInstance(elementType, size));

    MarshalFactory factory = env.getModuleContext().getMarshalFactory();
    Marshal elementMarshal = factory.create(elementType);

    int i = 0;

    for (Entry ptr = getHead(); ptr != null; ptr = ptr.getNext()) {
      Array.set(array, i++, elementMarshal.marshal(env,
                                                   ptr.getValue(),
                                                   elementType));
    }

    return array;
  }
 
  @Override
  public int hashCode()
  {
    return getSize();
  }
 
  @Override
  public boolean equals(Object o)
  {
    if (! (o instanceof ArrayValue))
      return false;
   
    // toValue() for CopyArrayValue
    ArrayValue array = (ArrayValue) ((Value) o).toValue();
   
    return cmp(array) == 0;
  }

  public class EntrySet extends AbstractSet<Map.Entry<Value,Value>> {
    EntrySet()
    {
    }

    @Override
    public int size()
    {
      return ArrayValue.this.getSize();
    }

    @Override
    public Iterator<Map.Entry<Value,Value>> iterator()
    {
      return new EntryIterator(getHead());
    }
  }

  public class KeySet extends AbstractSet<Value> {
    KeySet()
    {
    }

    @Override
    public int size()
    {
      return ArrayValue.this.getSize();
    }

    @Override
    public Iterator<Value> iterator()
    {
      return new KeyIterator(getHead());
    }
  }

  public class ValueCollection extends AbstractCollection<Value> {
    ValueCollection()
    {
    }

    @Override
    public int size()
    {
      return ArrayValue.this.getSize();
    }

    @Override
    public Iterator<Value> iterator()
    {
      return new ValueIterator(getHead());
    }
  }

  public static class EntryIterator
    implements Iterator<Map.Entry<Value,Value>> {
    private Entry _current;

    EntryIterator(Entry head)
    {
      _current = head;
    }

    public boolean hasNext()
    {
      return _current != null;
    }

    public Map.Entry<Value,Value> next()
    {
      if (_current != null) {
        Map.Entry<Value,Value> next = _current;
        _current = _current._next;

        return next;
      }
      else
        return null;
    }

    public void remove()
    {
      throw new UnsupportedOperationException();
    }
  }

  public static class KeyIterator
    implements Iterator<Value> {
    private Entry _current;

    KeyIterator(Entry head)
    {
      _current = head;
    }

    public boolean hasNext()
    {
      return _current != null;
    }

    public Value next()
    {
      if (_current != null) {
        Value next = _current.getKey();
        _current = _current._next;

        return next;
      }
      else
        return null;
    }

    public void remove()
    {
      throw new UnsupportedOperationException();
    }
  }

  public static class ValueIterator
    implements Iterator<Value> {
    private Entry _current;

    ValueIterator(Entry head)
    {
      _current = head;
    }

    public boolean hasNext()
    {
      return _current != null;
    }

    public Value next()
    {
      if (_current != null) {
        Value next = _current.getValue();
        _current = _current._next;

        return next;
      }
      else
        return null;
    }

    public void remove()
    {
      throw new UnsupportedOperationException();
    }
  }

  public static class ValueComparator
    implements Comparator<Map.Entry<Value,Value>>
  {
    public static final ValueComparator CMP = new ValueComparator();
   
    private ValueComparator()
    {
    }
   
    public int compare(Map.Entry<Value,Value> aEntry,
                       Map.Entry<Value,Value> bEntry)
    {
      try {
        Value aValue = aEntry.getValue();
        Value bValue = bEntry.getValue();

        if (aValue.eq(bValue))
          return 0;
        else if (aValue.lt(bValue))
          return -1;
        else
          return 1;
      } catch (Throwable e) {
        throw new RuntimeException(e);
      }
    }
  }

  public static class KeyComparator
    implements Comparator<Map.Entry<Value,Value>>
  {
    public static final KeyComparator CMP = new KeyComparator();
   
    private KeyComparator()
    {
    }
   
    public int compare(Map.Entry<Value,Value> aEntry,
                       Map.Entry<Value,Value> bEntry)
    {
      try {
        Value aKey = aEntry.getKey();
        Value bKey = bEntry.getKey();

        if (aKey.eq(bKey))
          return 0;
        else if (aKey.lt(bKey))
          return -1;
        else
          return 1;
      } catch (Throwable e) {
        throw new RuntimeException(e);
      }
    }
  }

  public static abstract class AbstractGet {
    public abstract Value get(Map.Entry<Value, Value> entry);
  }

  public static class GetKey extends AbstractGet
  {
    public static final GetKey GET = new GetKey();
   
    private GetKey()
    {
    }

    @Override
    public Value get(Map.Entry<Value, Value> entry)
    {
      return entry.getKey();
    }
  }

  public static class GetValue extends AbstractGet {
    public static final GetValue GET = new GetValue();
   
    private GetValue()
    {
    }

    @Override
    public Value get(Map.Entry<Value, Value> entry)
    {
      return entry.getValue();
    }
  }
}

TOP

Related Classes of com.caucho.quercus.env.ArrayValue

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.