package com.meterware.httpunit.javascript; /******************************************************************************************************************** * $Id: JavaScript.java,v 1.63 2004/12/02 03:43:20 russgold Exp $ * * Copyright (c) 2002-2004, Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and * to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * *******************************************************************************************************************/ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import org.mozilla.javascript.Context; import org.mozilla.javascript.EcmaError; import org.mozilla.javascript.EvaluatorException; import org.mozilla.javascript.Function; import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.xml.sax.SAXException; import com.meterware.httpunit.ClientProperties; import com.meterware.httpunit.HTMLPage; import com.meterware.httpunit.ScriptException; import com.meterware.httpunit.WebForm; import com.meterware.httpunit.WebImage; import com.meterware.httpunit.WebLink; import com.meterware.httpunit.WebResponse; import com.meterware.httpunit.scripting.DocumentElement; import com.meterware.httpunit.scripting.IdentifiedDelegate; import com.meterware.httpunit.scripting.Input; import com.meterware.httpunit.scripting.NamedDelegate; import com.meterware.httpunit.scripting.ScriptableDelegate; import com.meterware.httpunit.scripting.ScriptingEngine; import com.meterware.httpunit.scripting.SelectionOption; import com.meterware.httpunit.scripting.SelectionOptions; /** * This class is the Rhino-compatible implementation of the JavaScript DOM objects. * * @author Russell Gold **/ public class JavaScript { private final static Object[] NO_ARGS = new Object[0]; private static boolean _throwExceptionsOnError = true; private static ArrayList _errorMessages = new ArrayList(); static boolean isThrowExceptionsOnError() { return _throwExceptionsOnError; } static void setThrowExceptionsOnError( boolean throwExceptionsOnError ) { _throwExceptionsOnError = throwExceptionsOnError; } static void clearErrorMessages() { _errorMessages.clear(); } static String[] getErrorMessages() { return (String[]) _errorMessages.toArray( new String[ _errorMessages.size() ] ); } /** * Initiates JavaScript execution for the specified web response. */ static void run( WebResponse response ) throws IllegalAccessException, InstantiationException, InvocationTargetException, SAXException, JavaScriptException { Context context = Context.enter(); Scriptable scope = context.initStandardObjects( null ); initHTMLObjects( scope ); Window w = (Window) context.newObject( scope, "Window" ); w.initialize( null, response.getScriptableObject() ); } /** * Runs the onload event for the specified web response. */ public static void load( WebResponse response ) throws InstantiationException, IllegalAccessException, InvocationTargetException, JavaScriptException, SAXException { if (!(response.getScriptableObject().getScriptEngine() instanceof JavaScriptEngine)) run( response ); response.getScriptableObject().load(); } private static void initHTMLObjects( Scriptable scope ) throws IllegalAccessException, InstantiationException, InvocationTargetException { ScriptableObject.defineClass( scope, Window.class ); ScriptableObject.defineClass( scope, Document.class ); ScriptableObject.defineClass( scope, Style.class ); ScriptableObject.defineClass( scope, Location.class ); ScriptableObject.defineClass( scope, Navigator.class ); ScriptableObject.defineClass( scope, Screen.class ); ScriptableObject.defineClass( scope, Link.class ); ScriptableObject.defineClass( scope, Form.class ); ScriptableObject.defineClass( scope, Control.class ); ScriptableObject.defineClass( scope, Link.class ); ScriptableObject.defineClass( scope, Image.class ); ScriptableObject.defineClass( scope, Options.class ); ScriptableObject.defineClass( scope, Option.class ); ScriptableObject.defineClass( scope, ElementArray.class ); ScriptableObject.defineClass( scope, HTMLElement.class ); } abstract static class JavaScriptEngine extends ScriptableObject implements ScriptingEngine { protected ScriptableDelegate _scriptable; protected JavaScriptEngine _parent; public boolean supportsScriptLanguage( String language ) { return language == null || language.toLowerCase().startsWith( "javascript" ); } public String executeScript( String language, String script ) { if (!supportsScriptLanguage( language )) return ""; try { script = script.trim(); if (script.startsWith( "" )) script = script.substring( 0, script.lastIndexOf( "-->" )); } Context.getCurrentContext().evaluateString( this, script, "httpunit", 0, null ); StringBuffer buffer = getDocumentWriteBuffer(); return buffer.toString(); } catch (Exception e) { handleScriptException( e, "Script '" + script + "'" ); return ""; } finally { discardDocumentWriteBuffer(); } } protected StringBuffer getDocumentWriteBuffer() { throw new IllegalStateException( "may not run executeScript() from " + getClass() ); } protected void discardDocumentWriteBuffer() { throw new IllegalStateException( "may not run executeScript() from " + getClass() ); } private String withoutFirstLine( String script ) { for (int i=0; i < script.length(); i++) { if (isLineTerminator( script.charAt(i) )) return script.substring( i ).trim(); } return ""; } private boolean isLineTerminator( char c ) { return c == 0x0A || c == 0x0D; } public boolean performEvent( String eventScript ) { try { final Context context = Context.getCurrentContext(); context.setOptimizationLevel( -1 ); Function f = context.compileFunction( this, "function x() { " + eventScript + "}", "httpunit", 0, null ); Object result = f.call( context, this, this, NO_ARGS ); return (result instanceof Boolean) ? ((Boolean) result).booleanValue() : true; } catch (Exception e) { handleScriptException( e, "Event '" + eventScript + "'" ); return false; } } /** * Evaluates the specified string as JavaScript. Will return null if the script has no return value. */ public String evaluateScriptExpression( String urlString ) { try { Object result = Context.getCurrentContext().evaluateString( this, urlString, "httpunit", 0, null ); return (result == null || result instanceof Undefined) ? null : result.toString(); } catch (Exception e) { handleScriptException( e, "URL '" + urlString + "'" ); return null; } } private void handleScriptException( Exception e, String badScript ) { final String errorMessage = badScript + " failed: " + e; if (!(e instanceof EcmaError) && !(e instanceof EvaluatorException)) { e.printStackTrace(); throw new RuntimeException( errorMessage ); } else if (isThrowExceptionsOnError()) { e.printStackTrace(); throw new ScriptException( errorMessage ); } else { _errorMessages.add( errorMessage ); } } void initialize( JavaScriptEngine parent, ScriptableDelegate scriptable ) throws SAXException, JavaScriptException { _scriptable = scriptable; _scriptable.setScriptEngine( this ); _parent = parent; if (parent != null) setParentScope( parent ); } String getName() { return _scriptable instanceof NamedDelegate ? ((NamedDelegate) _scriptable).getName() : ""; } String getID() { return _scriptable instanceof IdentifiedDelegate ? ((IdentifiedDelegate) _scriptable).getID() : ""; } public boolean has( String propertyName, Scriptable scriptable ) { return super.has( propertyName, scriptable ) || (_scriptable != null && _scriptable.get( propertyName ) != null); } public Object get( String propertyName, Scriptable scriptable ) { Object result = super.get( propertyName, scriptable ); if (result != NOT_FOUND) return result; if (_scriptable == null) return NOT_FOUND; return convertIfNeeded( _scriptable.get( propertyName ) ); } public Object get( int i, Scriptable scriptable ) { Object result = super.get( i, scriptable ); if (result != NOT_FOUND) return result; if (_scriptable == null) return NOT_FOUND; return convertIfNeeded( _scriptable.get( i ) ); } private Object convertIfNeeded( final Object property ) { if (property == null) return NOT_FOUND; if (property instanceof ScriptableDelegate[]) return toScriptable( (ScriptableDelegate[]) property ); if (!(property instanceof ScriptableDelegate)) return property; return toScriptable( (ScriptableDelegate) property ); } private Object toScriptable( ScriptableDelegate[] list ) { Object[] delegates = new Object[ list.length ]; for (int i = 0; i < delegates.length; i++) { delegates[i] = toScriptable( list[i] ); } return Context.getCurrentContext().newArray( this, delegates ); } public void put( String propertyName, Scriptable scriptable, Object value ) { if (_scriptable == null || _scriptable.get( propertyName ) == null) { super.put( propertyName, scriptable, value ); } else { _scriptable.set( propertyName, value ); } } public String toString() { return (_scriptable == null ? "prototype " : "") + getClassName(); } public ScriptingEngine newScriptingEngine( ScriptableDelegate child ) { try { return (ScriptingEngine) toScriptable( child ); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException( e.toString() ); } } public void clearCaches() { } protected static String toStringIfNotUndefined( Object object ) { return (object == null || Undefined.instance.equals( object )) ? null : object.toString(); } /** * Converts a scriptable delegate obtained from a subobject into the appropriate Rhino-compatible Scriptable. **/ final Object toScriptable( ScriptableDelegate delegate ) { if (delegate == null) { return NOT_FOUND; } else if (delegate.getScriptEngine() instanceof Scriptable) { return (Scriptable) delegate.getScriptEngine(); } else { try { JavaScriptEngine element = (JavaScriptEngine) Context.getCurrentContext().newObject( this, getScriptableClassName( delegate ) ); element.initialize( this, delegate ); return element; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RhinoException( e ); } } } private String getScriptableClassName( ScriptableDelegate delegate ) { if (delegate instanceof WebResponse.Scriptable) return "Window"; if (delegate instanceof HTMLPage.Scriptable) return "Document"; if (delegate instanceof WebForm.Scriptable) return "Form"; if (delegate instanceof WebLink.Scriptable) return "Link"; if (delegate instanceof WebImage.Scriptable) return "Image"; if (delegate instanceof SelectionOptions) return "Options"; if (delegate instanceof SelectionOption) return "Option"; if (delegate instanceof Input) return "Control"; if (delegate instanceof DocumentElement) return "HTMLElement"; throw new IllegalArgumentException( "Unknown ScriptableDelegate class: " + delegate.getClass() ); } protected ElementArray toElementArray( ScriptableDelegate[] scriptables ) { JavaScriptEngine[] elements = new JavaScriptEngine[ scriptables.length ]; for (int i = 0; i < elements.length; i++) { elements[ i ] = (JavaScriptEngine) toScriptable( scriptables[ i ] ); } ElementArray result = ElementArray.newElementArray( this ); result.initialize( elements ); return result; } } static public class Window extends JavaScriptEngine { private Document _document; private Navigator _navigator; private Location _location; private Screen _screen; private ElementArray _frames; public String getClassName() { return "Window"; } public Window jsGet_window() { return this; } public Window jsGet_self() { return this; } public Document jsGet_document() { if (_document == null) { _document = (Document) toScriptable( getDelegate().getDocument() ); } return _document; } public Scriptable jsGet_frames() throws SAXException, JavaScriptException { if (_frames == null) { WebResponse.Scriptable scriptables[] = getDelegate().getFrames(); Window[] frames = new Window[ scriptables.length ]; for (int i = 0; i < frames.length; i++) { frames[ i ] = (Window) toScriptable( scriptables[ i ] ); } _frames = (ElementArray) Context.getCurrentContext().newObject( this, "ElementArray" ); _frames.initialize( frames ); } return _frames; } public Navigator jsGet_navigator() { return _navigator; } public Screen jsGet_screen() { return _screen; } public Location jsGet_location() { return _location; } public void jsSet_location( String relativeURL ) throws IOException, SAXException { setLocation( relativeURL ); } void setLocation( String relativeURL ) throws IOException, SAXException { getDelegate().setLocation( relativeURL ); } void initialize( JavaScriptEngine parent, ScriptableDelegate scriptable ) throws JavaScriptException, SAXException { super.initialize( parent, scriptable ); _location = (Location) Context.getCurrentContext().newObject( this, "Location" ); _location.initialize(this, ((WebResponse.Scriptable) scriptable).getURL() ); _navigator = (Navigator) Context.getCurrentContext().newObject( this, "Navigator" ); _navigator.setClientProperties( getDelegate().getClientProperties() ); _screen = (Screen) Context.getCurrentContext().newObject( this, "Screen" ); _screen.setClientProperties( getDelegate().getClientProperties() ); } public void jsFunction_alert( String message ) { getDelegate().alert( message ); } public boolean jsFunction_confirm( String message ) { return getDelegate().getConfirmationResponse( message ); } public String jsFunction_prompt( String message, String defaultResponse ) { return getDelegate().getUserResponse( message, defaultResponse ); } public void jsFunction_moveTo( int x, int y ) { } public void jsFunction_focus() { } public void jsFunction_setTimeout() { } public void jsFunction_close() { getDelegate().close(); } public Window jsFunction_open( Object url, String name, String features, boolean replace ) throws JavaScriptException, IOException, SAXException { WebResponse.Scriptable delegate = getDelegate().open( toStringIfNotUndefined( url ), name, features, replace ); return delegate == null ? null : (Window) toScriptable( delegate ); } public void clearCaches() { if (_document != null) _document.clearCaches(); } protected StringBuffer getDocumentWriteBuffer() { return jsGet_document().getWriteBuffer(); } protected void discardDocumentWriteBuffer() { jsGet_document().clearWriteBuffer(); } private WebResponse.Scriptable getDelegate() { return (WebResponse.Scriptable) _scriptable; } } static public class Document extends JavaScriptEngine { private ElementArray _forms; private ElementArray _links; private ElementArray _images; private StringBuffer _writeBuffer; private String _mimeType; public String getClassName() { return "Document"; } public void clearCaches() { _forms = _links = _images = null; } public String jsGet_title() throws SAXException { return getDelegate().getTitle(); } public Scriptable jsGet_images() throws SAXException{ if (_images == null) _images = toElementArray( getDelegate().getImages() ); return _images; } public Scriptable jsGet_links() throws SAXException { if (_links == null) _links = toElementArray( getDelegate().getLinks() ); return _links; } public Scriptable jsGet_forms() throws SAXException { if (_forms == null) _forms = toElementArray( getDelegate().getForms() ); return _forms; } public Object jsFunction_getElementById( String id ) { ScriptableDelegate elementWithID = getDelegate().getElementWithID( id ); return elementWithID == null ? null : toScriptable( elementWithID ); } public Object jsFunction_getElementsByName( String name ) { return toElementArray( getDelegate().getElementsByName( name ) ); } public Object jsFunction_getElementsByTagName( String name ) { return toElementArray( getDelegate().getElementsByTagName( name ) ); } public Object jsGet_location() { return _parent == null ? NOT_FOUND : getWindow().jsGet_location(); } public void jsSet_location( String urlString ) throws IOException, SAXException { if (urlString.startsWith( "color" )) return; getWindow().setLocation( urlString ); } public String jsGet_cookie() { return getDelegate().getCookie(); } public void jsSet_cookie( String cookieSpec ) { final int equalsIndex = cookieSpec.indexOf( '=' ); if (equalsIndex <0) return; int endIndex = cookieSpec.indexOf( ";", equalsIndex ); if (endIndex < 0) endIndex = cookieSpec.length(); String name = cookieSpec.substring( 0, equalsIndex ); String value = cookieSpec.substring( equalsIndex+1, endIndex ); getDelegate().setCookie( name, value ); } private Window getWindow() { return ((Window) _parent); } public void jsFunction_open( Object mimeType ) { _mimeType = toStringIfNotUndefined( mimeType ); } public void jsFunction_close() { if (getDelegate().replaceText( getWriteBuffer().toString(), _mimeType == null ? "text/html" : _mimeType )) { getWriteBuffer().setLength(0); } } public void jsFunction_write( String string ) { getWriteBuffer().append( string ); } public void jsFunction_writeln( String string ) { getWriteBuffer().append( string ).append( (char) 0x0D ).append( (char) 0x0A ); } protected StringBuffer getWriteBuffer() { if (_writeBuffer == null) _writeBuffer = new StringBuffer(); return _writeBuffer; } protected void clearWriteBuffer() { _writeBuffer = null; } private HTMLPage.Scriptable getDelegate() { return (HTMLPage.Scriptable) _scriptable; } } static public class Location extends JavaScriptEngine { private URL _url; private Window _window; public String getClassName() { return "Location"; } void initialize( Window window, URL url ) { _window = window; _url = url; } public void jsFunction_replace( String urlString ) throws IOException, SAXException { _window.setLocation( urlString ); } public String jsGet_href() { return toString(); } public void jsSet_href( String urlString ) throws SAXException, IOException { _window.setLocation( urlString ); } public String jsGet_protocol() { return _url.getProtocol() + ':'; } public String jsGet_host() { return _url.getHost() + ':' + _url.getPort(); } public String jsGet_hostname() { return _url.getHost(); } public String jsGet_port() { return String.valueOf( _url.getPort() ); } public String jsGet_pathname() { return _url.getPath(); } public void jsSet_pathname( String newPath ) throws SAXException, IOException { if (!newPath.startsWith( "/" )) newPath = '/' + newPath; URL newURL = new URL( _url, newPath ); _window.setLocation( newURL.toExternalForm() ); } public String jsGet_search() { return '?' + _url.getQuery(); } public void jsSet_search( String newSearch ) throws SAXException, IOException { if (!newSearch.startsWith( "?" )) newSearch = '?' + newSearch; _window.setLocation( jsGet_protocol() + "//" + jsGet_host() + jsGet_pathname() + newSearch ); } /** * Returns the default value of this scriptable object. In this case, it returns simply the URL as a string. * Note that this method is necessary, since Rhino will only call the toString method directly if there are no * Rhino methods defined (jsGet_*, jsFunction_*, etc.) */ public Object getDefaultValue( Class typeHint ) { return _url.toExternalForm(); } public String toString() { return _url.toExternalForm(); } } static public class Style extends JavaScriptEngine { private String _display = "inline"; private String _visibility = "visible"; public String getClassName() { return "Style"; } public String jsGet_display() { return _display; } public void jsSet_display( String display ) { _display = display; } public String jsGet_visibility() { return _visibility; } public void jsSet_visibility( String visibility ) { _visibility = visibility; } } static public class Navigator extends JavaScriptEngine { private ClientProperties _clientProperties; private ElementArray _plugins = new ElementArray(); public String getClassName() { return "Navigator"; } void setClientProperties( ClientProperties clientProperties ) { _clientProperties = clientProperties; } public String jsGet_appName() { return _clientProperties.getApplicationName(); } public String jsGet_appCodeName() { return _clientProperties.getApplicationCodeName(); } public String jsGet_appVersion() { return _clientProperties.getApplicationVersion(); } public String jsGet_userAgent() { return _clientProperties.getUserAgent(); } public String jsGet_platform() { return _clientProperties.getPlatform(); } public Scriptable jsGet_plugins() { return _plugins; } public boolean jsFunction_javaEnabled() { return false; // no support is provided for applets at present } } static public class Screen extends JavaScriptEngine { private ClientProperties _clientProperties; void setClientProperties( ClientProperties clientProperties ) { _clientProperties = clientProperties; } public String getClassName() { return "Screen"; } public int jsGet_availWidth() { return _clientProperties.getAvailableScreenWidth(); } public int jsGet_availHeight() { return _clientProperties.getAvailHeight(); } } static public class ElementArray extends ScriptableObject { private JavaScriptEngine _contents[] = new HTMLElement[0]; static ElementArray newElementArray( Scriptable parent ) { try { return (ElementArray) Context.getCurrentContext().newObject( parent, "ElementArray" ); } catch (JavaScriptException e) { throw new RhinoException( e ); } } public ElementArray() { } void initialize( JavaScriptEngine[] contents ) { _contents = contents; } public int jsGet_length() { return _contents.length; } public String getClassName() { return "ElementArray"; } public Object get( int i, Scriptable scriptable ) { if (i >= 0 && i < _contents.length) { return _contents[i]; } else { return super.get( i, scriptable ); } } public Object get( String name, Scriptable scriptable ) { for (int i = 0; i < _contents.length; i++) { JavaScriptEngine content = _contents[ i ]; if (name.equalsIgnoreCase( content.getID() )) return content; } for (int i = 0; i < _contents.length; i++) { JavaScriptEngine content = _contents[ i ]; if (name.equalsIgnoreCase( content.getName() )) return content; } return super.get( name, scriptable ); } protected JavaScriptEngine[] getContents() { return _contents; } } static public class HTMLElement extends JavaScriptEngine { private Style _style; private Document _document; public String getClassName() { return "HTMLElement"; } public Document jsGet_document() { return _document; } public Style jsGet_style() { return _style; } void initialize( JavaScriptEngine parent, ScriptableDelegate scriptable ) throws JavaScriptException, SAXException { super.initialize( parent, scriptable ); _document = (Document) parent; _style = (Style) Context.getCurrentContext().newObject( this, "Style" ); } } static public class Image extends HTMLElement { public String getClassName() { return "Image"; } } static public class Link extends HTMLElement { public Document jsGet_document() { return super.jsGet_document(); } public String getClassName() { return "Link"; } } static public class Form extends HTMLElement { private ElementArray _controls; public String getClassName() { return "Form"; } public String jsGet_action() { return getDelegate().getAction(); } public void jsSet_action( String action ) { getDelegate().setAction( action ); } public Scriptable jsGet_elements() throws JavaScriptException { if (_controls == null) { initializeControls(); } return _controls; } public Object jsFunction_getElementsByTagName( String name ) throws SAXException { return toElementArray( getDelegate().getElementsByTagName( name ) ); } public void jsFunction_submit() throws IOException, SAXException { getDelegate().submit(); } public void jsFunction_reset() throws IOException, SAXException { getDelegate().reset(); } private void initializeControls() throws JavaScriptException { ScriptableDelegate scriptables[] = getDelegate().getElementDelegates(); Control[] controls = new Control[ scriptables.length ]; for (int i = 0; i < controls.length; i++) { controls[ i ] = (Control) toScriptable( scriptables[ i ] ); } _controls = (ElementArray) Context.getCurrentContext().newObject( this, "ElementArray" ); _controls.initialize( controls ); } private WebForm.Scriptable getDelegate() { return (WebForm.Scriptable) _scriptable; } } static public class Control extends JavaScriptEngine { private Form _form; public String getClassName() { return "Control"; } public Form jsGet_form() { return _form; } public void jsFunction_focus() {} public void jsFunction_select() {} public void jsFunction_click() throws IOException, SAXException { getDelegate().click(); } private Input getDelegate() { return (Input) _scriptable; } void initialize( JavaScriptEngine parent, ScriptableDelegate scriptable ) throws JavaScriptException, SAXException { super.initialize( parent, scriptable ); if (parent instanceof Form) _form = (Form) parent; } } static public class Options extends JavaScriptEngine { public String getClassName() { return "Options"; } public int jsGet_length() { return getDelegate().getLength(); } public void jsSet_length( int length ) { getDelegate().setLength( length ); } public void put( int i, Scriptable scriptable, Object object ) { if (object == null) { getDelegate().put( i, null ); } else { if (!(object instanceof Option)) throw new IllegalArgumentException( "May only add an Option to this array" ); Option option = (Option) object; getDelegate().put( i, option.getDelegate() ); } } private SelectionOptions getDelegate() { return (SelectionOptions) _scriptable; } } static public class Option extends JavaScriptEngine { public String getClassName() { return "Option"; } public void jsConstructor( String text, String value, boolean defaultSelected, boolean selected ) { _scriptable = WebResponse.newDelegate( "Option" ); getDelegate().initialize( text, value, defaultSelected, selected ); } public int jsGet_index() { return getDelegate().getIndex(); } public String jsGet_text() { return getDelegate().getText(); } public void jsSet_text( String text ) { getDelegate().setText( text ); } public String jsGet_value() { return getDelegate().getValue(); } public void jsSet_value( String value ) { getDelegate().setValue( value ); } public boolean jsGet_selected() { return getDelegate().isSelected(); } public void jsSet_selected( boolean selected ) { getDelegate().setSelected( selected ); } public boolean jsGet_defaultSelected() { return getDelegate().isDefaultSelected(); } SelectionOption getDelegate() { return (SelectionOption) _scriptable; } } } class RhinoException extends RuntimeException { private Exception _cause; public RhinoException( Exception cause ) { _cause = cause; } public String getMessage() { return "Rhino exception: " + _cause; } }