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; } } }