/FileExtraction/Java_unseen/dlitz_resin/modules/kernel/src/com/caucho/transaction/XidImpl.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 SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.transaction;

import javax.transaction.xa.Xid;

import com.caucho.util.Hex;

/**
 * Transaction identifier implementation.
 */
public class XidImpl implements Xid {
  public static final int GLOBAL_LENGTH = 28;

  private byte [] _global;
  private byte [] _local;

  /**
   * Creates a new transaction id.
   * 
   * @param id
   *          the 64 bit number for the id.
   */
  public XidImpl(long serverId, 
                 long randomId, 
                 long sequence)
  {
    _global = new byte[GLOBAL_LENGTH];
    _local = new byte[4];
    _local[0] = 1;

    // the global id has the requirement of being globally unique

    // first 4 identify as Resin.
    _global[0] = 'R';
    _global[1] = 'e';
    _global[2] = 's';
    _global[3] = 'n';

    // next 8 is the crc64 of the caucho.server-id
    _global[4] = (byte) (serverId >> 56);
    _global[5] = (byte) (serverId >> 48);
    _global[6] = (byte) (serverId >> 40);
    _global[7] = (byte) (serverId >> 32);
    _global[8] = (byte) (serverId >> 24);
    _global[9] = (byte) (serverId >> 16);
    _global[10] = (byte) (serverId >> 8);
    _global[11] = (byte) (serverId);

    // next 8 is a 64-bit random long
    _global[12] = (byte) (randomId >> 56);
    _global[13] = (byte) (randomId >> 48);
    _global[14] = (byte) (randomId >> 40);
    _global[15] = (byte) (randomId >> 32);
    _global[16] = (byte) (randomId >> 24);
    _global[17] = (byte) (randomId >> 16);
    _global[18] = (byte) (randomId >> 8);
    _global[19] = (byte) (randomId);

    // next 8 is the current sequence

    _global[20] = (byte) (sequence >> 56);
    _global[21] = (byte) (sequence >> 48);
    _global[22] = (byte) (sequence >> 40);
    _global[23] = (byte) (sequence >> 32);
    _global[24] = (byte) (sequence >> 24);
    _global[25] = (byte) (sequence >> 16);
    _global[26] = (byte) (sequence >> 8);
    _global[27] = (byte) (sequence);
  }

  XidImpl(XidImpl base, int branch)
  {
    _global = new byte[base._global.length];
    _local = new byte[4];
    _local[0] = (byte) (branch);

    System.arraycopy(base._global, 0, _global, 0, _global.length);
  }

  public XidImpl(byte [] global, byte [] local)
  {
    _global = new byte[global.length];
    _local = new byte[local.length];

    System.arraycopy(global, 0, _global, 0, global.length);
    System.arraycopy(local, 0, _local, 0, local.length);
  }

  XidImpl(byte [] global)
  {
    _global = new byte[global.length];
    _local = new byte[4];

    System.arraycopy(global, 0, _global, 0, global.length);
    _local[0] = 1;
  }

  @Override
  public int getFormatId()
  {
    return 1234;
  }

  @Override
  public byte [] getBranchQualifier()
  {
    return _local;
  }

  @Override
  public byte [] getGlobalTransactionId()
  {
    return _global;
  }
  
  boolean isSameServer(long serverId)
  {
    // first 4 identify as Resin.
    if (_global[0] != 'R'
        || _global[1] != 'e'
        || _global[2] != 's'
        || _global[3] != 'n') {
      return false;
    }

    // next 8 is the crc64 of the caucho.server-id
    if (_global[4] != (byte) (serverId >> 56)
        || _global[5] != (byte) (serverId >> 48)
        || _global[6] != (byte) (serverId >> 40)
        || _global[7] != (byte) (serverId >> 32)
        || _global[8] != (byte) (serverId >> 24)
        || _global[9] != (byte) (serverId >> 16)
        || _global[10] != (byte) (serverId >> 8)
        || _global[11] != (byte) (serverId)) {
      return false;
    }
    
    return true;
    
  }

  /**
   * Clones the xid.
   */
  @Override
  public Object clone()
  {
    return new XidImpl(_global, _local);
  }

  /**
   * Returns hashCode.
   */
  @Override
  public int hashCode()
  {
    byte [] global = _global;

    int hash = 37;

    for (int i = global.length - 1; i >= 0; i--)
      hash = 65521 * hash + global[i];

    return hash;
  }

  /**
   * Returns equality.
   */
  @Override
  public boolean equals(Object o)
  {
    if (!(o instanceof Xid))
      return false;

    Xid xid = (Xid) o;

    byte [] global = xid.getGlobalTransactionId();
    byte [] local = xid.getBranchQualifier();

    if (global.length != _global.length)
      return false;

    byte [] selfGlobal = _global;
    byte [] selfLocal = _local;

    for (int i = global.length - 1; i >= 0; i--) {
      if (global[i] != selfGlobal[i]) {
        return false;
      }
    }

    for (int i = local.length - 1; i >= 0; i--) {
      if (local[i] != selfLocal[i]) {
        return false;
      }
    }

    return true;
  }

  /**
   * Printable version of the transaction id.
   */
  @Override
  public String toString()
  {
    StringBuilder cb = new StringBuilder();

    // cb.append(getClass().getSimpleName());
    cb.append("Xid");
    cb.append("[");

    byte [] branch = getBranchQualifier();

    addByte(cb, branch[0]);

    cb.append(":");

    byte [] global = getGlobalTransactionId();
    for (int i = 24; i < 28; i++)
      addByte(cb, global[i]);

    cb.append("]");

    return cb.toString();
  }

  /**
   * Adds hex for debug
   * 
   * @param cb
   *          the character buffer for the new value
   * @param b
   *          the byte value
   */
  static private void addByte(StringBuilder cb, int b)
  {
    b = b & 0xff;
    
    int h = (b / 16) & 0xf;
    int l = b & 0xf;

    if (h >= 10)
      cb.append((char) ('a' + h - 10));
    else
      cb.append((char) ('0' + h));

    if (l >= 10)
      cb.append((char) ('a' + l - 10));
    else
      cb.append((char) ('0' + l));
  }
}
