/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2003 Rob Nielsen * * Copyright (C) 2003-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.io; import totalcross.util.Vector; /** * An extension to PDBFile that allows storage and retrieval of objects that * implement the Storable interface. Create an ObjectPDBFile and use the * addObject() method on the objects you want to store. If you want a particular * object you can use loadObjectAt() to load the stored details into an object * or to search through all records call resetSearch() and then loop with * nextObject() until it returns false. The example below shows an example of * it's use with a PDBFile of identical data: * <p> * <blockquote> * * <pre> * ObjectPDBFile oc = new ObjectPDBFile("Test.DATA"); * MyObject obj = new MyObject(); * oc.resetSearch(); * while (oc.nextObject(obj)) * { * // do something with obj * } * </pre> * * </blockquote> * <p> * Here's an example using unknown data. The two sections of code save a vector * containing a number of Lines, Circles, and Squares (all implementing * Storable) in no particular order, then loads it back in again. * <p> * <blockquote> * * <pre> * // save data * ObjectPDBFile oc=new ObjectPDBFile("Test.DATA",ObjectPDBFile.CREATE); * for(int i=0,size=objs.getCount();i++) * oc.addObject((Storable)objs.get(i)); * oc.close(); * * // load data * ObjectPDBFile oc=new ObjectPDBFile("Test.DATA"); * oc.registerClass(new Line()); * oc.registerClass(new Circle()); * oc.registerClass(new Square()); * objs=new Vector(); * oc.resetSearch(); * Storable obj; * while ((obj=oc.nextObject())!=null) * { * objs.add(obj); * } * oc.close(); * </pre> * * </blockquote> * * @author <A HREF="mailto:rnielsen@cygnus.uwa.edu.au">Robert Nielsen</A>, * @version 1.2.0 16 October 1999 */ public class ObjectPDBFile extends PDBFile { /* the registered classes */ protected Vector classes; /* the position in the search through the records */ protected int cnt; protected byte[] buf; protected ByteArrayStream bs; protected DataStream ds; /** * Constructs a new ObjectPDBFile * * @param name * the name of the PDBFile * @param type * the mode to open the PDBFile with * @throws totalcross.io.IOException * @throws totalcross.io.FileNotFoundException * @throws totalcross.io.IllegalArgumentIOException */ public ObjectPDBFile(String name, int type) throws totalcross.io.IllegalArgumentIOException, totalcross.io.FileNotFoundException, totalcross.io.IOException { super(name, type); } /** * Construct a new ObjectPDBFile * * @param name * the name of the PDBFile * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public ObjectPDBFile(String name) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { super(name, PDBFile.CREATE); } /** * Registers this class with the PDBFile. Classes must be registered before * using the loadObjectAt(int) method. * * @param s * an instance of the class to register. The contents are ignored. */ public void registerClass(Storable s) { if (classes == null) classes = new Vector(); classes.addElement(s); } /** * Add an object to this PDBFile. * * @param s * the storable object to add * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public void addObject(Storable s) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { if (bs == null) { bs = new ByteArrayStream(1024); ds = new DataStream(bs); } else bs.reset(); if (s.getID() != 0) ds.writeByte(s.getID()); s.saveState(ds); byte[] buf = bs.getBuffer(); addRecord(buf.length); writeBytes(buf, 0, buf.length); setRecordPos(-1); } /** * Insert an object to this PDBFile. * * @param s * the storable object to add * @param i * the index where to insert * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public void insertObjectAt(Storable s, int i) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { if (bs == null) { bs = new ByteArrayStream(1024); ds = new DataStream(bs); } else bs.reset(); if (s.getID() != 0) ds.writeByte(s.getID()); s.saveState(ds); byte[] buf = bs.getBuffer(); addRecord(buf.length, i); writeBytes(buf, 0, buf.length); setRecordPos(-1); } /** * Load an object from the PDBFile into the given storable. Unpredictable * results will occur if the object in the PDBFile is not of the same class * as the storable given. Good for when you know what each record will * contain. * * @param s * the object to load the data into * @param i * the index in the PDBFile to load from * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public void loadObjectAt(Storable s, int i) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { // bs=null; // buf=null; setRecordPos(i); int size = getRecordSize(); if (buf == null || buf.length < size) buf = new byte[size]; readBytes(buf, 0, size); setRecordPos(-1); if (bs == null) { bs = new ByteArrayStream(buf); ds = new DataStream(bs); } else bs.setBuffer(buf); if (s.getID() != 0) ds.readByte(); s.loadState(ds); } /** * Loads an object from the PDBFile. Good for when you don't know which * classes are going to be in records. Note that you must call the * registerClass() with each storable class before this method will work * properly. * * @param i * the index in the PDBFile to load from * @return the loaded object, or null if unsucessful * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public Storable loadObjectAt(int i) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { Storable s = null; setRecordPos(i); if (classes != null) { int recsize = getRecordSize(); if (buf == null || buf.length < recsize) buf = new byte[recsize]; readBytes(buf, 0, recsize); if (bs == null) { bs = new ByteArrayStream(buf); ds = new DataStream(bs); } else bs.setBuffer(buf); setRecordPos(-1); byte type = ds.readByte(); for (int j = 0, size = classes.size(); j < size; j++) if ((s = (Storable) classes.items[j]).getID() == type) { s = s.getInstance(); s.loadState(ds); break; } } return s; } /** * Delete an object from the PDBFile * * @param i * the index to delete from * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public void deleteObjectAt(int i) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { setRecordPos(i); deleteRecord(); } /** * Set attributes of an object record from the PDBFile * * @param i * the index to set the attribute * @param a * the attribute to set, use the REC_ATTR_XXXX constants from * PDBFile class * @return true if sucessful, false otherwise * @throws totalcross.io.IOException */ public boolean setObjectAttribute(int i, byte a) throws totalcross.io.IOException { setRecordAttributes(i, a); return true; } /** * Get attributes of an object record from the PDBFile * * @param i * the index to get the attribute from * @return the record attributes * @throws totalcross.io.IOException */ public byte getObjectAttribute(int i) throws totalcross.io.IOException { return getRecordAttributes(i); } /** * Get the size of this PDBFile * * @return the number of records contained by it * @throws totalcross.io.IOException */ public int size() throws totalcross.io.IOException { return getRecordCount(); } /** * Resets a counter for iterating through the PDBFile. Should be called * before iterating with nextObject(). */ public void resetSearch() { setSearchIndex(0); } /** * Sets the search counter at the given index in the PDBFile. * * @param i * the index to start */ public void setSearchIndex(int i) { cnt = i; } /** * Gets the next object in the PDBFile and places it in the given storable. * * @return true if sucessful, false if the end of the PDBFile has been * reached * @throws totalcross.io.IOException * @throws totalcross.io.IllegalArgumentIOException */ public boolean nextObject(Storable s) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException { boolean ret = cnt < size(); if (ret) loadObjectAt(s, cnt++); return ret; } /** * Gets the next object in the PDBFile. * * @return the next object, or null on error or if the end has been reached * @throws totalcross.io.IOException */ public Storable nextObject() throws totalcross.io.IOException { return cnt < size() ? loadObjectAt(cnt++) : null; } }