001: /*
002: *
003: * Copyright (c) 2007, Sun Microsystems, Inc.
004: *
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * * Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * * Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * * Neither the name of Sun Microsystems nor the names of its contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package example.stock;
033:
034: import java.io.EOFException;
035:
036: import java.util.*;
037:
038: import javax.microedition.rms.*;
039:
040: /**
041: * <p>This class provides a wrapper class for the
042: * <code>RecordStore</code> class.
043: * It allows for easier addition and deletion as well as better searching and
044: * updating of records. The used recordIDs are kept in a <code>Vector</code>
045: * which we use to access the indices of the records. The last used recordID
046: * is stored at the beginning of the database and when the database is opened,
047: * each recordID up to the last one used is tested to see if a record exists in
048: * that position and a new <code>Vector</code> of used recordIDs
049: * is generated.</p>
050: */
051: public abstract class Database {
052: /**
053: * The database storing all the records and the last
054: * used recordID in position 1
055: */
056: protected volatile RecordStore database = null;
057:
058: /**
059: * The <code>Vector</code> of used recordIDs that are in the database
060: */
061: protected volatile Vector recordIDs = null;
062:
063: /**
064: * The last used ID in the database
065: */
066: protected int lastID = 1;
067:
068: /**
069: * The object used to compare two records and see if they are equal
070: */
071: protected RecordComparator rc = null;
072:
073: /**
074: * <p>Initializes the database and if it's not a new database, loads the
075: * recordID of the last record out of the first position in the
076: * <code>RecordStore</code>. We have stored it there when we closed the
077: * database, then checks each ID from 1 to lastID to see if they exist in
078: * the database and then add the IDs that exist to the recordIDs
079: * <code>Vector</code></p>
080: *
081: * @param fileName The name of the <code>RecordStore</code> to open
082: * @throws <code>RecordStoreNotFoundException</code> is thrown if the
083: * <code>RecordStore</code> indicated with <code>fileName</code>
084: * cannot be found
085: * @throws <code>RecordStoreException</code> is thrown when a general
086: * exception occurs in a <code>RecordStore</code> operation
087: * @throws <code>RecordStoreFullException</code> is thrown when the
088: * storage system is is full
089: */
090: public void open(String fileName)
091: throws RecordStoreNotFoundException, RecordStoreException,
092: RecordStoreFullException {
093: database = RecordStore.openRecordStore(fileName, true);
094: recordIDs = new Vector();
095:
096: try {
097: if (database.getNumRecords() != 0) {
098: try {
099: lastID = Integer.valueOf(
100: new String(database.getRecord(1)))
101: .intValue();
102:
103: for (int i = 1; i <= lastID; i++) {
104: try {
105: database.getRecord(i);
106: recordIDs.addElement(new Integer(i));
107: } catch (RecordStoreException rs) {
108: }
109: }
110: } catch (InvalidRecordIDException iri) {
111: throw new RecordStoreException(iri.getMessage());
112: }
113: }
114: } catch (RecordStoreNotOpenException rsno) {
115: throw new RecordStoreException(rsno.getMessage());
116: }
117: }
118:
119: /**
120: * <p>Close the database and remove it from persistant
121: * storage if it is empty</p>
122: *
123: * @throws <code>RecordStoreNotOpenException</code> is thrown when trying
124: * to close a <code>RecordStore</code> that is not open
125: * @throws <code>RecordStoreException</code> is thrown when a general
126: * exception occurs in a <code>RecordStore</code> operation
127: */
128: public void close() throws RecordStoreNotOpenException,
129: RecordStoreException {
130: if (database.getNumRecords() == 0) {
131: String fileName = database.getName();
132: database.closeRecordStore();
133: database.deleteRecordStore(fileName);
134: } else {
135: database.closeRecordStore();
136: }
137: }
138:
139: /**
140: * <p>Remove the database from persistant storage</p>
141: *
142: * @param fileName the name of the <code>RecordStore</code> to remove
143: */
144: public void cleanUp(String fileName)
145: throws RecordStoreNotFoundException, RecordStoreException {
146: RecordStore.deleteRecordStore(fileName);
147: open(fileName);
148: }
149:
150: /**
151: * <p>Add the record to the database<BR>
152: * Add the recordID to our vector<BR>
153: * Update the database's last ID counter</p>
154: *
155: * @param record The record data to be added to the database
156: * @throws <code>RecordStoreNotOpenException</code> is thrown when
157: * trying to close a <code>RecordStore</code> that is not open
158: * @throws <code>RecordStoreFullException</code> is thrown when the storage
159: * system is is full
160: * @throws <code>RecordStoreException</code> is thrown when a general
161: * exception occurs in a <code>RecordStore</code> operation
162: */
163: public synchronized void add(String record)
164: throws RecordStoreNotOpenException,
165: RecordStoreFullException, RecordStoreException {
166: if (database.getNumRecords() != 0) {
167: database.addRecord(record.getBytes(), 0,
168: record.getBytes().length);
169: recordIDs.addElement(new Integer(++lastID));
170: database.setRecord(1, (String.valueOf(lastID)).getBytes(),
171: 0, (String.valueOf(lastID)).length());
172: } else {
173: recordIDs.addElement(new Integer(++lastID));
174: database.addRecord((String.valueOf(lastID)).getBytes(), 0,
175: (String.valueOf(lastID)).length());
176:
177: try {
178: database.addRecord(record.getBytes(), 0, record
179: .getBytes().length);
180: } catch (RecordStoreException rs) {
181: recordIDs.removeElement(new Integer(lastID--));
182: database.setRecord(1, (String.valueOf(lastID))
183: .getBytes(), 0, (String.valueOf(lastID))
184: .length());
185: throw rs;
186: }
187: }
188: }
189:
190: /**
191: * <p>Delete the record from the database and remove that recordID from the
192: * vector of used recordIDs</p>
193: *
194: * @param s The name of the record to delete from the database
195: * @throws <code>RecordStoreNotOpenException</code> is thrown when trying
196: * to close a <code>RecordStore</code> that is not open
197: * @throws <code>RecordStoreException</code> is thrown when a general
198: * exception occurs in a <code>RecordStore</code> operation
199: */
200: public synchronized void delete(String s)
201: throws RecordStoreNotOpenException, RecordStoreException {
202: action(s, null, 0);
203: }
204:
205: /**
206: * <p>Find and return a record</p>
207: *
208: * @return The record that we're looking for or
209: * <code>null</code> if not found
210: * @param s The name of the record to search for
211: * @throws <code>RecordStoreNotOpenException</code> is thrown when trying
212: * to close a <code>RecordStore</code> that is not open
213: * @throws <code>RecordStoreException</code> is thrown when a general
214: * exception occurs in a <code>RecordStore</code> operation
215: */
216: public synchronized String search(String s)
217: throws RecordStoreNotOpenException, RecordStoreException {
218: return (String) action(s, null, 1);
219: }
220:
221: /**
222: * <p>Update the record with the name <code>s</code> with the data
223: * in the byte[] array</p>
224: *
225: * @param s The name of the record to update
226: * @param data the new data to update the record with
227: * @throws <code>RecordStoreNotOpenException</code> is thrown when trying
228: * to close a <code>RecordStore</code> that is not open
229: * @throws <code>RecordStoreFullException</code> is thrown when the storage
230: * system is is full
231: * @throws <code>RecordStoreException</code> is thrown when a general
232: * exception occurs in a <code>RecordStore</code> operation
233: */
234: public synchronized void update(String s, byte[] data)
235: throws RecordStoreNotOpenException,
236: RecordStoreFullException, RecordStoreException {
237: action(s, data, 2);
238: }
239:
240: /**
241: * <p>Go to the index of the record specified by <code>s</code> and perform
242: * an action. Either an update, search or deletion. This method is for
243: * code compaction as the process for updating, searching and
244: * deleting varies only slightly.</p>
245: *
246: * @param s The name of the record to perform the action on
247: * @param data Data to use in the action
248: * @param action What to do. 0 = delete, 1 = search, 2 = update
249: * @throws <code>RecordStoreNotOpenException</code> is thrown when trying
250: * to close a <code>RecordStore</code> that is not open
251: * @throws <code>RecordStoreFullException</code> is thrown when the storage
252: * system is is full
253: * @throws <code>RecordStoreException</code> is thrown when a general
254: * exception occurs in a <code>RecordStore</code> operation
255: */
256: private synchronized Object action(String s, byte[] data, int action)
257: throws RecordStoreNotOpenException,
258: RecordStoreFullException, RecordStoreException {
259: if ((action != 1) && (recordIDs.size() == 0)) {
260: throw new RecordStoreException();
261: }
262:
263: Enumeration IDs = recordIDs.elements();
264:
265: while (IDs.hasMoreElements()) {
266: int index = ((Integer) IDs.nextElement()).intValue();
267:
268: try {
269: if (rc.compare(database.getRecord(index), s.getBytes()) == RecordComparator.EQUIVALENT) {
270: switch (action) {
271: case 0:
272: database.deleteRecord(index);
273: recordIDs.removeElement(new Integer(index));
274:
275: return null;
276:
277: case 1:
278: return new String(database.getRecord(index));
279:
280: case 2:
281: database.setRecord(index, data, 0, data.length);
282:
283: return null;
284:
285: default:
286: break;
287: }
288: }
289: } catch (InvalidRecordIDException iri) {
290: throw new RecordStoreException(iri.getMessage());
291: }
292: }
293:
294: return null;
295: }
296:
297: /**
298: * <p>Return the number of records in the database</p>
299: *
300: * @return the number of records in the database
301: * @throws <code>RecordStoreNotOpenException</code> is thrown when trying
302: * to close a <code>RecordStore</code> that is not open
303: */
304: public int getNumRecords() throws RecordStoreNotOpenException {
305: return database.getNumRecords();
306: }
307: }
|