package net.sourceforge.pain.db;
import net.sourceforge.pain.util.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
/**
* User: fmike Date: 12.03.2003 Time: 18:31:56
* Maps objects to pages
*
*
*
* PAGE STRUCTURE:
* HEADER START
* 1 byte: 1 bit - is object start, 2 bit - first 4 bytes after header is nextPageNo
* 2-5 bytes - SCHEMA_ID
* 6-17 bytes - OID
* HEADER END
* image
* 4 bytes (PageNo) if flag ok
*
*
*/
final class DbObjectMapper {
private static final byte OBJECT_START_BIT = 1;
private static final byte NEXT_PAGE_PRESENT_BIT = 2;
private final int NEXT_PAGE_NUM_OFFSET;
private final int PAGE_SIZE;
private final PainDB db;
private final DbPageMapper pager;
private final DbByteBuffer objImageBuf = new DbByteBuffer(1024);
private final DbIntBuffer readPageNumsBuf = new DbIntBuffer(128);
private char[] charBuf = new char[4096];
private static final int[] ZERO_INT_ARRAY = new int[0];
private static final char[] ZERO_CHAR_ARRAY = new char[0];
private static final byte[] ZERO_BYTE_ARRAY = new byte[0];
private static final String[] ZERO_STRING_ARRAY = new String[0];
DbObjectMapper(final PainDB db, final DbPageMapper mapper) {
this.db = db;
this.pager = mapper;
PAGE_SIZE = mapper.pageSize;
NEXT_PAGE_NUM_OFFSET = PAGE_SIZE - 4;
}
DbRuntimeClass readClassSchema(final int startPageNo) throws Exception {
final DbClassImage obj = (DbClassImage) readObject(startPageNo);
return new DbRuntimeClass(obj);
}
DbObject readObject(final int startPageNo) throws Exception {
readObjectImage(startPageNo);
final byte[] data = objImageBuf.data; //synchr. start
final int[] pageNums = new int[readPageNumsBuf.getSize()];
System.arraycopy(readPageNumsBuf.data, 0, pageNums, 0, readPageNumsBuf.getSize());
final int classId = DbPacker.unpack4(data, 0);
final DbOid oid = new DbOid(DbPacker.unpack4(data, 4), DbPacker.unpack8(data, 8));
final DbClassImpl dbClass;
if (classId == oid.indexId) { // class desc -> hardcoded dbClass
dbClass = db.getDbClassMetaSchema();
} else { // simple object -> should find dbClass
dbClass = db.getDbClassSchema(classId);
}
final DbObject obj = db.reflectDbObject(dbClass, oid, pageNums);
final int dataIndex = obj.dataIndex;
final byte[] types = dbClass.getFieldTypes();
final MappingPointer p = new MappingPointer(16);
final int len = types.length;
for (int i = 0; i < len; i++) {
final byte type = types[i];
final Object values = dbClass.data.fieldsValues[i];
switch (type) {
case DbType.BOOLEAN:
((boolean[]) values)[dataIndex] = readBoolean(data, p);
break;
case DbType.BYTE:
((byte[]) values)[dataIndex] = readByte(data, p);
break;
case DbType.CHAR:
((char[]) values)[dataIndex] = readChar(data, p);
break;
case DbType.DOUBLE:
((double[]) values)[dataIndex] = readDouble(data, p);
break;
case DbType.FLOAT:
((float[]) values)[dataIndex] = readFloat(data, p);
break;
case DbType.INT:
((int[]) values)[dataIndex] = readInt(data, p);
break;
case DbType.LONG:
((long[]) values)[dataIndex] = readLong(data, p);
break;
case DbType.SHORT:
((short[]) values)[dataIndex] = readShort(data, p);
break;
case DbType.REFERENCE:
((int[]) ((Object[]) values)[0])[dataIndex] = readInt(data, p);
((long[]) ((Object[]) values)[1])[dataIndex] = readLong(data, p);
break;
case DbType.STRING:
((String[]) values)[dataIndex] = readString(data, p);
break;
case DbType.ARRAY_OF_BYTE:
((Object[]) values)[dataIndex] = readByteArray(data, p);
break;
case DbType.ARRAY_OF_CHAR:
((Object[]) values)[dataIndex] = readCharArray(data, p);
break;
case DbType.ARRAY_OF_INT:
((Object[]) values)[dataIndex] = readIntArray(data, p);
break;
case DbType.ARRAY_OF_STRING:
((Object[]) values)[dataIndex] = readStringArray(data, p);
break;
case DbType.LINKED_LIST:
case DbType.ARRAY_LIST:
case DbType.INT_KEY_MAP:
case DbType.REFERENCE_SET:
/**
* Collection instance will be instantiated latter, after all objects will loaded
* before this it slot will be used with it's image
*/
((Object[]) values)[dataIndex] = readIntArray(data, p);
break;
case DbType.STRING_KEY_MAP:
((Object[]) values)[dataIndex] = readStringKeyMap(data, p);
break;
case DbType.STRING_SET:
case DbType.STRING_MAP:
((Object[]) values)[dataIndex] = readStringArray(data, p);
break;
default:
throw new RuntimeException("not valid type:" + type);
}
}
return obj;
}
private Object readStringKeyMap(byte[] data, MappingPointer p) {
String[] keys = readStringArray(data, p);
int[] values = readIntArray(data, p);
return new Object[]{keys, values};
}
void writeObject(final DbObject obj) {
final DbClassImpl dbClass = obj.dbClass;
final byte[] types = dbClass.getFieldTypes();
final int dataIndex = obj.dataIndex;
objImageBuf.clear();
objImageBuf.add(db.getClassId(obj));
objImageBuf.add(obj.indexId);
objImageBuf.add(obj.versionId);
final int len = types.length;
for (int i = 0; i < len; i++) {
final byte type = types[i];
final Object values = dbClass.data.fieldsValues[i];
switch (type) {
case DbType.BOOLEAN:
writeBoolean(objImageBuf, ((boolean[]) values)[dataIndex]);
break;
case DbType.BYTE:
writeByte(objImageBuf, ((byte[]) values)[dataIndex]);
break;
case DbType.CHAR:
writeChar(objImageBuf, ((char[]) values)[dataIndex]);
break;
case DbType.DOUBLE:
writeDouble(objImageBuf, ((double[]) values)[dataIndex]);
break;
case DbType.FLOAT:
writeFloat(objImageBuf, ((float[]) values)[dataIndex]);
break;
case DbType.INT:
writeInt(objImageBuf, ((int[]) values)[dataIndex]);
break;
case DbType.LONG:
writeLong(objImageBuf, ((long[]) values)[dataIndex]);
break;
case DbType.SHORT:
writeShort(objImageBuf, ((short[]) values)[dataIndex]);
break;
case DbType.STRING:
writeString(objImageBuf, ((String[]) values)[dataIndex]);
break;
case DbType.REFERENCE:
writeInt(objImageBuf, ((int[]) ((Object[]) values)[0])[dataIndex]);
writeLong(objImageBuf, ((long[]) ((Object[]) values)[1])[dataIndex]);
break;
case DbType.ARRAY_OF_BYTE:
writeByteArray(objImageBuf, (byte[]) ((Object[]) values)[dataIndex]);
break;
case DbType.ARRAY_OF_CHAR:
writeCharArray(objImageBuf, (char[]) ((Object[]) values)[dataIndex]);
break;
case DbType.ARRAY_OF_INT:
writeIntArray(objImageBuf, (int[]) ((Object[]) values)[dataIndex]);
break;
case DbType.ARRAY_OF_STRING:
writeStringArray(objImageBuf, (String[]) ((Object[]) values)[dataIndex]);
break;
case DbType.LINKED_LIST:
writeLinkedList(objImageBuf, (DbLinkedList) ((Object[]) values)[dataIndex]);
break;
case DbType.ARRAY_LIST:
writeArrayList(objImageBuf, (DbArrayList) ((Object[]) values)[dataIndex]);
break;
case DbType.INT_KEY_MAP:
writeIntKeyMap(objImageBuf, (DbIntKeyMap) ((Object[]) values)[dataIndex]);
break;
case DbType.STRING_KEY_MAP:
writeStringKeyMap(objImageBuf, (DbStringKeyMap) ((Object[]) values)[dataIndex]);
break;
case DbType.REFERENCE_SET:
writeReferenceSet(objImageBuf, (DbReferenceSet) ((Object[]) values)[dataIndex]);
break;
case DbType.STRING_SET:
case DbType.STRING_MAP:
Object o = ((Object[]) values)[dataIndex];
if (o instanceof DbAbstractStringMap) {
if (type == DbType.STRING_SET) {
writeStringSet(objImageBuf, (DbStringSet) o);
} else {
writeStringMap(objImageBuf, (DbStringMap) o);
}
} else {
writeStringArray(objImageBuf, (String[]) o);
}
break;
default:
throw new RuntimeException("not valid type:" + type);
}
}
final int bufSize = objImageBuf.getSize();
int nPages = 1 + bufSize / (PAGE_SIZE - 1 - 4);
if (nPages > 1 && bufSize % (PAGE_SIZE - 1 - 4) < 4) { // if we have only 4 bytes on the last page we could put this data on the NEXT_PAGE_LINK place of the prev page
nPages--;
}
int availablePages = obj.pageNums.length;
if (nPages != availablePages) {
final int[] oldPageNums = obj.pageNums;
final int[] newPageNums = new int[nPages];
if (nPages < availablePages) {
System.arraycopy(oldPageNums, 0, newPageNums, 0, nPages);
while (nPages < availablePages) {
availablePages--;
pager.deallocatePage(oldPageNums[availablePages]);
}
} else {
if (availablePages != 0) {
System.arraycopy(oldPageNums, 0, newPageNums, 0, availablePages);
}
while (availablePages < nPages) {
newPageNums[availablePages] = pager.allocatePage();
availablePages++;
}
}
obj.pageNums = newPageNums;
}
final int[] pages = obj.pageNums;
byte[] pageData = pager.getPageImage();
pageData[0] = OBJECT_START_BIT;
for (int offset = 0, pageNumIndex = 0; offset < bufSize; pageNumIndex++) {
if (pageNumIndex < nPages - 1) { // if not last page
pageData[0] = (byte) (pageData[0] | NEXT_PAGE_PRESENT_BIT);
System.arraycopy(objImageBuf.data, offset, pageData, 1, PAGE_SIZE - 5);
DbPacker.pack4(pageData, PAGE_SIZE - 4, pages[pageNumIndex + 1]);
pager.writePage(pages[pageNumIndex], pageData);
pageData = pager.getPageImage();
pageData[0] = 0;
offset += PAGE_SIZE - 5;
} else {
final int dataSize = bufSize - offset;
System.arraycopy(objImageBuf.data, offset, pageData, 1, dataSize);
pager.writePage(pages[pageNumIndex], pageData);
break;
}
}
}
private void writeStringArray(final DbByteBuffer buf, final String[] strings) {
int len;
if (strings == null) {
buf.add(0);
writeBoolean(buf, true);
} else if ((len = strings.length) > 0) {
buf.add(len);
for (int i = 0; i < len; i++) {
writeString(buf, strings[i]);
}
} else { // empty array
buf.add(0);
writeBoolean(buf, false);
}
}
private static void writeLinkedList(final DbByteBuffer buf, final DbLinkedList l) {
final int size;
if (l == null || (size = l._size()) == 0) { // lazy instantiation allows it
writeIntArray(buf, null);
} else {
final DbLinkedList.Entry first = l.getFirstEntry();
buf.prepareToAdd(4 + 4 * size);
buf.add(size);
for (DbLinkedList.Entry runner = first; runner != null; runner = runner.nextInList) {
buf._add(runner.obj.indexId);
}
}
}
private static void writeArrayList(final DbByteBuffer buf, final DbArrayList l) {
final int size;
if (l == null || (size = l._size()) == 0) { // lazy instantiation allows it
writeIntArray(buf, null);
} else {
final DbArrayList.Entry[] items = l.getItems();
buf.prepareToAdd(4 + 4 * size);
buf.add(size);
for (int i = 0; i < size; i++) {
final DbArrayList.Entry e = items[i];
buf._add(e.obj == null ? -1 : e.obj.indexId);
}
}
}
private static void writeIntKeyMap(final DbByteBuffer buf, final DbIntKeyMap m) {
final int size;
if (m == null || (size = m._size()) == 0) { // lazy instantiation allows it
writeIntArray(buf, null);
} else {
buf.prepareToAdd(4 + 8 * size);
buf.add(size * 2);
final DbAbstractMap.AMapEntry data[] = m._getData();
final int len = data.length;
for (int i = 0; i < len; i++) {
for (DbIntKeyMap.AMapEntry e = data[i]; e != null; e = e.next) {
final DbObject obj = e.obj;
buf.add(((DbIntKeyMap.IntKeyMapEntry) e).key);
buf.add(obj.indexId); // map can't contains null values
}
}
}
}
private void writeStringSet(DbByteBuffer buf, DbStringSet set) {
final int size;
if ((size = set._size()) == 0) {
writeStringArray(buf, ZERO_STRING_ARRAY);
} else {
buf.prepareToAdd(4 + 16 * size);
buf.add(size); //4
final DbAbstractStringMap.AStringMapEntry data[] = set._getData();
final int len = data.length;
for (int i = 0; i < len; i++) {
for (DbAbstractStringMap.AStringMapEntry e = data[i]; e != null; e = e.next) {
writeString(buf, e.key);
}
}
}
}
private void writeStringMap(DbByteBuffer buf, DbStringMap map) {
final int size;
if ((size = map._size()) == 0) {
writeStringArray(buf, ZERO_STRING_ARRAY);
} else {
buf.prepareToAdd(4 + 16 * size);
buf.add(size * 2); //4 bytes, map image array len
final DbAbstractStringMap.AStringMapEntry data[] = map._getData();
final int len = data.length;
for (int i = 0; i < len; i++) {
for (DbAbstractStringMap.AStringMapEntry e = data[i]; e != null; e = e.next) {
writeString(buf, e.key);
writeString(buf, e.value);
}
}
}
}
private void writeStringKeyMap(DbByteBuffer buf, DbStringKeyMap m) {
final int size;
if (m == null || (size = m._size()) == 0) { // lazy instantiation allows it
writeStringArray(buf, ZERO_STRING_ARRAY);
writeIntArray(buf, ZERO_INT_ARRAY);
} else {
buf.prepareToAdd(4 + 16 * size);
// writing keys
buf.add(size);
final DbStringKeyMap.AMapEntry data[] = m._getData();
final int len = data.length;
for (int i = 0; i < len; i++) {
for (DbAbstractMap.AMapEntry e = data[i]; e != null; e = e.next) {
writeString(buf, ((DbStringKeyMap.StringKeyMapEntry) e).key);
}
}
// writing values
buf.add(size);
for (int i = 0; i < len; i++) {
for (DbStringKeyMap.AMapEntry e = data[i]; e != null; e = e.next) {
buf.add(e.obj.indexId);
}
}
}
}
private static void writeReferenceSet(final DbByteBuffer buf, final DbReferenceSet s) {
final int size;
if (s == null || (size = s._size()) == 0) { // lazy instantiation allows it
writeIntArray(buf, null);
} else {
buf.prepareToAdd(4 + 4 * size);
buf.add(size);
final DbAbstractMap.AMapEntry data[] = s._getData();
final int len = data.length;
for (int i = 0; i < len; i++) {
for (DbAbstractMap.AMapEntry e = data[i]; e != null; e = e.next) {
buf.add(e.obj.indexId); // reference does not contain nulls
}
}
}
}
private static void writeIntArray(final DbByteBuffer buf, final int[] ints) {
if (ints == null) {
buf.add(0);
writeBoolean(buf, true);
} else if (ints.length > 0) {
buf.add(ints.length);
buf.add(ints);
} else { // empty array
buf.add(0);
writeBoolean(buf, false);
}
}
private static void writeByteArray(final DbByteBuffer buf, final byte[] bytes) {
if (bytes == null) {
buf.add(0);
writeBoolean(buf, true);
} else if (bytes.length > 0) {
buf.add(bytes.length);
buf.add(bytes);
} else { // empty array
buf.add(0);
writeBoolean(buf, false);
}
}
private static void writeCharArray(final DbByteBuffer buf, final char[] chars) {
if (chars == null) {
buf.add(0);
writeBoolean(buf, true);
} else if (chars.length > 0) {
buf.add(chars.length);
buf.add(chars);
} else { // empty array
buf.add(0);
writeBoolean(buf, false);
}
}
private void writeString(final DbByteBuffer buf, final String s) {
int len;
if (s == null) {
buf.add(0);
writeBoolean(buf, true);
} else if ((len = s.length()) > 0) {
buf.add(len);
if (charBuf.length < len) {
charBuf = new char[len];
}
s.getChars(0, len, charBuf, 0);
buf.add(charBuf, 0, len);
} else { // empty string
buf.add(0);
writeBoolean(buf, false);
}
}
private static void writeFloat(final DbByteBuffer buf, final float v) {
buf.add(Float.floatToIntBits(v));
}
private static void writeDouble(final DbByteBuffer buf, final double v) {
buf.add(Double.doubleToLongBits(v));
}
private static void writeInt(final DbByteBuffer buf, final int v) {
buf.add(v);
}
private static void writeLong(final DbByteBuffer buf, final long v) {
buf.add(v);
}
private static void writeShort(final DbByteBuffer buf, final short v) {
buf.add(v);
}
private static void writeChar(final DbByteBuffer buf, final char v) {
buf.add((short) v);
}
private static void writeByte(final DbByteBuffer buf, final byte v) {
buf.add(v);
}
private static void writeBoolean(final DbByteBuffer buf, final boolean b) {
buf.add((byte) (b ? 1 : 0));
}
private static long readLong(final byte[] data, final MappingPointer p) {
final long result = DbPacker.unpack8(data, p.pos);
p.pos += 8;
return result;
}
private static int readInt(final byte[] data, final MappingPointer p) {
final int result = DbPacker.unpack4(data, p.pos);
p.pos += 4;
return result;
}
private static float readFloat(final byte[] data, final MappingPointer p) {
final float result = Float.intBitsToFloat(DbPacker.unpack4(data, p.pos));
p.pos += 4;
return result;
}
private static double readDouble(final byte[] data, final MappingPointer p) {
final double result = Double.longBitsToDouble(DbPacker.unpack8(data, p.pos));
p.pos += 8;
return result;
}
private static char readChar(final byte[] data, final MappingPointer p) {
final char result = (char) DbPacker.unpack2(data, p.pos);
p.pos += 2;
return result;
}
private static short readShort(final byte[] data, final MappingPointer p) {
final short result = DbPacker.unpack2(data, p.pos);
p.pos += 2;
return result;
}
private static byte readByte(final byte[] data, final MappingPointer p) {
final byte result = data[p.pos];
p.pos++;
return result;
}
private static boolean readBoolean(final byte[] data, final MappingPointer p) {
final boolean result = data[p.pos] == 1;
p.pos++;
return result;
}
private String[] readStringArray(final byte[] data, final MappingPointer p) {
final int len = DbPacker.unpack4(data, p.pos);
p.pos += 4;
if (len == 0) {
final boolean isNull = readBoolean(data, p);
if (isNull) {
return null;
} else {
return ZERO_STRING_ARRAY;
}
} else {
final String[] result = new String[len];
for (int i = 0; i < len; i++) {
result[i] = readString(data, p);
}
return result;
}
}
private static byte[] readByteArray(final byte[] data, final MappingPointer p) {
final int len = DbPacker.unpack4(data, p.pos);
p.pos += 4;
if (len == 0) {
final boolean isNull = readBoolean(data, p);
if (isNull) {
return null;
} else {
return ZERO_BYTE_ARRAY;
}
} else {
final byte[] result = new byte[len];
for (int i = 0, j = p.pos; i < len; i++, j += 1) {
result[i] = data[j];
}
p.pos += len;
return result;
}
}
private static int[] readIntArray(final byte[] data, final MappingPointer p) {
final int len = DbPacker.unpack4(data, p.pos);
p.pos += 4;
if (len == 0) {
final boolean isNull = readBoolean(data, p);
if (isNull) {
return null;
} else {
return ZERO_INT_ARRAY;
}
} else {
final int[] result = new int[len];
for (int i = 0, j = p.pos; i < len; i++, j += 4) {
result[i] = DbPacker.unpack4(data, j);
}
p.pos += len * 4;
return result;
}
}
private static char[] readCharArray(final byte[] data, final MappingPointer p) {
final int len = DbPacker.unpack4(data, p.pos);
p.pos += 4;
if (len == 0) {
final boolean isNull = readBoolean(data, p);
if (isNull) {
return null;
} else {
return ZERO_CHAR_ARRAY;
}
} else {
final char[] result = new char[len];
for (int i = 0, j = p.pos; i < len; i++, j += 2) {
result[i] = (char) DbPacker.unpack2(data, j);
}
p.pos += len << 1;
return result;
}
}
private String readString(final byte[] data, final MappingPointer p) {
final int len = DbPacker.unpack4(data, p.pos);
p.pos += 4;
if (len == 0) {
final boolean isNull = readBoolean(data, p);
if (isNull) {
return null;
} else {
return "";
}
} else {
if (charBuf.length < len) {
charBuf = new char[len];
}
for (int i = 0, j = p.pos; i < len; i++, j += 2) {
charBuf[i] = (char) DbPacker.unpack2(data, j);
}
final String result = new String(charBuf, 0, len);
p.pos += len * 2;
return result;
}
}
/**
* WARN: single threaded model!
* @param startPageNo
*/
private void readObjectImage(final int startPageNo) {
objImageBuf.clear();
readPageNumsBuf.clear();
int nextPageNum = startPageNo;
byte[] data;
do {
readPageNumsBuf.add(nextPageNum);
data = pager.startup_readPage(nextPageNum);
assert((nextPageNum == startPageNo) ? (data[0] & OBJECT_START_BIT) > 0 : (data[0] & OBJECT_START_BIT) == 0);
nextPageNum = ((data[0] & NEXT_PAGE_PRESENT_BIT) > 0) ? DbPacker.unpack4(data, NEXT_PAGE_NUM_OFFSET) : 0;
objImageBuf.addFromTo(data, 1, (nextPageNum != 0 ? NEXT_PAGE_NUM_OFFSET : PAGE_SIZE));
} while (nextPageNum > 0);
}
/**
* @return true if this page is class start page and not object start page
*/
static boolean isClassSchemaStartPage(final byte[] data) {
return (data[0] & OBJECT_START_BIT) > 0 && (DbPacker.unpack4(data, 1) == DbPacker.unpack4(data, 5));
}
/**
* @return true if this page is object start page and not class start page
*/
static boolean isObjectStartPage(final byte[] data) {
return (data[0] & OBJECT_START_BIT) > 0 && (DbPacker.unpack4(data, 1) != DbPacker.unpack4(data, 5));
}
static final class MappingPointer {
int pos;
MappingPointer(final int pos) {
this.pos = pos;
}
}
/**
* used by paindb during xml backups.
* for binary objects (Strings are also binary since it can contains 0 chars) - returns BASE64 encoded values
*/
String getXMLValue(byte[] fieldTypes, int i, DbClassImpl dbClass, DbObject obj) {
objImageBuf.clear();
final Object values = dbClass.data.fieldsValues[i];
String result;
final int dataIndex = obj.dataIndex;
final byte type = fieldTypes[i];
switch (type) {
case DbType.BOOLEAN:
result = ((boolean[]) values)[dataIndex] ? "true" : "false";
break;
case DbType.BYTE:
result = "" + ((byte[]) values)[dataIndex];
break;
case DbType.CHAR:
result = "" + (int) ((char[]) values)[dataIndex];
break;
case DbType.DOUBLE:
result = "" + ((double[]) values)[dataIndex];
break;
case DbType.FLOAT:
result = "" + ((float[]) values)[dataIndex];
break;
case DbType.INT:
result = "" + ((int[]) values)[dataIndex];
break;
case DbType.LONG:
result = "" + ((long[]) values)[dataIndex];
break;
case DbType.SHORT:
result = "" + ((short[]) values)[dataIndex];
break;
case DbType.REFERENCE:
int indexId = ((int[]) ((Object[]) values)[0])[dataIndex];
DbObject ref = indexId >= 0 ? db.getObjectByIndexId(indexId) : null;
if (ref != null) {
long versionId = ((long[]) ((Object[]) values)[1])[dataIndex];
if (versionId == ref.versionId) {
result = DbOid.toString(indexId, versionId);
break;
}
}
result = "null";
break;
// all futher code is copied from writeObject method!!! (should be synchronized)
case DbType.STRING:
writeString(objImageBuf, ((String[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.ARRAY_OF_BYTE:
writeByteArray(objImageBuf, (byte[]) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.ARRAY_OF_CHAR:
writeCharArray(objImageBuf, (char[]) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.ARRAY_OF_INT:
writeIntArray(objImageBuf, (int[]) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.ARRAY_OF_STRING:
writeStringArray(objImageBuf, (String[]) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.LINKED_LIST:
writeLinkedList(objImageBuf, (DbLinkedList) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.ARRAY_LIST:
writeArrayList(objImageBuf, (DbArrayList) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.INT_KEY_MAP:
writeIntKeyMap(objImageBuf, (DbIntKeyMap) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.STRING_KEY_MAP:
writeStringKeyMap(objImageBuf, (DbStringKeyMap) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.REFERENCE_SET:
writeReferenceSet(objImageBuf, (DbReferenceSet) ((Object[]) values)[dataIndex]);
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
case DbType.STRING_SET:
case DbType.STRING_MAP:
Object o = ((Object[]) values)[dataIndex];
if (o instanceof DbAbstractStringMap) {
if (type == DbType.STRING_SET) {
writeStringSet(objImageBuf, (DbStringSet) o);
} else {
writeStringMap(objImageBuf, (DbStringMap) o);
}
} else {
writeStringArray(objImageBuf, (String[]) o);
}
result = Base64.encodeBytes(objImageBuf.data, 0, objImageBuf.getSize());
break;
default:
throw new RuntimeException("not valid type:" + type);
}
return result;
}
XMLImportDataHandler createXmlImportDataHandler() {
return new XMLImportDataHandler();
}
final class XMLImportDataHandler extends DefaultHandler {
private static final int STATE_WAIT_ROOT = 1;
private static final int STATE_WAIT_CLASS_START = 2;
private static final int STATE_WAIT_CLASS_END = 3;
private static final int STATE_WAIT_OBJECT_START = 4;
private static final int STATE_WAIT_OBJECT_END = 5;
private static final int STATE_FINISHED = 6;
public static final String rootTag = "paindb";
public static final String classTag = "class";
public static final String objectTag = "object";
public static final String fieldTag = "field";
public static final String metadataTag = "metadata";
public static final String objectsTag = "objects";
private int state = STATE_WAIT_ROOT;
private XMLClassData classData = new XMLClassData();
private XMLObjectData objectData = new XMLObjectData();
String rootOid = null;
int nItems;
int maxUsedIndexId;
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
qName = qName.toLowerCase();
switch (state) {
case STATE_WAIT_ROOT:
if (rootTag.equals(qName)) {
rootOid = attributes.getValue("rootOid");
maxUsedIndexId = Integer.parseInt(attributes.getValue("maxIndex"));
nItems = Integer.parseInt(attributes.getValue("nItems"));
db.extendObjectsIndex(maxUsedIndexId + 1, false);
} else if (metadataTag.equals(qName)) {
state = STATE_WAIT_CLASS_START;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_CLASS_START:
if (classTag.equals(qName)) {
classData.clear();
classData.name = attributes.getValue("name");
classData.oid = attributes.getValue("id");
state = STATE_WAIT_CLASS_END;
} else if (metadataTag.equals(qName)) {
state = STATE_WAIT_OBJECT_START;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_CLASS_END:
if (fieldTag.equals(qName)) {
classData.names.add(attributes.getValue("name"));
classData.types.add(Byte.parseByte(attributes.getValue("type")));
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_OBJECT_START:
if (objectTag.equals(qName)) {
objectData.clear();
objectData.className = attributes.getValue("class");
objectData.oid = attributes.getValue("id");
state = STATE_WAIT_OBJECT_END;
} else if (objectsTag.equals(qName)) {
break;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_OBJECT_END:
if (fieldTag.equals(qName)) {
objectData.fieldValues.add(attributes.getValue("value"));
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_FINISHED:
throw new RuntimeException("elements after document end!:" + qName);
}
}
public void endElement(String uri, String localName, String qName) throws SAXException {
qName = qName.toLowerCase();
switch (state) {
case STATE_WAIT_ROOT:
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
case STATE_WAIT_CLASS_START:
if (metadataTag.equals(qName)) {
state = STATE_WAIT_OBJECT_START;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_CLASS_END:
if (fieldTag.equals(qName)) {
break;
} else if (classTag.equals(qName)) {
try {
registerClass();
} catch (Exception e) {
throw new RuntimeException(e);
}
state = STATE_WAIT_CLASS_START;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_OBJECT_START:
if (objectsTag.equals(qName)) {
state = STATE_FINISHED;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_WAIT_OBJECT_END:
if (fieldTag.equals(qName)) {
break;
} else if (objectTag.equals(qName)) {
try {
registerObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
state = STATE_WAIT_OBJECT_START;
} else {
throw new SAXNotRecognizedException("Illegal element name or sequence:" + qName);
}
break;
case STATE_FINISHED:
if (!rootTag.equals(qName)) {
throw new RuntimeException("elements after document end!:" + qName);
}
}
}
// debug
// int nClasses = 0;
// int nObjects = 0;
private void registerClass() throws Exception {
// nClasses++;
db.registerNewClass(Class.forName(classData.name), new DbClassSchema(classData.types.toArray(), classData.names.toArray()), new DbOid(classData.oid));
}
private void registerObject() throws Exception {
// nObjects++;
DbRuntimeClass dbClass = db.getDbClassByClassName(objectData.className);
final byte[] types = dbClass.getFieldTypes();
final DbStringBuffer fieldValues = objectData.fieldValues;
if (types.length != fieldValues.getSize()) {
throw new RuntimeException("Incomplete objects image:" + objectData.oid);
}
final DbObject obj = db.reflectDbObject(dbClass, new DbOid(objectData.oid), DbConstants.ZERO_INT_ARRAY);
final int dataIndex = obj.dataIndex;
final int len = types.length;
MappingPointer p = new MappingPointer(0);
byte[] image;
for (int i = 0; i < len; i++) {
final byte type = types[i];
final Object values = dbClass.data.fieldsValues[i];
final String fieldValue = fieldValues.data[i];
switch (type) {
case DbType.BOOLEAN:
((boolean[]) values)[dataIndex] = "true".equals(fieldValue);
break;
case DbType.BYTE:
((byte[]) values)[dataIndex] = Byte.parseByte(fieldValue);
break;
case DbType.CHAR:
((char[]) values)[dataIndex] = (char) Integer.parseInt(fieldValue);
break;
case DbType.DOUBLE:
((double[]) values)[dataIndex] = Double.parseDouble(fieldValue);
break;
case DbType.FLOAT:
((float[]) values)[dataIndex] = Float.parseFloat(fieldValue);
break;
case DbType.INT:
((int[]) values)[dataIndex] = Integer.parseInt(fieldValue);
break;
case DbType.LONG:
((long[]) values)[dataIndex] = Long.parseLong(fieldValue);
break;
case DbType.SHORT:
((short[]) values)[dataIndex] = Short.parseShort(fieldValue);
break;
case DbType.REFERENCE:
if ("null".equals(fieldValue)) {
((int[]) ((Object[]) values)[0])[dataIndex] = -1;
((long[]) ((Object[]) values)[1])[dataIndex] = 0;
} else {
DbOid refId = new DbOid(fieldValue);
((int[]) ((Object[]) values)[0])[dataIndex] = refId.indexId;
((long[]) ((Object[]) values)[1])[dataIndex] = refId.versionId;
}
break;
case DbType.STRING:
p.pos = 0;
image = fieldValue.getBytes();
((String[]) values)[dataIndex] = readString(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.ARRAY_OF_BYTE:
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readByteArray(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.ARRAY_OF_CHAR:
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readCharArray(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.ARRAY_OF_INT:
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readIntArray(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.ARRAY_OF_STRING:
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readStringArray(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.LINKED_LIST:
case DbType.ARRAY_LIST:
case DbType.INT_KEY_MAP:
case DbType.REFERENCE_SET:
/**
* Collection instance will be instantiated latter, after all objects will loaded
* before this it slot will be filled with it's image
*/
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readIntArray(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.STRING_KEY_MAP:
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readStringKeyMap(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
case DbType.STRING_SET:
case DbType.STRING_MAP:
p.pos = 0;
image = fieldValue.getBytes();
((Object[]) values)[dataIndex] = readStringArray(Base64.decode(fieldValue.getBytes(), 0, image.length), p);
break;
default:
throw new RuntimeException("not valid type:" + type);
}
}
}
}
private static class XMLClassData {
String name;
DbByteBuffer types = new DbByteBuffer();
DbStringBuffer names = new DbStringBuffer();
String oid;
public void clear() {
oid = null;
name = null;
types.clear();
names.clear();
}
}
private static class XMLObjectData {
public String oid;
public String className;
public DbStringBuffer fieldValues = new DbStringBuffer();
public void clear() {
oid = null;
fieldValues.clear();
className = null;
}
}
}