/*
* Copyright (C) 2006 http://www.chaidb.org
*
* 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.
*
*/
package org.chaidb.db.api.keys;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.index.Key;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class NodeId implements Key {
// we need to hidden the doc id from outside
private int docId;
public int nodeId;
private byte nodeType;
private boolean isSpecialId = false;
public static final boolean SPECIAL_NODE_ID = true;
public static final boolean NORMAL_NODE_ID = false;
/**
* the byte array representation of this Id
*/
private byte[] _bytesRep = null;
/**
* Construct a NodeIdKey from byte[].
*/
public NodeId(byte[] encoded) {
// retrieve docId
docId = 0;
docId = docId | ((encoded[0] & 0xff) << 16);
docId = docId | ((encoded[1] & 0xff) << 8);
docId = docId | ((encoded[2] & 0xff));
// retrieve nodeType
nodeType = (byte) (encoded[3] >> 4 & 0x0f);
// take 0000000x of reserved byte as special flag
int special = (int) (encoded[3] & 0x01);
isSpecialId = (special == 0) ? NORMAL_NODE_ID : SPECIAL_NODE_ID;
// retrieve nodeId
nodeId = 0;
nodeId = nodeId | ((encoded[4] & 0xff) << 24);
nodeId = nodeId | ((encoded[5] & 0xff) << 16);
nodeId = nodeId | ((encoded[6] & 0xff) << 8);
nodeId = nodeId | ((encoded[7] & 0xff));
}
/**
* Constructor
*/
public NodeId(int docId, int nodeId, byte nodeType) {
this.docId = docId;
this.nodeId = nodeId;
this.nodeType = nodeType;
this.isSpecialId = NORMAL_NODE_ID;
}
/**
* Constructor
*/
public NodeId createSpecialNodeId() {
NodeId specialNodeId = new NodeId(this.docId, this.nodeId, this.nodeType);
specialNodeId.setToSpecialId();
return specialNodeId;
}
/**
* Constructor
* create special node id by doc id , node id, node type
*/
public static NodeId createSpecialNodeId(int docId, int nodeId, byte nodeType) {
NodeId specialNodeId = new NodeId(docId, nodeId, nodeType);
specialNodeId.setToSpecialId();
return specialNodeId;
}
/**
* Constructor
* create normal node, node id by doc id , node id, node type
*/
public static NodeId createNormalNodeId(int docId, int nodeId, byte nodeType) {
NodeId specialNodeId = new NodeId(docId, nodeId, nodeType);
specialNodeId.setToNormalId();
return specialNodeId;
}
/**
* Construct a NodeId object from the given str, which is a counterpart of
* toString().
*/
public NodeId(String str) {
StringTokenizer tokens = new StringTokenizer(str, "-");
this.docId = Integer.parseInt(tokens.nextToken());
this.nodeId = Integer.parseInt(tokens.nextToken());
this.nodeType = Byte.parseByte(tokens.nextToken());
this.isSpecialId = tokens.nextToken().charAt(0) == '1';
}
// implemnt Key methods
/**
* Node ID length. A node is currently composed of 8 bytes,
* 3 bytes for DocumentID,
* 4 bits for node type,
* 4 bits reserved,
* 4 bytes for node number
* NOTE: the returned byte[] is immutable !!!
*/
public byte[] toBytes() {
if (_bytesRep == null) {
_bytesRep = _toBytes();
}
return _bytesRep;
}
private byte[] _toBytes() {
byte[] ret = new byte[8];
ret[0] = (byte) ((docId >>> 16) & 0xFF);
ret[1] = (byte) ((docId >>> 8) & 0xFF);
ret[2] = (byte) ((docId) & 0xFF);
int specialByte = (isSpecialId) ? 0x01 : 0x00;
ret[3] = (byte) ((nodeType << 4) | specialByte);
ret[4] = (byte) ((nodeId >>> 24) & 0xFF);
ret[5] = (byte) ((nodeId >>> 16) & 0xFF);
ret[6] = (byte) ((nodeId >>> 8) & 0xFF);
ret[7] = (byte) ((nodeId) & 0xFF);
return ret;
}
public int size() {
return 8;
}
/**
* judge the node id is special node, it is used by Index
*/
public boolean isSpecialId() {
return isSpecialId;
}
public void setToSpecialId() {
isSpecialId = true;
}
public void setToNormalId() {
isSpecialId = false;
}
/**
* "docId-nodeId-type-<0|1>"
* 0 : normal Id
* 1 : special Id
*/
public String toString() {
char specialId = this.isSpecialId ? '1' : '0';
return new String(String.valueOf(docId) + '-' + String.valueOf(nodeId) + '-' + nodeType + '-' + specialId);
}
public int getKeyType() {
return NODE_ID_KEY;
}
/**
* get document id
*
* @return document id
*/
public int getDocId() {
return docId;
}
/**
* Compares the whole key.
*
* @return 0 if the two key are exactly the same; negative integer (<0) if
* the other key's docId is bigger, or if the docIds are the same, the
* other key's nodeId is bigger; otherwise positive interger (>0).
*/
public int compareTo(Key otherKey) throws ChaiDBException {
checkKeyType(otherKey);
return compare((NodeId) otherKey);
}
/**
* small document special note, now we compare two node by doc id and node id,
* so special node and normal node will be treated as same in BTree level.
*
* @param key2
*/
private int compare(NodeId key2) {
int ret = KeyTool.compareInt(docId, key2.getDocId());
return ret != 0 ? ret : KeyTool.compareInt(nodeId, key2.nodeId);
}
private void checkKeyType(Key key) throws ChaiDBException {
int keyType = key.getKeyType();
if (keyType != NODE_ID_KEY) {
ArrayList params = new ArrayList(2);
// required
params.add(Key.KEY_TYPES[Key.NODE_ID_KEY]);
// found
params.add(Key.KEY_TYPES[keyType]);
throw new ChaiDBException(ErrorCode.INDEX_KEY_TYPE_ERROR, params);
}
}
// own methods
/**
* Compares only document id.
*/
public int compareDocId(Key otherKey) throws ChaiDBException {
// checking whether the same key type
checkKeyType(otherKey);
NodeId key2 = (NodeId) otherKey;
return KeyTool.compareInt(docId, key2.getDocId());
}
public short getNodeType() {
return (short) nodeType;
}
// override java.lang.Object methods
public int hashCode() {
return docId ^ nodeId;
}
public boolean equals(Object obj) {
if (obj instanceof NodeId) return compare((NodeId) obj) == 0;
return false;
}
public Object clone() {
if (this.isSpecialId) return createSpecialNodeId(docId, nodeId, nodeType);
else return createNormalNodeId(docId, nodeId, nodeType);
}
}