modules/meep-rms/src/main/java/javax/microedition/rms/RecordStore.java
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
// Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------
package javax.microedition.rms;
import cc.squirreljme.runtime.cldc.annotation.Api;
import cc.squirreljme.runtime.cldc.annotation.ApiDefinedDeprecated;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import cc.squirreljme.runtime.midlet.ApplicationHandler;
import cc.squirreljme.runtime.rms.SuiteIdentifier;
import cc.squirreljme.runtime.rms.TemporaryVinylRecord;
import cc.squirreljme.runtime.rms.VinylLock;
import cc.squirreljme.runtime.rms.VinylRecord;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.multiphasicapps.collections.IdentityLinkedHashSet;
/**
* This is a record store which may be used by an application to store
* information about it in an implementation defined manner.
*
* Opened record stores have an open count, as such for every open operation
* there must be a close operation.
*
* Whenever the record store is modified, the version number will be
* incremented.
*
* Record stores may optionally permit other suites to access and potentially
* write their records, otherwise only the current suite may modify its own
* records.
*
* @since 2017/02/26
*/
@Api
@SuppressWarnings("DuplicateThrows")
public class RecordStore
implements AutoCloseable
{
/** Specifies that any suite may access the records. */
@Api
public static final int AUTHMODE_ANY =
1;
/** Specifies that only record store creator may access the records. */
@Api
public static final int AUTHMODE_PRIVATE =
0;
/** The vinyl record where everything is stored. */
static final VinylRecord _VINYL;
/** Existing record stores. */
static final Map<Integer, RecordStore> _STORE_CACHE =
new LinkedHashMap<>();
/** Identity map for listeners */
private final Set<RecordListener> _listeners =
new IdentityLinkedHashSet<>();
/** The volume ID. */
private final int _vid;
/** The name. */
private final String _name;
/** Write to this? */
private final boolean _write;
/** How many times has this been opened? */
private volatile int _opens;
/**
* Initializes the record store manager.
*
* @since 2017/02/27
*/
static
{
// See if there is a service, this will fall back to an implementation
// that is not shared and will only exist as long as the current
// program is running
VinylRecord vr;
try
{
Debugging.todoNote("Implement storage backed RMS.");
String vclass = null;//Debugging.<String>todoObject();
vr = (vclass == null ? new TemporaryVinylRecord() :
(VinylRecord)Class.forName(vclass).newInstance());
}
// If it fails to initialize, just use a blank one
catch (ClassNotFoundException|IllegalAccessException|
InstantiationException e)
{
vr = new TemporaryVinylRecord();
}
// Set
_VINYL = vr;
}
/**
* Initializes the access to the record store.
*
* @param __vid The volume ID.
* @param __name The name.
* @param __w Write to this?
* @throws NullPointerException On null arguments.
* @since 2019/04/14
*/
@Api
private RecordStore(int __vid, String __name, boolean __w)
throws NullPointerException
{
if (__name == null)
throw new NullPointerException("NARG");
this._vid = __vid;
this._name = __name;
this._write = __w;
this._opens = 1;
}
/**
* Adds the specified record to the record store and returns the record
* id for the newly added record.
*
* @param __b The data to store.
* @param __o The offset into the array.
* @param __l The length of the array.
* @param __tag The tag to identify the given record with.
* @return The record ID of the newly created record.
* @throws ArrayIndexOutOfBoundsException If the offset and/or length
* are negative or exceed the array bounds.
* @throws NullPointerException On null arguments.
* @throws RecordStoreNotOpenException If the record store is not open.
* @throws RecordStoreException If some other error occurs.
* @throws RecordStoreFullException If there is not enough space to store
* the data.
* @since 2017/02/26
*/
@Api
public int addRecord(byte[] __b, int __o, int __l, int __tag)
throws ArrayIndexOutOfBoundsException, NullPointerException,
RecordStoreNotOpenException, RecordStoreException,
RecordStoreFullException
{
if (__b == null)
throw new NullPointerException("NARG");
if (__o < 0 || __l < 0 || (__o + __l) < 0 || (__o + __l) > __b.length)
throw new ArrayIndexOutOfBoundsException("IOOB");
/* {@squirreljme.error DC01 Cannot write record to read-only store.} */
if (!this._write)
throw new RecordStoreException("DC01");
// Used for later
int rv;
RecordListener[] listeners = this.__listeners();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Add the page
rv = vinyl.pageAdd(this._vid, __b, __o, __l, __tag);
RecordStore.__checkError(rv);
}
// Report to the listeners
for (RecordListener l : listeners)
l.recordAdded(this, rv);
return rv;
}
/**
* Calls {@code addRecord(__b, __o, __l, 0)}.
*
* @param __b As forwarded.
* @param __o As forwarded.
* @param __l As forwarded.
* @return As forwarded.
* @throws ArrayIndexOutOfBoundsException As forwarded.
* @throws NullPointerException As forwarded.
* @throws RecordStoreNotOpenException As forwarded.
* @throws RecordStoreException As forwarded.
* @throws RecordStoreFullException As forwarded.
* @since 2017/02/26
*/
@Api
public int addRecord(byte[] __b, int __o, int __l)
throws ArrayIndexOutOfBoundsException, NullPointerException,
RecordStoreNotOpenException, RecordStoreException,
RecordStoreFullException
{
return this.addRecord(__b, __o, __l, 0);
}
/**
* Adds a record listener to the given store to notify of when changes
* are made to records.
*
* If the record store is closed then this has no effect.
*
* @param __l The listener to call for changes, a listener which has
* already been added will not be added a second time.
* @since 2017/02/26
*/
@Api
public void addRecordListener(RecordListener __l)
{
// Ignore
if (__l == null)
return;
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// No effect if closed
if (this._opens <= 0)
return;
// Add listener
Set<RecordListener> listeners = this._listeners;
synchronized (listeners)
{
listeners.add(__l);
}
}
}
/**
* {@inheritDoc}
* @since 2017/02/26
*/
@Override
public void close()
throws RecordStoreNotOpenException, RecordStoreException
{
this.closeRecordStore();
}
/**
* Closes the record store.
*
* Note that due to the ability to have record stores opened multiple times
* the open count must reach zero before it is actually closed.
*
* When the store is fully closed all listeners will be removed.
*
* @throws RecordStoreNotOpenException If the record is not open.
* @throws RecordStoreException If there was an issue closing it.
* @since 2017/02/26
*/
@Api
public void closeRecordStore()
throws RecordStoreNotOpenException, RecordStoreException
{
// Lock the record, so that only a single thread is messing with the
// open counts and such
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// If closed then remove all the listeners
if ((--this._opens) <= 0)
this._listeners.clear();
}
}
/**
* Deletes the specified record.
*
* @param __id The record to delete.
* @throws InvalidRecordIDException If the record ID is not valid.
* @throws RecordStoreNotOpenException If the record store is not open.
* @throws RecordStoreException If there was an issue deleting the record.
* @throws SecurityException If the record cannot be deleted.
* @since 2017/02/26
*/
@Api
public void deleteRecord(int __id)
throws InvalidRecordIDException, RecordStoreNotOpenException,
RecordStoreException, SecurityException
{
// Used later
RecordListener[] listeners = this.__listeners();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Delete it
int rv = vinyl.pageDelete(this._vid, __id);
RecordStore.__checkError(rv);
}
// Report to the listeners
for (RecordListener l : listeners)
l.recordDeleted(this, __id);
}
/**
* Enumerates through the records that exist within this store.
*
* If a comparator is not specified then the traversal order is not
* defined.
*
* @param __f An optional filter used to filter records, may be
* {@code null}.
* @param __c An optional comparator used to modify the sort order, may
* be {@code null}.
* @param __ku If {@code true} then the enumeration is kept up to date.
* @param __tags The tags to use for basic filtering, if this is empty then
* an empty enumeration will be returned, if this is {@code null} then all
* tags will be selected.
* @return The enumeration over the records.
* @throws RecordStoreNotOpenException If this record store is not open.
* @since 2017/02/26
*/
@Api
public RecordEnumeration enumerateRecords(RecordFilter __f,
RecordComparator __c, boolean __ku, int[] __tags)
throws RecordStoreNotOpenException
{
// Check open
this.__checkOpen();
// Build one and perform a rebuild to initialize it
__VolumeEnumeration__ rv = new __VolumeEnumeration__(this, __f,
__c, __ku, __tags);
rv.rebuild();
// Use it
return rv;
}
/**
* Calls {@code enumerateRecords(__f, __c, __ku, null)}.
*
* @param __f As forwarded.
* @param __c As forwarded.
* @param __ku As forwarded.
* @return As forwarded.
* @throws RecordStoreNotOpenException As forwarded.
* @since 2017/02/26
*/
@Api
public RecordEnumeration enumerateRecords(RecordFilter __f,
RecordComparator __c, boolean __ku)
throws RecordStoreNotOpenException
{
return this.enumerateRecords(__f, __c, __ku, null);
}
/**
* Returns the last modification date of the record store.
*
* @return The last modification date of the record store.
* @throws RecordStoreNotOpenException If this record store is not open.
* @since 2017/02/26
*/
@Api
public long getLastModified()
throws RecordStoreNotOpenException
{
// Check open
this.__checkOpen();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
long[] time = new long[1];
int rv = vinyl.volumeModTime(this._vid, time);
try
{
RecordStore.__checkError(rv);
}
catch (RecordStoreException e)
{
if (e instanceof RecordStoreNotOpenException)
throw (RecordStoreNotOpenException)e;
/* {@squirreljme.error DC02 Could not get the record store
time.} */
throw new RuntimeException("DC02", e);
}
return time[0];
}
}
/**
* Returns the name of the record store.
*
* @return The name of the record store.
* @throws RecordStoreNotOpenException If this record store is not open.
* @since 2017/02/26
*/
@Api
public String getName()
throws RecordStoreNotOpenException
{
// Check open
this.__checkOpen();
return this._name;
}
/**
* This returns the next record ID which would be used if a new record
* were to be added to this record store.
*
* The returned ID is only valid while the store remains open and before
* {@code addRecord()} is called.
*
* @return The next record ID.
* @throws RecordStoreException If there was another issue with the
* record store.
* @throws RecordStoreNotOpenException If this record store is not open.
* @since 2017/02/26
*/
@Api
public int getNextRecordID()
throws RecordStoreException, RecordStoreNotOpenException
{
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Get next ID as a guess
int rv = vinyl.pageNextId(this._vid);
RecordStore.__checkError(rv);
return rv;
}
}
/**
* Returns the number of records in this store.
*
* @return The number of records in this store.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2019/05/09
*/
@Api
public int getNumRecords()
throws RecordStoreNotOpenException
{
// Check open
this.__checkOpen();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Get record list
int[] pages = vinyl.pageList(this._vid);
// Check for error
if (pages.length > 0)
try
{
RecordStore.__checkError(pages[0]);
}
catch (RecordStoreNotOpenException e)
{
throw e;
}
catch (RecordStoreException e)
{
/* {@squirreljme.error DC03 Error getting list of
records.} */
RecordStoreNotOpenException t =
new RecordStoreNotOpenException("DC03");
t.initCause(e);
throw t;
}
// Return array size
return pages.length;
}
}
/**
* Returns a copy of the data which is stored in the given record.
*
* @param __id The ID of the record to get.
* @return A copy of the data stored in this record, if there is no data
* then this will return {@code null}.
* @throws InvalidRecordIDException If the ID is not valid.
* @throws RecordStoreException If there is a problem with the record
* store.
* @throws RecordStoreNotOpenException If this record store is not open.
* @since 2017/02/26
*/
@Api
public byte[] getRecord(int __id)
throws InvalidRecordIDException, RecordStoreException,
RecordStoreNotOpenException
{
// Check open
this.__checkOpen();
// This volume
int vid = this._vid;
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Need to know the size of the record
int size = vinyl.pageSize(vid, __id);
RecordStore.__checkError(size);
// Allocate data to read from it
byte[] rv = new byte[size];
// Read data
int read = vinyl.pageRead(vid, __id, rv, 0, size);
RecordStore.__checkError(read);
return rv;
}
}
/**
* Fills the specified array with a copy of the data within the given
* record.
*
* @param __id The ID of the record to get.
* @param __b The array to write data to.
* @param __o The offset into the array.
* @return The number of bytes copied into the array.
* @throws ArrayIndexOutOfBoundsException If the offset is negative or
* the record data exceeds the size of the output array.
* @throws InvalidRecordIDException If the record ID is not valid.
* @throws NullPointerException On null arguments.
* @throws RecordStoreException If another problem occurs with the record
* store.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2017/02/26
*/
@Api
public int getRecord(int __id, byte[] __b, int __o)
throws ArrayIndexOutOfBoundsException, InvalidRecordIDException,
NullPointerException, RecordStoreException,
RecordStoreNotOpenException
{
if (__b == null)
throw new NullPointerException("NARG");
if (__o < 0)
throw new ArrayIndexOutOfBoundsException("IOOB");
// This volume
int vid = this._vid;
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Need to know the size of the record
int size = vinyl.pageSize(vid, __id);
RecordStore.__checkError(size);
/* {@squirreljme.error DC04 The record does not fit into the
output.} */
if (size < 0 || (__o + size) > __b.length)
throw new ArrayIndexOutOfBoundsException("DC04");
// Read data
int read = vinyl.pageRead(vid, __id, __b, __o, size);
RecordStore.__checkError(read);
// Size is used as the return value
return size;
}
}
/**
* Returns the size of the given record.
*
* @param __id The record ID to get the size for.
* @return The size of the given record.
* @throws InvalidRecordIDException If the record ID is not valid.
* @throws RecordStoreException If another problem occurs with the record
* store.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2016/02/26
*/
@Api
public int getRecordSize(int __id)
throws InvalidRecordIDException, RecordStoreException,
RecordStoreNotOpenException
{
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Need to know the size of the record
int size = vinyl.pageSize(this._vid, __id);
RecordStore.__checkError(size);
// Return it
return size;
}
}
/**
* Returns the record store information.
*
* @return The record store information.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2016/02/26
*/
@Api
public RecordStoreInfo getRecordStoreInfo()
throws RecordStoreNotOpenException
{
// Check open
this.__checkOpen();
// Just quickly create
return new RecordStoreInfo(this._vid);
}
/**
* Returns the size of the record store.
*
* @return The record store size, not to exceed {@link Integer#MAX_VALUE}.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2016/02/26
*/
@Api
@ApiDefinedDeprecated
public int getSize()
throws RecordStoreNotOpenException
{
return (int)Math.min(Integer.MAX_VALUE,
this.getRecordStoreInfo().getSize());
}
/**
* Returns the available size of the record store.
*
* @return The available record store size, not to exceed
* {@link Integer#MAX_VALUE}.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2016/02/26
*/
@Api
@ApiDefinedDeprecated
public int getSizeAvailable()
throws RecordStoreNotOpenException
{
return (int)Math.min(Integer.MAX_VALUE,
this.getRecordStoreInfo().getSizeAvailable());
}
/**
* Returns the tag of the given record.
*
* @param __id The record ID to get the tag for.
* @return The tag of the given record.
* @throws InvalidRecordIDException If the record ID is not valid.
* @throws RecordStoreException If another problem occurs with the record
* store.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2016/02/26
*/
@Api
public int getTag(int __id)
throws InvalidRecordIDException, RecordStoreException,
RecordStoreNotOpenException
{
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Get and check tag
int rv = vinyl.pageTag(this._vid, __id);
RecordStore.__checkError(rv);
return rv;
}
}
/**
* Returns the version of the record store, this may be used to quickly
* determine if a store has been modified.
*
* @return The version of this record store.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2016/02/26
*/
@Api
public int getVersion()
throws RecordStoreNotOpenException
{
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
int rv = vinyl.volumeModCount(this._vid);
try
{
RecordStore.__checkError(rv);
}
catch (RecordStoreException e)
{
if (e instanceof RecordStoreNotOpenException)
throw (RecordStoreNotOpenException)e;
/* {@squirreljme.error DC05 Could not get the record store
version.} */
throw new RuntimeException("DC05", e);
}
return rv;
}
}
/**
* Removes the specified record listener, this has no effect if it has
* already been removed or was never added.
*
* @param __l The record listener to remove.
* @since 2017/02/26
*/
@Api
public void removeRecordListener(RecordListener __l)
{
// Ignore
if (__l == null)
return;
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// No effect if closed
if (this._opens <= 0)
return;
// Remove listener
Set<RecordListener> listeners = this._listeners;
synchronized (listeners)
{
listeners.remove(__l);
}
}
}
/**
* Sets the mode of the record store which permits or denies other suites
* access to this record store.
*
* This may only operate on fully closed record stores and no other
* suites must have this record store open when this is called.
*
* @param __auth The authorization mode to use.
* @param __write Whether writing should be permitted.
* @throws IllegalArgumentException If the authorization mode is not
* valid.
* @throws IllegalStateException If the record store is opened by any
* application.
* @throws RecordStoreException If some other problem occurs with the
* record store.
* @throws SecurityException If changing the mode is not permitted.
* @since 2017/02/26
*/
@Api
public void setMode(int __auth, boolean __write)
throws IllegalArgumentException, IllegalStateException,
RecordStoreException, SecurityException
{
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
throw Debugging.todo();
}
}
/**
* Sets the data for a record.
*
* @param __id The record ID to set.
* @param __b The input data.
* @param __o The offset into the array
* @param __l The number of bytes to write.
* @param __tag The new tag to set for the record, this replaces the
* old tag.
* @throws ArrayIndexOutOfBoundsException If the offset and/or length are
* negative or exceed the array bounds.
* @throws InvalidRecordIDException If the record ID is not valid.
* @throws NullPointerException On null arguments.
* @throws RecordStoreException If another unspecified error happens.
* @throws RecordStoreFullException If there is not enough space to store
* the data.
* @throws RecordStoreNotOpenException If the record store is not open.
* @since 2017/02/26
*/
@Api
public void setRecord(int __id, byte[] __b, int __o, int __l, int __tag)
throws ArrayIndexOutOfBoundsException, InvalidRecordIDException,
NullPointerException, RecordStoreException,
RecordStoreFullException, RecordStoreNotOpenException
{
if (__b == null)
throw new NullPointerException("NARG");
if (__o < 0 || __l < 0 || (__o + __l) < 0 || (__o + __l) > __b.length)
throw new ArrayIndexOutOfBoundsException("IOOB");
/* {@squirreljme.error DC06 Cannot write record to read-only store.} */
if (!this._write)
throw new RecordStoreException("DC06");
// Used for later
RecordListener[] listeners = this.__listeners();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
// Set the page
__id = vinyl.pageSet(this._vid, __id, __b, __o, __l, __tag);
RecordStore.__checkError(__id);
}
// Report to the listeners
for (RecordListener l : listeners)
l.recordChanged(this, __id);
}
/**
* Calls {@code setRecord(__id, __b, __o, __l, getTag(__id))}.
*
* @param __id As forwarded.
* @param __b As forwarded.
* @param __o As forwarded.
* @param __l As forwarded.
* @throws ArrayIndexOutOfBoundsException As forwarded.
* @throws InvalidRecordIDException As forwarded.
* @throws NullPointerException As forwarded.
* @throws RecordStoreException As forwarded.
* @throws RecordStoreFullException As forwarded.
* @throws RecordStoreNotOpenException As forwarded.
* @since 2017/02/26
*/
@Api
public void setRecord(int __id, byte[] __b, int __o, int __l)
throws ArrayIndexOutOfBoundsException, InvalidRecordIDException,
NullPointerException, RecordStoreException,
RecordStoreFullException, RecordStoreNotOpenException
{
this.setRecord(__id, __b, __o, __l, this.getTag(__id));
}
/**
* Checks that this record store is open.
*
* @throws RecordStoreNotOpenException If it is not open.
* @since 2019/04/15
*/
private void __checkOpen()
throws RecordStoreNotOpenException
{
/* {@squirreljme.error DC07 This record store is not open.} */
if (this._opens <= 0)
throw new RecordStoreNotOpenException("DC07");
}
/**
* Lists the pages that exist within this record store.
*
* @return The page IDs.
* @since 2019/05/13
*/
final int[] __listPages()
throws RecordStoreNotOpenException
{
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Check open
this.__checkOpen();
return vinyl.pageList(this._vid);
}
}
/**
* Returns all of the listeners for this record store.
*
* @return The listeners.
* @since 2019/04/15
*/
private RecordListener[] __listeners()
{
Set<RecordListener> listeners = this._listeners;
return listeners.<RecordListener>toArray(
new RecordListener[listeners.size()]);
}
/**
* Deletes the specified record store.
*
* Suites may only delete their own record store.
*
* This will not call
* {@link RecordListener#recordDeleted(RecordStore, int)}
* listeners associated with the given record store.
*
* @param __n The name of the record store to delete.
* @throws RecordStoreException If the record store cannot be deleted due
* to being owned by another suite or deletion is not possible.
* @throws RecordStoreNotFoundException If the given record store was not
* found.
* @since 2017/02/26
*/
@Api
public static void deleteRecordStore(String __n)
throws NullPointerException, RecordStoreException,
RecordStoreNotFoundException
{
if (__n == null)
throw new NullPointerException("NARG");
// Our suite identifier to find our own records
long mysid = SuiteIdentifier.currentIdentifier();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Try to locate our record
int got = -1;
for (int rid : vinyl.volumeList())
{
// Another suite's volume
if (mysid != vinyl.volumeSuiteIdentifier(rid))
continue;
// Found the record?
if (__n.equals(vinyl.volumeName(rid)))
{
got = -1;
break;
}
}
/* {@squirreljme.error DC08 Cannot delete the specified record
store because it does not exist. (The name of the store)} */
if (got == -1)
throw new RecordStoreNotFoundException("DC08 " + __n);
throw Debugging.todo();
}
}
/**
* Returns the list of record stores that are available and owned by
* this suite.
*
* @return The list of available record stores, the order is unspecified
* and implementation dependent. If there are no records then {@code null}
* will be returned.
* @since 2017/02/26
*/
@Api
public static String[] listRecordStores()
{
// Our suite identifier to find our own records
long mysid = SuiteIdentifier.currentIdentifier();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
List<String> rv = new ArrayList<>();
// Go through all IDs and locate record store info
for (int rid : vinyl.volumeList())
{
// Do not add records which belong to another suite
if (mysid != vinyl.volumeSuiteIdentifier(rid))
continue;
rv.add(vinyl.volumeName(rid));
}
return rv.<String>toArray(new String[rv.size()]);
}
}
/**
* Attempts to open and optionally create the record store for this midlet
* with the specified name.
*
* If a password is specified then the record store will be encrypted to
* prevent tampering.
*
* If the record store has already been opened then it will return a
* previously opened record store.
*
* @param __n The name of the record store, must consist of 1 to 32
* Unicode characters.
* @param __create If {@code true} then if the record store does not
* exist it will be created.
* @param __auth The authorization mode of the record which may permit
* other applications to access this record. If the record already exists
* then this argument will be ignored.
* @param __write If {@code true} then the record store may be written to
* by other suites. If the record already exists then this argument will be
* ignored.
* @param __pass The password.
* @return The newly opened or created record store, if the record store
* is already open then it will return the already open one.
* @throws IllegalArgumentException If the name is not valid or the
* authorization mode is not valid.
* @throws RecordStoreException If it could not be opened for another
* reason.
* @throws RecordStoreFullException If there is no space remaining.
* @throws RecordStoreNotFoundException If the record store could not be
* located.
* @throws SecureRecordStoreException The secured record could not be
* initialized.
* @throws SecurityException If the encryption password does not
* match an existing password.
* @since 2017/02/26
*/
@Api
public static RecordStore openRecordStore(String __n, boolean __create,
int __auth, boolean __write, String __pass)
throws IllegalArgumentException, RecordStoreException,
RecordStoreFullException, RecordStoreNotFoundException,
SecureRecordStoreException, SecurityException
{
return RecordStore.__openRecordStore(__n,
ApplicationHandler.currentVendor(), ApplicationHandler.currentName(),
__create, __auth, __write, __pass);
}
/**
* Forwards to {@code openRecordStore(__n, __create, __auth, __write, "")}.
*
* @param __n As forwarded.
* @param __create As forwarded.
* @param __auth As forwarded.
* @param __write As forwarded.
* @return As forwarded.
* @throws IllegalArgumentException As forwarded.
* @throws RecordStoreException As forwarded.
* @throws RecordStoreFullException As forwarded.
* @throws RecordStoreNotFoundException As forwarded.
* @throws SecureRecordStoreException As forwarded.
* @throws SecurityException As forwarded.
* @since 2017/02/26
*/
@Api
public static RecordStore openRecordStore(String __n, boolean __create,
int __auth, boolean __write)
throws IllegalArgumentException, RecordStoreException,
RecordStoreFullException, RecordStoreNotFoundException,
SecureRecordStoreException, SecurityException
{
return RecordStore.openRecordStore(__n, __create, __auth, __write, "");
}
/**
* Forwards to {@code openRecordStore(__n, __create, AUTHMODE_PRIVATE,
* true, "")}.
*
* @param __n As forwarded.
* @param __create As forwarded.
* @return As forwarded.
* @throws IllegalArgumentException As forwarded.
* @throws RecordStoreException As forwarded.
* @throws RecordStoreFullException As forwarded.
* @throws RecordStoreNotFoundException As forwarded.
* @throws SecureRecordStoreException As forwarded.
* @throws SecurityException As forwarded.
* @since 2017/02/26
*/
@Api
public static RecordStore openRecordStore(String __n, boolean __create)
throws IllegalArgumentException, RecordStoreException,
RecordStoreFullException, RecordStoreNotFoundException,
SecureRecordStoreException, SecurityException
{
return RecordStore.openRecordStore(__n, __create,
RecordStore.AUTHMODE_PRIVATE,
true, "");
}
/**
* Attempts to open the record store created by another application.
*
* The record store must have been created with the {@link #AUTHMODE_ANY}
* authorization. If it is encrypted then the same password must be
* specified.
*
* If the vendor and suite is our own then this will be the same as
* calling: {@code openRecordStore(__n, false, AUTHMODE_PRIVATE, true,
* __pass)}.
*
* @param __n The name of the record store, must consist of 1 to 32
* Unicode characters.
* @param __vend The vendor of the other suite.
* @param __suite The suite.
* @param __pass The password to the record store.
* @return The opened record store.
* @throws IllegalArgumentException If the name, vendor, or suite names
* are not valid.
* @throws RecordStoreException If it could not be opened for another
* reason.
* @throws RecordStoreNotFoundException If the record store could not be
* located.
* @throws SecureRecordStoreException The secured record could not be
* initialized.
* @throws SecurityException If the encryption password does not
* match an existing password.
* @since 2017/02/26
*/
@Api
public static RecordStore openRecordStore(String __n, String __vend,
String __suite, String __pass)
throws IllegalArgumentException, RecordStoreException,
RecordStoreNotFoundException, SecureRecordStoreException,
SecurityException
{
return RecordStore.__openRecordStore(__n, __vend, __suite, false,
RecordStore.AUTHMODE_ANY, false, __pass);
}
/**
* Calls {@code openRecordStore(__n, __vend, __suite, "")}.
*
* @param __n As forwarded.
* @param __vend As forwarded.
* @param __suite As forwarded.
* @return As forwarded.
* @throws IllegalArgumentException As forwarded.
* @throws RecordStoreException As forwarded.
* @throws RecordStoreNotFoundException As forwarded.
* @throws SecureRecordStoreException As forwarded.
* @throws SecurityException As forwarded.
* @since 2017/02/26
*/
@Api
public static RecordStore openRecordStore(String __n, String __vend,
String __suite)
throws IllegalArgumentException, RecordStoreException,
RecordStoreNotFoundException, SecureRecordStoreException,
SecurityException
{
return RecordStore.openRecordStore(__n, __vend, __suite, "");
}
/**
* Checks for an error and throws an exception potentially.
*
* @param __id The ID to check, negative indicates error.
* @throws RecordStoreException If there is an error.
* @since 2019/05/01
*/
private static void __checkError(int __id)
throws RecordStoreException
{
// Error was detected
if (__id < 0)
{
/* {@squirreljme.error DC09 Could not add the record, there might
not be enough free space available.} */
if (__id == VinylRecord.ERROR_NO_MEMORY)
throw new RecordStoreFullException("DC09");
/* {@squirreljme.error DC0a No such record store exists.} */
if (__id == VinylRecord.ERROR_NO_VOLUME)
throw new RecordStoreNotFoundException("DC0a");
/* {@squirreljme.error DC0b No such record exists.} */
if (__id == VinylRecord.ERROR_NO_PAGE)
throw new InvalidRecordIDException("DC0b");
/* {@squirreljme.error DC0c Unknown record store error. (Error)} */
throw new RecordStoreException("DC0c " + __id);
}
}
/**
* Attempts to open and optionally create the record store for the
* specified MIDlet.
*
* If a password is specified then the record store will be encrypted to
* prevent tampering.
*
* If the record store has already been opened then it will return a
* previously opened record store.
*
* @param __name The name of the record store, must consist of 1 to 32
* Unicode characters.
* @param __create If {@code true} then if the record store does not
* exist it will be created.
* @param __auth The authorization mode of the record which may permit
* other applications to access this record. If the record already exists
* then this argument will be ignored.
* @param __write If {@code true} then the record store may be written to
* by other suites. If the record already exists then this argument will be
* ignored.
* @return The newly opened or created record store, if the record store
* is already open then it will return the already open one.
* @throws IllegalArgumentException If the name is not valid or the
* authorization mode is not valid.
* @throws NullPointerException On null arguments.
* @throws RecordStoreException If it could not be opened for another
* reason.
* @throws RecordStoreFullException If there is no space remaining.
* @throws RecordStoreNotFoundException If the record store could not be
* located.
* @throws SecureRecordStoreException The secured record could not be
* initialized.
* @throws SecurityException If the encryption password does not
* match an existing password.
* @since 2018/12/15
*/
private static RecordStore __openRecordStore(String __name, String __vend,
String __suite, boolean __create, int __auth, boolean __write,
String __pass)
throws IllegalArgumentException, NullPointerException,
RecordStoreException, RecordStoreFullException,
RecordStoreNotFoundException, SecureRecordStoreException,
SecurityException
{
if (__name == null || __vend == null || __suite == null)
throw new NullPointerException("NARG");
/* {@squirreljme.error DC0d The name is not valid.} */
int namelen = __name.length();
if (namelen < 1 || namelen > 32)
throw new IllegalArgumentException("DC0d " + __name);
// Get identifier, used to find the record
long sid = SuiteIdentifier.identifier(__vend, __suite),
mysid = SuiteIdentifier.currentIdentifier();
// Lock
VinylRecord vinyl = RecordStore._VINYL;
try (VinylLock lock = vinyl.lock())
{
// Go through all records and try to find a pre-existing one
int rv = -1;
for (int rid : vinyl.volumeList())
{
// Belongs to another suite?
if (sid != vinyl.volumeSuiteIdentifier(rid))
continue;
// Same name?
if (__name.equals(vinyl.volumeName(rid)))
{
rv = rid;
break;
}
}
// Open a record which already exists
if (rv >= 0)
{
// Use a pre-cached store
Map<Integer, RecordStore> cache = RecordStore._STORE_CACHE;
RecordStore rs = cache.get(rv);
if (rs == null)
cache.put(rv, (rs = new RecordStore(rv, __name,
sid == mysid || vinyl.volumeOtherWritable(rv))));
// Increment the open count
rs._opens++;
return rs;
}
/* {@squirreljme.error DC0e Could not find the specified record
store. (The name; The vendor; The suite)} */
if (!__create)
throw new RecordStoreNotFoundException(
String.format("DC0e %s %s %s", __name, __vend, __suite));
/* {@squirreljme.error DC0f Could not create the record, it is
likely that there is not enough space remaining.} */
rv = vinyl.volumeCreate(sid, __name, __write);
if (rv < 0)
throw new RecordStoreFullException("DC0f");
// Since we created it, we can just return the info
return new RecordStore(rv, __name, sid == mysid || __write);
}
}
}