/FileExtraction/Java_unseen/dlitz_resin/modules/quercus/src/com/caucho/quercus/env/LongValue.java
/*
 * 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.marshal.Marshal;
import com.caucho.vfs.WriteStream;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.IdentityHashMap;

/**
 * Represents a PHP long value.
 */
@SuppressWarnings("serial")
public class LongValue extends NumberValue
{
  public static final LongValue MINUS_ONE;
  public static final LongValue ZERO;
  public static final LongValue ONE;

  public static final int STATIC_MIN = -1024;
  public static final int STATIC_MAX = 16 * 1024;

  public static final LongCacheValue[]STATIC_VALUES;

  private final long _value;

  public LongValue(long value)
  {
    _value = value;
  }

  public static LongValue create(long value)
  {
    if (STATIC_MIN <= value && value <= STATIC_MAX)
      return STATIC_VALUES[(int) (value - STATIC_MIN)];
    else
      return new LongValue(value);
  }

  public static LongValue create(Number value)
  {
    if (value == null)
      return LongValue.ZERO;
    else
      return LongValue.create(value.longValue());
  }

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

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

  /**
   * Returns true for a long.
   */
  @Override
  public boolean isLongConvertible()
  {
    return true;
  }

  /**
   * Returns true for is_numeric
   */
  @Override
  public boolean isNumeric()
  {
    return true;
  }

  /**
   * Returns true for a long-value.
   */
  @Override
  public boolean isLong()
  {
    return true;
  }

  /**
   * Returns true for a double-value.
   */
  @Override
  public boolean isDouble()
  {
    return false;
  }

  /**
   * Returns true for a scalar
   */
  public boolean isScalar()
  {
    return true;
  }

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

  //
  // marshal cost
  //

