001: // kelondroFlexWidthArray.java
002: // (C) 2006 by Michael Peter Christen; mc@anomic.de, Frankfurt a. M., Germany
003: // first published 01.06.2006 on http://www.anomic.de
004: //
005: // $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
006: // $LastChangedRevision: 1986 $
007: // $LastChangedBy: orbiter $
008: //
009: // LICENSE
010: //
011: // This program is free software; you can redistribute it and/or modify
012: // it under the terms of the GNU General Public License as published by
013: // the Free Software Foundation; either version 2 of the License, or
014: // (at your option) any later version.
015: //
016: // This program is distributed in the hope that it will be useful,
017: // but WITHOUT ANY WARRANTY; without even the implied warranty of
018: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: // GNU General Public License for more details.
020: //
021: // You should have received a copy of the GNU General Public License
022: // along with this program; if not, write to the Free Software
023: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: package de.anomic.kelondro;
026:
027: import java.io.File;
028: import java.io.IOException;
029: import java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Map;
033: import java.util.TreeMap;
034:
035: import de.anomic.server.serverFileUtils;
036: import de.anomic.server.logging.serverLog;
037:
038: public class kelondroFlexWidthArray implements kelondroArray {
039:
040: protected kelondroFixedWidthArray[] col;
041: protected kelondroRow rowdef;
042: protected File path;
043: protected String tablename;
044: protected String filename;
045:
046: public kelondroFlexWidthArray(File path, String tablename,
047: kelondroRow rowdef, boolean resetOnFail) {
048: this .path = path;
049: this .rowdef = rowdef;
050: this .tablename = tablename;
051: try {
052: init();
053: } catch (IOException e) {
054: if (resetOnFail) {
055: serverLog.logSevere("kelondroFlexWidthArray",
056: "IOException during initialization of "
057: + new File(path, tablename).toString()
058: + ": reset");
059: delete(path, tablename);
060: try {
061: init();
062: } catch (IOException e1) {
063: e1.printStackTrace();
064: throw new kelondroException(
065: "IOException during initialization of "
066: + new File(path, tablename)
067: .toString()
068: + ": cannot reset: "
069: + e1.getMessage());
070: }
071: } else {
072: throw new kelondroException(
073: "IOException during initialization of "
074: + new File(path, tablename).toString()
075: + ": not allowed to reset: "
076: + e.getMessage());
077: }
078: } catch (kelondroException e) {
079: if (resetOnFail) {
080: serverLog.logSevere("kelondroFlexWidthArray",
081: "kelondroException during initialization of "
082: + new File(path, tablename).toString()
083: + ": reset");
084: delete(path, tablename);
085: try {
086: init();
087: } catch (IOException e1) {
088: e1.printStackTrace();
089: throw new kelondroException(
090: "kelondroException during initialization of "
091: + new File(path, tablename)
092: .toString()
093: + ": cannot reset: "
094: + e1.getMessage());
095: }
096: } else {
097: throw new kelondroException(
098: "kelondroException during initialization of "
099: + new File(path, tablename).toString()
100: + ": not allowed to reset: "
101: + e.getMessage());
102: }
103: }
104: }
105:
106: public void init() throws IOException {
107:
108: // initialize columns
109: col = new kelondroFixedWidthArray[rowdef.columns()];
110: String check = "";
111: for (int i = 0; i < rowdef.columns(); i++) {
112: col[i] = null;
113: check += '_';
114: }
115:
116: // check if table directory exists
117: File tabledir = new File(path, tablename);
118: if (tabledir.exists()) {
119: if (!(tabledir.isDirectory()))
120: throw new IOException("path " + tabledir.toString()
121: + " must be a directory");
122: } else {
123: tabledir.mkdirs();
124: tabledir.mkdir();
125: }
126: this .filename = tabledir.getCanonicalPath();
127:
128: // save/check property file for this array
129: File propfile = new File(tabledir, "properties");
130: Map<String, String> props = new HashMap<String, String>();
131: if (propfile.exists()) {
132: props = serverFileUtils.loadHashMap(propfile);
133: String stored_rowdef = props.get("rowdef");
134: if ((stored_rowdef == null)
135: || (!(rowdef.subsumes(new kelondroRow(
136: stored_rowdef, rowdef.objectOrder, 0))))) {
137: System.out.println("FATAL ERROR: stored rowdef '"
138: + stored_rowdef
139: + "' does not match with new rowdef '" + rowdef
140: + "' for flex table '" + path + "', table "
141: + tablename);
142: System.exit(-1);
143: }
144: }
145: props.put("rowdef", rowdef.toString());
146: serverFileUtils.saveMap(propfile, props,
147: "FlexWidthArray properties");
148:
149: // open existing files
150: String[] files = tabledir.list();
151: for (int i = 0; i < files.length; i++) {
152: if ((files[i].startsWith("col.") && (files[i]
153: .endsWith(".list")))) {
154: int colstart = Integer.parseInt(files[i]
155: .substring(4, 7));
156: int colend = (files[i].charAt(7) == '-') ? Integer
157: .parseInt(files[i].substring(8, 11)) : colstart;
158:
159: kelondroColumn columns[] = new kelondroColumn[colend
160: - colstart + 1];
161: for (int j = colstart; j <= colend; j++)
162: columns[j - colstart] = rowdef.column(j);
163: col[colstart] = new kelondroFixedWidthArray(
164: new File(tabledir, files[i]),
165: new kelondroRow(
166: columns,
167: (colstart == 0) ? rowdef.objectOrder
168: : kelondroNaturalOrder.naturalOrder,
169: 0), 16);
170: for (int j = colstart; j <= colend; j++)
171: check = check.substring(0, j) + "X"
172: + check.substring(j + 1);
173: }
174: }
175:
176: // check if all columns are there
177: int p, q;
178: while ((p = check.indexOf('_')) >= 0) {
179: q = p;
180: if (p != 0) {
181: while ((q <= check.length() - 1)
182: && (check.charAt(q) == '_'))
183: q++;
184: q--;
185: }
186: // create new array file
187: kelondroColumn[] columns = new kelondroColumn[q - p + 1];
188: for (int j = p; j <= q; j++) {
189: columns[j - p] = rowdef.column(j);
190: check = check.substring(0, j) + "X"
191: + check.substring(j + 1);
192: }
193: col[p] = new kelondroFixedWidthArray(new File(tabledir,
194: colfilename(p, q)), new kelondroRow(columns,
195: (p == 0) ? rowdef.objectOrder
196: : kelondroNaturalOrder.naturalOrder, 0), 16);
197: }
198: }
199:
200: public final String filename() {
201: return this .filename;
202: }
203:
204: public static int staticsize(File path, String tablename) {
205:
206: // check if table directory exists
207: File tabledir = new File(path, tablename);
208: if (tabledir.exists()) {
209: if (!(tabledir.isDirectory()))
210: return 0;
211: } else {
212: return 0;
213: }
214:
215: // open existing files
216: File file = new File(tabledir, "col.000.list");
217: return kelondroCachedRecords.staticsize(file);
218: }
219:
220: public static void delete(File path, String tablename) {
221: File tabledir = new File(path, tablename);
222: if (!(tabledir.exists()))
223: return;
224: if ((!(tabledir.isDirectory()))) {
225: tabledir.delete();
226: return;
227: }
228:
229: String[] files = tabledir.list();
230: for (int i = 0; i < files.length; i++) {
231: new File(tabledir, files[i]).delete();
232: }
233:
234: tabledir.delete();
235: }
236:
237: public void reset() throws IOException {
238: this .close();
239: delete(path, tablename);
240: this .init();
241: }
242:
243: public synchronized void close() {
244: if (col != null) {
245: for (int i = 0; i < col.length; i++) {
246: if (col[i] != null) {
247: // a column can be null, this is normal
248: col[i].close();
249: col[i] = null;
250: }
251: }
252: }
253: }
254:
255: protected static final String colfilename(int start, int end) {
256: String f = Integer.toString(end);
257: while (f.length() < 3)
258: f = "0" + f;
259: if (start == end)
260: return "col." + f + ".list";
261: f = Integer.toString(start) + "-" + f;
262: while (f.length() < 7)
263: f = "0" + f;
264: return "col." + f + ".list";
265: }
266:
267: public kelondroRow row() {
268: return rowdef;
269: }
270:
271: public int size() {
272: //assert ((rowdef.columns() == 1) || (col[0].size() == col[1].size())) : "col[0].size() = " + col[0].size() + ", col[1].size() = " + col[1].size() + ", file = " + filename;
273: return col[0].size();
274: }
275:
276: public synchronized void setMultiple(
277: TreeMap<Integer, kelondroRow.Entry> entries)
278: throws IOException {
279: // a R/W head path-optimized option to write a set of entries
280: Iterator<Map.Entry<Integer, kelondroRow.Entry>> i;
281: Map.Entry<Integer, kelondroRow.Entry> entry;
282: kelondroRow.Entry rowentry, e;
283: int c = 0, index;
284: // go across each file
285: while (c < rowdef.columns()) {
286: i = entries.entrySet().iterator();
287: while (i.hasNext()) {
288: entry = i.next();
289: index = entry.getKey().intValue();
290: rowentry = entry.getValue();
291: assert rowentry.objectsize() == this .rowdef.objectsize;
292:
293: e = col[c].row().newEntry(rowentry.bytes(),
294: rowdef.colstart[c], false);
295: col[c].set(index, e);
296: }
297: c = c + col[c].row().columns();
298: }
299: }
300:
301: public synchronized void set(int index, kelondroRow.Entry rowentry)
302: throws IOException {
303: assert rowentry.objectsize() == this .rowdef.objectsize;
304: int c = 0;
305: kelondroRow.Entry e;
306: byte[] reb = rowentry.bytes();
307: while (c < rowdef.columns()) {
308: e = col[c].row().newEntry(reb, rowdef.colstart[c], false);
309: col[c].set(index, e);
310: c = c + col[c].row().columns();
311: }
312: }
313:
314: public synchronized int add(kelondroRow.Entry rowentry)
315: throws IOException {
316: assert rowentry.objectsize() == this .rowdef.objectsize;
317: int index = -1;
318: byte[] reb = rowentry.bytes();
319: index = col[0].add(col[0].row().newEntry(reb, 0, false));
320: int c = col[0].row().columns();
321:
322: while (c < rowdef.columns()) {
323: col[c].set(index, col[c].row().newEntry(reb,
324: rowdef.colstart[c], false));
325: c = c + col[c].row().columns();
326: }
327: return index;
328: }
329:
330: @SuppressWarnings("unchecked")
331: protected synchronized TreeMap<Integer, byte[]> addMultiple(
332: List<kelondroRow.Entry> rows) throws IOException {
333: // result is a Integer/byte[] relation
334: // of newly added rows (index, key)
335: TreeMap<Integer, byte[]> indexref = new TreeMap<Integer, byte[]>();
336: Iterator<kelondroRow.Entry> i;
337: kelondroRow.Entry rowentry;
338: // prepare storage for other columns
339: TreeMap<Integer, kelondroRow.Entry>[] colm = new TreeMap[col.length];
340: for (int j = 0; j < col.length; j++) {
341: if (col[j] == null)
342: colm[j] = null;
343: else
344: colm[j] = new TreeMap<Integer, kelondroRow.Entry>();
345: }
346: i = rows.iterator();
347: while (i.hasNext()) {
348: rowentry = i.next();
349: assert rowentry.objectsize() == this .rowdef.objectsize;
350:
351: kelondroRow.Entry e;
352: int index = -1;
353: byte[] reb = rowentry.bytes();
354: e = col[0].row().newEntry(reb, 0, false);
355: index = col[0].add(e);
356: int c = col[0].row().columns();
357:
358: while (c < rowdef.columns()) {
359: e = col[c].row().newEntry(reb, rowdef.colstart[c],
360: false);
361: // remember write to column, but do not write directly
362: colm[c].put(new Integer(index), e); // col[c].set(index,e);
363: c = c + col[c].row().columns();
364: }
365: indexref.put(new Integer(index), rowentry.getColBytes(0));
366: }
367: // write the other columns
368: for (int j = 1; j < col.length; j++) {
369: if (col[j] != null)
370: col[j].setMultiple(colm[j]);
371: }
372: // return references to entries with key
373: return indexref;
374: }
375:
376: public synchronized kelondroRow.Entry get(int index)
377: throws IOException {
378: kelondroRow.Entry e = col[0].getIfValid(index);
379: //assert e != null;
380: if (e == null)
381: return null; // probably a deleted entry
382: kelondroRow.Entry p = rowdef.newEntry();
383: p.setCol(0, e.getColBytes(0));
384: int r = col[0].row().columns();
385: while (r < rowdef.columns()) {
386: e = col[r].get(index);
387: for (int i = 0; i < col[r].row().columns(); i++) {
388: p.setCol(r + i, e.getColBytes(i));
389: }
390: r = r + col[r].row().columns();
391: }
392: return p;
393: }
394:
395: public synchronized kelondroRow.Entry getOmitCol0(int index,
396: byte[] col0) throws IOException {
397: assert col[0].row().columns() == 1;
398: kelondroRow.Entry p = rowdef.newEntry();
399: kelondroRow.Entry e;
400: p.setCol(0, col0);
401: int r = 1;
402: while (r < rowdef.columns()) {
403: e = col[r].get(index);
404: for (int i = 0; i < col[r].row().columns(); i++) {
405: p.setCol(r + i, e.getColBytes(i));
406: }
407: r = r + col[r].row().columns();
408: }
409: return p;
410: }
411:
412: public synchronized void remove(int index) throws IOException {
413: int r = 0;
414:
415: // remove only from the first column
416: col[0].remove(index);
417: r = r + col[r].row().columns();
418:
419: // the other columns will be blanked out only
420: while (r < rowdef.columns()) {
421: col[r].set(index, null);
422: r = r + col[r].row().columns();
423: }
424: }
425:
426: public void print() throws IOException {
427: System.out.println("PRINTOUT of table, length=" + size());
428: kelondroRow.Entry row;
429: for (int i = 0; i < (col[0].free() + col[0].size()); i++) {
430: System.out.print("row " + i + ": ");
431: row = get(i);
432: System.out.println(row.toString());
433: //for (int j = 0; j < row().columns(); j++) System.out.print(((row.empty(j)) ? "NULL" : row.getColString(j, "UTF-8")) + ", ");
434: //System.out.println();
435: }
436: System.out.println("EndOfTable");
437: }
438:
439: public static void main(String[] args) {
440: //File f = new File("d:\\\\mc\\privat\\fixtest.db");
441: File f = new File("/Users/admin/");
442: kelondroRow rowdef = new kelondroRow("byte[] a-12, byte[] b-4",
443: kelondroNaturalOrder.naturalOrder, 0);
444: String testname = "flextest";
445: try {
446: System.out.println("erster Test");
447: kelondroFlexWidthArray.delete(f, testname);
448: kelondroFlexWidthArray k = new kelondroFlexWidthArray(f,
449: "flextest", rowdef, true);
450: k
451: .add(k.row().newEntry(
452: new byte[][] { "a".getBytes(),
453: "xxxx".getBytes() }));
454: k
455: .add(k.row().newEntry(
456: new byte[][] { "b".getBytes(),
457: "xxxx".getBytes() }));
458: k.remove(0);
459:
460: k
461: .add(k.row().newEntry(
462: new byte[][] { "c".getBytes(),
463: "xxxx".getBytes() }));
464: k
465: .add(k.row().newEntry(
466: new byte[][] { "d".getBytes(),
467: "xxxx".getBytes() }));
468: k
469: .add(k.row().newEntry(
470: new byte[][] { "e".getBytes(),
471: "xxxx".getBytes() }));
472: k
473: .add(k.row().newEntry(
474: new byte[][] { "f".getBytes(),
475: "xxxx".getBytes() }));
476: k.remove(0);
477: k.remove(1);
478:
479: k.print();
480: k.col[0].print();
481: k.col[1].print();
482: k.close();
483:
484: System.out.println("zweiter Test");
485: kelondroFlexWidthArray.delete(f, testname);
486: //k = kelondroFlexWidthArray.open(f, "flextest", rowdef);
487: for (int i = 1; i <= 20; i = i * 2) {
488: System.out.println("LOOP: " + i);
489: k = new kelondroFlexWidthArray(f, "flextest", rowdef,
490: true);
491: for (int j = 0; j < i * 2; j++) {
492: k
493: .add(k
494: .row()
495: .newEntry(
496: new byte[][] {
497: (Integer
498: .toString(i)
499: + "-" + Integer
500: .toString(j))
501: .getBytes(),
502: "xxxx".getBytes() }));
503: }
504: k.close();
505: k = new kelondroFlexWidthArray(f, "flextest", rowdef,
506: true);
507: for (int j = 0; j < i; j++) {
508: k.remove(i * 2 - j - 1);
509: }
510: k.close();
511: }
512: k = new kelondroFlexWidthArray(f, "flextest", rowdef, true);
513: k.print();
514: k.col[0].print();
515: k.close();
516:
517: } catch (IOException e) {
518: e.printStackTrace();
519: }
520: }
521: }
|