/******************************************************************************* * 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.swt.internal.browser.browserkit; import java.io.*; import java.util.ArrayList; import java.util.List; import org.eclipse.rwt.internal.resources.ResourceManager; import org.eclipse.rwt.internal.service.ContextProvider; import org.eclipse.rwt.internal.service.IServiceStateInfo; import org.eclipse.rwt.lifecycle.*; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.browser.BrowserFunction; import org.eclipse.swt.internal.widgets.IBrowserAdapter; import org.eclipse.swt.widgets.Widget; public final class BrowserLCA extends AbstractWidgetLCA { static final String BLANK_HTML = "<html><script></script></html>"; private static final String QX_TYPE = "org.eclipse.swt.browser.Browser"; private static final String QX_FIELD_SOURCE = "source"; private static final String PARAM_EXECUTE_RESULT = "executeResult"; static final String PARAM_EXECUTE_FUNCTION = "executeFunction"; static final String PARAM_EXECUTE_ARGUMENTS = "executeArguments"; static final String EXECUTED_FUNCTION_NAME = Browser.class.getName() + "#executedFunctionName."; static final String EXECUTED_FUNCTION_RESULT = Browser.class.getName() + "#executedFunctionResult."; static final String EXECUTED_FUNCTION_ERROR = Browser.class.getName() + "#executedFunctionError."; private static final String FUNCTIONS_TO_CREATE = Browser.class.getName() + "#functionsToCreate."; private static final String FUNCTIONS_TO_DESTROY = Browser.class.getName() + "#functionsToDestroy."; private static final String PROP_URL = "url"; private static final String PROP_TEXT = "text"; public void preserveValues( final Widget widget ) { Browser browser = ( Browser )widget; ControlLCAUtil.preserveValues( browser ); IWidgetAdapter adapter = WidgetUtil.getAdapter( browser ); adapter.preserve( PROP_URL, browser.getUrl() ); adapter.preserve( PROP_TEXT, getText( browser ) ); WidgetLCAUtil.preserveCustomVariant( browser ); } public void readData( final Widget widget ) { Browser browser = ( Browser )widget; String value = WidgetLCAUtil.readPropertyValue( browser, PARAM_EXECUTE_RESULT ); if( value != null ) { boolean executeResult = Boolean.valueOf( value ).booleanValue(); getAdapter( browser ).setExecuteResult( executeResult ); } executeFunction( browser ); } public void renderInitialization( final Widget widget ) throws IOException { Browser browser = ( Browser )widget; JSWriter writer = JSWriter.getWriterFor( browser ); writer.newWidget( QX_TYPE ); ControlLCAUtil.writeStyleFlags( browser ); } public void renderChanges( final Widget widget ) throws IOException { Browser browser = ( Browser )widget; // TODO [rh] though implemented in DefaultAppearanceTheme, setting border // does not work ControlLCAUtil.writeChanges( browser ); destroyBrowserFunctions( browser ); writeUrl( browser ); createBrowserFunctions( browser ); writeExecute( browser ); writeFunctionResult( browser ); WidgetLCAUtil.writeCustomVariant( browser ); } public void renderDispose( final Widget widget ) throws IOException { JSWriter writer = JSWriter.getWriterFor( widget ); writer.dispose(); } private static void writeUrl( final Browser browser ) throws IOException { if( hasUrlChanged( browser ) ) { JSWriter writer = JSWriter.getWriterFor( browser ); writer.set( QX_FIELD_SOURCE, getUrl( browser ) ); } } static boolean hasUrlChanged( final Browser browser ) { boolean initialized = WidgetUtil.getAdapter( browser ).isInitialized(); return !initialized || WidgetLCAUtil.hasChanged( browser, PROP_TEXT, getText( browser ) ) || WidgetLCAUtil.hasChanged( browser, PROP_URL, browser.getUrl() ); } static String getUrl( final Browser browser ) throws IOException { String text = getText( browser ); String url = browser.getUrl(); String result; if( !"".equals( text.trim() ) ) { result = registerHtml( text ); } else if( !"".equals( url.trim() ) ) { result = url; } else { result = registerHtml( BLANK_HTML ); } return result; } private static void writeExecute( final Browser browser ) throws IOException { IBrowserAdapter adapter = getAdapter( browser ); String executeScript = adapter.getExecuteScript(); if( executeScript != null ) { JSWriter writer = JSWriter.getWriterFor( browser ); writer.call( "execute", new Object[] { executeScript } ); } } private static String registerHtml( final String html ) throws IOException { String name = createUrlFromHtml( html ); byte[] bytes = html.getBytes( "UTF-8" ); InputStream inputStream = new ByteArrayInputStream( bytes ); ResourceManager.getInstance().register( name, inputStream ); return ResourceManager.getInstance().getLocation( name ); } private static String createUrlFromHtml( final String html ) { StringBuffer result = new StringBuffer(); result.append( "org.eclipse.swt.browser/text" ); result.append( String.valueOf( html.hashCode() ) ); result.append( ".html" ); return result.toString(); } private static String getText( final Browser browser ) { Object adapter = browser.getAdapter( IBrowserAdapter.class ); IBrowserAdapter browserAdapter = ( IBrowserAdapter )adapter; return browserAdapter.getText(); } private static IBrowserAdapter getAdapter( final Browser browser ) { return ( IBrowserAdapter )browser.getAdapter( IBrowserAdapter.class ); } ////////////////////////////////////// // Helping methods for BrowserFunction private static void createBrowserFunctions( final Browser browser ) throws IOException { IServiceStateInfo stateInfo = ContextProvider.getStateInfo(); String id = WidgetUtil.getId( browser ); String[] functions = ( String[] )stateInfo.getAttribute( FUNCTIONS_TO_CREATE + id ); if( functions != null ) { for( int i = 0; i < functions.length; i++ ) { JSWriter writer = JSWriter.getWriterFor( browser ); writer.call( "createFunction", new Object[]{ functions[ i ] } ); } } } private static void destroyBrowserFunctions( final Browser browser ) throws IOException { IServiceStateInfo stateInfo = ContextProvider.getStateInfo(); String id = WidgetUtil.getId( browser ); String[] functions = ( String[] )stateInfo.getAttribute( FUNCTIONS_TO_DESTROY + id ); if( functions != null ) { for( int i = 0; i < functions.length; i++ ) { JSWriter writer = JSWriter.getWriterFor( browser ); writer.call( "destroyFunction", new Object[]{ functions[ i ] } ); } } } private void executeFunction( final Browser browser ) { String function = WidgetLCAUtil.readPropertyValue( browser, PARAM_EXECUTE_FUNCTION ); String arguments = WidgetLCAUtil.readPropertyValue( browser, PARAM_EXECUTE_ARGUMENTS ); if( function != null ) { IBrowserAdapter adapter = getAdapter( browser ); BrowserFunction[] functions = adapter.getBrowserFunctions(); boolean found = false; for( int i = 0; i < functions.length && !found; i++ ) { final BrowserFunction current = functions[ i ]; if( current.getName().equals( function ) ) { final Object[] args = parseArguments( arguments ); ProcessActionRunner.add( new Runnable() { public void run() { try { setExecutedFunctionName( browser, current.getName() ); Object executedFunctionResult = current.function( args ); setExecutedFunctionResult( browser, executedFunctionResult ); } catch( Exception e ) { setExecutedFunctionError( browser, e.getMessage() ); } } } ); found = true; } } } } private static void writeFunctionResult( final Browser browser ) throws IOException { IServiceStateInfo stateInfo = ContextProvider.getStateInfo(); String id = WidgetUtil.getId( browser ); String function = ( String )stateInfo.getAttribute( EXECUTED_FUNCTION_NAME + id ); if( function != null ) { Object result = stateInfo.getAttribute( EXECUTED_FUNCTION_RESULT + id ); if( result != null ) { result = new JSVar( toJson( result, true ) ); } String error = ( String )stateInfo.getAttribute( EXECUTED_FUNCTION_ERROR + id ); JSWriter writer = JSWriter.getWriterFor( browser ); writer.call( "setFunctionResult", new Object[]{ result, error } ); } } private static void setExecutedFunctionName( final Browser browser, final String name ) { IServiceStateInfo stateInfo = ContextProvider.getStateInfo(); String id = WidgetUtil.getId( browser ); stateInfo.setAttribute( EXECUTED_FUNCTION_NAME + id, name ); } private static void setExecutedFunctionResult( final Browser browser, final Object result ) { IServiceStateInfo stateInfo = ContextProvider.getStateInfo(); String id = WidgetUtil.getId( browser ); stateInfo.setAttribute( EXECUTED_FUNCTION_RESULT + id, result ); } private static void setExecutedFunctionError( final Browser browser, final String error ) { IServiceStateInfo stateInfo = ContextProvider.getStateInfo(); String id = WidgetUtil.getId( browser ); stateInfo.setAttribute( EXECUTED_FUNCTION_ERROR + id, error ); } static Object[] parseArguments( final String arguments ) { List result = new ArrayList(); if( arguments.startsWith( "[" ) && arguments.endsWith( "]" ) ) { // remove [ ] brackets String args = arguments.substring( 1, arguments.length() - 1 ); int openQuotes = 0; int openBrackets = 0; String arg; StringBuffer argBuff = new StringBuffer(); char prevChar = ' '; for( int i = 0; i < args.length(); i++ ) { char ch = args.charAt( i ); if( ch == ',' && openQuotes == 0 && openBrackets == 0 ) { arg = argBuff.toString(); if( arg.startsWith( "[" ) ) { result.add( parseArguments( arg ) ); } else { arg = arg.replaceAll( "\\\\\"", "\"" ); result.add( withType( arg ) ); } argBuff.setLength( 0 ); } else { if( ch == '"' && prevChar != '\\' ) { if( openQuotes == 0 ) { openQuotes++; } else { openQuotes--; } } else if( ch == '[' && openQuotes == 0 ) { openBrackets++; } else if( ch == ']'&& openQuotes == 0 ) { openBrackets--; } argBuff.append( ch ); } prevChar = ch; } // append last segment arg = argBuff.toString(); if( arg.startsWith( "[" ) ) { result.add( parseArguments( arg ) ); } else if( !arg.equals( "" ) ) { arg = arg.replaceAll( "\\\\\"", "\"" ); result.add( withType( arg ) ); } } return result.toArray(); } static Object withType( final String argument ) { Object result; if( argument.equals( "null" ) || argument.equals( "undefined" ) ) { result = null; } else if( argument.equals( "true" ) || argument.equals( "false" ) ) { result = new Boolean( argument ); } else if( argument.startsWith( "\"" ) ) { result = new String( argument.substring( 1, argument.length() - 1 ) ); } else { try { result = Double.valueOf( argument ); } catch( NumberFormatException nfe ) { result = argument; } } return result; } static String toJson( final Object object, final boolean deleteLastChar ) { StringBuffer result = new StringBuffer(); if( object == null ) { result.append( "null" ); result.append( "," ); } else if( object instanceof String ) { result.append( "\"" ); result.append( ( String )object ); result.append( "\"" ); result.append( "," ); } else if( object instanceof Boolean ) { result.append( ( ( Boolean )object ).toString() ); result.append( "," ); } else if( object instanceof Number ) { result.append( ( ( Number )object ).toString() ); result.append( "," ); } else if( object.getClass().isArray() ) { Object[] array = ( Object[] )object; result.append( "[" ); for( int i = 0; i < array.length; i++ ) { result.append( toJson( array[ i ], false ) ); } result.insert( result.length() - 1, "]" ); } if( deleteLastChar ) { result.deleteCharAt( result.length() - 1 ); } return result.toString(); } }