package com.kreative.cff; import java.io.*; import java.util.Vector; public class ChunkFileEditor { public static final int CREATE_NEVER = 0; public static final int CREATE_IF_EMPTY = 1; public static final int CREATE_ALWAYS = 2; private ChunkFileSpec spec; private ChunkFile cf; private File f; public ChunkFileEditor(ChunkFileSpec spec) { this.spec = spec; this.cf = new ChunkFile(spec.fileHeaderSpec().createHeader()); this.f = null; } public ChunkFileEditor(ChunkFileSpec spec, ChunkFile cf) { this.spec = spec; this.cf = cf; this.f = null; } public ChunkFileEditor(ChunkFileSpec spec, File f, int create) throws IOException { this.spec = spec; if ((create == CREATE_ALWAYS) || ((create == CREATE_IF_EMPTY) && ((!f.exists()) || (f.length() == 0)))) { this.cf = new ChunkFile(spec.fileHeaderSpec().createHeader()); } else { this.cf = spec.readChunkFile(new DataInputStream(new FileInputStream(f))); } this.f = f; } public ChunkFileSpec getChunkFileSpec() { return spec; } public ChunkFile getChunkFile() { return cf; } public File getFile() { return f; } public synchronized byte[] toByteArray() throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); spec.writeChunkFile(dout, cf); dout.flush(); bout.flush(); dout.close(); bout.close(); return bout.toByteArray(); } public synchronized void flush() throws IOException { if (f != null) { FileOutputStream fout = new FileOutputStream(f); DataOutputStream dout = new DataOutputStream(fout); spec.writeChunkFile(dout, cf); dout.flush(); fout.flush(); dout.close(); fout.close(); } } public synchronized void close() throws IOException { if (f != null) { FileOutputStream fout = new FileOutputStream(f); DataOutputStream dout = new DataOutputStream(fout); spec.writeChunkFile(dout, cf); dout.flush(); fout.flush(); dout.close(); fout.close(); } } public synchronized Header getHeader() { return cf.getHeader(); } public synchronized void setHeader(Header h) { cf.setHeader(h); } public synchronized boolean add(Chunk ch) throws ChunkAlreadyExistsException { Header h = ch.getHeader(); if (h.containsKey(FieldType.ID_NUMBER) && contains(h.get(FieldType.CHARACTER_TYPE), h.get(FieldType.INTEGER_TYPE), h.get(FieldType.ID_NUMBER))) { throw new ChunkAlreadyExistsException(); } else { cf.add(ch); return true; } } public synchronized boolean contains(Number ctype, Number itype, Number id) { return contains(getChunkIndex(ctype, itype, id)); } public synchronized boolean contains(int index) { return (index >= 0 && index < cf.size()); } public synchronized Chunk get(Number ctype, Number itype, Number id) { return get(getChunkIndex(ctype, itype, id)); } public synchronized Chunk get(int index) { if (index >= 0 && index < cf.size()) { return cf.get(index); } else { return null; } } public synchronized Header getAttributes(Number ctype, Number itype, Number id) { return getAttributes(getChunkIndex(ctype, itype, id)); } public synchronized Header getAttributes(int index) { if (index >= 0 && index < cf.size()) { return cf.get(index).getHeader(); } else { return null; } } public synchronized byte[] getData(Number ctype, Number itype, Number id) { return getData(getChunkIndex(ctype, itype, id)); } public synchronized byte[] getData(int index) { if (index >= 0 && index < cf.size()) { return cf.get(index).getData(); } else { return null; } } public synchronized boolean insert(Number ctype, Number itype, Number id, Chunk ch) throws ChunkAlreadyExistsException { int index = getChunkIndex(ctype, itype, id); return (index < 0) ? add(ch) : insert(index, ch); } public synchronized boolean insert(int index, Chunk ch) throws ChunkAlreadyExistsException { Header h = ch.getHeader(); if (h.containsKey(FieldType.ID_NUMBER) && contains(h.get(FieldType.CHARACTER_TYPE), h.get(FieldType.INTEGER_TYPE), h.get(FieldType.ID_NUMBER))) { throw new ChunkAlreadyExistsException(); } else { if (index < 0) index = 0; if (index > cf.size()) index = cf.size(); cf.add(index, ch); return true; } } public synchronized boolean remove(Number ctype, Number itype, Number id) { return remove(getChunkIndex(ctype, itype, id)); } public synchronized boolean remove(int index) { if (index >= 0 && index < cf.size()) { cf.remove(index); return true; } else { return false; } } public synchronized boolean set(Number ctype, Number itype, Number id, Chunk ch) throws ChunkAlreadyExistsException { return set(getChunkIndex(ctype, itype, id), ch); } public synchronized boolean set(int index, Chunk ch) throws ChunkAlreadyExistsException { if (index >= 0 && index < cf.size()) { Header h = ch.getHeader(); Header eh = cf.get(index).getHeader(); if ( eq(eh.get(FieldType.CHARACTER_TYPE), h.get(FieldType.CHARACTER_TYPE)) && eq(eh.get(FieldType.INTEGER_TYPE), h.get(FieldType.INTEGER_TYPE)) && eq(eh.get(FieldType.ID_NUMBER), h.get(FieldType.ID_NUMBER)) ) { cf.set(index, ch); return true; } else if (h.containsKey(FieldType.ID_NUMBER) && contains(h.get(FieldType.CHARACTER_TYPE), h.get(FieldType.INTEGER_TYPE), h.get(FieldType.ID_NUMBER))) { throw new ChunkAlreadyExistsException(); } else { cf.set(index, ch); return true; } } else { return false; } } public synchronized boolean setAttributes(Number ctype, Number itype, Number id, Header h) throws ChunkAlreadyExistsException { return setAttributes(getChunkIndex(ctype, itype, id), h); } public synchronized boolean setAttributes(int index, Header h) throws ChunkAlreadyExistsException { if (index >= 0 && index < cf.size()) { Header eh = cf.get(index).getHeader(); if ( eq(eh.get(FieldType.CHARACTER_TYPE), h.get(FieldType.CHARACTER_TYPE)) && eq(eh.get(FieldType.INTEGER_TYPE), h.get(FieldType.INTEGER_TYPE)) && eq(eh.get(FieldType.ID_NUMBER), h.get(FieldType.ID_NUMBER)) ) { cf.get(index).setHeader(h); return true; } else if (h.containsKey(FieldType.ID_NUMBER) && contains(h.get(FieldType.CHARACTER_TYPE), h.get(FieldType.INTEGER_TYPE), h.get(FieldType.ID_NUMBER))) { throw new ChunkAlreadyExistsException(); } else { cf.get(index).setHeader(h); return true; } } else { return false; } } private static boolean eq(Number a, Number b) { return (a == null) ? (b == null) : (b == null) ? (a == null) : (a.longValue() == b.longValue()); } public synchronized boolean setData(Number ctype, Number itype, Number id, byte[] data) { return setData(getChunkIndex(ctype, itype, id), data); } public synchronized boolean setData(int index, byte[] data) { if (index >= 0 && index < cf.size()) { cf.get(index).setData(data); return true; } else { return false; } } public synchronized int getChunkCount() { return cf.size(); } public synchronized int getChunkCount(Number ctype, Number itype) { int count = 0; for (int i = 0; i < cf.size(); i++) { Header h = cf.get(i).getHeader(); if (!h.containsKey(FieldType.CHARACTER_TYPE) || (h.get(FieldType.CHARACTER_TYPE).longValue() == ctype.longValue())) { if (!h.containsKey(FieldType.INTEGER_TYPE) || (h.get(FieldType.INTEGER_TYPE).longValue() == itype.longValue())) { count++; } } } return count; } public synchronized Number[] getChunkIDs(Number ctype, Number itype) { Vector<Number> ids = new Vector<Number>(); long localIndex = 0; for (int i = 0; i < cf.size(); i++) { Header h = cf.get(i).getHeader(); if (!h.containsKey(FieldType.CHARACTER_TYPE) || (h.get(FieldType.CHARACTER_TYPE).longValue() == ctype.longValue())) { if (!h.containsKey(FieldType.INTEGER_TYPE) || (h.get(FieldType.INTEGER_TYPE).longValue() == itype.longValue())) { if (h.containsKey(FieldType.ID_NUMBER)) { ids.add(h.get(FieldType.ID_NUMBER)); } else { ids.add(localIndex); } localIndex++; } } } return ids.toArray(new Number[0]); } public synchronized Number getChunkID(Number ctype, Number itype, int index) { long localIndex = 0; for (int i = 0; i < cf.size(); i++) { Header h = cf.get(i).getHeader(); if (!h.containsKey(FieldType.CHARACTER_TYPE) || (h.get(FieldType.CHARACTER_TYPE).longValue() == ctype.longValue())) { if (!h.containsKey(FieldType.INTEGER_TYPE) || (h.get(FieldType.INTEGER_TYPE).longValue() == itype.longValue())) { if (index == 0) { if (h.containsKey(FieldType.ID_NUMBER)) return h.get(FieldType.ID_NUMBER); else return localIndex; } else { localIndex++; index--; } } } } return null; } public synchronized int getChunkIndex(Number ctype, Number itype, Number id) { long localIndex = 0; for (int i = 0; i < cf.size(); i++) { Header h = cf.get(i).getHeader(); if (!h.containsKey(FieldType.CHARACTER_TYPE) || (h.get(FieldType.CHARACTER_TYPE).longValue() == ctype.longValue())) { if (!h.containsKey(FieldType.INTEGER_TYPE) || (h.get(FieldType.INTEGER_TYPE).longValue() == itype.longValue())) { if (h.containsKey(FieldType.ID_NUMBER)) { if (h.get(FieldType.ID_NUMBER).longValue() == id.longValue()) { return i; } } else { if (localIndex == id.longValue()) { return i; } } localIndex++; } } } return -1; } public synchronized Number getNextAvailableID(Number ctype, Number itype) { return getNextAvailableID(ctype, itype, 0); } public synchronized Number getNextAvailableID(Number ctype, Number itype, Number start) { Vector<Long> ids = new Vector<Long>(); long localIndex = 0; for (int i = 0; i < cf.size(); i++) { Header h = cf.get(i).getHeader(); if (!h.containsKey(FieldType.CHARACTER_TYPE) || (h.get(FieldType.CHARACTER_TYPE).longValue() == ctype.longValue())) { if (!h.containsKey(FieldType.INTEGER_TYPE) || (h.get(FieldType.INTEGER_TYPE).longValue() == itype.longValue())) { if (h.containsKey(FieldType.ID_NUMBER)) { ids.add(h.get(FieldType.ID_NUMBER).longValue()); } else { ids.add(localIndex); } localIndex++; } } } long next = start.longValue(); while (ids.contains(next)) next++; if (spec.chunkHeaderSpec().containsType(FieldType.ID_NUMBER)) { switch (spec.chunkHeaderSpec().getField(FieldType.ID_NUMBER).size()) { case BYTE: return (byte)next; case SHORT: return (short)next; case MEDIUM: return (int)next; case LONG: return (long)next; default: return next; } } else { return next; } } }