/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package javax.microedition.m3g;
import java.lang.ref.WeakReference;
import java.util.Hashtable;
import org.eclipse.swt.widgets.Display;
//#ifdef RD_JAVA_OMJ
import com.nokia.mj.impl.rt.support.Finalizer;
//#endif // RD_JAVA_OMJ
/**
* M3G interface object. An interface is automatically created for
* each MIDlet using the 3D API to keep track of Java-side object
* lifetimes etc.
*/
class Interface
{
//------------------------------------------------------------------
// Static data
//------------------------------------------------------------------
// Common class enumeration for Java and native code
private static final int ANIMATION_CONTROLLER = 0x01;
private static final int ANIMATION_TRACK = 0x02;
private static final int APPEARANCE = 0x03;
private static final int BACKGROUND = 0x04;
private static final int CAMERA = 0x05;
private static final int COMPOSITING_MODE = 0x06;
private static final int FOG = 0x07;
private static final int GROUP = 0x08;
private static final int IMAGE_2D = 0x09;
private static final int INDEX_BUFFER = 0x0A;
private static final int KEYFRAME_SEQUENCE = 0x0B;
private static final int LIGHT = 0x0C;
private static final int LOADER = 0x0D;
private static final int MATERIAL = 0x0E;
private static final int MESH = 0x0F;
private static final int MORPHING_MESH = 0x10;
private static final int POLYGON_MODE = 0x11;
private static final int RENDER_CONTEXT = 0x12;
private static final int SKINNED_MESH = 0x13;
private static final int SPRITE_3D = 0x14;
private static final int TEXTURE_2D = 0x15;
private static final int VERTEX_ARRAY = 0x16;
private static final int VERTEX_BUFFER = 0x17;
private static final int WORLD = 0x18;
// Once created, the interface singleton currently remains in
// memory until VM exit. By using a WeakReference here, with hard
// references stored in each object, it could be GC'd when no more
// objects exist, but that probably isn't worth the extra memory
// overhead.
//private static Hashtable s_instances = new Hashtable();
private static Interface instance = null;
//------------------------------------------------------------------
// Instance data
//------------------------------------------------------------------
/**
* Handle of the native interface object.
*/
private int handle;
/**
* Global handle-to-Object3D map used to both find the Java
* counterparts of objects returned from the native methods, and
* keep certain objects from being garbage collected.
*/
private final Hashtable liveObjects = new Hashtable();
/**
* Flag for shutdown signal
*/
private boolean iShutdown = false;
/**
* Flag for native peer init state
*/
private boolean iNativeInitialized = false;
//#ifdef RD_JAVA_OMJ
private Finalizer mFinalizer;
//#endif // RD_JAVA_OMJ
//------------------------------------------------------------------
// Constructors
//------------------------------------------------------------------
private Interface()
{
// Contruct native peer
initNativePeer();
//#ifdef RD_JAVA_OMJ
mFinalizer = new Finalizer()
{
public void finalizeImpl()
{
doFinalize();
}
};
//#else // RD_JAVA_OMJ
// Platform.registerFinalizer(this);
//#endif // RD_JAVA_OMJ
}
//------------------------------------------------------------------
// Package methods
//------------------------------------------------------------------
/**
* Returns the M3G interface instance for the current MIDlet.
*/
static final Interface getInstance()
{
if (instance == null)
{
instance = new Interface();
}
return instance;
}
/**
* Returns the native handle of the current Interface instance.
*/
static final int getHandle()
{
getInstance().integrityCheck();
return getInstance().handle;
}
/**
* Registers an Object3D with this interface. The object is added
* to the global handle-to-object map, and the native finalization
* callback is set up. The handle of the object must already be
* set at this point!
*/
static final void register(Object3D obj)
{
Platform.registerFinalizer(obj);
getInstance().liveObjects.put(new Integer(obj.handle),
new WeakReference(obj));
}
static final void register(Loader obj)
{
Platform.registerFinalizer(obj);
getInstance().liveObjects.put(new Integer(obj.handle),
new WeakReference(obj));
}
/**
* Finds an Object3D in the global handle-to-object map. Also
* removes dead objects (that is, null references) from the map
* upon encountering them.
*/
static final Object3D findObject(int handle)
{
Interface self = getInstance();
Integer iHandle = new Integer(handle);
Object ref = self.liveObjects.get(iHandle);
if (ref != null)
{
Object3D obj = (Object3D)((WeakReference)ref).get();
if (obj == null)
{
self.liveObjects.remove(iHandle);
}
return obj;
}
else
{
return null;
}
}
/**
* Returns the Java object representing a native object, or
* creates a new proxy/peer if one doesn't exist yet.
*/
static final Object3D getObjectInstance(int handle)
{
// A zero handle equals null
if (handle == 0)
{
return null;
}
// Then try to find an existing Java representative for the
// object
Object3D obj = findObject(handle);
if (obj != null)
{
return obj;
}
// Not found, create a new Java object. Note that only
// non-abstract classes can possibly be returned.
switch (_getClassID(handle))
{
case ANIMATION_CONTROLLER:
return new AnimationController(handle);
case ANIMATION_TRACK:
return new AnimationTrack(handle);
case APPEARANCE:
return new Appearance(handle);
case BACKGROUND:
return new Background(handle);
case CAMERA:
return new Camera(handle);
case COMPOSITING_MODE:
return new CompositingMode(handle);
case FOG:
return new Fog(handle);
case GROUP:
return new Group(handle);
case IMAGE_2D:
return new Image2D(handle);
case INDEX_BUFFER:
return new TriangleStripArray(handle);
case KEYFRAME_SEQUENCE:
return new KeyframeSequence(handle);
case LIGHT:
return new Light(handle);
//case LOADER:
case MATERIAL:
return new Material(handle);
case MESH:
return new Mesh(handle);
case MORPHING_MESH:
return new MorphingMesh(handle);
case POLYGON_MODE:
return new PolygonMode(handle);
//case RENDER_CONTEXT:
case SKINNED_MESH:
return new SkinnedMesh(handle);
case SPRITE_3D:
return new Sprite3D(handle);
case TEXTURE_2D:
return new Texture2D(handle);
case VERTEX_ARRAY:
return new VertexArray(handle);
case VERTEX_BUFFER:
return new VertexBuffer(handle);
case WORLD:
return new World(handle);
default:
throw new Error();
}
}
/**
* Forces removal of an object from the handle-to-object map.
*/
static final void deregister(Object3D obj, Interface self)
{
self.liveObjects.remove(new Integer(obj.handle));
if (self.liveObjects.isEmpty() && self.iShutdown)
{
self.registeredFinalize();
}
}
/**
* Forces removal of an object from the handle-to-object map.
*/
static final void deregister(Loader obj, Interface self)
{
self.liveObjects.remove(new Integer(obj.handle));
if (self.liveObjects.isEmpty() && self.iShutdown)
{
self.registeredFinalize();
}
}
/**
* Sets shutdown indication flag. Actual native
* cleanup occurs when liveObjects count is zero
*/
void signalShutdown()
{
iShutdown = true;
}
/**
* Gets the state of this interface
* @return true if interface is fully constructed, otherwise false
*/
boolean isFullyInitialized()
{
return iNativeInitialized;
}
//------------------------------------------------------------------
// Private methods
//------------------------------------------------------------------
/**
* Checks the status of the native interface
*/
private void integrityCheck()
{
if (!iNativeInitialized)
{
// If native interface cannot be initialized we cannot recover from it
if (!initNativePeer())
{
throw new Error("UI thread not available");
}
}
}
/**
* Initializes native peer
* @return true if native interface was succesfully inialized otherwise false
*/
private boolean initNativePeer()
{
if (iNativeInitialized)
{
return true;
}
if (Platform.uiThreadAvailable())
{
Platform.executeInUIThread(
new M3gRunnable()
{
public void doRun()
{
handle = _ctor();
}
});
iNativeInitialized = true;
return true;
}
else
{
return false;
}
}
//#ifdef RD_JAVA_OMJ
private void doFinalize()
{
if (mFinalizer != null)
{
registeredFinalize();
mFinalizer = null;
}
}
//#endif // RD_JAVA_OMJ
// Native finalization hook, for Symbian only
final private void registeredFinalize()
{
if (Interface.instance != null)
{
// Finalize M3G interface
Platform.executeInUIThread(
new M3gRunnable()
{
public void doRun()
{
Platform.finalizeInterface(handle);
}
});
Interface.instance = null;
}
}
// Native constructor
private static native int _ctor();
// Native class ID resolver
private static native int _getClassID(int hObject);
}