/*
*
*
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.j2me.content;
import java.util.Vector;
import com.sun.j2me.security.Token;
/**
* Standalone Registry Storage manager.
* All protected methods, which are all static, redirect their work
* to alone instance allowed for given Java runtime (for MIDP
* it is Isolate).
* The standalone instance initializes resources in the private
* constructor and then releases its in the native finalizer.
*/
class RegistryStore {
/**
* Content Handler fields indexes.
* <BR>Used with functions: @link findHandler(), @link getValues() and
* @link getArrayField().
* <BR> They should match according enums in jsr211_registry.h
*/
static final int FIELD_ID = 0; /** Handler ID */
static final int FIELD_TYPES = 1; /** Types supported by a handler */
static final int FIELD_SUFFIXES = 2; /** Suffixes supported */
/** by a handler */
static final int FIELD_ACTIONS = 3; /** Actions supported */
/** by a handler */
static final int FIELD_LOCALES = 4; /** Locales supported */
/** by a handler */
static final int FIELD_ACTION_MAP = 5; /** Handler action map */
static final int FIELD_ACCESSES = 6; /** Access list */
static final int FIELD_COUNT = 7; /** Total number of fields */
static final Vector emptyVector = new Vector();
static final ContentHandlerImpl[] emptyHandlersArray = new ContentHandlerImpl[0];
/**
* Search flags for @link getHandler() method.
*/
static final int SEARCH_EXACT = 0; /** Search by exact match with ID */
static final int SEARCH_PREFIX = 1; /** Search by prefix of given value */
/** This class has a different security domain than the MIDlet suite */
private static Token classSecurityToken;
static ContentHandlerImpl.Handle register(ApplicationID appID,
ContentHandlerRegData handlerData) {
if( !store.register0(CLDCAppID.from(appID).suiteID, CLDCAppID.from(appID).className,
handlerData) )
return null;
return new ContentHandlerHandle( handlerData.ID );
}
/**
* Unregisters content handler specified by its ID.
* @param handlerId ID of unregistered handler.
* @return true if success, false - otherwise.
*/
static boolean unregister(String handlerId) {
return store.unregister0(handlerId);
}
static void enumHandlers(String callerId, int searchBy, String value,
ContentHandlerImpl.Handle.Receiver output) {
ContentHandlerImpl[] result = findHandler(callerId, searchBy, value);
for( int i = 0; i < result.length; i++)
output.push(result[i].handle);
}
/**
* Tests ID value for registering handler accordingly with JSR claim:
* <BR><CITE>Each content handler is uniquely identified by an ID. ...
* <BR> The ID MUST NOT be equal to any other registered handler ID.
* <BR> Every other ID MUST NOT be a prefix of this ID.
* <BR> The ID MUST NOT be a prefix of any other registered ID. </CITE>
* @param testID tested value
*
* @return conflicted handlers array.
*/
static ContentHandlerImpl[] findConflicted(String testID) {
ContentHandlerImpl[] result = findHandler(null, FIELD_ID, testID);
if(AppProxy.LOGGER != null){
AppProxy.LOGGER.println( "conflictedHandlers for '" + testID + "' [" + result.length + "]:" );
for( int i = 0; i < result.length; i++){
AppProxy.LOGGER.println( "app = " + result[i].applicationID + ", ID = '" + result[i].ID + "'" );
}
}
return result;
}
/**
* Searches content handlers by searchable fields values. As specified in
* JSR 211 API:
* <BR><CITE> Only content handlers that this application is allowed to
* access will be included. </CITE> (in result).
* @param callerId ID value to check access
* @param searchBy indicator of searchable field. Allowed:
* @link FIELD_TYPES, @link FIELD_SUFFIXES, @link FIELD_ACTIONS
* values. The special case for the testId implementation:
* @link FIELD_ID specified.
* @param value Searched value
* @return found handlers array.
*/
static ContentHandlerImpl[] findHandler(String callerId, int searchBy,
String value) {
/* Check value for null */
value.length();
HandlersCollection collection = new HandlersCollection();
deserializeCHArray(store.findHandler0(callerId, searchBy, value), collection);
return collection.getArray();
}
/**
* The special finder for exploring handlers registered by the given suite.
* @param suiteId explored suite Id
*
* @return found handlers array.
*/
static ContentHandlerImpl[] forSuite(int suiteId) {
HandlersCollection collection = new HandlersCollection();
deserializeCHArray(store.forSuite0(suiteId), collection);
return collection.getArray();
}
static ContentHandlerImpl getHandler( ApplicationID appID ){
ContentHandlerImpl[] arr =
RegistryStore.forSuite(CLDCAppID.from(appID).suiteID);
String classname = CLDCAppID.from(appID).className;
for (int i = 0; i < arr.length; i++) {
if (classname.equals(CLDCAppID.from(arr[i].applicationID).className)) {
return arr[i];
}
}
return null;
}
/**
* Returns all stored in the Registry values for specified field.
* @param callerId ID value to check access
* @param searchBy index of searchable field. Allowed:
* @link FIELD_TYPES, @link FIELD_SUFFIXES, @link FIELD_ACTIONS,
* @link FIELD_ID values.
* @return found values array.
*/
static String[] getValues(String callerId, int searchBy) {
String res = store.getValues0(callerId, searchBy);
Vector v = deserializeString(res);
String[] result = new String[ v.size() ];
v.copyInto(result);
return result;
}
/**
* Returns array field
* @param handlerId ID for access check
* @param fieldId index of field. Allowed:
* @link FIELD_TYPES, @link FIELD_SUFFIXES, @link FIELD_ACTIONS
* @link FIELD_LOCALES, @link FIELD_ACTION_MAP, @link FIELD_ACCESSES
* values.
* @return array of values
*/
static String[] getArrayField(String handlerId, int fieldId) {
String res = store.loadFieldValues0(handlerId, fieldId);
Vector v = deserializeString(res);
String[] result = new String[ v.size() ];
v.copyInto(result);
return result;
}
static class HandlerData {
int suiteId;
String classname;
int registrationMethod;
public String ID;
}
/**
* Creates and loads handler's data.
* @param handlerId ID of content handler to be loaded.
* @param searchMode ID matching mode. Used <ul>
* <li> @link SEARCH_EXACT
* <li> @link SEARCH_PREFIX </ul>
*
* @return loaded ContentHandlerImpl object or
* <code>null</code> if given handler ID is not found in Registry database.
*/
static ContentHandlerImpl getHandler(String callerId, String id, int searchMode) {
if (id.length() != 0) {
HandlerData data = deserializeCH( store.getHandler0( callerId, id, searchMode ) );
if( data != null )
return new ContentHandlerHandle( data ).get();
}
return null;
}
static HandlerData getHandler(String handlerID) {
return deserializeCH( store.getHandler0( null, handlerID, SEARCH_EXACT ) );
}
/**
* Returns content handler suitable for URL.
* @param callerId ID of calling application.
* @param URL content URL.
* @param action requested action.
* @return found handler if any or null.
*/
static ContentHandlerImpl getByURL(String callerId, String url,
String action) {
return new ContentHandlerHandle( deserializeCH( store.getByURL0(callerId, url, action) ) ).get();
}
/**
* Transforms serialized form to array of Strings.
* <BR>Serialization format is the same as ContentHandlerImpl
* used.
* @param str String in serialized form to transform to array of Strings.
* @return array of Strings. If input String is NULL 0-length array
* returned. ... And we believe that input string is not misformed.
*/
private static Vector/*<String>*/ deserializeString(String str) {
if( str == null )
return emptyVector;
Vector result = new Vector();
// all lengths in bytes
int pos = 0;
// if(AppProxy.LOGGER != null)
// AppProxy.LOGGER.println( "deserializeString: string length = " + str.length() );
while( pos < str.length() ){
int elem_length = (int)str.charAt(pos++) / 2;
// if(AppProxy.LOGGER != null)
// AppProxy.LOGGER.println( "deserializeString: pos = " + pos +
// ", elem_length = " + elem_length );
result.addElement(str.substring(pos, pos + elem_length));
// if(AppProxy.LOGGER != null)
// AppProxy.LOGGER.println( "deserializeString: '" + str.substring(pos, pos + elem_length) + "'" );
pos += elem_length;
}
return result;
}
/**
* Restores ContentHandler main fields (ID, suite_ID, class_name and flag)
* from serialized form to ContentHandlerImpl object.
* @param str ContentHandler main data in serialized form.
* @return restored ContentHandlerImpl object or null
*/
private static HandlerData deserializeCH(String str) {
if(AppProxy.LOGGER != null)
AppProxy.LOGGER.println( "RegistryStore.deserializeCH '" + str + "'");
Vector components = deserializeString(str);
if (components.size() < 1) return null;
String id = (String)components.elementAt(0);
if (id.length() == 0) return null; // ID is significant field
if (components.size() < 2) return null;
String storageId = (String)components.elementAt(1);
if (components.size() < 3) return null;
String class_name = (String)components.elementAt(2);
if (components.size() < 4) return null;
int regMethod = Integer.parseInt((String)components.elementAt(3), 16);
HandlerData ch = new HandlerData();
ch.ID = id;
ch.suiteId = Integer.parseInt(storageId, 16);
ch.classname = class_name;
ch.registrationMethod = regMethod;
return ch;
}
/**
* Restores ContentHandlerImpl array from serialized form.
* @param str ContentHandlerImpl array in serialized form.
* @return restored ContentHandlerImpl array
*/
private static void deserializeCHArray(String str,
ContentHandlerImpl.Handle.Receiver output) {
if( str != null ){
Vector strs = deserializeString(str);
for (int i = 0; i < strs.size(); i++)
output.push(new ContentHandlerHandle(deserializeCH( (String)strs.elementAt(i) )));
}
}
/**
* Sets the security token used for privileged operations.
* The token may only be set once.
* @param token a Security token
*/
static void setSecurityToken(Token token) {
if (classSecurityToken != null) {
throw new SecurityException();
}
classSecurityToken = token;
}
/** Singleton instance. Worker for the class static methods. */
private static RegistryStore store = new RegistryStore();
/**
* Private constructor for the singleton storage class.
* If ClassNotFoundException is thrown during ActionNameMap
* loading the constructor throws RuntimeException
*/
private RegistryStore() {
try {
Class.forName("javax.microedition.content.ActionNameMap");
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe.getMessage());
}
if (!init()) {
throw new RuntimeException("RegistryStore initialization failed");
}
}
/**
* Native implementation of <code>findHandler</code>.
* @param callerId ID value to check access
* @param searchBy index of searchable field.
* @param value searched value
* @return found handlers array in serialized form.
*/
private native String findHandler0(String callerId, int searchBy,
String value);
/**
* Native implementation of <code>findBySuite</code>.
* @param suiteId explored suite Id
* @return handlers registered for the given suite in serialized form.
*/
private native String forSuite0(int suiteId);
/**
* Native implementation of <code>getValues</code>.
* @param callerId ID value to check access
* @param searchBy index of searchable field.
* @return found values in serialized form.
*/
private native String getValues0(String callerId, int searchBy);
/**
* Loads content handler data.
* @param callerId ID value to check access.
* @param id Id of required content handler.
* @param mode flag defined search mode applied for the operation.
* @return serialized content handler or null.
*/
private native String getHandler0(String callerId, String id, int mode);
/**
* Loads values for array fields.
* @param handlerId ID of content handler ID.
* @param fieldId fieldId to be loaded.
* @return loaded field values in serialized form.
*/
private native String loadFieldValues0(String handlerId, int fieldId);
/**
* Returns content handler suitable for URL.
* @param callerId ID of calling application.
* @param URL content URL.
* @param action requested action.
* @return ID of found handler if any or null.
*/
private native String getByURL0(String callerId, String url, String action);
/**
* Initialize persistence storage.
* @return <code>true</code> or
* <BR><code>false</code> if initialization fails.
*/
private native boolean init();
/**
* Cleanup native resources.
*/
private native void finalize();
/**
* Registers given content handler.
* @param contentHandler content handler being registered.
* @return true if success, false - otherwise.
*/
private native boolean register0(int storageId, String classname,
ContentHandlerRegData handlerData);
/**
* Unregisters content handler specified by its ID.
* @param handlerId ID of unregistered handler.
* @return true if success, false - otherwise.
*/
private native boolean unregister0(String handlerId);
}
class ContentHandlerHandle implements ContentHandlerImpl.Handle {
private ContentHandlerImpl created = null;
private final String handlerID;
ContentHandlerHandle( String handlerID ){
this.handlerID = handlerID;
}
ContentHandlerHandle( RegistryStore.HandlerData data ){
this( data.ID );
Init( data );
}
private void Init( final RegistryStore.HandlerData data ){
created = new ContentHandlerImpl(new CLDCAppID(data.suiteId, data.classname), this){{
this.ID = handlerID;
this.registrationMethod = data.registrationMethod;
}};
}
public ContentHandlerImpl get(){
if( created == null )
Init(RegistryStore.getHandler( handlerID ));
return created;
}
public String getID() { return handlerID; }
//public int getSuiteId() { return get().storageId; }
public String[] getArrayField(int fieldId) {
return RegistryStore.getArrayField( getID(), fieldId );
}
}