/*******************************************************************************
* Copyright (c) 2002, 2009 Innoopract Informationssysteme GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.rwt.lifecycle;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.rwt.internal.lifecycle.*;
import org.eclipse.rwt.internal.service.ContextProvider;
import org.eclipse.rwt.internal.service.IServiceStateInfo;
import org.eclipse.rwt.internal.util.EncodingUtil;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.internal.widgets.WidgetAdapter;
import org.eclipse.swt.widgets.*;
/**
* This class provides helper methods to generate Javascript used to update the
* client-side state of widgets.
* <p>Note that the Javascript code that is rendered relies on the client-side
* <code>org.eclipse.swt.WidgetManager</code> to be present.</p>
*
* @see AbstractWidgetLCA
* @see ControlLCAUtil
* @see WidgetLCAUtil
*
* @since 1.0
*/
public final class JSWriter {
/**
* A reference to the current widget manager on the client side.
*
* <p><strong>IMPORTANT:</strong> This method is <em>not</em> part of the RWT
* public API. It is marked public only so that it can be shared
* within the packages provided by RWT. It should never be accessed
* from application code.
* </p>
*/
public static JSVar WIDGET_MANAGER_REF = new JSVar( "wm" );
/**
* Reference to the widget of this JSWriter instance.
*
* <p><strong>IMPORTANT:</strong> This method is <em>not</em> part of the RWT
* public API. It is marked public only so that it can be shared
* within the packages provided by RWT. It should never be accessed
* from application code.
* </p>
*/
public static JSVar WIDGET_REF = new JSVar( "w" );
private static final JSVar TARGET_REF = new JSVar( "t" );
private static final String WRITER_MAP
= JSWriter.class.getName() + "#map";
private static final String HAS_WINDOW_MANAGER
= JSWriter.class.getName() + "#hasWindowManager";
private static final String CURRENT_WIDGET_REF
= JSWriter.class.getName() + "#currentWidgetRef";
private static final Map setterNames = new HashMap();
private static final Map getterNames = new HashMap();
private static final Map resetterNames = new HashMap();
private final Widget widget;
/**
* Returns an instance of {@link JSWriter} for the specified
* <code>widget</code>. Only this writer can modify attributes and call
* methods on the client-side representation of the widget.
*
* @param widget the widget for the requested {@link JSWriter}
* @return the corresponding {@link JSWriter}
*/
public static JSWriter getWriterFor( final Widget widget ) {
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
JSWriter result;
Map map = ( Map )stateInfo.getAttribute( WRITER_MAP );
if( map == null ) {
map = new HashMap();
stateInfo.setAttribute( WRITER_MAP, map );
}
if( map.containsKey( widget ) ) {
result = ( JSWriter )map.get( widget );
} else {
result = new JSWriter( widget );
map.put( widget, result );
}
return result;
}
/**
* Returns the {@link JSWriter} instance used to reset a widgets attributes in
* order to take part in the pooling mechanism.
*
* @return the {@link JSWriter} instance
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method should not be used anymore.
*/
public static JSWriter getWriterForResetHandler() {
return new JSWriter( null );
}
private JSWriter( final Widget widget ) {
this.widget = widget;
}
/**
* Creates a new widget on the client-side by creating an instance of the
* corresponding javascript class defined by <code>className</code>. This
* is normally done in the <code>renderInitialization</code> method of the
* widgets life-cycle adapter (LCA).
*
* @param className the javascript class to initiate
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA#renderInitialization
*/
public void newWidget( final String className ) throws IOException {
newWidget( className, null );
}
/**
* Creates a new widget on the client-side by creating an instance of the
* corresponding javascript class definition. This is normally done in
* the <code>renderInitialization</code> method of the widgets life-cycle
* adapter (LCA). All arguments passed to this function will be transmitted
* to the client and used to call the constructor of the javascript widget.
*
* @param className the javascript class to initiate
* @param args the arguments for the widgets constructor on the client-side
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA#renderInitialization
*/
public void newWidget( final String className, final Object[] args )
throws IOException
{
ensureWidgetManager();
StringBuffer buffer = new StringBuffer();
boolean isControl = widget instanceof Control;
boolean isShell = widget instanceof Shell;
buffer.append( "var w = new " + className );
buffer.append( "(" + createParamList( " ", args, " ", false ) + ");" );
buffer.append( "wm.add( w, " );
buffer.append( "\"" + WidgetUtil.getId( widget ) + "\", " );
buffer.append( isControl + " );" );
getWriter().write( buffer.toString() );
setCurrentWidgetRef( widget );
if( isControl && !isShell ) {
setParent( getJSParentId( widget ) );
} else if( isShell ) {
call( "addToDocument", null );
}
}
/**
* Explicitly sets the parent of the client-side widget.
*
* @param parentId the widget id of the parent
* @throws IOException if an I/O error occurs
* @see WidgetUtil
*/
public void setParent( final String parentId ) throws IOException {
call( WIDGET_MANAGER_REF, "setParent", new Object[] { widget, parentId } );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param value the new value
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final String value )
throws IOException
{
call( getSetterName( jsProperty ), new Object[] { value } );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param value the new value
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final int value )
throws IOException
{
set( jsProperty, new int[] { value } );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param value the new value
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final float value )
throws IOException
{
set( jsProperty, new float[] { value } );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param value the new value
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final boolean value )
throws IOException
{
set( jsProperty, new boolean[] { value } );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param values the new values
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final int[] values )
throws IOException
{
String functionName = getSetterName( jsProperty );
Integer[] integers = new Integer[ values.length ];
for( int i = 0; i < values.length; i++ ) {
integers[ i ] = new Integer( values[ i ] );
}
call( widget, functionName, integers );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param values the new values
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final float[] values )
throws IOException
{
String functionName = getSetterName( jsProperty );
Float[] floats = new Float[ values.length ];
for( int i = 0; i < values.length; i++ ) {
floats[ i ] = new Float( values[ i ] );
}
call( widget, functionName, floats );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param values the new values
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final boolean[] values )
throws IOException
{
ensureWidgetRef();
String functionName = getSetterName( jsProperty );
Boolean[] parameters = new Boolean[ values.length ];
for( int i = 0; i < values.length; i++ ) {
parameters[ i ] = Boolean.valueOf( values[ i ] );
}
call( widget, functionName, parameters );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param value the new value
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final Object value )
throws IOException
{
set( jsProperty, new Object[] { value } );
}
/**
* Sets the specified property of the client-side widget to a new value.
*
* @param jsProperty the attribute to change
* @param values the new values
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String jsProperty, final Object[] values )
throws IOException
{
call( widget, getSetterName( jsProperty ), values );
}
/**
* Sets the specified properties of the client-side widget to new values.
*
* @param jsPropertyChain the attributes to change
* @param values the new values
*
* @throws IOException if an I/O error occurs
* @see AbstractWidgetLCA
*/
public void set( final String[] jsPropertyChain, final Object[] values )
throws IOException
{
call( widget, createPropertyChain( jsPropertyChain, false ), values );
}
/**
* Sets the specified <code>jsProperty</code> of the client-side widget to
* the new value. Uses the specified <code>javaProperty</code> as a key
* to obtain the preserved value and only sets the new value if it has
* changed since it was preserved.
*
* @param javaProperty the key used to obtain the preserved value
* @param jsProperty the client-side attribute to change
* @param newValue the new values
*
* @return <code>true</code> if the new value differs from the preserved
* value (meaning that JavaScript was written); <code>false</code> otherwise
*
* @throws IOException if an I/O error occurs
*
* @see AbstractWidgetLCA#preserveValues(Widget)
*/
public boolean set( final String javaProperty,
final String jsProperty,
final Object newValue )
throws IOException
{
IWidgetAdapter adapter = WidgetUtil.getAdapter( widget );
boolean changed
= !adapter.isInitialized()
|| WidgetLCAUtil.hasChanged( widget, javaProperty, newValue );
if( changed ) {
set( jsProperty, newValue );
}
return changed;
}
/**
* Sets the specified <code>jsProperty</code> of the client-side widget to
* the new value. Uses the specified <code>javaProperty</code> as a key
* to obtain the preserved value and only sets the new value if it has
* changed since it was preserved.
* <p>If the widget is rendered for the first time, there is no preserved
* value present. In this case the <code>defValue</code> is taken into
* account instead of the preserved value of the <code>javaProperty</code>.
* Therefore, the default value must match the initial value of the attribute
* of the client-side widget. If there is no constant initial client-side
* value, resort the {@link #set(String,String,Object)}.</p>
*
* @param javaProperty the key used to obtain the preserved value
* @param jsProperty the client-side attribute to change
* @param newValue the new values
* @param defValue the default value
*
* @return <code>true</code> if the new value differs from the preserved
* value (meaning that JavaScript was written); <code>false</code> otherwise
*
* @throws IOException if an I/O error occurs
*
* @see AbstractWidgetLCA#preserveValues(Widget)
*/
public boolean set( final String javaProperty,
final String jsProperty,
final Object newValue,
final Object defValue )
throws IOException
{
boolean changed
= WidgetLCAUtil.hasChanged( widget, javaProperty, newValue, defValue );
if( changed ) {
set( jsProperty, new Object[] { newValue } );
}
return changed;
}
/**
* Resets the specified javascript property to its initial value.
*
* @param jsProperty the javascript property to reset
*
* @throws IOException if an I/O error occurs
*/
public void reset( final String jsProperty ) throws IOException {
call( widget, getResetterName( jsProperty ), null );
}
/**
* Resets the specified javascript properties to their initial values.
*
* @param jsPropertyChain the javascript properties to reset
*
* @throws IOException if an I/O error occurs
*/
public void reset( final String[] jsPropertyChain ) throws IOException {
call( widget, createPropertyChain( jsPropertyChain, true ), null );
}
/**
* This will add a listener to an object specified by the property of
* the widget. The listener has to be a javascript function which accepts
* exact one parameter - an <code>qx.event.type.Event</code> object.
*
* @param property the property of the widget to what the listener should be added
* @param eventType the type of the event
* @param listener reference to the listener function
*
* @throws IOException if an I/O error occurs
*/
public void addListener( final String property,
final String eventType,
final String listener )
throws IOException
{
ensureWidgetRef();
if( property == null ) {
// TODO [rh] HACK to allow 'instance' listener instead of static listener
// functions
if( listener.startsWith( "this." ) ) {
String thisListener = listener.substring( 5 );
String code = "w.addEventListener( \"{0}\", w.{1}, w );";
write( code, eventType, thisListener );
} else {
String code = "w.addEventListener( \"{0}\", {1} );";
write( code, eventType, listener );
}
} else {
String code = "w.{0}().addEventListener( \"{1}\", {2} );";
write( code, getGetterName( property ), eventType, listener );
}
}
/**
* This will add a listener to the widget of this {@link JSWriter}. The
* listener has to be a javascript function which accepts exact one
* parameter - an <code>qx.event.type.Event</code> object.
*
* @param eventType the type of the event
* @param listener reference to the listener function
*
* @throws IOException if an I/O error occurs
*/
public void addListener( final String eventType, final String listener )
throws IOException
{
addListener( null, eventType, listener );
}
public void updateListener( final String property,
final JSListenerInfo info,
final String javaListener,
final boolean hasListeners )
throws IOException
{
if( info.getJSListenerType() == JSListenerType.ACTION ) {
updateActionListener( property, info, javaListener, hasListeners );
} else {
updateStateAndActionListener( property,
info,
javaListener,
hasListeners );
}
}
public void updateListener( final JSListenerInfo info,
final String javaListener,
final boolean hasListeners )
throws IOException
{
updateListener( null, info, javaListener, hasListeners );
}
public void removeListener( final String eventType, final String listener )
throws IOException
{
removeListener( null, eventType, listener );
}
public void removeListener( final String property,
final String eventType,
final String listener )
throws IOException
{
ensureWidgetRef();
if( property == null ) {
// TODO [rh] HACK to allow 'instance' listener instead of static listener
// functions
if( listener.startsWith( "this." ) ) {
String thisListener = listener.substring( 5 );
String code = "w.removeEventListener( \"{0}\", w.{1}, w );";
write( code, eventType, thisListener );
} else {
String code = "w.removeEventListener( \"{0}\", {1} );";
write( code, eventType, listener );
}
} else {
String code = "w.{0}().removeEventListener( \"{1}\", {2} );";
write( code, getGetterName( property ), eventType, listener );
}
}
/**
* Calls a specific function of the widget on the client-side.
*
* @param function the function name
* @param args the arguments for the function
*
* @throws IOException if an I/O error occurs
*/
public void call( final String function, final Object[] args )
throws IOException
{
call( widget, function, args );
}
/**
* Calls the specific <code>function</code> of the widget identified by
* <code>target</code> on the client-side.
*
* @param target the widget on which the function should be called
* @param function the function to be called
* @param args the arguments for the function
*
* @throws IOException if an I/O error occurs
*/
public void call( final Widget target,
final String function,
final Object[] args )
throws IOException
{
ensureWidgetManager();
JSVar refVariable;
if( target == widget ) {
ensureWidgetRef();
refVariable = WIDGET_REF;
} else {
refVariable = TARGET_REF;
writeVarAssignment( refVariable, createFindWidgetById( target ) );
}
String params = createParamList( args );
writeCall( refVariable, function, params );
}
/**
* Calls the specific <code>function</code> of the widget identified by
* <code>target</code> on the client-side.
*
* @param target the widget on which the function should be called
* @param function the function name
* @param args the arguments for the function
*
* @throws IOException if an I/O error occurs
*/
public void call( final JSVar target,
final String function,
final Object[] args )
throws IOException
{
ensureWidgetManager();
String params = createParamList( args );
writeCall( target, function, params );
}
public void startCall( final JSVar target,
final String function,
final Object[] args )
throws IOException
{
ensureWidgetManager();
String params = createParamList( " ", args, "", false );
write( "{0}.{1}({2}", target, function, params );
}
public void endCall( final Object[] args ) throws IOException {
getWriter().write( createParamList( "", args, "", false ) );
getWriter().write( " );" );
}
/**
* Calls the specified Javascript function with the given arguments.
*
* @param function the function name
* @param args the arguments for the function
*
* @throws IOException if an I/O error occurs
*/
// TODO [rh] should we name this method 'call' and make it a static method?
public void callStatic( final String function, final Object[] args )
throws IOException
{
ensureWidgetManager();
String params = createParamList( args );
StringBuffer buffer = new StringBuffer();
buffer.append( function );
buffer.append( '(' );
buffer.append( params );
buffer.append( ");" );
getWriter().write( buffer.toString() );
}
public void callFieldAssignment( final JSVar target,
final String field,
final String value )
throws IOException
{
write( "{0}.{1} = {2};", target, field, value );
}
public void varAssignment( final JSVar var,
final String method )
throws IOException
{
ensureWidgetManager();
ensureWidgetRef();
String value = WIDGET_REF + "." + method + "()";
writeVarAssignment( var, value );
}
/**
* Dispose is used to dispose of the widget of this {@link JSWriter} on the
* client side.
*
* @throws IOException if an I/O error occurs
*/
public void dispose() throws IOException {
ensureWidgetManager();
String widgetId = WidgetUtil.getId( widget );
if( widget instanceof Control ) {
ControlLCAUtil.resetActivateListener( ( Control )widget );
}
call( WIDGET_MANAGER_REF, "dispose", new Object[] { widgetId } );
}
////////////////////////////////////////////////////////////////
// helping methods for client side listener addition and removal
private void updateActionListener( final String property,
final JSListenerInfo info,
final String javaListener,
final boolean hasListeners )
throws IOException
{
IWidgetAdapter adapter = WidgetUtil.getAdapter( widget );
if( adapter.isInitialized() ) {
Boolean hadListeners = ( Boolean )adapter.getPreserved( javaListener );
if( hadListeners == null || Boolean.FALSE.equals( hadListeners ) ) {
if( hasListeners ) {
addListener( property, info.getEventType(), info.getJSListener() );
}
} else if( !hasListeners ) {
removeListener( property, info.getEventType(), info.getJSListener() );
}
} else {
if( hasListeners ) {
addListener( property, info.getEventType(), info.getJSListener() );
}
}
}
private void updateStateAndActionListener( final String property,
final JSListenerInfo info,
final String javaListener,
final boolean hasListeners )
throws IOException
{
IWidgetAdapter adapter = WidgetUtil.getAdapter( widget );
if( adapter.isInitialized() ) {
Boolean hadListeners = ( Boolean )adapter.getPreserved( javaListener );
if( hadListeners == null || Boolean.FALSE.equals( hadListeners ) ) {
if( hasListeners ) {
removeListener( property, info.getEventType(), info.getJSListener() );
addListener( property,
info.getEventType(),
createJsActionListener( info ) );
}
} else {
if( !hasListeners ) {
removeListener( property,
info.getEventType(),
createJsActionListener( info ) );
addListener( property, info.getEventType(), info.getJSListener() );
}
}
} else {
if( hasListeners ) {
addListener( property,
info.getEventType(),
createJsActionListener( info ) );
} else {
addListener( property, info.getEventType(), info.getJSListener() );
}
}
}
private String createJsActionListener( final JSListenerInfo info ) {
StringBuffer buffer = new StringBuffer();
buffer.append( info.getJSListener() );
buffer.append( "Action" );
return buffer.toString();
}
/////////////////////////////////////////////////////////////////////
// Helping methods for JavaScript WidgetManager and Widget references
private String getJSParentId( final Widget widget ) {
String result = "";
if( !( widget instanceof Shell ) && widget instanceof Control ) {
Control control = ( Control )widget;
WidgetAdapter adapter = ( WidgetAdapter )WidgetUtil.getAdapter( control );
if( adapter.getJSParent() == null ) {
result = WidgetUtil.getId( control.getParent() );
} else {
result = adapter.getJSParent();
}
}
return result;
}
private void ensureWidgetManager() throws IOException {
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
if( currentPhaseIsRender()
&& widget != null
&& stateInfo.getAttribute( HAS_WINDOW_MANAGER ) == null )
{
writeVarAssignment( WIDGET_MANAGER_REF,
"org.eclipse.swt.WidgetManager.getInstance()" );
stateInfo.setAttribute( HAS_WINDOW_MANAGER, Boolean.TRUE );
}
}
// TODO [fappel]: FontSizeCalculation causes problems with widget manager
// in IE. See FontSizeCalculationHandler#createFontParam.
// Until a better solution is found this hack is needed.
private boolean currentPhaseIsRender() {
return CurrentPhase.get() != null
&& CurrentPhase.get() != PhaseId.PROCESS_ACTION
&& CurrentPhase.get() != PhaseId.PREPARE_UI_ROOT
&& CurrentPhase.get() != PhaseId.READ_DATA;
}
private void ensureWidgetRef() throws IOException {
ensureWidgetManager();
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
Object currentWidgetRef = stateInfo.getAttribute( CURRENT_WIDGET_REF );
if( widget != currentWidgetRef && widget != null ) {
writeVarAssignment( WIDGET_REF, createFindWidgetById( widget ) );
setCurrentWidgetRef( widget );
}
}
private static void setCurrentWidgetRef( final Widget widget ) {
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
stateInfo.setAttribute( CURRENT_WIDGET_REF, widget );
}
private static Widget getCurrentWidgetRef() {
IServiceStateInfo stateInfo = ContextProvider.getStateInfo();
return ( Widget )stateInfo.getAttribute( CURRENT_WIDGET_REF );
}
private static String createFindWidgetById( final Widget widget ) {
return createFindWidgetById( WidgetUtil.getId( widget ) );
}
private static String createFindWidgetById( final String id ) {
StringBuffer buffer = new StringBuffer();
buffer.append( WIDGET_MANAGER_REF.toString() );
buffer.append( ".findWidgetById( \"" );
buffer.append( id );
buffer.append( "\" )" );
return buffer.toString();
}
///////////////////////////////////////////////
// Helping methods to construct parameter lists
private static String createParamList( final Object[] args ) {
return createParamList( " ", args, " ", true );
}
private static String createParamList( final String startList,
final Object[] args,
final String endList,
final boolean useCurrentWidgetRef )
{
StringBuffer params = new StringBuffer();
if( args != null ) {
for( int i = 0; i < args.length; i++ ) {
if( i == 0 ) {
params.append( startList );
}
if( args[ i ] instanceof String ) {
params.append( '"' );
params.append( escapeString( ( String )args[ i ] ) );
params.append( '"' );
} else if( args[ i ] instanceof Character ) {
params.append( '"' );
params.append( args[ i ] );
params.append( '"' );
} else if( args[ i ] instanceof Widget ) {
if( useCurrentWidgetRef && args[ i ] == getCurrentWidgetRef() ) {
params.append( "w" );
} else {
params.append( createFindWidgetById( ( Widget )args[ i ] ) );
}
} else if( args[ i ] instanceof JSVar ) {
params.append( args[ i ] );
} else if( args[ i ] instanceof Color ) {
params.append( '"' );
params.append( getColorValue( ( Color )args[ i ] ) );
params.append( '"' );
} else if( args[ i ] instanceof Object[] ) {
params.append( createArray( ( Object[] )args[ i ] ) );
} else {
params.append( args[ i ] );
}
if( i == args.length - 1 ) {
params.append( endList );
} else {
params.append( ", " );
}
}
}
return params.toString();
}
private static String createArray( final Object[] array ) {
StringBuffer buffer = new StringBuffer();
buffer.append( '[' );
for( int i = 0; i < array.length; i++ ) {
if( i > 0 ) {
buffer.append( "," );
}
if( array[ i ] instanceof String ) {
buffer.append( " \"" );
buffer.append( escapeString( array[ i ].toString() ) );
buffer.append( '"' );
} else if( array[ i ] instanceof Widget ) {
buffer.append( createFindWidgetById( ( Widget )array[ i ] ) );
} else if( array[ i ] instanceof Color ) {
buffer.append( '"' );
buffer.append( getColorValue( ( Color )array[ i ] ) );
buffer.append( '"' );
} else {
buffer.append( array[ i ] );
}
if( i == array.length - 1 ) {
buffer.append( ' ' );
}
}
buffer.append( ']' );
return buffer.toString();
}
private String createPropertyChain( final String[] jsPropertyChain,
final boolean forReset )
{
StringBuffer buffer = new StringBuffer();
int last = jsPropertyChain.length - 1;
for( int i = 0; i < last; i++ ) {
buffer.append( getGetterName( jsPropertyChain[ i ] ) );
buffer.append( "()." );
}
if( forReset ) {
buffer.append( getResetterName( jsPropertyChain[ last ] ) );
} else {
buffer.append( getSetterName( jsPropertyChain[ last ] ) );
}
return buffer.toString();
}
private static String getColorValue( final Color color ) {
StringBuffer buffer = new StringBuffer();
buffer.append( "#" );
String red = Integer.toHexString( color.getRed() );
if( red.length() == 1 ) {
buffer.append( "0" );
}
buffer.append( red );
String green = Integer.toHexString( color.getGreen() );
if( green.length() == 1 ) {
buffer.append( "0" );
}
buffer.append( green );
String blue = Integer.toHexString( color.getBlue() );
if( blue.length() == 1 ) {
buffer.append( "0" );
}
buffer.append( blue );
return buffer.toString();
}
////////////////////////////////////////
// Helping methods to manipulate strings
private static String capitalize( final String text ) {
String result;
if( Character.isUpperCase( text.charAt( 0 ) ) ) {
result = text;
} else {
StringBuffer buffer = new StringBuffer( text );
char firstLetter = buffer.charAt( 0 );
firstLetter = Character.toUpperCase( firstLetter );
buffer.setCharAt( 0, firstLetter );
result = buffer.toString();
}
return result;
}
// TODO [rh] revise how to handle newline characters (\n)
private static String escapeString( final String input ) {
String result = EncodingUtil.escapeDoubleQuoted( input );
return EncodingUtil.replaceNewLines( result );
}
private static String getSetterName( final String jsProperty ) {
synchronized( setterNames ) {
String result = ( String )setterNames.get( jsProperty );
if( result == null ) {
StringBuffer functionName = new StringBuffer();
functionName.append( "set" );
functionName.append( capitalize( jsProperty ) );
result = functionName.toString();
setterNames.put( jsProperty, result );
}
return result;
}
}
private static String getResetterName( final String jsProperty ) {
synchronized( resetterNames ) {
String result = ( String )resetterNames.get( jsProperty );
if( result == null ) {
StringBuffer functionName = new StringBuffer();
functionName.append( "reset" );
functionName.append( capitalize( jsProperty ) );
result = functionName.toString();
resetterNames.put( jsProperty, result );
}
return result;
}
}
private static String getGetterName( final String jsProperty ) {
synchronized( getterNames ) {
String result = ( String )getterNames.get( jsProperty );
if( result == null ) {
StringBuffer functionName = new StringBuffer();
functionName.append( "get" );
functionName.append( capitalize( jsProperty ) );
result = functionName.toString();
getterNames.put( jsProperty, result );
}
return result;
}
}
/////////////////////////////////////////////////////////
// Helping methods to write to the actual response writer
private void writeCall( final JSVar target,
final String function,
final String params )
throws IOException
{
StringBuffer buffer = new StringBuffer();
buffer.append( target.toString() );
buffer.append( '.' );
buffer.append( function );
buffer.append( '(' );
buffer.append( params );
buffer.append( ");" );
getWriter().write( buffer.toString() );
}
private void writeVarAssignment( final JSVar var, final String value )
throws IOException
{
StringBuffer buffer = new StringBuffer();
buffer.append( "var " );
buffer.append( var.toString() );
buffer.append( " = " );
buffer.append( value );
buffer.append( ';' );
getWriter().write( buffer.toString() );
}
private static String format( final String pattern,
final Object[] arguments )
{
return MessageFormat.format( pattern, arguments );
}
private static void write( final String pattern,
final Object arg1,
final Object arg2 )
throws IOException
{
Object[] args = new Object[] { arg1, arg2 };
getWriter().write( format( pattern, args ) );
}
private static void write( final String pattern,
final Object arg1,
final Object arg2,
final Object arg3 )
throws IOException
{
Object[] args = new Object[] { arg1, arg2, arg3 };
getWriter().write( format( pattern, args ) );
}
private static HtmlResponseWriter getWriter() {
return ContextProvider.getStateInfo().getResponseWriter();
}
}