001: package org.garret.perst.impl;
002:
003: import java.io.*;
004: import java.util.Date;
005: import java.lang.reflect.Field;
006: import org.garret.perst.Assert;
007: import org.garret.perst.CustomSerializable;
008:
009: public class XMLExporter {
010: public XMLExporter(StorageImpl storage, Writer writer) {
011: this .storage = storage;
012: this .writer = writer;
013: }
014:
015: public void exportDatabase(int rootOid) throws IOException {
016: if (storage.encoding != null) {
017: writer.write("<?xml version=\"1.0\" encoding=\""
018: + storage.encoding + "\"?>\n");
019: } else {
020: writer
021: .write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
022: }
023: writer.write("<database root=\"" + rootOid + "\">\n");
024: exportedBitmap = new int[(storage.currIndexSize + 31) / 32];
025: markedBitmap = new int[(storage.currIndexSize + 31) / 32];
026: markedBitmap[rootOid >> 5] |= 1 << (rootOid & 31);
027: int nExportedObjects;
028: do {
029: nExportedObjects = 0;
030: for (int i = 0; i < markedBitmap.length; i++) {
031: int mask = markedBitmap[i];
032: if (mask != 0) {
033: for (int j = 0, bit = 1; j < 32; j++, bit <<= 1) {
034: if ((mask & bit) != 0) {
035: int oid = (i << 5) + j;
036: exportedBitmap[i] |= bit;
037: markedBitmap[i] &= ~bit;
038: byte[] obj = storage.get(oid);
039: int typeOid = ObjectHeader.getType(obj, 0);
040: ClassDescriptor desc = storage
041: .findClassDescriptor(typeOid);
042: if (desc.cls == Btree.class) {
043: exportIndex(oid, obj,
044: "org.garret.perst.impl.Btree");
045: } else if (desc.cls == BitIndexImpl.class) {
046: exportIndex(oid, obj,
047: "org.garret.perst.impl.BitIndexImpl");
048: } else if (desc.cls == PersistentSet.class) {
049: exportSet(oid, obj);
050: } else if (desc.cls == BtreeFieldIndex.class) {
051: exportFieldIndex(oid, obj,
052: "org.garret.perst.impl.BtreeFieldIndex");
053: } else if (desc.cls == BtreeCaseInsensitiveFieldIndex.class) {
054: exportFieldIndex(oid, obj,
055: "org.garret.perst.impl.BtreeCaseInsensitiveFieldIndex");
056: } else if (desc.cls == BtreeMultiFieldIndex.class) {
057: exportMultiFieldIndex(oid, obj,
058: "org.garret.perst.impl.BtreeMultiFieldIndex");
059: } else if (desc.cls == BtreeCaseInsensitiveMultiFieldIndex.class) {
060: exportMultiFieldIndex(oid, obj,
061: "org.garret.perst.impl.BtreeCaseInsensitiveMultiFieldIndex");
062: } else if (desc.cls == BtreeCompoundIndex.class) {
063: exportCompoundIndex(oid, obj);
064: } else {
065: String className = exportIdentifier(desc.name);
066: writer.write(" <" + className
067: + " id=\"" + oid + "\">\n");
068: exportObject(desc, obj,
069: ObjectHeader.sizeof, 2);
070: writer.write(" </" + className + ">\n");
071: }
072: nExportedObjects += 1;
073: }
074: }
075: }
076: }
077: } while (nExportedObjects != 0);
078: writer.write("</database>\n");
079: writer.flush(); // writer should be closed by calling code
080: }
081:
082: final String exportIdentifier(String name) {
083: return name.replace('$', '-');
084: }
085:
086: final void exportSet(int oid, byte[] data) throws IOException {
087: Btree btree = new Btree(data, ObjectHeader.sizeof);
088: storage.assignOid(btree, oid);
089: writer.write(" <org.garret.perst.impl.PersistentSet id=\""
090: + oid + "\">\n");
091: btree.export(this );
092: writer.write(" </org.garret.perst.impl.PersistentSet>\n");
093: }
094:
095: final void exportIndex(int oid, byte[] data, String name)
096: throws IOException {
097: Btree btree = new Btree(data, ObjectHeader.sizeof);
098: storage.assignOid(btree, oid);
099: writer.write(" <" + name + " id=\"" + oid + "\" unique=\""
100: + (btree.unique ? '1' : '0') + "\" type=\""
101: + ClassDescriptor.signature[btree.type] + "\">\n");
102: btree.export(this );
103: writer.write(" </" + name + ">\n");
104: }
105:
106: final void exportFieldIndex(int oid, byte[] data, String name)
107: throws IOException {
108: Btree btree = new Btree(data, ObjectHeader.sizeof);
109: storage.assignOid(btree, oid);
110: writer.write(" <" + name + " id=\"" + oid + "\" unique=\""
111: + (btree.unique ? '1' : '0') + "\"");
112: int offs = Btree.sizeof;
113: writer.write(" autoinc=\"" + Bytes.unpack8(data, offs) + "\"");
114: offs += 8;
115: writer.write(" class=");
116: offs = exportString(data, offs);
117: writer.write(" field=");
118: offs = exportString(data, offs);
119: writer.write(">\n");
120: btree.export(this );
121: writer.write(" </" + name + ">\n");
122: }
123:
124: final void exportMultiFieldIndex(int oid, byte[] data, String name)
125: throws IOException {
126: Btree btree = new Btree(data, ObjectHeader.sizeof);
127: storage.assignOid(btree, oid);
128: writer.write(" <" + name + " id=\"" + oid + "\" unique=\""
129: + (btree.unique ? '1' : '0') + "\" class=");
130: int offs = exportString(data, Btree.sizeof);
131: int nFields = Bytes.unpack4(data, offs);
132: offs += 4;
133: for (int i = 0; i < nFields; i++) {
134: writer.write(" field" + i + "=");
135: offs = exportString(data, offs);
136: }
137: writer.write(">\n");
138: int nTypes = Bytes.unpack4(data, offs);
139: offs += 4;
140: compoundKeyTypes = new int[nTypes];
141: for (int i = 0; i < nTypes; i++) {
142: compoundKeyTypes[i] = Bytes.unpack4(data, offs);
143: offs += 4;
144: }
145: btree.export(this );
146: compoundKeyTypes = null;
147: writer.write(" </" + name + ">\n");
148: }
149:
150: final void exportCompoundIndex(int oid, byte[] data)
151: throws IOException {
152: Btree btree = new Btree(data, ObjectHeader.sizeof);
153: storage.assignOid(btree, oid);
154: writer.write(" <org.garret.perst.impl.BtreeCompoundIndex id=\""
155: + oid + "\" unique=\"" + (btree.unique ? '1' : '0')
156: + "\"");
157: int offs = Btree.sizeof;
158: int nTypes = Bytes.unpack4(data, offs);
159: offs += 4;
160: compoundKeyTypes = new int[nTypes];
161: for (int i = 0; i < nTypes; i++) {
162: int type = Bytes.unpack4(data, offs);
163: writer.write(" type" + i + "=\""
164: + ClassDescriptor.signature[type] + "\"");
165:
166: compoundKeyTypes[i] = type;
167: offs += 4;
168: }
169: writer.write(">\n");
170: btree.export(this );
171: compoundKeyTypes = null;
172: writer.write(" </org.garret.perst.impl.BtreeCompoundIndex>\n");
173: }
174:
175: final int exportKey(byte[] body, int offs, int size, int type)
176: throws IOException {
177: switch (type) {
178: case ClassDescriptor.tpBoolean:
179: writer.write(body[offs++] != 0 ? "1" : "0");
180: break;
181: case ClassDescriptor.tpByte:
182: writer.write(Integer.toString(body[offs++]));
183: break;
184: case ClassDescriptor.tpChar:
185: writer.write(Integer.toString((char) Bytes.unpack2(body,
186: offs)));
187: offs += 2;
188: break;
189: case ClassDescriptor.tpShort:
190: writer.write(Integer.toString(Bytes.unpack2(body, offs)));
191: offs += 2;
192: break;
193: case ClassDescriptor.tpInt:
194: case ClassDescriptor.tpObject:
195: case ClassDescriptor.tpEnum:
196: writer.write(Integer.toString(Bytes.unpack4(body, offs)));
197: offs += 4;
198: break;
199: case ClassDescriptor.tpLong:
200: writer.write(Long.toString(Bytes.unpack8(body, offs)));
201: offs += 8;
202: break;
203: case ClassDescriptor.tpFloat:
204: writer.write(Float.toString(Float.intBitsToFloat(Bytes
205: .unpack4(body, offs))));
206: offs += 4;
207: break;
208: case ClassDescriptor.tpDouble:
209: writer.write(Double.toString(Double.longBitsToDouble(Bytes
210: .unpack8(body, offs))));
211: offs += 8;
212: break;
213: case ClassDescriptor.tpString:
214: for (int i = 0; i < size; i++) {
215: exportChar((char) Bytes.unpack2(body, offs));
216: offs += 2;
217: }
218: break;
219: case ClassDescriptor.tpArrayOfByte:
220: for (int i = 0; i < size; i++) {
221: byte b = body[offs++];
222: writer.write(hexDigit[(b >>> 4) & 0xF]);
223: writer.write(hexDigit[b & 0xF]);
224: }
225: break;
226: case ClassDescriptor.tpDate: {
227: long msec = Bytes.unpack8(body, offs);
228: offs += 8;
229: if (msec >= 0) {
230: writer.write(XMLImporter.httpFormatter.format(new Date(
231: msec)));
232: } else {
233: writer.write("null");
234: }
235: break;
236: }
237: default:
238: Assert.that(false);
239: }
240: return offs;
241: }
242:
243: final void exportCompoundKey(byte[] body, int offs, int size,
244: int type) throws IOException {
245: Assert.that(type == ClassDescriptor.tpArrayOfByte);
246: int end = offs + size;
247: for (int i = 0; i < compoundKeyTypes.length; i++) {
248: type = compoundKeyTypes[i];
249: if (type == ClassDescriptor.tpArrayOfByte
250: || type == ClassDescriptor.tpString) {
251: size = Bytes.unpack4(body, offs);
252: offs += 4;
253: }
254: writer.write(" key" + i + "=\"");
255: offs = exportKey(body, offs, size, type);
256: writer.write("\"");
257: }
258: Assert.that(offs == end);
259: }
260:
261: final void exportAssoc(int oid, byte[] body, int offs, int size,
262: int type) throws IOException {
263: writer.write(" <ref id=\"" + oid + "\"");
264: if ((exportedBitmap[oid >> 5] & (1 << (oid & 31))) == 0) {
265: markedBitmap[oid >> 5] |= 1 << (oid & 31);
266: }
267: if (compoundKeyTypes != null) {
268: exportCompoundKey(body, offs, size, type);
269: } else {
270: writer.write(" key=\"");
271: exportKey(body, offs, size, type);
272: writer.write("\"");
273: }
274: writer.write("/>\n");
275: }
276:
277: final void indentation(int indent) throws IOException {
278: while (--indent >= 0) {
279: writer.write(' ');
280: }
281: }
282:
283: final void exportChar(char ch) throws IOException {
284: switch (ch) {
285: case '<':
286: writer.write("<");
287: break;
288: case '>':
289: writer.write(">");
290: break;
291: case '&':
292: writer.write("&");
293: break;
294: case '"':
295: writer.write(""");
296: break;
297: default:
298: writer.write(ch);
299: }
300: }
301:
302: final int exportString(byte[] body, int offs) throws IOException {
303: int len = Bytes.unpack4(body, offs);
304: offs += 4;
305: if (len >= 0) {
306: writer.write("\"");
307: while (--len >= 0) {
308: exportChar((char) Bytes.unpack2(body, offs));
309: offs += 2;
310: }
311: writer.write("\"");
312: } else if (len < -1) {
313: writer.write("\"");
314: String s;
315: if (storage.encoding != null) {
316: s = new String(body, offs, -len - 2, storage.encoding);
317: } else {
318: s = new String(body, offs, -len - 2);
319: }
320: offs -= len + 2;
321: for (int i = 0, n = s.length(); i < n; i++) {
322: exportChar(s.charAt(i));
323: }
324: writer.write("\"");
325: } else {
326: writer.write("null");
327: }
328: return offs;
329: }
330:
331: static final char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6',
332: '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
333:
334: final int exportBinary(byte[] body, int offs) throws IOException {
335: int len = Bytes.unpack4(body, offs);
336: offs += 4;
337: if (len < 0) {
338: if (len == -2 - ClassDescriptor.tpObject) {
339: exportRef(Bytes.unpack4(body, offs));
340: offs += 4;
341: } else if (len < -1) {
342: writer.write("\"#");
343: writer.write(hexDigit[-2 - len]);
344: len = ClassDescriptor.sizeof[-2 - len];
345: while (--len >= 0) {
346: byte b = body[offs++];
347: writer.write(hexDigit[(b >>> 4) & 0xF]);
348: writer.write(hexDigit[b & 0xF]);
349: }
350: writer.write('\"');
351: } else {
352: writer.write("null");
353: }
354: } else {
355: writer.write('\"');
356: while (--len >= 0) {
357: byte b = body[offs++];
358: writer.write(hexDigit[(b >>> 4) & 0xF]);
359: writer.write(hexDigit[b & 0xF]);
360: }
361: writer.write('\"');
362: }
363: return offs;
364: }
365:
366: final void exportRef(int oid) throws IOException {
367: writer.write("<ref id=\"" + oid + "\"/>");
368: if (oid != 0
369: && (exportedBitmap[oid >> 5] & (1 << (oid & 31))) == 0) {
370: markedBitmap[oid >> 5] |= 1 << (oid & 31);
371: }
372: }
373:
374: final int exportObject(ClassDescriptor desc, byte[] body, int offs,
375: int indent) throws IOException {
376: ClassDescriptor.FieldDescriptor[] all = desc.allFields;
377:
378: for (int i = 0, n = all.length; i < n; i++) {
379: ClassDescriptor.FieldDescriptor fd = all[i];
380: indentation(indent);
381: String fieldName = exportIdentifier(fd.fieldName);
382: writer.write("<" + fieldName + ">");
383: switch (fd.type) {
384: case ClassDescriptor.tpBoolean:
385: writer.write(body[offs++] != 0 ? "1" : "0");
386: break;
387: case ClassDescriptor.tpByte:
388: writer.write(Integer.toString(body[offs++]));
389: break;
390: case ClassDescriptor.tpChar:
391: writer.write(Integer.toString((char) Bytes.unpack2(
392: body, offs)));
393: offs += 2;
394: break;
395: case ClassDescriptor.tpShort:
396: writer.write(Integer
397: .toString(Bytes.unpack2(body, offs)));
398: offs += 2;
399: break;
400: case ClassDescriptor.tpInt:
401: writer.write(Integer
402: .toString(Bytes.unpack4(body, offs)));
403: offs += 4;
404: break;
405: case ClassDescriptor.tpLong:
406: writer.write(Long.toString(Bytes.unpack8(body, offs)));
407: offs += 8;
408: break;
409: case ClassDescriptor.tpFloat:
410: writer.write(Float.toString(Float.intBitsToFloat(Bytes
411: .unpack4(body, offs))));
412: offs += 4;
413: break;
414: case ClassDescriptor.tpDouble:
415: writer.write(Double.toString(Double
416: .longBitsToDouble(Bytes.unpack8(body, offs))));
417: offs += 8;
418: break;
419: case ClassDescriptor.tpEnum: {
420: int ordinal = Bytes.unpack4(body, offs);
421: if (ordinal < 0) {
422: writer.write("null");
423: } else {
424: writer.write("\""
425: + ((Enum) fd.field.getType()
426: .getEnumConstants()[ordinal])
427: .name() + "\"");
428: }
429: offs += 4;
430: break;
431: }
432: case ClassDescriptor.tpString:
433: offs = exportString(body, offs);
434: break;
435: case ClassDescriptor.tpDate: {
436: long msec = Bytes.unpack8(body, offs);
437: offs += 8;
438: if (msec >= 0) {
439: writer.write("\""
440: + XMLImporter.httpFormatter
441: .format(new Date(msec)) + "\"");
442: } else {
443: writer.write("null");
444: }
445: break;
446: }
447: case ClassDescriptor.tpObject:
448: exportRef(Bytes.unpack4(body, offs));
449: offs += 4;
450: break;
451: case ClassDescriptor.tpValue:
452: writer.write('\n');
453: offs = exportObject(fd.valueDesc, body, offs,
454: indent + 1);
455: indentation(indent);
456: break;
457: case ClassDescriptor.tpRaw:
458: case ClassDescriptor.tpArrayOfByte:
459: offs = exportBinary(body, offs);
460: break;
461: case ClassDescriptor.tpCustom: {
462: ByteArrayInputStream in = new ByteArrayInputStream(
463: body, offs, body.length - offs);
464: CustomSerializable obj = storage.serializer.unpack(in);
465: String str = obj.toString();
466: writer.write("\"");
467: for (int j = 0, len = str.length(); j < len; j++) {
468: exportChar(str.charAt(j));
469: }
470: writer.write("\"");
471: offs = body.length - in.available();
472: break;
473: }
474: case ClassDescriptor.tpArrayOfBoolean: {
475: int len = Bytes.unpack4(body, offs);
476: offs += 4;
477: if (len < 0) {
478: writer.write("null");
479: } else {
480: writer.write('\n');
481: while (--len >= 0) {
482: indentation(indent + 1);
483: writer.write("<element>"
484: + (body[offs++] != 0 ? "1" : "0")
485: + "</element>\n");
486: }
487: indentation(indent);
488: }
489: break;
490: }
491: case ClassDescriptor.tpArrayOfChar: {
492: int len = Bytes.unpack4(body, offs);
493: offs += 4;
494: if (len < 0) {
495: writer.write("null");
496: } else {
497: writer.write('\n');
498: while (--len >= 0) {
499: indentation(indent + 1);
500: writer.write("<element>"
501: + (Bytes.unpack2(body, offs) & 0xFFFF)
502: + "</element>\n");
503: offs += 2;
504: }
505: indentation(indent);
506: }
507: break;
508: }
509: case ClassDescriptor.tpArrayOfShort: {
510: int len = Bytes.unpack4(body, offs);
511: offs += 4;
512: if (len < 0) {
513: writer.write("null");
514: } else {
515: writer.write('\n');
516: while (--len >= 0) {
517: indentation(indent + 1);
518: writer.write("<element>"
519: + Bytes.unpack2(body, offs)
520: + "</element>\n");
521: offs += 2;
522: }
523: indentation(indent);
524: }
525: break;
526: }
527: case ClassDescriptor.tpArrayOfEnum: {
528: int len = Bytes.unpack4(body, offs);
529: offs += 4;
530: if (len < 0) {
531: writer.write("null");
532: } else {
533: writer.write('\n');
534: Enum[] enumConstants = (Enum[]) fd.field.getType()
535: .getEnumConstants();
536: while (--len >= 0) {
537: indentation(indent + 1);
538: int ordinal = Bytes.unpack4(body, offs);
539: if (ordinal < 0) {
540: writer.write("null");
541: } else {
542: writer.write("<element>\""
543: + enumConstants[ordinal].name()
544: + "\"</element>\n");
545: }
546: offs += 4;
547: }
548: indentation(indent);
549: }
550: break;
551: }
552: case ClassDescriptor.tpArrayOfInt: {
553: int len = Bytes.unpack4(body, offs);
554: offs += 4;
555: if (len < 0) {
556: writer.write("null");
557: } else {
558: writer.write('\n');
559: while (--len >= 0) {
560: indentation(indent + 1);
561: writer.write("<element>"
562: + Bytes.unpack4(body, offs)
563: + "</element>\n");
564: offs += 4;
565: }
566: indentation(indent);
567: }
568: break;
569: }
570: case ClassDescriptor.tpArrayOfLong: {
571: int len = Bytes.unpack4(body, offs);
572: offs += 4;
573: if (len < 0) {
574: writer.write("null");
575: } else {
576: writer.write('\n');
577: while (--len >= 0) {
578: indentation(indent + 1);
579: writer.write("<element>"
580: + Bytes.unpack8(body, offs)
581: + "</element>\n");
582: offs += 8;
583: }
584: indentation(indent);
585: }
586: break;
587: }
588: case ClassDescriptor.tpArrayOfFloat: {
589: int len = Bytes.unpack4(body, offs);
590: offs += 4;
591: if (len < 0) {
592: writer.write("null");
593: } else {
594: writer.write('\n');
595: while (--len >= 0) {
596: indentation(indent + 1);
597: writer.write("<element>"
598: + Float.intBitsToFloat(Bytes.unpack4(
599: body, offs)) + "</element>\n");
600: offs += 4;
601: }
602: indentation(indent);
603: }
604: break;
605: }
606: case ClassDescriptor.tpArrayOfDouble: {
607: int len = Bytes.unpack4(body, offs);
608: offs += 4;
609: if (len < 0) {
610: writer.write("null");
611: } else {
612: writer.write('\n');
613: while (--len >= 0) {
614: indentation(indent + 1);
615: writer.write("<element>"
616: + Double.longBitsToDouble(Bytes
617: .unpack8(body, offs))
618: + "</element>\n");
619: offs += 8;
620: }
621: indentation(indent);
622: }
623: break;
624: }
625: case ClassDescriptor.tpArrayOfDate: {
626: int len = Bytes.unpack4(body, offs);
627: offs += 4;
628: if (len < 0) {
629: writer.write("null");
630: } else {
631: writer.write('\n');
632: while (--len >= 0) {
633: indentation(indent + 1);
634: long msec = Bytes.unpack8(body, offs);
635: offs += 8;
636: if (msec >= 0) {
637: writer.write("<element>\"");
638: writer.write(XMLImporter.httpFormatter
639: .format(new Date(msec)));
640: writer.write("\"</element>\n");
641: } else {
642: writer.write("<element>null</element>\n");
643: }
644: }
645: }
646: break;
647: }
648: case ClassDescriptor.tpArrayOfString: {
649: int len = Bytes.unpack4(body, offs);
650: offs += 4;
651: if (len < 0) {
652: writer.write("null");
653: } else {
654: writer.write('\n');
655: while (--len >= 0) {
656: indentation(indent + 1);
657: writer.write("<element>");
658: offs = exportString(body, offs);
659: writer.write("</element>\n");
660: }
661: indentation(indent);
662: }
663: break;
664: }
665: case ClassDescriptor.tpLink:
666: case ClassDescriptor.tpArrayOfObject: {
667: int len = Bytes.unpack4(body, offs);
668: offs += 4;
669: if (len < 0) {
670: writer.write("null");
671: } else {
672: writer.write('\n');
673: while (--len >= 0) {
674: indentation(indent + 1);
675: int oid = Bytes.unpack4(body, offs);
676: if (oid != 0
677: && (exportedBitmap[oid >> 5] & (1 << (oid & 31))) == 0) {
678: markedBitmap[oid >> 5] |= 1 << (oid & 31);
679: }
680: writer.write("<element><ref id=\"" + oid
681: + "\"/></element>\n");
682: offs += 4;
683: }
684: indentation(indent);
685: }
686: break;
687: }
688: case ClassDescriptor.tpArrayOfValue: {
689: int len = Bytes.unpack4(body, offs);
690: offs += 4;
691: if (len < 0) {
692: writer.write("null");
693: } else {
694: writer.write('\n');
695: while (--len >= 0) {
696: indentation(indent + 1);
697: writer.write("<element>\n");
698: offs = exportObject(fd.valueDesc, body, offs,
699: indent + 2);
700: indentation(indent + 1);
701: writer.write("</element>\n");
702: }
703: indentation(indent);
704: }
705: break;
706: }
707: case ClassDescriptor.tpArrayOfRaw: {
708: int len = Bytes.unpack4(body, offs);
709: offs += 4;
710: if (len < 0) {
711: writer.write("null");
712: } else {
713: writer.write('\n');
714: while (--len >= 0) {
715: indentation(indent + 1);
716: writer.write("<element>");
717: offs = exportBinary(body, offs);
718: writer.write("</element>\n");
719: }
720: indentation(indent);
721: }
722: break;
723: }
724: }
725: writer.write("</" + fieldName + ">\n");
726: }
727: return offs;
728: }
729:
730: private StorageImpl storage;
731: private Writer writer;
732: private int[] markedBitmap;
733: private int[] exportedBitmap;
734: private int[] compoundKeyTypes;
735: }
|