/*
* Copyright (c) 2005-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 com.nokia.microedition.m2g;
import org.w3c.dom.*;
import org.w3c.dom.events.*;
import org.w3c.dom.svg.SVGElement;
import java.io.IOException;
import java.util.Hashtable;
import java.lang.ref.WeakReference;
import java.util.Vector;
import java.util.Enumeration;
import javax.microedition.m2g.*;
import com.nokia.microedition.m2g.connection.*;
import com.nokia.mj.impl.utils.Logger;
public class M2GDocument extends M2GObject
implements Document, M2GConnectionPolicyHandler
{
//--------------------------------------------------
// STATIC CONSTANTS
//--------------------------------------------------
// Error constants
//--------------------------------------------------
// VARIABLES
//--------------------------------------------------
private M2GEventDispatcher iEventDispatcher = null;
private ExternalResourceHandler iResourceHandler = null;
private M2GSVGImage iImage = null;
private M2GSVGSVGElement iRootElement = null;
private final M2GLiveElements iLiveElements = new M2GLiveElements(true);
private M2GConnectionPolicy iConnectionPolicy =
M2GConnectionFactory.getDefaultPolicy();
boolean iConnectionRight = true;
Vector iExternalResources = new Vector();
/** A base url from where a document was downloaded */
String iBaseUrl;
/** The suffix of the url from where the document was open*/
String iSuffixUrl;
/**
* Observer to be notified about changes in DOM
* @see M2GDOMChangeObserver.notifyDOMChange()
*/
private M2GDOMChangeObserver iDOMChangeObserver = null;
//--------------------------------------------------
// METHODS
//--------------------------------------------------
/**
* Creates new document object. If aData is null or empty then
* an empty document is create.
* @param aImage Svg image
* @param aBaseUrl A base url from where a document is downloaded.
* @param aSuffixUrl A suffix url from where a document is downloaded
* @param aData Plain text svg data
*/
public M2GDocument(M2GSVGImage aImage, String aBaseUrl, String aSuffixUrl,
String aData)
{
super();
iImage = aImage;
iBaseUrl = aBaseUrl;
iSuffixUrl = aSuffixUrl;
setHandle(_createDocument(
getNativeSVGProxyHandle(),
aData)
);
doConstruct();
}
/**
* @see org.w3c.dom.Node#appendChild()
*/
public Node appendChild(Node newChild) throws DOMException
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI, "appendChild() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
Logger.ELOG(Logger.EJavaUI, "appendChild() - exception: "
+ /*SF*/"Cannot appendChild to a Document node."/*SF*/);
throw new DOMException(
DOMException.HIERARCHY_REQUEST_ERR,
/*SF*/"Cannot appendChild to a Document node."/*SF*/);
}
/**
* Appends external resource uri to internal container
* @param aUri External resource uri
* @return true if succeeds
*/
boolean appendExternalResourceURI(String aUri)
{
boolean result = false;
if (aUri != null && !aUri.equals(""))
{
synchronized (iExternalResources)
{
if (!iExternalResources.contains(aUri))
{
iExternalResources.addElement(aUri);
result = true;
}
}
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo, "appendExternalResourceURI() uri=" +
((aUri != null) ? aUri : "null") + ", result=" + result + " - end");
return result;
}
/**
* Checks if document contains the element
* @param id
* @return true if document contains the element
*/
public boolean containsElement(String id)
{
if (id == null || id.equals(""))
{
return false;
}
int handle = _getElementById(
getNativeSVGProxyHandle(),
getHandle(), id);
return M2GObject.checkHandle(handle);
}
/**
* Check if external resource uri exists in internal container
* @param aUri External resource uri
* @return true if uri exists in container
*/
boolean containsExternalResourceURI(String aUri)
{
boolean result = false;
if (aUri != null && !aUri.equals(""))
{
synchronized (iExternalResources)
{
result = iExternalResources.contains(aUri);
}
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo, "containsExternalResourceURI() uri=" +
((aUri != null) ? aUri : "null") + ", result=" + result + " - end");
return result;
}
/**
* @see org.w3c.dom.Document#createElementNS()
*/
public Element createElementNS(String namespaceURI, String qualifiedName)
{
if (namespaceURI == null || namespaceURI.equals(""))
{
Logger.ELOG(Logger.EJavaUI,
"createElementNS() exception because namespace is null. qualifiedName="
+ qualifiedName);
throw new NullPointerException(/*SF*/"The namespace URI is null."/*SF*/);
}
if (qualifiedName == null || qualifiedName.equals(""))
{
Logger.ELOG(Logger.EJavaUI,
"createElementNS() exception - " + namespaceURI + ", "
+ qualifiedName);
throw new NullPointerException(/*SF*/"The qualifiedName is null."/*SF*/);
}
// Checks element validity
if (!M2GSVGConstants.isQualifiedElement(qualifiedName))
{
Logger.ELOG(Logger.EJavaUI, "createElementNS() exception - "
+ namespaceURI + ", " + qualifiedName);
throw new DOMException(
DOMException.NOT_SUPPORTED_ERR,
/*SF*/"The type of element is not supported by the implementation."/*SF*/);
}
// Checks namespace validity
if (!namespaceURI.equals(M2GSVGConstants.SVG_NAMESPACE_URI))
{
Logger.ELOG(Logger.EJavaUI, "createElementNS() exception - "
+ namespaceURI + ", " + qualifiedName);
throw new DOMException(
DOMException.NOT_SUPPORTED_ERR,
/*SF*/"The URI is not the SVG namespace URI."/*SF*/);
}
// Creates native element. A native document owns the new element.
// NOTE: The element is NOT located in the DOM tree yet.
int elementHandle = _createElementNS(
getNativeSVGProxyHandle(),
M2GSVGConstants.parseElementTypeId(qualifiedName),
getHandle());
M2GSVGLocatableElement element = null;
// Checks native handle validity
if (M2GObject.checkHandle(elementHandle))
{
// Creates java element object
element = new M2GSVGLocatableElement(elementHandle, this);
registerLiveElement(element, new Integer(elementHandle));
}
else
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"createElementNS(): " + qualifiedName + " - end, NOK!!!");
}
return element;
}
/**
* Checks document validity.
* @return true if valid
*/
public boolean doCheckValidity() throws IOException
{
boolean result = false;
if (isHandleValid())
{
M2GSVGSVGElement root = getRootElement();
if (root != null)
{
// Check svg version and base profile
root.checkVersionAndProfile();
result = true;
}
}
return result;
}
/**
* Cleanup
* @see com.nokia.microedition.m2g.M2GObject#doCleanup()
*/
public void doCleanup()
{
if (isHandleValid())
{
_deleteDocument(
getNativeSVGProxyHandle(),
getHandle());
}
iImage = null;
if (iEventDispatcher != null)
{
iEventDispatcher.clear();
iEventDispatcher = null;
}
resetHandles();
}
/**
* @see com.nokia.microedition.m2g.M2GObject#doConstruct()
*/
protected void doConstruct()
{
super.doConstruct();
iEventDispatcher = new M2GEventDispatcher();
}
/**
* Find an element from the handle-to-element map.
* @see M2GLiveElements#findElement()
*/
public SVGElement findLiveElement(Integer aElementHandle)
{
return iLiveElements.findElement(aElementHandle);
}
/**
* Get connection policy
* @see M2GConnectionListener#getConnectionPolicy()
*/
public M2GConnectionPolicy getConnectionPolicy()
{
return iConnectionPolicy;
}
/**
* @see org.w3c.dom.Document#getDocumentElement()
*/
public Element getDocumentElement()
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI,
"getDocumentElement() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
return getRootElement();
}
/**
* @see org.w3c.dom.Document#getElementById()
*/
public Element getElementById(String id)
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI, "getElementById() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
if (id == null || id.equals(""))
{
Logger.ELOG(Logger.EJavaUI, "getElementById() - exception");
throw new NullPointerException(/*SF*/"The ID is null."/*SF*/);
}
int elementHandle = _getElementById(
getNativeSVGProxyHandle(),
getHandle(),
id);
return M2GSVGElement.buildElement(elementHandle, this);
}
/**
* Returns the image
*/
public M2GSVGImage getImage()
{
return iImage;
}
/**
* @see org.w3c.dom.Node#getLocalName()
*/
public String getLocalName()
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI, "getLocalName() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
return null;
}
/**
* @see org.w3c.dom.Node#getNamespaceURI()
*/
public String getNamespaceURI()
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI, "getNamespaceURI() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
return null;
}
/**
* @see org.w3c.dom.Node#getParentNode()
*/
public Node getParentNode()
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI,
"getParentNode() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
return null;
}
/**
* Get root element
* @return M2GSVGSVGElement
*/
public M2GSVGSVGElement getRootElement()
{
if (iRootElement == null)
{
iRootElement = M2GSVGSVGElement.buildRootElement(this);
}
return iRootElement;
}
/**
* @see javax.microedition.m2g.ScalableImage#getViewportHeight()
*/
public int getViewportHeight()
{
return _getViewportHeight(
getNativeSVGProxyHandle(),
getHandle());
}
/**
* @see javax.microedition.m2g.ScalableImage#getViewportWidth()
*/
public int getViewportWidth()
{
return _getViewportWidth(
getNativeSVGProxyHandle(),
getHandle());
}
/**
* Called by SVGImage upon creating an Event instance
* Calls the handleEvent() of all the listeners of the event
*/
public void handleEvent(Event aEvent)
{
iEventDispatcher.handleEvent(aEvent);
}
/**
* @see org.w3c.dom.Node#insertBefore()
*/
public Node insertBefore(Node newChild, Node refChild)
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI, "insertBefore() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
Logger.ELOG(Logger.EJavaUI, "insertBefore() DOMException");
throw new DOMException(
DOMException.HIERARCHY_REQUEST_ERR,
/*SF*/"Cannot insertBefore a Document node."/*SF*/);
}
/**
* Checks if element is in DOM
* @param aElementHandle -
* @return true if element is in DOM
*/
public boolean isElementInDOM(int aElementHandle)
{
if (!M2GObject.checkHandle(aElementHandle))
{
return false;
}
return (_isElementInDOM(
getNativeSVGProxyHandle(),
getHandle(),
aElementHandle) > 0 ? true : false);
}
/**
* The external resource handler is invoked
* for any external resource referenced in the document.
* @param aUri External resource referenced in the document
*/
public void invokeResourceHandler(String aUri)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo, "invokeResourceHandler() - URI="
+ aUri + " - begin");
if ((iImage != null) && (iResourceHandler != null) &&
(aUri != null) && (aUri.length() > 4))
{
StringBuffer urlBuf = new StringBuffer();
// The uri format for external resource must include the base url
// from which the document was loaded. It's also checked that the
// url read from svg doesn't contain already the base part.
if ((iBaseUrl != null) &&
!iBaseUrl.equals("") &&
(aUri.indexOf(iBaseUrl) == -1))
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"invokeResourceHandler() append base url=" + iBaseUrl);
urlBuf.append(iBaseUrl);
}
urlBuf.append(aUri);
// append the url suffix in case of drm open mode
if (iSuffixUrl != null && !iSuffixUrl.equals(""))
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"invokeResourceHandler() append suffix url=" + iSuffixUrl);
urlBuf.append(iSuffixUrl);
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"invokeResourceHandler() handler uri=" + urlBuf.toString());
iResourceHandler.requestResource(iImage, urlBuf.toString());
appendExternalResourceURI(aUri);
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"invokeResourceHandler() - URI:" + aUri + " - end");
}
/**
* Registers an event target and an event listener.
* @param aTarget Target element
* @param aType Event type
* @param aListener Event listener
*/
void register(EventTarget aTarget, String aType, EventListener aListener)
{
iEventDispatcher.register(aTarget, aType, aListener);
}
/**
* Register element
* @see M2GLiveElements#registerElement()
*/
public void registerLiveElement(SVGElement aElement, Integer aElementHandle)
{
iLiveElements.registerElement(aElement, aElementHandle);
}
/**
* @see org.w3c.dom.Node#removeChild()
*/
public Node removeChild(Node oldChild) throws DOMException
{
// Optimization: if(!getConnectionPolicy().getAccessRight())
if (!iConnectionRight)
{
Logger.ELOG(Logger.EJavaUI, "removeChild() - access rights failure");
throw new SecurityException(M2GSVGConstants.ACCESS_RIGHTS_ESTR);
}
Logger.ELOG(Logger.EJavaUI, "removeChild() DOMException");
throw new DOMException(
DOMException.NOT_SUPPORTED_ERR,
/*SF*/"Cannot removeChild from a Document node."/*SF*/);
}
/**
* Removes external resource uri from internal container
* @param aUri External resource uri
* @return true if succeeds
*/
boolean removeExternalResourceURI(String aUri)
{
boolean result = false;
if (aUri != null && !aUri.equals(""))
{
synchronized (iExternalResources)
{
result = iExternalResources.removeElement(aUri);
}
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"removeExternalResourceURI() uri="
+ ((aUri != null) ? aUri : "null") + ", result=" + result + " - end");
return result;
}
/*
* @see com.nokia.microedition.m2g.M2GSVGImage#requestCompleted()
*/
public int requestCompleted(String aUri, byte[] aResourceData)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"requestCompleted() uri=" + aUri + " - begin");
// If the aUri contains a base part it will be cut off
if (!aUri.equals("") &&
(iBaseUrl != null) &&
!containsExternalResourceURI(aUri) &&
(aUri.indexOf(iBaseUrl, 0) != -1))
{
aUri = aUri.substring(iBaseUrl.length(), aUri.length());
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"requestCompleted() uri when base part is cut off=" + aUri);
}
// Execute tha request command.
int status = _requestCompleted(
getNativeSVGProxyHandle(),
getHandle(), aUri, aResourceData);
if ((status == 0) || aUri.equals(""))
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"requestCompleted() remove URI=" + aUri);
removeExternalResourceURI(aUri);
}
return status;
}
/**
* Set connection policy
* @see M2GConnectionListener#setConnectionPolicy()
*/
public void setConnectionPolicy(M2GConnectionPolicy aConnectionPolicy)
{
iConnectionPolicy = aConnectionPolicy;
if (iConnectionPolicy != null)
{
iConnectionRight = iConnectionPolicy.getAccessRight();
}
else
{
iConnectionRight = true;
}
}
/**
* Set image
* @param aImage Image
*/
public void setImage(M2GSVGImage aImage)
{
iImage = aImage;
}
/**
* Set external resource handler
* @param aHandler External resource handler
*/
public void setResourceHandler(ExternalResourceHandler aHandler)
{
iResourceHandler = aHandler;
}
/**
* @see javax.microedition.m2g.ScalableImage#setViewportHeight()
*/
public void setViewportHeight(int height)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"setViewportHeight():" + height + " - begin");
if (height < 0)
{
Logger.ELOG(Logger.EJavaUI, "setViewportHeight():"
+ height + " - IllegalArgumentException is thrown");
throw new IllegalArgumentException(/*SF*/"The height is negative."/*SF*/);
}
_setViewportHeight(
getNativeSVGProxyHandle(),
getHandle(),
height);
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"setViewportHeight():" + height + " - end");
}
/**
* @see javax.microedition.m2g.ScalableImage#setViewportWidth()
*/
public void setViewportWidth(int width)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo, "setViewportWidth:" + width + " - begin");
if (width < 0)
{
Logger.ELOG(Logger.EJavaUI, "setViewportWidth():"
+ width + " - IllegalArgumentException is thrown");
throw new IllegalArgumentException(/*SF*/"The width is negative."/*SF*/);
}
_setViewportWidth(
getNativeSVGProxyHandle(),
getHandle(),
width);
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"setViewportWidth:" + width + " - end");
}
/**
* Unregister event listener
* @param aTarget Target element
* @param aType Event type
* @param aListener Event listener
*/
void unregister(EventTarget aTarget, String aType, EventListener aListener)
{
iEventDispatcher.unregister(aTarget, aType, aListener);
}
/**
* Unregister an element from the handle-to-element map
* @see M2GLiveElements#unregisterElement()
*/
public void unregisterLiveElement(Integer aElementHandle)
{
iLiveElements.unregisterElement(aElementHandle);
}
//--------------------------------------------------
// INNER CLASSES
//--------------------------------------------------
/**
* M2GLiveElements
*/
class M2GLiveElements
{
private Hashtable iLiveElements = new Hashtable();
private boolean iUseWeakReference;
/**
* Constructor
*/
public M2GLiveElements(boolean aUseWeakReference)
{
iUseWeakReference = aUseWeakReference;
}
/**
* Find an element from the handle-to-element map.
* @param aElementHandle Handle
* @return SVGElement. Null if not found
*/
public SVGElement findElement(Integer aElementHandle)
{
SVGElement element = null;
Object weakObject = null;
Object object = null;
synchronized (this)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"** findElement() handle: " + aElementHandle + " - begin **");
object = iLiveElements.get(aElementHandle);
if (object != null)
{
if (iUseWeakReference)
{
weakObject = ((WeakReference)object).get();
if (weakObject == null)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"** findElement() handle: " + aElementHandle
+ " is garbage collected. **");
iLiveElements.remove(aElementHandle);
}
else
{
element = (SVGElement)weakObject;
}
}
else
{
element = (SVGElement)object;
}
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo,"** findElement() handle: " + aElementHandle
+ ((element != null) ? " FOUND" : " NOT FOUND") + " - end **");
}
return element;
}
/**
* Register element
* @param aElement Element
* @param aElementHandle Handle
*/
public void registerElement(SVGElement aElement, Integer aElementHandle)
{
if (aElement == null)
{
return;
}
synchronized (this)
{
if (iUseWeakReference)
{
iLiveElements.put(aElementHandle, new WeakReference(aElement));
}
else
{
iLiveElements.put(aElementHandle, aElement);
}
}
}
/**
* Unregister an element from the handle-to-element map
* @param aElementHandle Handle
*/
public void unregisterElement(Integer aElementHandle)
{
synchronized (this)
{
iLiveElements.remove(aElementHandle);
}
}
}
/**
* M2GEventDispatcher
*/
protected class M2GEventDispatcher
{
static public final int INDEX_UNDEFINED = -1;
static public final int INDEX_CLICK = 0;
static public final int INDEX_ACTIVATE = 1;
static public final int INDEX_FOCUS_IN = 2;
static public final int INDEX_FOCUS_OUT = 3;
Hashtable[] iEventTypes;
int iCurrentIndex;
Hashtable iDelayedRegistrations = new Hashtable();
M2GEventData iCurrentEventData = new M2GEventData();
/**
* Constructor
*/
public M2GEventDispatcher()
{
iEventTypes = new Hashtable[4];
iCurrentIndex = INDEX_UNDEFINED;
}
/**
* Clear all
*/
public void clear()
{
synchronized (this)
{
for (int index = 0; index < iEventTypes.length; index++)
{
if (iEventTypes[index] != null)
{
iEventTypes[index].clear();
iEventTypes[index] = null;
}
}
}
}
/**
* Execute delayed registration
* If an EventListener is added to an EventTarget while it is
* processing an event, it will not be triggered by the current actions.
*/
private void doDelayedRegistration()
{
try
{
Enumeration registrations = iDelayedRegistrations.elements();
while (registrations.hasMoreElements())
{
M2GEventData eventData =
(M2GEventData)registrations.nextElement();
register(
eventData.iTarget, eventData.iType, eventData.iListener);
}
}
catch (NullPointerException e)
{
}
iDelayedRegistrations.clear();
}
/**
* Handle event
* @param aEvent Event
*/
public void handleEvent(Event aEvent)
{
synchronized (this)
{
if ((aEvent == null) || !prepare(aEvent.getType()))
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent() FAILED - event invalid");
return;
}
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent(): " + aEvent.getType() + " - begin");
EventTarget target = aEvent.getCurrentTarget();
if (target == null)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent() FAILED - event's target is null");
return;
}
// Check if wrapped event target
if (target instanceof M2GSVGElementWrapper)
{
target = ((M2GSVGElementWrapper)target).getWrappedTarget();
if (target == null)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent() FAILED - wrapped event's target is null");
return;
}
}
if (iEventTypes[iCurrentIndex].containsKey(target))
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent() - target handling begin");
iCurrentEventData.set(target, aEvent.getType(), null);
Vector listeners =
(Vector)iEventTypes[iCurrentIndex].get(iCurrentEventData.iTarget);
if (listeners != null)
{
Enumeration index = listeners.elements();
while (index.hasMoreElements())
{
Object listener = index.nextElement();
if ((listener != null) && (listener instanceof EventListener))
{
((EventListener)listener).handleEvent(aEvent);
}
else
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent(): listener is null!");
}
}
}
else
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent(): listeners is null!");
}
iCurrentEventData.clear();
}
else
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"handleEvent(): target not found");
}
// Execute the delayed registration.
doDelayedRegistration();
}
}
/**
* Check is event targets are equal
* @param aLhs Left hand side
* @param aRhs Right hand side
* @return True if equal
*/
private boolean isEquals(EventTarget aLhs, EventTarget aRhs)
{
int lhsHandle = M2GObject.INVALID_NATIVE_HANDLE;
int rhsHandle = M2GObject.INVALID_NATIVE_HANDLE;
if (aLhs instanceof M2GSVGElement)
{
lhsHandle = ((M2GSVGElement)aLhs).getHandle();
}
if (aRhs instanceof M2GSVGElement)
{
rhsHandle = ((M2GSVGElement)aRhs).getHandle();
}
if ((lhsHandle == rhsHandle) && (lhsHandle != M2GObject.INVALID_NATIVE_HANDLE))
{
return true;
}
return false;
}
/**
* Set an index that points to an event type hash table.
* The hash table is created if needed.
* @param aType Type
*/
private boolean prepare(String aType)
{
if (!setIndex(aType))
{
return false;
}
if (iEventTypes[iCurrentIndex] == null)
{
iEventTypes[iCurrentIndex] = new Hashtable();
}
return true;
}
/**
* Register an event target and an event listener pair
* @param aTarget Target
* @param aType Type
* @param aListener Listener
*/
public void register(EventTarget aTarget,
String aType,
EventListener aListener)
{
// NOTE: no need to check validity of aTarget & aListener. It's
// already done.
synchronized (this)
{
if (!prepare(aType))
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"register() - failed: " + aType);
return;
}
// Check if delayed registration is needed
// If an EventListener is added to an EventTarget while it is
// processing an event, it will not be triggered by
// the current actions.
if ((iCurrentEventData.iType == aType) &&
isEquals(iCurrentEventData.iTarget, aTarget))
{
try
{
iDelayedRegistrations.put(
aListener,
new M2GEventData(aTarget, aType, aListener));
}
catch (NullPointerException e)
{
}
return;
}
Vector listeners = null;
if (iEventTypes[iCurrentIndex].containsKey(aTarget))
{
listeners = (Vector)iEventTypes[iCurrentIndex].get(aTarget);
}
else
{
listeners = new Vector();
iEventTypes[iCurrentIndex].put(aTarget, listeners);
}
// Don't add if the event target already contains the same listener
// for the same event.
if (!listeners.contains(aListener))
{
listeners.addElement(aListener);
}
}
}
/**
* Set an event type container index
* @param aType Type
* @return False if the type is invalid
*/
private boolean setIndex(String aType)
{
if (aType.equals(M2GSVGConstants.EVENT_ACTIVATE))
{
iCurrentIndex = INDEX_ACTIVATE;
}
else if (aType.equals(M2GSVGConstants.EVENT_CLICK))
{
iCurrentIndex = INDEX_CLICK;
}
else if (aType.equals(M2GSVGConstants.EVENT_FOCUS_IN))
{
iCurrentIndex = INDEX_FOCUS_IN;
}
else if (aType.equals(M2GSVGConstants.EVENT_FOCUS_OUT))
{
iCurrentIndex = INDEX_FOCUS_OUT;
}
else
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"setIndex() type:" + aType + " FALSE - end");
// Wrong event type
iCurrentIndex = INDEX_UNDEFINED;
return false;
}
return true;
}
/**
* Unregister an event target and an event listener pair
* @param aTarget Target
* @param aType Type
* @param aListener Listener
*/
public void unregister(EventTarget aTarget,
String aType,
EventListener aListener)
{
synchronized (this)
{
if (!prepare(aType))
{
return;
}
// Remove delayed registration
iDelayedRegistrations.remove(aListener);
// Remove real registration
Vector listeners = (Vector)iEventTypes[iCurrentIndex].get(aTarget);
boolean removed = false;
if (listeners != null)
{
removed = listeners.removeElement(aListener);
if (listeners.isEmpty())
{
iEventTypes[iCurrentIndex].remove(aTarget);
}
}
if (removed)
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"unregister() ok - end");
}
else
{
Logger.LOG(Logger.EJavaUI, Logger.EInfo,
"unregister() failed - end");
}
}
}
/**
* Helper class for handling delayed registration
*/
class M2GEventData
{
public EventTarget iTarget = null;
public String iType = null;
public EventListener iListener = null;
/**
* Ctor
*/
public M2GEventData()
{
}
/**
* Ctor
* @param aTarget Target
* @param aType Event type
* @param aListener Event listener
*/
public M2GEventData(
EventTarget aTarget,
String aType,
EventListener aListener)
{
set(aTarget, aType, aListener);
}
/**
* Clearing
*/
public void clear()
{
iTarget = null;
iType = null;
iListener = null;
}
/**
* Setter
* @param aTarget Target
* @param aType Event type
* @param aListener Event listener
*/
public void set(EventTarget aTarget,
String aType,
EventListener aListener)
{
iTarget = aTarget;
iType = aType;
iListener = aListener;
}
}
}
//--------------------------------------------------
// STATIC METHODS
//--------------------------------------------------
/**
* Creates an empty SVG document. The document contains a
* root <svg> element with default viewport size of 100x100.
* @param aImage -
* @param aHander -
* @return document object
* @throws IllegalArgumentException
* @throws IOException
*/
static public M2GDocument buildEmptyDocument(M2GSVGImage aImage,
ExternalResourceHandler aHandler)
throws IllegalArgumentException, IOException
{
return M2GDocument.buildDocument(
aImage,
null,
null,
M2GSVGConstants.getInstance().iEmptySvgDocument,
aHandler);
}
/**
* Builds document
* @param aImage Svg image
* @param aBaseUrl A base url from where a document is downloaded.
* @param aSuffixUrl The suffix url from where a document is downloaded.
* @param aData Plain text svg data
* @param aHandler Resource handler. If null the default resource handler is
* used.
* @return document object
* @throws IllegalArgumentException
* @throws IOException
*/
static public M2GDocument buildDocument(M2GSVGImage aImage,
String aBaseUrl,
String aSuffixUrl,
String aData,
ExternalResourceHandler aHandler)
throws IOException
{
if ((aData == null) || (aData.equals("")))
{
throw new IOException(
/*SF*/"The SVG document does not conform to the XML 1.0 specification."/*SF*/);
}
M2GDocument document = new M2GDocument(aImage, aBaseUrl, aSuffixUrl, aData);
if (document.doCheckValidity())
{
document.setResourceHandler(aHandler);
return document;
}
throw new IOException(
/*SF*/"The SVG document does not conform to the XML 1.0 specification."/*SF*/);
}
/**
* Registers an observer to any DOM change
* @since S60 3.2
*/
public void registerDOMChangeObserver(M2GDOMChangeObserver aDOMChangeObserver)
{
iDOMChangeObserver = aDOMChangeObserver;
}
/**
* Notify the observer about any change in DOM
* SVGSVGElement is always part of DOM
* @since S60 3.2
*/
public void notifyDOMChangeObserver()
{
if (iDOMChangeObserver != null)
{
iDOMChangeObserver.notifyDOMChange();
}
}
/**
* Notify the observer about any change in DOM only if the node is in DOM
* @param aNativeHandle the handle of a node
* @since S60 3.2
*/
public void notifyDOMChangeObserver(int aNativeHandle)
{
if (iDOMChangeObserver != null && isElementInDOM(aNativeHandle))
{
iDOMChangeObserver.notifyDOMChange();
}
}
//--------------------------------------------------
// NATIVE METHODS
//--------------------------------------------------
private native static int _createDocument(
int aSvgProxyHandle, String aString);
private native static int _createElementNS(
int aSvgProxyHandle, short aType,
int aDocumentHandle);
private native static void _deleteDocument(
int aSvgProxyHandle, int aDocumentHandle);
private native static int _getElementById(
int aSvgProxyHandle, int aDocumentHandle,
String aId);
private static native int _getViewportHeight(
int aSvgProxyHandle, int aDocumentHandle);
private static native int _getViewportWidth(
int aSvgProxyHandle, int aDocumentHandle);
private native static int _isElementInDOM(
int aSvgProxyHandle, int aDocumentHandle,
int aElementHandle);
private static native int _requestCompleted(
int aSvgProxyHandle, int aDocumentHandle,
String aUri, byte[] aResourceData);
private static native void _setViewportHeight(
int aSvgProxyHandle, int aDocumentHandle, int aHeight);
private static native void _setViewportWidth(
int aSvgProxyHandle, int aDocumentHandle, int aWidth);
}