/**
* Copyright Mobixess Inc. 2007
*/
package com.mobixess.jodb.core.io;
import java.io.DataInput;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.Vector;
import com.mobixess.jodb.core.JodbIOException;
import com.mobixess.jodb.core.index.IndexingRecord;
import com.mobixess.jodb.core.transaction.JODBSession;
import com.mobixess.jodb.core.transaction.JODBSession.ClassDescriptor;
import com.mobixess.jodb.core.transaction.JODBSession.FieldAndIDRecord;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil;
import com.mobixess.jodb.util.Utils;
/**
* @author Mobixess
*
*/
public class ObjectDataContainer {
public static enum FIELD_CATEGORIES{DIRECTLY_ADDRESSED, RELATIVELY_ADDRESSED, PRIMITIVE}
//primary data mask shift
private final static int DIRECT_ADDR_BIT_SHIFT = FIELD_CATEGORIES.DIRECTLY_ADDRESSED.ordinal();
private final static int RELATIVE_ADDR_BIT_SHIFT = FIELD_CATEGORIES.RELATIVELY_ADDRESSED.ordinal();
private final static int PRIMITIVE_BIT_SHIFT = FIELD_CATEGORIES.PRIMITIVE.ordinal();
private final static int ARRAY_ID_BIT_SHIFT = FIELD_CATEGORIES.values().length ;
private final static int MODIFICATION_TS_BIT_SHIFT = FIELD_CATEGORIES.values().length + 1 ;
private final static int CREATION_TS_BIT_SHIFT = FIELD_CATEGORIES.values().length+2;
private final static int UID_BIT_SHIFT = FIELD_CATEGORIES.values().length+3;
private final static int TRANSLATED_BIT_SHIFT = FIELD_CATEGORIES.values().length+4;
//secondary data mask
private final static int AGENT_BIT_SHIFT = 7;
private final static int CYCLICAL_COUNTER_BIT_SHIFT = AGENT_BIT_SHIFT-1;
private final static byte[] AUXILIARY_MAX_PRIMITIVE_CLEAN_ARRAY = new byte[64];
private boolean _deleted;
private short _id;
private long _uid;
private long _bodyLength;
private long _headerLength;
private long _createdTimeStamp;
private long _modificationTimeStamp;
private byte _primaryDataMask;
private byte _secondaryDataMask;
private long _redirectionOffset;
private long _offset;
private boolean _reseted;
private long _auxiliaryDataStartOffset;
private long _hierarchyDataStartOffset;
private long _objectDataStartOffset;
private short _cyclicVersionCounter;
private IRandomAccessDataBuffer _input;
private Vector<IndexingRecord> _indexes = new Vector<IndexingRecord>();
private int _processedIndexes;
private int _hashIdentity;
private ObjectFieldsIterator _objectFieldsIterator = new ObjectFieldsIterator();
private ArrayFieldsIterator _arrayFieldsIterator = new ArrayFieldsIterator();
private FieldsIterator _activeIterator;
private FieldRecord _recordCache = new FieldRecord();
//private int[] _classHierarchy;
private int _originalClassType;
private ClassDescriptor _translatedClassDescriptor;
private int _translatedClassType;
private Class _arrayType;
public ObjectDataContainer() {
reset();
}
public void reset(){
_deleted = false;
_id = -1;
_bodyLength = -1;
_createdTimeStamp = -1;
_uid = -1;
_primaryDataMask = 0;
_secondaryDataMask = 0;
_redirectionOffset = -1;
_offset = -1;
_originalClassType = -1;
_reseted = true;
_auxiliaryDataStartOffset = -1;
_hierarchyDataStartOffset = -1;
_objectDataStartOffset = -1;
_translatedClassType = -1;
_hashIdentity = -1;
_input = null;
_arrayType = null;
_activeIterator = null;
_indexes = null;
_processedIndexes = 0;
_cyclicVersionCounter = 0;
}
private void setOffset(long offset) throws JodbIOException {
if(!_reseted){
throw new JodbIOException("Container must be reset before accepting new data");
}
_reseted = false;
_offset = offset;
}
public long getOffset() {
return _offset;
}
public short getCyclicVersionCounter() {
return _cyclicVersionCounter;
}
// public final ClassDescriptor getClassDescForOriginalType(JODBSession session) throws ClassNotStorableException, ClassNotFoundException{
// int typeID = getOriginalClassType();
// return session.getDescriptorForClass(typeID);
// }
public int getOriginalClassType() {
return _originalClassType;
}
public boolean isDeleted() {
return _deleted;
}
public boolean isTranslated() {
return hasTranslatedBit(_primaryDataMask);
}
private void setDeleted(boolean deleted) {
_deleted = deleted;
}
public Class getArrayType() {
return _arrayType;
}
public short getId() {
return _id;
}
private void setId(short id) {
_id = id;
}
public int getHashIdentity() throws IOException{
if(_hashIdentity == -1){
calcHashIdentity();
}
return _hashIdentity;
}
private void calcHashIdentity() throws IOException{
long positionBefore = _input.getCursorOffset();
_input.seek(_offset);
_hashIdentity = Utils.oathash(_input, getTotalLength());
if(positionBefore != _input.getCursorOffset()){
_input.seek(positionBefore);
}
}
public boolean isRedirectedObject(){
return JODBIOBase.hasRedirectedObjectModifier(_id);
}
public int getObjectTypeID(){
return _id & JODBIOBase.LEN_MODIFIER_EXCLUSION_MASK & (~JODBIOBase.REDIRECTED_OBJECT_MODIFIER);
}
public int getLengthModifierFromID(){
return _id & (~JODBIOBase.LEN_MODIFIER_EXCLUSION_MASK) ;
}
public long getBodyLength() {
return _bodyLength;
}
public long getTotalLength(){
return _headerLength+_bodyLength;
}
private void setLength(long headerLength, long bodyLength) {
_headerLength = headerLength;
_bodyLength = bodyLength;
}
public byte getPrimaryDataMask() {
return _primaryDataMask;
}
public byte getSecondaryDataMask() {
return _secondaryDataMask;
}
public long getUID() {
return _uid;
}
public void setUID(long uid) {
_uid = uid;
}
public long getCreatedTimeStamp() {
return _createdTimeStamp;
}
private void setCreatedTimeStamp(long createdTimeStamp) {
_createdTimeStamp = createdTimeStamp;
}
public long getModificationTimeStamp() {
return _modificationTimeStamp;
}
private void setModificationTimeStamp(long modificationTimeStamp) {
_modificationTimeStamp = modificationTimeStamp;
}
public static int addPrimitiveFieldsBit(int mask){
return (mask|(1<<PRIMITIVE_BIT_SHIFT));
}
public static boolean hasPrimitiveFieldsBit(int mask){
return (mask&(1<<PRIMITIVE_BIT_SHIFT))!=0;
}
public static boolean hasTranslatedBit(int mask){
return (mask&(1<<TRANSLATED_BIT_SHIFT))!=0;
}
public static boolean hasAgentBit(int mask){
return (mask&(1<<AGENT_BIT_SHIFT))!=0;
}
public final boolean isJodbAgentObject(){
return hasAgentBit(_secondaryDataMask);
}
public static boolean hasCyclicalCounterBit(int mask){
return (mask&(1<<CYCLICAL_COUNTER_BIT_SHIFT))!=0;
}
public final boolean hasCyclicalCounterBit(){
return hasCyclicalCounterBit(_secondaryDataMask);
}
public final boolean hasPrimitiveFields(){
return hasPrimitiveFieldsBit(_primaryDataMask);
}
public static int addArrayIDBit(int mask){
return (mask|(1<<ARRAY_ID_BIT_SHIFT));
}
public static int addTranslatedBit(int mask){
return (mask|(1<<TRANSLATED_BIT_SHIFT));
}
public static int addAgentBit(int mask){
return (mask|(1<<AGENT_BIT_SHIFT));
}
public static int addCyclicalCounterBit(int mask){
return (mask|(1<<CYCLICAL_COUNTER_BIT_SHIFT));
}
public static boolean hasArrayIDBit(int mask){
return (mask&(1<<ARRAY_ID_BIT_SHIFT))!=0;
}
public boolean isArray(){
return hasArrayIDBit(_primaryDataMask);
}
// public boolean markAsArray(){
// _dataMask = addArrayIDBit(_dataMask);
// }
public static int addDirectlyAddressedFieldsBit(int mask){
return (mask|(1<<DIRECT_ADDR_BIT_SHIFT));
}
public static boolean hasDirectlyAddressedFieldsBit(int mask){
return (mask&(1<<DIRECT_ADDR_BIT_SHIFT))!=0;
}
public boolean hasDirectlyAddressedFields(){
return hasDirectlyAddressedFieldsBit(_primaryDataMask);
}
public static int addRelativelyAddressedFieldsID(int mask){
return (mask|(1<<RELATIVE_ADDR_BIT_SHIFT));
}
public static boolean hasRelativelyAddressedFieldsBit(int mask){
return (mask&(1<<RELATIVE_ADDR_BIT_SHIFT))!=0;
}
public static int addUIDFieldBit(int mask){
return (mask|(1<<UID_BIT_SHIFT));
}
public static boolean hasUIDFieldBit(int mask){
return (mask&(1<<UID_BIT_SHIFT))!=0;
}
public boolean hasUIDField(){
return hasUIDFieldBit(_primaryDataMask);
}
public static int addCreationTSFieldBit(int mask){
return (mask|(1<<CREATION_TS_BIT_SHIFT));
}
public static boolean hasCreationTSBit(int mask){
return (mask&(1<<CREATION_TS_BIT_SHIFT))!=0;
}
public boolean hasCreationTSField(){
return hasCreationTSBit(_primaryDataMask);
}
public static int addModificationTSFieldBit(int mask){
return (mask|(1<<MODIFICATION_TS_BIT_SHIFT));
}
public static boolean hasModificationTSBit(int mask){
return (mask&(1<<MODIFICATION_TS_BIT_SHIFT))!=0;
}
public boolean hasModificationTSField(){
return hasModificationTSBit(_primaryDataMask);
}
public boolean hasRelativelyAddressedFields(){
return hasRelativelyAddressedFieldsBit(_primaryDataMask);
}
private void setRedirectionOffset(long redirectionOffset) {
_redirectionOffset = redirectionOffset;
}
public long getRedirectionOffset() {
return _redirectionOffset;
}
public boolean isRedirection(){
return _redirectionOffset!=-1;
}
// private void addField(int fieldID, Object value){
// _records.add(new FieldRecord(fieldID,value));
// }
//
// private void addField(int fieldID, long offset){
// _records.add(new FieldRecord(fieldID,offset));
// }
//
//
//
// public void getFieldsRecords(Vector<FieldRecord> result) {
// result.addAll(_records);
// }
// public int[] getClassHierarchy() {
// return _classHierarchy;
// }
public int getTranslatedClassType() {
return _translatedClassType;
}
// private void setClassHierarchy(int[] classHierarchy) {
// _classHierarchy = classHierarchy;
// }
/*package*/ void printContent(IRandomAccessDataBuffer input, IOBase base, JODBSession session, long offset, boolean followRedirection, PrintStream printStream) throws IOException{
reset();
readHeader(input, offset, followRedirection);
FieldsIterator fieldsIterator=null;
if(!isDeleted() && !isRedirection()){
readAuxiliaryData();
readHierarchyData(base,session);
fieldsIterator = getFieldsIterator();
}
printStream.println("<<< Object Data Container: >>> "+"agent="+isJodbAgentObject());
printStream.println("Offset ="+_offset+" total len="+getTotalLength()+" Array="+isArray()+" deleted="+_deleted);
printStream.println("Redirection="+isRedirection()+":"+_redirectionOffset+" Redirected="+isRedirectedObject());
printStream.println("Class Hierarchy:");
printStream.println("original type "+_originalClassType+" translated "+_translatedClassType);
printStream.println("Fields:");
//TODO restore
for (int i = 0; fieldsIterator!=null && fieldsIterator.hasNext(); i++) {
_recordCache.clear();
fieldsIterator.next(_recordCache, base);
int fieldID = _recordCache._fieldID;
String fieldName = base.getFullFieldNameForID(fieldID);
printStream.println(" "+fieldID+" "+fieldName+ " value="+_recordCache._value+" offset="+_recordCache._objectOffset);
}
printStream.println("Object Data Container: >>>");
}
private boolean isClean(){
return _reseted;
}
// public void init(IRandomAccessDataBuffer input, long offset, boolean followRedirection) throws IOException{
// readHeader(input, offset, followRedirection);
// }
/**
*
* @param ioTicket
* @return true if object deleted
* @throws IOException
*/
public void readHeader(IOTicket ticket, boolean followRedirection) throws IOException{
IRandomAccessDataBuffer randomAccessDataBuffer = ticket.getRandomAccessBuffer();
readHeader(randomAccessDataBuffer, randomAccessDataBuffer.getCursorOffset(), followRedirection);
}
/**
*
* @param ioTicket
* @return true if object deleted
* @throws IOException
*/
public void readHeader(IOTicket ticket, boolean followRedirection, Vector<IndexingRecord> indexes) throws IOException{
IRandomAccessDataBuffer randomAccessDataBuffer = ticket.getRandomAccessBuffer();
readHeader(randomAccessDataBuffer, randomAccessDataBuffer.getCursorOffset(), followRedirection, indexes);
}
public void readHeader(IRandomAccessDataBuffer input, long offset, boolean followRedirection) throws IOException{
readHeader(input, offset, followRedirection, null);
}
/**
*
* @param ioTicket
* @return true if object deleted
* @throws IOException
*/
public void readHeader(IRandomAccessDataBuffer input, long offset, boolean followRedirection, Vector<IndexingRecord> indexes) throws IOException{
if(!isClean()){
throw new IOException();
}
input.prefetch(offset);
setOffset(offset);
_input = input;
_indexes = indexes;
input.seek(offset);
long recordStartOffset = input.getCursorOffset();
short id = input.readShort();
setId(id);
long bodyLength = readEntryLen(id, input);
long bodyStartOffset = input.getCursorOffset();
setLength(bodyStartOffset-recordStartOffset, bodyLength);
switch (getObjectTypeID()) {
case JODBIOBase.ENTRY_EMPTY_ID:
setDeleted(true);
break;
case JODBIOBase.ENTRY_OBJECT_ID:
_primaryDataMask = input.readByte();
_secondaryDataMask = input.readByte();
if(hasCyclicalCounterBit()){
_cyclicVersionCounter = (short) (input.readByte()&0xFF);
}
//setDataMask(mask);
break;
case JODBIOBase.ENTRY_REDIRECTOR_ID:
long redirectionOffset = input.readLong();
if(followRedirection){
//input.seek(redirectionOffset);
reset();
readHeader(input, redirectionOffset, false);
}else{
setRedirectionOffset(redirectionOffset);
}
break;
default:
throw new JodbIOException("Illegal entry ID "+id);
}
_auxiliaryDataStartOffset = input.getCursorOffset();
//setRemainingBytesInRecord( bodyLength - (_auxiliaryDataStartOffset - bodyStartOffset) );
}
public void readAuxiliaryData() throws IOException{
if(_auxiliaryDataStartOffset == -1 || isDeleted() || isRedirection()){
throw new JodbIOException("Illegal IO state");
}
if(_hierarchyDataStartOffset != -1){
return;//data has been read already
}
_input.seek(_auxiliaryDataStartOffset);
if (hasUIDField()) {
long uid = _input.readLong();
setUID(uid);
}
if(hasCreationTSField()){
long creationTS = _input.readLong();
setCreatedTimeStamp(creationTS);
}
if(hasModificationTSField()){
long modifTS = _input.readLong();
setModificationTimeStamp(modifTS);
}
_hierarchyDataStartOffset = _input.getCursorOffset();
}
public void readHierarchyData(IOBase base, JODBSession session) throws IOException{
if(_hierarchyDataStartOffset == -1 || isDeleted() || isRedirection()){
throw new JodbIOException("Illegal IO state");
}
if(_objectDataStartOffset != -1){
return;//data has been read already
}
_input.seek(_hierarchyDataStartOffset);
_originalClassType = _input.readShort()&0xFFFF;
if(isTranslated()){
_translatedClassType = _input.readShort()&0xFFFF;
}else{
_translatedClassType = _originalClassType;
}
try {
_translatedClassDescriptor = session.getDescriptorForClass(_translatedClassType);
} catch (Exception e1) {
//throw new IOException("reason: "+e1.getMessage()); //TODO add warning?
}
// int totalHierarchyIDs = _input.readShort()&0xFFFF;
// int[] hierarchyIDs = new int[totalHierarchyIDs];
// for (int i = 0; i < hierarchyIDs.length; i++) {
// hierarchyIDs[i] = _input.readShort()&0xFFFF;
// }
// _classHierarchy = hierarchyIDs;
_objectDataStartOffset = _input.getCursorOffset();
if(isArray()){
String fieldType = base.getClassTypeForID(_translatedClassType);
_arrayType = null;
try {
_arrayType = Class.forName(fieldType);
} catch (ClassNotFoundException e) {
_arrayType = PrimitiveJavaTypesUtil.primitiveClassForName(fieldType);
if(_arrayType == null){
e.printStackTrace();
}
//TODO debug output???
}
}
}
public FieldsIterator readObject(JODBOperationContext context, long offset, boolean followRedirection) throws IOException{
return readObject(context.getIoTicket().getRandomAccessBuffer(), context.getBase(), context.getSession(), offset, followRedirection, null);
}
public FieldsIterator readObject(JODBOperationContext context, long offset, boolean followRedirection, Vector<IndexingRecord> indexes) throws IOException{
return readObject(context.getIoTicket().getRandomAccessBuffer(), context.getBase(), context.getSession(), offset, followRedirection, indexes);
}
public FieldsIterator readObject(IRandomAccessDataBuffer dataBuffer, IOBase base, JODBSession session, long offset, boolean followRedirection) throws IOException{
return readObject(dataBuffer, base, session, offset, followRedirection, null);
}
public FieldsIterator readObject(IRandomAccessDataBuffer dataBuffer, IOBase base, JODBSession session, long offset, boolean followRedirection, Vector<IndexingRecord> indexes) throws IOException{
readHeader(dataBuffer, offset, followRedirection, indexes);
if(isDeleted() || isRedirection()){
resetInputSourceToEnd(base);
return null;
}
readAuxiliaryData();
readHierarchyData(base, session);
if(isArray() && _arrayType == null){
return null;
}
return getFieldsIterator();
}
private FieldsIterator getFieldsIterator() throws IOException{
if(_translatedClassDescriptor == null){
return null;
}
if(isArray()){
_arrayFieldsIterator.init();
_activeIterator = _arrayFieldsIterator;
return _arrayFieldsIterator;
}else{
_objectFieldsIterator.init();
_activeIterator = _objectFieldsIterator;
return _objectFieldsIterator;
}
}
public FieldsIterator getActiveFieldsIterator() {
return _activeIterator;
}
private static long readEntryLen(short entryID, DataInput input) throws IOException{
switch (entryID&(JODBIOBase.LEN_MODIFIER_BYTE|JODBIOBase.LEN_MODIFIER_LONG)) {
case JODBIOBase.LEN_MODIFIER_BYTE:
return input.readByte()&0xff;
case JODBIOBase.LEN_MODIFIER_LONG:
return input.readLong();
case 0:
return input.readShort()&0xffff;
default:
throw new JodbIOException("format error: unknown len modifier in id="+entryID);
}
}
public void resetInputSourceToEnd(IOBase base) throws IOException{
if (base!=null && _activeIterator!=null && _indexes !=null) {
while (_processedIndexes < _indexes.size() && _activeIterator.hasNext()) {
_activeIterator.next(_recordCache, base, false);
}
}
_input.seek(_offset+getTotalLength());
}
public long getEndOffset(){
return _offset+getTotalLength();
}
public FieldRecord getRecordCache() {
return _recordCache;
}
public ClassDescriptor getClassDescriptorForPersistedObject() {
return _translatedClassDescriptor;
}
// public IndexingRecord getIndexingRecord(int fieldId){
// if(_indexes == null){
// return null;
// }
// for (int i = 0; i < _indexes.size() ; i++) {
// IndexingRecord record = _indexes.elementAt(i);
// if(record.getIndexingAgent().getClassId() == fieldId){
// return record;
// }
// }
// return null;
// }
public static class FieldRecord {
public int _fieldID;
public int _fieldTypeID;
public String _fieldTypeName;
public Object _value;
public long _objectOffset;
public ByteBuffer _primitiveRawDataBuffer = ByteBuffer.allocateDirect(16);//double the size of long, to make sure any primitive fits
public FIELD_CATEGORIES _category;
public FieldRecord() {
}
public FieldRecord(int fieldID, Object value) {
super();
_fieldID = fieldID;
_value = value;
}
public FieldRecord(int fieldID, long objectOffset) {
super();
_fieldID = fieldID;
_objectOffset = objectOffset;
}
public void clear(){
_fieldID =-1;
_value = null;
_objectOffset = -1;
_primitiveRawDataBuffer.clear();
_category = null;
_fieldTypeName = null;
}
@Override
public String toString()
{
return ""+_fieldID+" "+_value+" "+_objectOffset;
}
}
public interface FieldsIterator{
boolean hasNext();
void next(FieldRecord record, IOBase base) throws IOException;
void next(FieldRecord record, IOBase base, boolean unfoldPrimitive) throws IOException;
int getRemainingInCurrentCategory();
void reset() throws IOException;
}
public class ArrayFieldsIterator implements FieldsIterator{
private long[] _slot = new long[8];//allocate read cache, max 8 londs to cache
private int _elementLen;
private int _indexInSlot;
private int _remainingElementsInArray;
private byte _currentSlotMask;
private long _slotStartOffset;
private long _readingOffset;
private void init() throws IOException{
if(_objectDataStartOffset == -1 || _arrayType == null || isDeleted() || isRedirection()){
throw new JodbIOException("Illegal IO state");
}
_input.seek(_objectDataStartOffset);
_remainingElementsInArray = _input.readInt();
_elementLen = _input.readUnsignedByte();
_indexInSlot = Integer.MAX_VALUE;
_readingOffset = _input.getCursorOffset();
if( !_arrayType.isPrimitive() ){
checkNextSlot();
}
}
private void checkNextSlot() throws IOException{
if(_indexInSlot < 8 || _remainingElementsInArray == 0){
return;
}
_input.seek(_readingOffset);
_indexInSlot = 0;
int maxToRead = Math.min(8, _remainingElementsInArray);
_slotStartOffset = _input.getCursorOffset();
for (int i = 0; i < maxToRead; i++) {
if(_elementLen > 0xFFFFFFFFL){
_slot[i] = _input.readLong();
}else{
_slot[i] = _input.readInt();
}
}
_currentSlotMask = _input.readByte();
_readingOffset = _input.getCursorOffset();
}
public final boolean hasNext() {
return _remainingElementsInArray > 0;
}
private boolean isAbsoluteOffset(){
return (_currentSlotMask & (1<<_indexInSlot))!=0;
}
public void next(FieldRecord record, IOBase base, boolean unfoldPrimitive) throws IOException {
if(!hasNext()){
throw new IOException();
}
if(_arrayType.isPrimitive()){//read primitive
if (unfoldPrimitive) {
Object value = Utils.readPrimitive(_arrayType.getName(), _input);
record._value = value;
}else{
record._primitiveRawDataBuffer.clear();
record._primitiveRawDataBuffer.limit(_elementLen);
_input.getChannel().read(record._primitiveRawDataBuffer);
record._primitiveRawDataBuffer.flip();
}
_remainingElementsInArray--;
return;
}
record._objectOffset = _slot[_indexInSlot];
if(record._objectOffset!=0 && !isAbsoluteOffset()){
record._objectOffset+=_slotStartOffset+(_indexInSlot+1)*_elementLen;
}
_indexInSlot++;
_remainingElementsInArray--;
checkNextSlot();
}
public void next(FieldRecord record, IOBase base) throws IOException {
next(record, base, true);
}
public int getRemainingInCurrentCategory() {
return _remainingElementsInArray;
}
public void reset() throws IOException {
init();
}
}
public class ObjectFieldsIterator implements FieldsIterator{
private FIELD_CATEGORIES _fieldsCategory;
private int _remainingPersistentFieldsInCategory;
private long _readingOffset;
private BitSet _unprocessedFields = new BitSet();
private void init() throws IOException{
if(_objectDataStartOffset == -1 || isDeleted() || isRedirection()){
throw new JodbIOException("Illegal IO state");
}
_fieldsCategory = null;
_remainingPersistentFieldsInCategory = 0;
_unprocessedFields.clear();
if(_translatedClassDescriptor!=null){
int total = _translatedClassDescriptor.getAllFields().length;
_unprocessedFields.set(0, total);
}
if(_objectDataStartOffset != _offset+getTotalLength()){
_input.seek(_objectDataStartOffset);
checkNextCategoryNeed();
}
}
private void checkNextCategoryNeed() throws IOException{
while (_remainingPersistentFieldsInCategory == 0) {
int index = _fieldsCategory == null? 0: _fieldsCategory.ordinal() + 1;
if (index < FIELD_CATEGORIES.values().length) {
_fieldsCategory = FIELD_CATEGORIES.values()[index];
if((_primaryDataMask & (1 << index)) != 0){
_remainingPersistentFieldsInCategory = _input.readShort() & 0xFFFF;
}
}else{
break;
}
}
_readingOffset = _input.getCursorOffset();
}
public boolean hasNext(){
return _unprocessedFields.cardinality()>0;
}
private boolean hasNextPersistent(){
return _remainingPersistentFieldsInCategory!=0;
}
public void next(FieldRecord record, IOBase base) throws IOException {
next(record, base, true);
}
public void next(FieldRecord record, IOBase base, boolean unfoldPrimitive) throws IOException {
if(!hasNext()){
throw new IOException();
}
if(hasNextPersistent())_input.seek(_readingOffset);
boolean validRecord = false;
while(hasNextPersistent()){
record._fieldID = _input.readShort()&0xffff;
String fieldID = base.getFullFieldNameForID(record._fieldID);
record._fieldTypeID = fieldID.charAt(1);
record._fieldTypeName = base.getClassTypeForID(record._fieldTypeID);
record._category = _fieldsCategory;
switch (_fieldsCategory) {
case DIRECTLY_ADDRESSED:
record._objectOffset = _input.readLong();
break;
case RELATIVELY_ADDRESSED:
record._objectOffset = _input.readInt()+_input.getCursorOffset();
break;
case PRIMITIVE:
record._primitiveRawDataBuffer.clear();
IndexingRecord indexingRecord = IndexingRecord.findIndexingRecord(record._fieldID, _indexes);
if (unfoldPrimitive) {
Object value;
if( indexingRecord!=null ){
int dataLen = PrimitiveJavaTypesUtil.getDataOutputWriteLen(record._fieldTypeName);
record._primitiveRawDataBuffer.limit(dataLen);
ByteBuffer buffer = indexingRecord.getPersistedDataBuffer();
_input.getChannel().read(buffer);
buffer.flip();
value = PrimitiveJavaTypesUtil.getAsWrappedPrimitive( PrimitiveJavaTypesUtil.getEnumeratedType(record._fieldTypeName) , buffer);
_processedIndexes++;
}else{
value = Utils.readPrimitive(record._fieldTypeName, _input);
}
record._value = value;
}else{
int dataLen = PrimitiveJavaTypesUtil.getDataOutputWriteLen(record._fieldTypeName);
record._primitiveRawDataBuffer.limit(dataLen);
_input.getChannel().read(record._primitiveRawDataBuffer);
record._primitiveRawDataBuffer.flip();
if( indexingRecord!=null ){
indexingRecord.setPersistedDataBufferValue(record._primitiveRawDataBuffer);
record._primitiveRawDataBuffer.rewind();
_processedIndexes++;
}
}
}
_remainingPersistentFieldsInCategory--;
checkNextCategoryNeed();
if(_translatedClassDescriptor!=null){
int fieldIndex = _translatedClassDescriptor.getFieldIndexForID(record._fieldID);
if(fieldIndex == -1){//field doesn't exist in class definition
record.clear();
continue;
}else{
_unprocessedFields.clear(fieldIndex);
}
}
validRecord = true;
break;
}
if(!validRecord && _translatedClassDescriptor != null){
int nextUnrocessedIndex = _unprocessedFields.nextSetBit(0);
if(nextUnrocessedIndex == -1){
throw new JodbIOException("nextUnrocessedIndex == -1)");
}
FieldAndIDRecord fieldAndIDRecord = _translatedClassDescriptor.getAllFields()[nextUnrocessedIndex];
Class fieldType = fieldAndIDRecord._field.getType();
record._fieldID = fieldAndIDRecord._id;
IndexingRecord indexingRecord = IndexingRecord.findIndexingRecord(record._fieldID, _indexes);
record._fieldTypeName = fieldType.getName();
record._fieldTypeID = base.getOrSetClassTypeSubstitutionID( record._fieldTypeName );
if(fieldType.isPrimitive()){
record._category = FIELD_CATEGORIES.PRIMITIVE;
if(unfoldPrimitive){
record._value = PrimitiveJavaTypesUtil.getDefaultWrapperInstance(record._fieldTypeName);
}else{
int dataLen = PrimitiveJavaTypesUtil.getDataOutputWriteLen(record._fieldTypeName);
record._primitiveRawDataBuffer.limit(dataLen);
record._primitiveRawDataBuffer.put(AUXILIARY_MAX_PRIMITIVE_CLEAN_ARRAY,0,dataLen);
record._primitiveRawDataBuffer.flip();
}
if(indexingRecord!=null){
indexingRecord.setPersistedDataBufferValue(PrimitiveJavaTypesUtil.getDefaultValueAsByteBuffer(record._fieldTypeName));
_processedIndexes++;
}
}else{
record._category = FIELD_CATEGORIES.RELATIVELY_ADDRESSED;
record._objectOffset = 0;
}
_unprocessedFields.clear(nextUnrocessedIndex);
}
}
public int getRemainingInCurrentCategory() {
return _remainingPersistentFieldsInCategory;
}
public void reset() throws IOException {
init();
}
}
}