package com.dbxml.db.core.data;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * This program 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.
 *
 * This program 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: Value.java,v 1.6 2004/02/12 00:17:57 bradford Exp $
 */

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.dbxml.util.UTF8;

/**
 * Value is the primary base class for all data storing objects.
 * The content window of Value objects are immutable, but the
 * underlying byte array is not.
 */

public class Value implements Comparable {
   protected byte[] data;
   protected int pos = 0;
   protected int len = -1;

   protected Value() {
   }

   public Value(Value value) {
      data = value.data;
      pos = value.pos;
      len = value.len;
   }

   public Value(byte[] data) {
      this.data = data;
      len = data.length;
   }

   public Value(byte[] data, int pos, int len) {
      this.data = data;
      this.pos = pos;
      this.len = len;
   }

   public Value(String data) {
      this(data, true);
   }

   public Value(String data, boolean utf8) {
      this(utf8 ? UTF8.toUTF8(data) : data.getBytes());
   }

   /**
    * getData retrieves the data being stored by the Value as a byte array.
    *
    * @return The Data
    */
   public final byte[] getData() {
      if ( len != data.length ) {
         byte[] b = new byte[len];
         System.arraycopy(data, pos, b, 0, len);
         return b;
      }
      else
         return data;
   }

   /**
    * getRawData returns the byte array that this Value belongs to,
    * which may ultimately be larger than the actual value.
    *
    * @return The raw byte array
    */
   public final byte[] getRawData() {
      return data;
   }

   /**
    * getOffset returns the offset into the data being stored by the
    * Value that the value begins.
    *
    * @return The Value offset
    */
   public final int getOffset() {
      return pos;
   }

   /**
    * getLength retrieves the length of the data being stored by the Value.
    *
    * @return The Value length
    */
   public final int getLength() {
      return len;
   }

   /**
    * getInputStream returns an InputStream for the Value.
    *
    * @return An InputStream
    */
   public final InputStream getInputStream() {
      return new ByteArrayInputStream(data, pos, len);
   }

   /**
    * streamTo streams the content of the Value to an OutputStream.
    *
    * @param out the OutputStream
    */
   public final void streamTo(OutputStream out) throws IOException {
      out.write(data, pos, len);
   }

   public final void copyTo(byte[] tdata, int tpos) {
      System.arraycopy(data, pos, tdata, tpos, len);
   }

	public final String toString(boolean utf8) {
		if ( utf8 )
			return UTF8.toString(data, pos, len);
		else
			return new String(data, pos, len);
	}
	
   public final String toString() {
		return toString(true);
   }

   public int hashCode() {
      return toString().hashCode();
   }

   public boolean equals(Value value) {
      if ( len == value.len )
         return compareTo(value) == 0;
      else
         return false;
   }

   public boolean equals(Object obj) {
      if ( this == obj )
         return true;
      if ( obj instanceof Value )
         return equals((Value)obj);
      else
         return false;
   }

   public int compareTo(Value value) {
      byte[] ddata = value.data;
      int dpos = value.pos;
      int dlen = value.len;

      int stop;
      if ( len > dlen )
         stop = dlen;
      else
         stop = len;

      for ( int i = 0; i < stop; i++ ) {
         byte b1 = data[pos + i];
         byte b2 = ddata[dpos + i];

         if ( b1 == b2 )
            continue;
         else {
            int i1 = b1 & 0xff;
            int i2 = b2 & 0xff;

            if ( i1 > i2 )
               return i + 1;
            else
               return -(i + 1);
         }
      }

      if ( len == dlen )
         return 0;
      else {
         if ( len > dlen )
            return stop + 1;
         else
            return -(stop + 1);
      }
   }

   public final int compareTo(Object obj) {
      if ( obj instanceof Value )
         return compareTo((Value)obj);
      else
         throw new ClassCastException("This Object isn't a Value");
   }

   public final boolean startsWith(Value value) {
      if ( len < value.len )
         return false;

      byte[] ddata = value.data;
      int dpos = value.pos;

      for ( int i = 0; i < value.len; i++ )
         if ( data[i + pos] != ddata[i + dpos] )
            return false;

      return true;
   }
}