  /**
   * Cost to convert to a double
   */
  @Override
  public int toDoubleMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSLESS;
  }

  /**
   * Cost to convert to a long
   */
  @Override
  public int toLongMarshalCost()
  {
    return Marshal.COST_EQUAL;
  }

  /**
   * Cost to convert to an integer
   */
  @Override
  public int toIntegerMarshalCost()
  {
    return Marshal.COST_EQUAL + 1;
  }

  /**
   * Cost to convert to a short
   */
  @Override
  public int toShortMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSY;
  }

  /**
   * Cost to convert to a byte
   */
  @Override
  public int toByteMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSY;
  }

  /**
   * Converts to a boolean.
   */
  @Override
  public boolean toBoolean()
  {
    return _value != 0;
  }

  /**
   * Converts to a long.
   */
  @Override
  public long toLong()
  {
    return _value;
  }

  /**
   * Converts to a double.
   */
  @Override
  public double toDouble()
  {
    return _value;
  }

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

  /**
   * Converts to a string builder
   */
  @Override
  public StringValue toStringBuilder(Env env)
  {
    return env.createUnicodeBuilder().append(_value);
  }

  /**
   * Converts to a long value
   */
  @Override
  public LongValue toLongValue()
  {
    return this;
  }

  /**
   * Converts to a key.
   */
  @Override
  public Value toKey()
  {
    return this;
  }

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

  /**
   * Converts to a java object.
   */
  @Override
  public Object toJavaObject()
  {
    return new Long(_value);
  }
  
  /*
  @Override
  public Value toAutoArray()
  {
    if (_value == 0)
      return new ArrayValueImpl();
    else
      return super.toAutoArray();
  }
  */

  /**
   * Negates the value.
   */
  @Override
  public Value neg()
  {
    return LongValue.create(- _value);
  }

  /**
   * Negates the value.
   */
  @Override
  public Value pos()
  {
    return this;
  }

  /**
   * The next integer
   */
  @Override
  public Value addOne()
  {
    long newValue = _value + 1;
    
    return LongValue.create(newValue);
  }

  /**
   * The previous integer
   */
  @Override
  public Value subOne()
  {
    long newValue = _value - 1;
    
    return LongValue.create(newValue);
  }

  /**
   * Pre-increment the following value.
   */
  @Override
  public Value preincr()
  {
    long newValue = _value + 1;
    
    return LongValue.create(newValue);
  }

  /**
   * Pre-increment the following value.
   */
  @Override
  public Value predecr()
  {
    long newValue = _value - 1;
    
    return LongValue.create(newValue);
  }

  /**
   * Post-increment the following value.
   */
  @Override
  public Value postincr()
  {
    long newValue = _value + 1;
    
    return LongValue.create(newValue);
  }

  /**
   * Post-decrement the following value.
   */
  @Override
  public Value postdecr()
  {
    long newValue = _value - 1;
    
    return LongValue.create(newValue);
  }

  /**
   * Post-increment the following value.
   */
  @Override
  public Value increment(int incr)
  {
    long newValue = _value + incr;
    
    return LongValue.create(newValue);
  }

  /**
   * Adds to the following value.
   */
  @Override
  public Value add(Value value)
  {
    return value.add(_value);
  }

  /**
   * Adds to the following value.
   */
  @Override
  public Value add(long lLong)
  {
    return LongValue.create(lLong + _value);
  }

  /**
   * Subtracts to the following value.
   */
  @Override
  public Value sub(Value rValue)
  {
    if (rValue.isLongConvertible())
      return LongValue.create(_value - rValue.toLong());
    else
      return DoubleValue.create(_value - rValue.toDouble());
  }

  /**
   * Subtracts the following value.
   */
  @Override
  public Value sub(long rLong)
  {
    return LongValue.create(_value - rLong);
  }

  /**
   * Absolute value.
   */
  @Override
  public Value abs()
  {
    if (_value >= 0)
      return this;
    else
      return LongValue.create(- _value);
  }

  /**
   * Returns true for equality
   */
  @Override
  public boolean eql(Value rValue)
  {
    rValue = rValue.toValue();

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

    long rLong = ((LongValue) rValue)._value;

    return _value == rLong;
  }
  /**
   * Returns true for equality
   */
  @Override
  public int cmp(Value rValue)
  {
    if (rValue.isBoolean()) {
      boolean lBool = toBoolean();
      boolean rBool = rValue.toBoolean();

      if (! lBool && rBool)
        return -1;
      if (lBool && ! rBool)
        return 1;

      return 0;
    }

    long l = _value;
    double r = rValue.toDouble();

    if (l == r)
      return 0;
    else if (l < r)
      return -1;
    else
      return 1;
  }

  /**
   * Returns the next array index based on this value.
   */
  @Override
  public long nextIndex(long oldIndex)
  {
    if (oldIndex <= _value)
      return _value + 1;
    else
      return oldIndex;
  }

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

  /**
   * Append to a unicode builder.
   */
  @Override
  public StringValue appendTo(UnicodeBuilderValue sb)
  {
    return sb.append(_value);
  }

  /**
   * Append to a binary builder.
   */
  @Override
  public StringValue appendTo(BinaryBuilderValue sb)
  {
    return sb.append(_value);
  }

  /**
   * Append to a string builder.
   */
  @Override
  public StringValue appendTo(StringBuilderValue sb)
  {
    return sb.append(_value);
  }

  /**
   * Append to a string builder.
   */
  @Override
  public StringValue appendTo(LargeStringBuilderValue sb)
  {
    return sb.append(_value);
  }

  /**
   * Serializes the value.
   */
  @Override
  public void serialize(Env env, StringBuilder sb)
  {
    sb.append("i:");
    sb.append(_value);
    sb.append(";");
  }

  /**
   * Exports the value.
   */
  @Override
  public void varExport(StringBuilder sb)
  {
    sb.append(_value);
  }

  //
  // Java generator code
  //

  /**
   * Generates code to recreate the expression.
   *
   * @param out the writer to the Java source code.
   */
  @Override
  public void generate(PrintWriter out)
    throws IOException
  {
    if (_value == 0)
      out.print("LongValue.ZERO");
    else if (_value == 1)
      out.print("LongValue.ONE");
    else if (_value == -1)
      out.print("LongValue.MINUS_ONE");
    else if (STATIC_MIN <= _value && _value <= STATIC_MAX)
      out.print("LongValue.STATIC_VALUES[" + (_value - STATIC_MIN) + "]");
    else
      out.print("new LongValue(" + _value + "L)");
  }

  /**
   * Returns the hash code
   */
  @Override
  public final int hashCode()
  {
    long v = _value;

    return (int) (17 * v + 65537 * (v >> 32));
  }

  /**
   * Compare for equality.
   */
  @Override
  public boolean equals(Object o)
  {
    if (this == o)
      return true;
    else if (! (o instanceof LongValue))
      return false;

    LongValue value = (LongValue) o;

    return _value == value._value;
  }

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

  //
  // Java Serialization
  //

  private Object readResolve()
  {
    if (STATIC_MIN <= _value && _value <= STATIC_MAX)
      return STATIC_VALUES[(int) (_value - STATIC_MIN)];
    else
      return this;
  }

  static {
    STATIC_VALUES = new LongCacheValue[STATIC_MAX - STATIC_MIN + 1];
    
    try {

    for (int i = STATIC_MAX; i >= STATIC_MIN; i--) {
      LongCacheValue value = new LongCacheValue(i, create(i + 1));

      STATIC_VALUES[i - STATIC_MIN] = value;
      
      if (i < STATIC_MAX)
        STATIC_VALUES[i - STATIC_MIN + 1].setPrev(value);
    }
    
    STATIC_VALUES[0].setPrev(create(STATIC_MIN - 1));
    } catch (Exception e) {
      e.printStackTrace();
    }
    
    ZERO = create(0);
    ONE = create(1);
    MINUS_ONE = create(-1);
  }
}
