package net.xoetrope.html;
import com.sun.java.browser.dom.DOMAccessException;
import com.sun.java.browser.dom.DOMAccessor;
import com.sun.java.browser.dom.DOMAction;
import com.sun.java.browser.dom.DOMService;
import com.sun.java.browser.dom.DOMUnsupportedException;
import java.awt.Color;
import java.awt.Dimension;
import java.util.EventObject;
import java.util.Hashtable;
import java.util.Vector;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xui.PageSupport;
import net.xoetrope.xui.WidgetAdapter;
import net.xoetrope.xui.XComponentFactory;
import net.xoetrope.xui.XPageHelper;
import net.xoetrope.xui.XPageManager;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.data.XDataBinding;
import net.xoetrope.xui.data.XModel;
import net.xoetrope.xui.events.XuiEventHandler;
import net.xoetrope.xui.exception.XExceptionHandler;
import net.xoetrope.xui.style.XStyleFactory;
import net.xoetrope.xui.validation.XValidationFactory;
import net.xoetrope.xui.validation.XValidationHandler;
import net.xoetrope.xui.validation.XValidator;
import netscape.javascript.JSObject;
import org.w3c.dom.Element;
import org.w3c.dom.html.HTMLDivElement;
import org.w3c.dom.html.HTMLDocument;
import org.w3c.dom.html.HTMLImageElement;
import org.w3c.dom.html.HTMLInputElement;
import org.w3c.dom.html.HTMLLabelElement;
import org.w3c.dom.html.HTMLMetaElement;
import org.w3c.dom.html.HTMLOListElement;
import org.w3c.dom.html.HTMLSelectElement;
import org.w3c.dom.html.HTMLTableElement;
import org.w3c.dom.html.HTMLTextAreaElement;
import org.w3c.dom.html.HTMLUListElement;
/**
* A wrapper for a HTML page
* <p>
* Copyright (c) Xoetrope Ltd., 2002-2004
* </p>
* <p>
* License: see license.txt
* </p>
* $Revision: 2.2 $
*/
public class XHtmlPage implements XExceptionHandler, PageSupport
{
// - Start of HTML Specific members ------------------------------------------
protected JSObject win, doc;
protected HTMLDocument htmlDoc;
// - End of HTML Specific members --------------------------------------------
/**
* The factory used to construct components
*/
protected XStyleFactory componentFactory;
/**
* The owner project
*/
protected XProject project;
/**
* The page manager for the current project
*/
protected XPageManager pageMgr;
/**
* The root model of the current project
*/
protected XModel rootMdl;
/**
* The component adapter for the current widget set
*/
protected WidgetAdapter adapter;
/**
* The helper class that implements many functions on behlaf of the page,
* particularly the widget specific operations
*/
protected XPageHelper pageHelper;
private Hashtable classTypes;
/** Creates a new instance of XHtmlPage */
public XHtmlPage( XProject currentProject )
{
win = JSObject.getWindow( XApplet.getApplet() );
doc = (JSObject)win.getMember( "document" );
project = currentProject;
rootMdl = project.getModel();
pageMgr = project.getPageManager();
adapter = WidgetAdapter.getInstance();
pageHelper = new XPageHelper( project, this );
DOMService service = null;
try {
service = DOMService.getService( XApplet.getApplet() );
/*String title = (String)*/service.invokeAndWait( new DOMAction()
{
public Object run( DOMAccessor accessor )
{
htmlDoc = (HTMLDocument)accessor.getDocument( XApplet.getApplet() );
return htmlDoc.getTitle();
}
} );
}
catch ( DOMUnsupportedException e1 ) {
}
catch ( DOMAccessException e2 ) {
}
}
/**
* Find a named component in the container. Any child containers of the
* container will be searched recursively till the named component is found.
* The first component with a matching name will be returned.
*
* @param name
* the name to locate
* @return the component ornull if nothing is found
*/
public Object findComponent( String name )
{
return findComponent( this, name );
}
/**
* Find a named component in the container. Any child containers of the
* container will be searched recursively till the named component is found.
* The first component with a matching name will be returned.
*
* @param container
* the page or container to search
* @param name
* the name to locate
* @return the component or null if nothing is found
*/
public Object findComponent( Object container, final String name )
{
// returned Object
Object component;
Element e;
HTMLDivElement divElement = null;
HTMLLabelElement labelElement = null;
HTMLSelectElement selectElement = null;
HTMLInputElement inputElement = null;
HTMLImageElement imageElement = null;
HTMLOListElement olElement = null;
HTMLUListElement ulElement = null;
HTMLMetaElement metaElement = null;
HTMLTextAreaElement textareaElement = null;
HTMLTableElement tableElement = null;
String type = "";
if ( classTypes == null ) {
// array containing the classes the program can find
final String[] clsTab = {
"[object HTMLInputElement]", "[object HTMLSelectElement]", "[object HTMLLabelElement]", "[object HTMLImageElement]",
"[object HTMLOListElement]", "[object HTMLUListElement]", "[object HTMLMetaElement]", "[object HTMLTextAreaElement]",
"[object HTMLTableElement]", "[object HTMLDivElement]"
};
classTypes = new Hashtable();
for ( int i = 0; i < clsTab.length; i++ )
classTypes.put( clsTab[ i ], new Integer( i ) );
}
if ( container != this || htmlDoc == null ) {
DOMService service = null;
try {
service = DOMService.getService( XApplet.getApplet() );
type = (String)service.invokeAndWait( new DOMAction()
{
public Object run( DOMAccessor accessor )
{
htmlDoc = (HTMLDocument)accessor.getDocument( XApplet.getApplet() );
return "";
}
} );
}
catch ( DOMUnsupportedException e1 ) {
}
catch ( DOMAccessException e2 ) {
}
}
e = htmlDoc.getElementById( name );
type = e.toString();
// calls the constructor of the class found
switch ( ( (Integer)classTypes.get( type ) ).intValue() ) {
case 0:
inputElement = (HTMLInputElement)e;
String attribName = e.getAttribute( "type" );
if ( attribName.equals( "text" ) ) {
component = new XEdit( inputElement );
break;
}
else if ( attribName.equals( "password" ) ) {
component = new XPassword( inputElement );
break;
}
else if ( attribName.equals( "radio" ) ) {
component = new XRadioButton( inputElement );
break;
}
else if ( attribName.equals( "checkbox" ) ) {
component = new XCheckbox( inputElement );
break;
}
else if ( attribName.equals( "button" ) || attribName.equals( "submit" ) || attribName.equals( "image" ) ) {
component = new XButton( inputElement );
break;
}
else {
component = null;
break;
}
case 1:
selectElement = (HTMLSelectElement)e;
component = new XComboBox( selectElement );
break;
case 2:
labelElement = (HTMLLabelElement)e;
component = new XLabel( labelElement );
break;
case 3:
imageElement = (HTMLImageElement)e;
component = new XImage( imageElement );
break;
case 4:
olElement = (HTMLOListElement)e;
component = new XList( olElement );
break;
case 5:
ulElement = (HTMLUListElement)e;
component = new XList( ulElement );
break;
case 6:
metaElement = (HTMLMetaElement)e;
component = new XMetaContent( metaElement );
break;
case 7:
textareaElement = (HTMLTextAreaElement)e;
component = new XTextArea( textareaElement );
break;
case 8:
tableElement = (HTMLTableElement)e;
component = new XTable( tableElement );
break;
case 9:
divElement = (HTMLDivElement)e;
component = new XPanel( divElement );
break;
default:
component = null;
break;
}
// returns the component Object with the type of the class found
return component;
}
/**
* Get the parent
*
* @return the owner/parent of the page
*/
public Object getOwner()
{
return null;// getParent();
}
/**
* Show or hide the components. In the AWT the heavyweight peers are created
* visible and paint themselves once created and therefore cause problems for
* page transitions.
*
* @param recursionLevel
* the number of layers of recursion (occurs with nested panels)
* @param container
* the container whose components are being modified
* @param visible
* the visibility flag, true to make the components visible
*/
public void showComponents( Object container, boolean visible, int recursionLevel )
{
pageHelper.showComponents( container, visible, recursionLevel );
}
// --Start of
// Validation-------------------------------------------------------
/**
* Set the validation exception handler called when a validation exception is
* trapped
*
* @param eh
* the new event handler
*/
public void setExceptionHandler( XExceptionHandler eh )
{
pageHelper.setExceptionHandler( eh );
}
/**
* A method called when a validation exeption has been trapped.
*
* @param comp
* Component being validated
* @param ex
* The exception caused
* @param validator
* The validator being used to validate.
* @return true to continue with error validation or false to suppress further
* validation.
*/
public boolean handleException( Object comp, Exception ex, Object validator )
{
return pageHelper.handleException( comp, ex, validator );
}
/**
* Reset/removes all validations
*/
public void clearValidations()
{
pageHelper.clearValidations();
}
/**
* Check all validations for this page. Typically this method should be
* invoked prior to a page transition or a critical transaction.
*
* @return the maximum error level raised by the validators
*/
public int checkValidations()
{
return pageHelper.checkValidations();
}
/**
* informs the handler when a page validation is starting or stopping.
* Typically when it starts the page will begin to accumulate message which
* are to be displayed. When the parameter is false the page will usually
* display the accumulated messages
*
* @param start
* boolean to indicate whether the accumulation is started or
* stopped.
* @param level
* int which indicates the most serious level of error encountered
* @return the new level which might be set to zero if a confirm dialog is
* displayed
*/
public int accumulateMessages( boolean start, int level )
{
return pageHelper.accumulateMessages( start, level );
}
/**
* Handle an exception during the invocation of a page's event handler. The
* page normally implements this interface and has the first chance at
* handling the error. Thereafter if false is returned a central (optional)
* exception handler owned by the project is invoked.
*
* @param project
* the current project
* @param container
* the page
* @param error
* the exception or error that was thrown
* @return true to continue processing, false to stop processing
*/
public boolean handleEventHandlerException( XProject project, Object container, Throwable error )
{
return pageHelper.handleEventHandlerException( project, container, error );
}
/**
* Adds a validation to this page.
*
* @param comp
* the component being validated
* @param validationName
* the name of the validation in the validation file
* @param method
* the method used to get the component's value if any
* @param mask
* the event mask used to filter the events that trigger the
* validation
* @param pageEle
* the XML element which is declared in the page
* @return the new and initialized XValidator
*/
public XValidator addValidation( Object comp, String validationName, String method, int mask, XmlElement pageEle )
{
return pageHelper.addValidation( comp, validationName, method, mask, pageEle );
}
/**
* Adds a validation to this page. It is assumed that the validation will be
* invoked in response to FocusEvent.FOCUS_LOST events
*
* @param comp
* the component being validated
* @param validationName
* the name of the validation in the validation file
* @param method
* the method used to get the component's value if any
* @return the new and initialized XValidator
*/
public XValidator addValidation( Object comp, String validationName, String method )
{
return pageHelper.addValidation( comp, validationName, method );
}
/**
* Adds a validation to this page. It is assumed that the validation will be
* invoked in response to FocusEvent.FOCUS_LOST events
*
* @param comp
* the component being validated
* @param validationName
* the name of the validation in the validation file
* @return the new and initialized XValidator
*/
public XValidator addValidation( Object comp, String validationName )
{
return pageHelper.addValidation( comp, validationName );
}
/**
* Sets the factory used to create XValidator objects
*
* @param vf
* The validation factory
*/
public void setValidationFactory( XValidationFactory vf )
{
pageHelper.setValidationFactory( vf );
}
/**
* Gets the validation handler
*
* @return the validation handler
*/
public XValidationHandler getValidationHandler()
{
return pageHelper.getValidationHandler();
}
/**
* Invoke the validators for the last event. Multiple validations are checked
* in the order in which they were added.
*
* @return the maximum level returned by the validators
*/
public int validationHandler()
{
return pageHelper.validationHandler( getEventHandler() );
}
// --End of
// Validation---------------------------------------------------------
// --Start of Event
// Handling---------------------------------------------------
/**
* Get the current event handler
*
* @return the event handler
*/
public XuiEventHandler getEventHandler()
{
return pageHelper.getEventHandler();
}
/**
* Set the current event handler
*
* @param eh
* The event handler
*/
public void setEventHandler( XuiEventHandler eh )
{
pageHelper.setEventHandler( eh );
}
/**
* Get the current event
*
* @return the AWTEvent that was last triggered
*/
public EventObject getCurrentEvent()
{
return pageHelper.getCurrentEvent();
}
/**
* Adds a listener for an event type. This method should not normally be
* called by an application
*
* @param comp
* the component that fires events
* @param listenerName
* the name of the listener interface
* @param argType
* the listener arguments
* @param listener
* the listener implementation
*/
public void addListener( Object comp, String listenerName, String argType, Object listener )
{
pageHelper.addListener( comp, listenerName, argType, listener );
}
/**
* Adds an event handler. A specific handler such as the addActionHandler
* should be used instead of calling this method
*
* @param comp
* the component that fires the event
* @param eventType
* the event ID/mask
* @param methodName
* the method to be invoked in response to the object
* @throws java.lang.Exception
* The handler could not be found or added
*/
public void addHandler( Object comp, long eventType, String methodName ) throws Exception
{
pageHelper.addHandler( comp, eventType, methodName );
}
/**
* Adds a handler for action events
*
* @param srcObj
* the menu item that fires the events
* @param methodName
* the method to be invoked in response to the action event
* @param adderMethod
* the adder method name e.g. addActionListener
* @param listenerInterface
* the listener interface e.g. java.awt.event.ActionListener
* @param eventMask
* the event mask e.g. AWTEvent.ACTION_EVENT_MASK
* @param listener
* the listener implementation, usually the page's this pointer
* @see java.awt.event.ActionListener
* @see java.awt.event.ActionEvent
*/
public void addHandler( Object srcObj, String methodName, String adderMethod, String listenerInterface, long eventMask, Object listener )
{
pageHelper.addHandler( srcObj, methodName, adderMethod, listenerInterface, eventMask, listener );
}
/**
* Check the focus change status
*
* @return true if the focus change events are being suppressed.
*/
public boolean isFocusChangeSuppressed()
{
return pageHelper.isFocusChangeSuppressed();
}
/**
* A utility method used to determine if the last event corrseponds to a mouse
* click. The notion of a click is extended by assuming the a mouse press and
* release within a single component constitutes a click even if not at the
* same coordinate. A MouseEvent.MOUSE_CLICKED is only triggered when the
* press and release are at the same location and this is often inadequate for
* end-user interaction.
*
* @return true if the mouse was clicked
*/
public boolean wasMouseClicked()
{
return pageHelper.wasMouseClicked();
}
/**
* A utility method used to determine if the last event corrseponds to a mouse
* double click. The notion of a click is extended by assuming the a mouse
* press and release within a single component constitutes a click even if not
* at the same coordinate. A MouseEvent.MOUSE_CLICKED is only triggered when
* the press and release are at the same location and this is often inadequate
* for end-user interaction.
*
* @return true if the mouse was double clicked
*/
public boolean wasMouseDoubleClicked()
{
return pageHelper.wasMouseDoubleClicked();
}
/**
* A utility method used to determine if the last event corrseponds to a mouse
* right click. The notion of a click is extended by assuming the a mouse
* press and release within a single component constitutes a click even if not
* at the same coordinate. A MouseEvent.MOUSE_CLICKED is only triggered when
* the press and release are at the same location and this is often inadequate
* for end-user interaction.
*
* @return true if the mouse was right clicked
*/
public boolean wasMouseRightClicked()
{
return pageHelper.wasMouseRightClicked();
}
/**
* Show the hand/pointer cursor for this component
*
* @param comp
* the component
*/
public void showHandCursor( Object comp )
{
pageHelper.showHandCursor( comp );
}
// --End of Event
// Handling-----------------------------------------------------
// --Start of Data
// Binding-----------------------------------------------------
/**
* Get the data bindings
*
* @return the bindings collection
*/
public Vector getBindings()
{
return pageHelper.getBindings();
}
/**
* Add a binding of a component to the data model. If the page has already
* been activated this method will update the binding automatically.
*
* @param b
* the binding
*/
public void addBinding( XDataBinding b )
{
pageHelper.addBinding( b );
}
/**
* Remove a binding of a component to the data model.
*
* @param b
* the binding
*/
public void removeBinding( XDataBinding b )
{
pageHelper.removeBinding( b );
}
/**
* Iterate all of the bindings in the page to reflect the model state.
*/
public void updateBindings()
{
pageHelper.updateBindings();
}
/**
* Update the bound model node for the binding. First the output path is
* reevaluated and then updated by setting the output node. Then the source
* path is reevaluated and set. Evaluation of the paths allows derived classes
* to dynamically modify the bindings. Some bindings may save the selection or
* state information to the output node and subsequently use it to restore the
* component state. This method does not alter the data held by the bound
* model nodes. To actually save the data use saveBoundComponentValues and to
* update the UI use updateBoundComponentValues.
*
* @param binding
* the binding to update
*/
public void updateBinding( XDataBinding binding )
{
pageHelper.updateBinding( binding );
}
/**
* Update the UI with values from the model
*/
public void updateBoundComponentValues()
{
pageHelper.updateBoundComponentValues();
}
/**
* Save the component values to the model
*/
public void saveBoundComponentValues()
{
pageHelper.saveBoundComponentValues();
}
/**
* Find the data binding associated with a component
*
* @param targetComp
* the component whose binding is required
* @return the binding or null if no binding is found
*/
public XDataBinding getBinding( Object targetComp )
{
return pageHelper.getBinding( targetComp );
}
/**
* Find the data binding associated with a data source path
*
* @param targetPath
* the path to the bound model
* @return the binding or null if no binding is found
*/
public XDataBinding getBinding( String targetPath )
{
return pageHelper.getBinding( targetPath );
}
/**
* Get the page status
*
* @return the current status
*/
public int getStatus()
{
return pageHelper.getStatus();
}
/**
* Set the page status
*
* @param newStatus
* the new page status
*/
public void setStatus( int newStatus )
{
pageHelper.setStatus( newStatus );
}
/**
* A method called once the page has been created and initialized but just
* prior to display
*/
public void pageActivated()
{
pageHelper.pageActivated();
}
/**
* A method called once the page has been added to its parent container but
* not yet displayed
*/
public void pageAdded()
{
pageHelper.pageAdded();
}
/**
* A method called once the page has been created but not yet initialized.
*/
public void pageCreated()
{
pageHelper.pageCreated();
}
/**
* Called when the page is about to loose scope and be hidden.
*/
public void pageDeactivated()
{
pageHelper.pageDeactivated();
}
// --End of Data
// Binding-------------------------------------------------------
// --End of Attribute
// Methods--------------------------------------------------
/**
* <p>
* Set a named attributes. The attributes are stored in a hashtable owned by
* the page. Derived classes may access the hashtable directly but the
* preferred method of access is the getAttribute method. Attributes are used
* by the XuiBuilder class for component attributes other than those it
* handles directly. The attributes can be thought of as component properties
* or extra data and need not be used directly by the component.
* </p>
* <p>
* Attributes are stored using a key in the form attribName_compName or just
* the attribName if compName is null.
* </p>
*
* @param attribName
* the attribute name
* @param compName
* the component name or null if it is a page attribute
* @param attribValue
* the attribute value
* @see #getAttribute
*/
public void setAttribute( String attribName, String compName, Object attribValue )
{
pageHelper.setAttribute( attribName, compName, attribValue );
}
/**
* Gets an attribute value
*
* @param attribName
* the name of the attribute
* @return the value
*/
public Object getAttribute( String attribName )
{
return pageHelper.getAttribute( attribName );
}
/**
* Gets an attribute value
*
* @param attribName
* the name of the attribute
* @param compName
* the component name
* @return the value
*/
public Object getAttribute( String attribName, String compName )
{
return pageHelper.getAttribute( attribName, compName );
}
/**
* Get a name for a component. If the component doesn't have one use the
* component hashcode
*
* @param comp
* the component
* @return the name
*/
public String getComponentName( Object comp )
{
return pageHelper.getComponentName( comp );
}
/**
* Gets an attribute value
*
* @return the value
* @param c
* the component whose attribute is being requested
* @param attribName
* the name of the attribute
*/
public Object getEventAttribute( Object c, String attribName )
{
return pageHelper.getEventAttribute( c, attribName );
}
/**
* Evaluates an attribute value. An attribute may be a value or a method call.
* If brackets are part of the value it is assumed that a method call is
* intended. The method call is indicated by the '$' symbol e.g. ${myMethod()}
*
* @param attribValue
* the raw attribute value
* @return the evaluated attribute
*/
public Object evaluateAttribute( String attribValue )
{
return pageHelper.evaluateAttribute( attribValue );
}
/**
* Evaluates a path (potentially) containing a method call
*
* @param path
* the raw path
* @return the evaluated path
*/
public String evaluatePath( String path )
{
return pageHelper.evaluatePath( path );
}
/**
* Remove the attribute paths from a path e.g. remove
*
* @value=ignore
* @param path
* the path to strip
* @return the stripped path
*/
public String stripAttributeValues( String path )
{
return pageHelper.stripAttributeValues( path );
}
/**
* Get the component factory instance being used by this page.
*
* @return the component factory
*/
public XComponentFactory getComponentFactory()
{
return pageHelper.getComponentFactory();
}
/**
* Set the component factory instance being used by this page when
* constructing new pages.
*
* @param factory
* the component factory
*/
public void setComponentFactory( XStyleFactory factory )
{
pageHelper.setComponentFactory( factory );
componentFactory = factory;
}
/**
* Translate a string by looking it up in the current resource bundle
*
* @param key
* the resource bundle key
* @return the translated text
*/
public String translate( String key )
{
return pageHelper.translate( key );
}
// --End of Attribute
// Methods--------------------------------------------------
// --Start of MessageBox
// methods-----------------------------------------------
/**
* Shows a modal message box
*
* @param title
* the message dialog title
* @param msg
* the text of the message
*/
public void showMessage( String title, String msg )
{
pageHelper.showMessage( title, msg );
}
/**
* Shows a modal message box
*
* @param parent
* the message dialog parent
* @param title
* the message dialog title
* @param msg
* the text of the message
*/
public void showMessage( Object parent, String title, String msg )
{
pageHelper.showMessage( parent, title, msg );
}
// --End of MessageBox
// methods-------------------------------------------------
/**
* Set the page location
*
* @param x
* the new page's left edge coordinate
* @param y
* the new page's top coordinate
*/
public void setLocation( int x, int y )
{
}
/**
* Set the page size
*
* @param width
* the new page width
* @param height
* the new page height
*/
public void setSize( int width, int height )
{
}
/**
* Get the size
*
* @return The page's dimensions
*/
public Dimension getPageSize()
{
return null;
}
/**
* Validates this container and all of its subcomponents.
*/
public void validate()
{
}
/**
* Set the name of the component
*
* @param name
* the new name for the page
*/
public void setName( String name )
{
}
/**
* Modify the clearPage flag. This flag determines if the default behaviour is
* used to update the page whereby the background is first erased and then the
* content painted or alternatively if the erase is suppressed.
*
* @param value
* the new flag value, true to clear
*/
public void setClearPage( boolean value )
{
}
/**
* Set the visible state of the component
*
* @param state
* true to make the component visible
*/
public void setVisible( boolean state )
{
}
/**
* Set the background color
*
* @param c
* The color
*/
public void setBackground( Color c )
{
}
/**
* Set the layout manager
*
* @param obj
* the layout manager instance
*/
public void setLayout( Object obj )
{
}
/**
* Layout the page
*/
public void doLayout()
{
}
/**
* Get the name of the page
*
* @return the name
*/
public String getName()
{
return null;
}
/**
* Set the file extension of the page
*
* @param ext
* the file extension
*/
public void setExtension( String ext )
{
}
/**
* Get the file extension of the page
*
* @return the file extension
*/
public String getExtension()
{
return null;
}
/**
* Get the current project, the project that owns this page
*
* @return the owner project
*/
public XProject getProject()
{
return project;
}
}