Browse Source

Javadocs and minor tweaks for extensions, JavaScript and js wrappers

tags/7.0.0.alpha3
Leif Åstrand 12 years ago
parent
commit
f505d99754

+ 30
- 1
src/com/vaadin/terminal/AbstractClientConnector.java View File

import com.vaadin.ui.Root; import com.vaadin.ui.Root;


/** /**
*
* An abstract base class for ClientConnector implementations. This class
* provides all the basic functionality required for connectors.
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/ */
public abstract class AbstractClientConnector implements ClientConnector { public abstract class AbstractClientConnector implements ClientConnector {
/** /**
throw new RuntimeException( throw new RuntimeException(
"Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface"); "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface");
} }
@SuppressWarnings("unchecked")
Class<T> type = (Class<T>) interfaces[0]; Class<T> type = (Class<T>) interfaces[0];
registerRpc(implementation, type); registerRpc(implementation, type);
} }
} }
} }


/**
* Get an Iterable for iterating over all child connectors, including both
* extensions and child components.
*
* @param connector
* the connector to get children for
* @return an Iterable giving all child connectors.
*/
public static Iterable<ClientConnector> getAllChildrenIterable( public static Iterable<ClientConnector> getAllChildrenIterable(
final ClientConnector connector) { final ClientConnector connector) {
return new AllChildrenIterable(connector); return new AllChildrenIterable(connector);
return Collections.unmodifiableCollection(extensions); return Collections.unmodifiableCollection(extensions);
} }


/**
* Add an extension to this connector. This method is protected to allow
* extensions to select which targets they can extend.
*
* @param extension
* the extension to add
*/
protected void addExtension(Extension extension) { protected void addExtension(Extension extension) {
ClientConnector previousParent = extension.getParent(); ClientConnector previousParent = extension.getParent();
if (previousParent == this) { if (previousParent == this) {
} }
} }


/**
* {@inheritDoc}
*
* <p>
* The {@link #getApplication()} and {@link #getRoot()} methods might return
* <code>null</code> after this method is called.
* </p>
*/
public void detach() { public void detach() {
for (ClientConnector connector : getAllChildrenIterable(this)) { for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.detach(); connector.detach();

+ 37
- 5
src/com/vaadin/terminal/AbstractExtension.java View File



import com.vaadin.terminal.gwt.server.ClientConnector; import com.vaadin.terminal.gwt.server.ClientConnector;


/**
* An extension is an entity that is attached to a Component or another
* Extension and independently communicates between client and server.
* <p>
* Extensions can use shared state and RPC in the same way as components.
* <p>
* AbstractExtension adds a mechanism for adding the extension to any Connector
* (extend). To let the Extension determine what kind target it can be added to,
* the extend method is declared as protected.
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public abstract class AbstractExtension extends AbstractClientConnector public abstract class AbstractExtension extends AbstractClientConnector
implements Extension { implements Extension {
private boolean previouslyAttached = false; private boolean previouslyAttached = false;


protected Class<? extends ClientConnector> getAcceptedParentType() {
/**
* Gets a type that the parent must be an instance of. Override this if the
* extension only support certain targets, e.g. if only TextFields can be
* extended.
*
* @return a type that the parent must be an instance of
*/
protected Class<? extends ClientConnector> getSupportedParentType() {
return ClientConnector.class; return ClientConnector.class;
} }


/**
* Add this extension to the target connector. This method is protected to
* allow subclasses to require a more specific type of target.
*
* @param target
* the connector to attach this extension to
*/
protected void extend(AbstractClientConnector target) { protected void extend(AbstractClientConnector target) {
target.addExtension(this); target.addExtension(this);
} }


protected void removeFromTarget() {
/**
* Remove this extension from its target. After an extension has been
* removed, it can not be attached again.
*/
public void removeFromTarget() {
getParent().removeExtension(this); getParent().removeExtension(this);
} }


"An extension can not be set to extend a new target after getting detached from the previous."); "An extension can not be set to extend a new target after getting detached from the previous.");
} }


Class<? extends ClientConnector> acceptedParentType = getAcceptedParentType();
if (parent == null || acceptedParentType.isInstance(parent)) {
Class<? extends ClientConnector> supportedParentType = getSupportedParentType();
if (parent == null || supportedParentType.isInstance(parent)) {
super.setParent(parent); super.setParent(parent);
previouslyAttached = true; previouslyAttached = true;
} else { } else {
throw new IllegalArgumentException(getClass().getName() throw new IllegalArgumentException(getClass().getName()
+ " can only be attached to targets of type " + " can only be attached to targets of type "
+ acceptedParentType.getName() + " but attach to "
+ supportedParentType.getName() + " but attach to "
+ parent.getClass().getName() + " was attempted."); + parent.getClass().getName() + " was attempted.");
} }
} }

+ 127
- 3
src/com/vaadin/terminal/AbstractJavaScriptExtension.java View File

import com.vaadin.terminal.gwt.client.JavaScriptExtensionState; import com.vaadin.terminal.gwt.client.JavaScriptExtensionState;
import com.vaadin.ui.JavaScriptCallback; import com.vaadin.ui.JavaScriptCallback;


public class AbstractJavaScriptExtension extends AbstractExtension {
/**
* Base class for Extensions with all client-side logic implemented using
* JavaScript.
* <p>
* When a new JavaScript extension is initialized in the browser, the framework
* will look for a globally defined JavaScript function that will initialize the
* extension. The name of the initialization function is formed by replacing .
* with _ in the name of the server-side class. If no such function is defined,
* each super class is used in turn until a match is found. The framework will
* thus first attempt with <code>com_example_MyExtension</code> for the
* server-side
* <code>com.example.MyExtension extends AbstractJavaScriptExtension</code>
* class. If MyExtension instead extends <code>com.example.SuperExtension</code>
* , then <code>com_example_SuperExtension</code> will also be attempted if
* <code>com_example_MyExtension</code> has not been defined.
* <p>
*
* The initialization function will be called with <code>this</code> pointing to
* a connector wrapper object providing integration to Vaadin with the following
* functions:
* <ul>
* <li><code>getConnectorId()</code> - returns a string with the id of the
* connector.</li>
* <li><code>getParentId([connectorId])</code> - returns a string with the id of
* the connector's parent. If <code>connectorId</code> is provided, the id of
* the parent of the corresponding connector with the passed id is returned
* instead.</li>
* <li><code>getWidgetElement([connectorId])</code> - returns the DOM Element
* that is the root of a connector's widget. <code>null</code> is returned if
* the connector can not be found or if the connector doesn't have a widget. If
* <code>connectorId</code> is not provided, the connector id of the current
* connector will be used.</li>
* <li><code>getState()</code> - returns an object corresponding to the shared
* state defined on the server. The scheme for conversion between Java and
* JavaScript types is described bellow.</li>
* <li><code>registerRpc([name, ] rpcObject)</code> - registers the
* <code>rpcObject</code> as a RPC handler. <code>rpcObject</code> should be an
* object with field containing functions for all eligible RPC functions. If
* <code>name</code> is provided, the RPC handler will only used for RPC calls
* for the RPC interface with the same fully qualified Java name. If no
* <code>name</code> is provided, the RPC handler will be used for all incoming
* RPC invocations where the RPC method name is defined as a function field in
* the handler. The scheme for conversion between Java types in the RPC
* interface definition and the JavaScript values passed as arguments to the
* handler functions is described bellow.</li>
* <li><code>getRpcProxy([name])</code> - returns an RPC proxy object. If
* <code>name</code> is provided, the proxy object will contain functions for
* all methods in the RPC interface with the same fully qualified name, provided
* a RPC handler has been registered by the server-side code. If no
* <code>name</code> is provided, the returned RPC proxy object will contain
* functions for all methods in all RPC interfaces registered for the connector
* on the server. If the same method name is present in multiple registered RPC
* interfaces, the corresponding function in the RPC proxy object will throw an
* exception when called. The scheme for conversion between Java types in the
* RPC interface and the JavaScript values that should be passed to the
* functions is described bellow.</li>
* </ul>
* The connector wrapper also supports these special functions:
* <ul>
* <li><code>onStateChange</code> - If the JavaScript code assigns a function to
* the field, that function is called whenever the contents of the shared state
* is changed.</li>
* <li>Any field name corresponding to a call to
* {@link #registerCallback(String, JavaScriptCallback)} on the server will
* automatically be present as a function that triggers the registered callback
* on the server.</li>
* <li>Any field name referred to using
* {@link #invokeCallback(String, Object...)} on the server will be called if a
* function has been assigned to the field.</li>
* </ul>
* <p>
*
* Values in the Shared State and in RPC calls are converted between Java and
* JavaScript using the following conventions:
* <ul>
* <li>Primitive Java numbers (byte, char, int, long, float, double) and their
* boxed types (Byte, Character, Integer, Long, Float, Double) are represented
* by JavaScript numbers.</li>
* <li>The primitive Java boolean and the boxed Boolean are represented by
* JavaScript booleans.</li>
* <li>Java Strings are represented by JavaScript strings.</li>
* <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
* <li>Map<String, ?> in Java is represented by JavaScript object with fields
* corresponding to the map keys.</li>
* <li>Any other Java Map is represented by a JavaScript array containing two
* arrays, the first contains the keys and the second contains the values in the
* same order.</li>
* <li>A Java Bean is represented by a JavaScript object with fields
* corresponding to the bean's properties.</li>
* <li>A Java Connector is represented by a JavaScript string containing the
* connector's id.</li>
* <li>A pluggable serialization mechanism is provided for types not described
* here. Please refer to the documentation for specific types for serialization
* information.</li>
* </ul>
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public abstract class AbstractJavaScriptExtension extends AbstractExtension {
private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper(
this); this);


@Override @Override
protected <T> void registerRpc(T implementation,
java.lang.Class<T> rpcInterfaceType) {
protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) {
super.registerRpc(implementation, rpcInterfaceType); super.registerRpc(implementation, rpcInterfaceType);
callbackHelper.registerRpc(rpcInterfaceType); callbackHelper.registerRpc(rpcInterfaceType);
} }


/**
* Register a {@link JavaScriptCallback} that can be called from the
* JavaScript using the provided name. A JavaScript function with the
* provided name will be added to the connector wrapper object (initially
* available as <code>this</code>). Calling that JavaScript function will
* cause the call method in the registered {@link JavaScriptCallback} to be
* invoked with the same arguments.
*
* @param functionName
* the name that should be used for client-side callback
* @param javaScriptCallback
* the callback object that will be invoked when the JavaScript
* function is called
*/
protected void registerCallback(String functionName, protected void registerCallback(String functionName,
JavaScriptCallback javaScriptCallback) { JavaScriptCallback javaScriptCallback) {
callbackHelper.registerCallback(functionName, javaScriptCallback); callbackHelper.registerCallback(functionName, javaScriptCallback);
} }


/**
* Invoke a named function that the connector JavaScript has added to the
* JavaScript connector wrapper object. The arguments should only contain
* data types that can be represented in JavaScript, including primitive
* boxing types, arrays, String, List, Set, Map, Connector and JavaBeans.
*
* @param name
* the name of the function
* @param arguments
* function arguments
*/
protected void invokeCallback(String name, Object... arguments) { protected void invokeCallback(String name, Object... arguments) {
callbackHelper.invokeCallback(name, arguments); callbackHelper.invokeCallback(name, arguments);
} }

+ 17
- 1
src/com/vaadin/terminal/Extension.java View File



import com.vaadin.terminal.gwt.server.ClientConnector; import com.vaadin.terminal.gwt.server.ClientConnector;


/**
* An extension is an entity that is attached to a Component or another
* Extension and independently communicates between client and server.
* <p>
* An extension can only be attached once. It is not supported to move an
* extension from one target to another.
* <p>
* Extensions can use shared state and RPC in the same way as components.
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public interface Extension extends ClientConnector { public interface Extension extends ClientConnector {

/*
* Currently just an empty marker interface to distinguish between
* extensions and other connectors, e.g. components
*/
} }

+ 14
- 0
src/com/vaadin/terminal/JavaScriptCallbackHelper.java View File



import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONArray;
import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONException;
import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper;
import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper.JavaScriptConnectorState; import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper.JavaScriptConnectorState;
import com.vaadin.tools.ReflectTools; import com.vaadin.tools.ReflectTools;
import com.vaadin.ui.AbstractJavaScriptComponent;
import com.vaadin.ui.JavaScript.JavaScriptCallbackRpc; import com.vaadin.ui.JavaScript.JavaScriptCallbackRpc;
import com.vaadin.ui.JavaScriptCallback; import com.vaadin.ui.JavaScriptCallback;


/**
* Internal helper class used to implement functionality common to
* {@link AbstractJavaScriptComponent} and {@link AbstractJavaScriptExtension}.
* Corresponding support in client-side code is in
* {@link JavaScriptConnectorHelper}.
* <p>
* You should most likely no use this class directly.
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public class JavaScriptCallbackHelper implements Serializable { public class JavaScriptCallbackHelper implements Serializable {


private static final Method CALL_METHOD = ReflectTools.findMethod( private static final Method CALL_METHOD = ReflectTools.findMethod(

+ 2
- 2
src/com/vaadin/terminal/gwt/client/JavaScriptExtension.java View File

import com.vaadin.terminal.gwt.client.ui.Connect; import com.vaadin.terminal.gwt.client.ui.Connect;


@Connect(AbstractJavaScriptExtension.class) @Connect(AbstractJavaScriptExtension.class)
public class JavaScriptExtension extends AbstractExtensionConnector implements
HasJavaScriptConnectorHelper {
public final class JavaScriptExtension extends AbstractExtensionConnector
implements HasJavaScriptConnectorHelper {
private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper( private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper(
this); this);



+ 2
- 2
src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentConnector.java View File

import com.vaadin.ui.AbstractJavaScriptComponent; import com.vaadin.ui.AbstractJavaScriptComponent;


@Connect(AbstractJavaScriptComponent.class) @Connect(AbstractJavaScriptComponent.class)
public class JavaScriptComponentConnector extends AbstractComponentConnector
implements HasJavaScriptConnectorHelper {
public final class JavaScriptComponentConnector extends
AbstractComponentConnector implements HasJavaScriptConnectorHelper {


private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper( private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper(
this) { this) {

+ 26
- 30
src/com/vaadin/terminal/gwt/server/ClientConnector.java View File

import com.vaadin.terminal.gwt.client.communication.SharedState; import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.ui.Component; import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer; import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.Root;


/** /**
* Interface implemented by all connectors that are capable of communicating * Interface implemented by all connectors that are capable of communicating
* *
* @return an unmodifiable ordered list of pending server to client method * @return an unmodifiable ordered list of pending server to client method
* calls (not null) * calls (not null)
*
* @since 7.0
*/ */
public List<ClientMethodInvocation> retrievePendingRpcCalls(); public List<ClientMethodInvocation> retrievePendingRpcCalls();


* Causes a repaint of this connector, and all connectors below it. * Causes a repaint of this connector, and all connectors below it.
* *
* This should only be used in special cases, e.g when the state of a * This should only be used in special cases, e.g when the state of a
* descendant depends on the state of a ancestor.
* descendant depends on the state of an ancestor.
*/ */
public void requestRepaintAll(); public void requestRepaintAll();


* Sets the parent connector of the connector. * Sets the parent connector of the connector.
* *
* <p> * <p>
* This method automatically calls {@link #attach()} if the parent becomes
* attached to the application, regardless of whether it was attached
* previously. Conversely, if the parent is {@code null} and the connector
* is attached to the application, {@link #detach()} is called for the
* connector.
* This method automatically calls {@link #attach()} if the connector
* becomes attached to the application, regardless of whether it was
* attached previously. Conversely, if the parent is {@code null} and the
* connector is attached to the application, {@link #detach()} is called for
* the connector.
* </p> * </p>
* <p> * <p>
* This method is rarely called directly. One of the * This method is rarely called directly. One of the
* {@link ComponentContainer#addComponent(Component)} or * {@link ComponentContainer#addComponent(Component)} or
* {@link AbstractClientConnector#addFeature(Feature)} methods is normally
* used for adding connectors to a container and it will call this method
* implicitly.
* {@link AbstractClientConnector#addExtension(Extension)} methods are
* normally used for adding connectors to a parent and they will call this
* method implicitly.
* </p> * </p>
* *
* <p> * <p>
* The caller of this method is {@link #setParent(ClientConnector)} if the * The caller of this method is {@link #setParent(ClientConnector)} if the
* parent is itself already attached to the application. If not, the parent * parent is itself already attached to the application. If not, the parent
* will call the {@link #attach()} for all its children when it is attached * will call the {@link #attach()} for all its children when it is attached
* to the application. This method is always called before the connector is
* painted for the first time.
* to the application. This method is always called before the connector's
* data is sent to the client-side for the first time.
* </p> * </p>
* *
* <p> * <p>
* The attachment logic is implemented in {@link AbstractClientConnector}. * The attachment logic is implemented in {@link AbstractClientConnector}.
* </p> * </p>
*
* @see #getApplication()
*/ */
public void attach(); public void attach();


* Notifies the component that it is detached from the application. * Notifies the component that it is detached from the application.
* *
* <p> * <p>
* The {@link #getApplication()} and {@link #getRoot()} methods might return
* <code>null</code> after this method is called.
* </p>
*
* <p>
* This method must call {@link Root#componentDetached(Component)} to let
* the Root know that a new Component has been attached.
* </p>
* *
* <p>
* The caller of this method is {@link #setParent(Component)} if the parent
* is in the application. When the parent is detached from the application
* it is its response to call {@link #detach()} for all the children and to
* detach itself from the terminal.
* The caller of this method is {@link #setParent(ClientConnector)} if the
* parent is in the application. When the parent is detached from the
* application it is its response to call {@link #detach()} for all the
* children and to detach itself from the terminal.
* </p> * </p>
*/ */
public void detach(); public void detach();


/**
* Get a read-only collection of all extensions attached to this connector.
*
* @return a collection of extensions
*/
public Collection<Extension> getExtensions(); public Collection<Extension> getExtensions();


/**
* Remove an extension from this connector.
*
* @param extension
* the extension to remove.
*/
public void removeExtension(Extension extension); public void removeExtension(Extension extension);
} }

+ 132
- 4
src/com/vaadin/ui/AbstractJavaScriptComponent.java View File



import com.vaadin.terminal.JavaScriptCallbackHelper; import com.vaadin.terminal.JavaScriptCallbackHelper;
import com.vaadin.terminal.gwt.client.ui.JavaScriptComponentState; import com.vaadin.terminal.gwt.client.ui.JavaScriptComponentState;
import com.vaadin.terminal.gwt.client.ui.JavaScriptWidget;


/**
* Base class for Components with all client-side logic implemented using
* JavaScript.
* <p>
* When a new JavaScript component is initialized in the browser, the framework
* will look for a globally defined JavaScript function that will initialize the
* component. The name of the initialization function is formed by replacing .
* with _ in the name of the server-side class. If no such function is defined,
* each super class is used in turn until a match is found. The framework will
* thus first attempt with <code>com_example_MyComponent</code> for the
* server-side
* <code>com.example.MyComponent extends AbstractJavaScriptComponent</code>
* class. If MyComponent instead extends <code>com.example.SuperComponent</code>
* , then <code>com_example_SuperComponent</code> will also be attempted if
* <code>com_example_MyComponent</code> has not been defined.
* <p>
* JavaScript components have a very simple GWT widget ({@link JavaScriptWidget}
* ) just consisting of a <code>div</code> element to which the JavaScript code
* should initialize its own user interface.
* <p>
* The initialization function will be called with <code>this</code> pointing to
* a connector wrapper object providing integration to Vaadin with the following
* functions:
* <ul>
* <li><code>getConnectorId()</code> - returns a string with the id of the
* connector.</li>
* <li><code>getParentId([connectorId])</code> - returns a string with the id of
* the connector's parent. If <code>connectorId</code> is provided, the id of
* the parent of the corresponding connector with the passed id is returned
* instead.</li>
* <li><code>getWidgetElement([connectorId])</code> - returns the DOM Element
* that is the root of a connector's widget. <code>null</code> is returned if
* the connector can not be found or if the connector doesn't have a widget. If
* <code>connectorId</code> is not provided, the connector id of the current
* connector will be used.</li>
* <li><code>getState()</code> - returns an object corresponding to the shared
* state defined on the server. The scheme for conversion between Java and
* JavaScript types is described bellow.</li>
* <li><code>registerRpc([name, ] rpcObject)</code> - registers the
* <code>rpcObject</code> as a RPC handler. <code>rpcObject</code> should be an
* object with field containing functions for all eligible RPC functions. If
* <code>name</code> is provided, the RPC handler will only used for RPC calls
* for the RPC interface with the same fully qualified Java name. If no
* <code>name</code> is provided, the RPC handler will be used for all incoming
* RPC invocations where the RPC method name is defined as a function field in
* the handler. The scheme for conversion between Java types in the RPC
* interface definition and the JavaScript values passed as arguments to the
* handler functions is described bellow.</li>
* <li><code>getRpcProxy([name])</code> - returns an RPC proxy object. If
* <code>name</code> is provided, the proxy object will contain functions for
* all methods in the RPC interface with the same fully qualified name, provided
* a RPC handler has been registered by the server-side code. If no
* <code>name</code> is provided, the returned RPC proxy object will contain
* functions for all methods in all RPC interfaces registered for the connector
* on the server. If the same method name is present in multiple registered RPC
* interfaces, the corresponding function in the RPC proxy object will throw an
* exception when called. The scheme for conversion between Java types in the
* RPC interface and the JavaScript values that should be passed to the
* functions is described bellow.</li>
* </ul>
* The connector wrapper also supports these special functions:
* <ul>
* <li><code>onStateChange</code> - If the JavaScript code assigns a function to
* the field, that function is called whenever the contents of the shared state
* is changed.</li>
* <li>Any field name corresponding to a call to
* {@link #registerCallback(String, JavaScriptCallback)} on the server will
* automatically be present as a function that triggers the registered callback
* on the server.</li>
* <li>Any field name referred to using
* {@link #invokeCallback(String, Object...)} on the server will be called if a
* function has been assigned to the field.</li>
* </ul>
* <p>
*
* Values in the Shared State and in RPC calls are converted between Java and
* JavaScript using the following conventions:
* <ul>
* <li>Primitive Java numbers (byte, char, int, long, float, double) and their
* boxed types (Byte, Character, Integer, Long, Float, Double) are represented
* by JavaScript numbers.</li>
* <li>The primitive Java boolean and the boxed Boolean are represented by
* JavaScript booleans.</li>
* <li>Java Strings are represented by JavaScript strings.</li>
* <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
* <li>Map<String, ?> in Java is represented by JavaScript object with fields
* corresponding to the map keys.</li>
* <li>Any other Java Map is represented by a JavaScript array containing two
* arrays, the first contains the keys and the second contains the values in the
* same order.</li>
* <li>A Java Bean is represented by a JavaScript object with fields
* corresponding to the bean's properties.</li>
* <li>A Java Connector is represented by a JavaScript string containing the
* connector's id.</li>
* <li>A pluggable serialization mechanism is provided for types not described
* here. Please refer to the documentation for specific types for serialization
* information.</li>
* </ul>
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public class AbstractJavaScriptComponent extends AbstractComponent { public class AbstractJavaScriptComponent extends AbstractComponent {
private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper(
this); this);


@Override @Override
protected <T> void registerRpc(T implementation,
java.lang.Class<T> rpcInterfaceType) {
protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) {
super.registerRpc(implementation, rpcInterfaceType); super.registerRpc(implementation, rpcInterfaceType);
callbackHelper.registerRpc(rpcInterfaceType); callbackHelper.registerRpc(rpcInterfaceType);
} }


/**
* Register a {@link JavaScriptCallback} that can be called from the
* JavaScript using the provided name. A JavaScript function with the
* provided name will be added to the connector wrapper object (initially
* available as <code>this</code>). Calling that JavaScript function will
* cause the call method in the registered {@link JavaScriptCallback} to be
* invoked with the same arguments.
*
* @param functionName
* the name that should be used for client-side callback
* @param javaScriptCallback
* the callback object that will be invoked when the JavaScript
* function is called
*/
protected void registerCallback(String functionName, protected void registerCallback(String functionName,
JavaScriptCallback javascriptCallback) {
callbackHelper.registerCallback(functionName, javascriptCallback);
JavaScriptCallback javaScriptCallback) {
callbackHelper.registerCallback(functionName, javaScriptCallback);
} }


/**
* Invoke a named function that the connector JavaScript has added to the
* JavaScript connector wrapper object. The arguments should only contain
* data types that can be represented in JavaScript, including primitive
* boxing types, arrays, String, List, Set, Map, Connector and JavaBeans.
*
* @param name
* the name of the function
* @param arguments
* function arguments
*/
protected void invokeCallback(String name, Object... arguments) { protected void invokeCallback(String name, Object... arguments) {
callbackHelper.invokeCallback(name, arguments); callbackHelper.invokeCallback(name, arguments);
} }

+ 74
- 0
src/com/vaadin/ui/JavaScript.java View File

import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.ExecuteJavaScriptRpc; import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.ExecuteJavaScriptRpc;
import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavaScriptManagerState; import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavaScriptManagerState;


/**
* Provides access to JavaScript functionality in the web browser. To get an
* instance of JavaScript, either use Page.getJavaScript() or
* JavaScript.getCurrent() as a shorthand for getting the JavaScript object
* corresponding to the current Page.
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public class JavaScript extends AbstractExtension { public class JavaScript extends AbstractExtension {
private Map<String, JavaScriptCallback> callbacks = new HashMap<String, JavaScriptCallback>(); private Map<String, JavaScriptCallback> callbacks = new HashMap<String, JavaScriptCallback>();


public void call(String name, JSONArray arguments); public void call(String name, JSONArray arguments);
} }


/**
* Creates a new JavaScript object. You should typically not this, but
* instead use the JavaScript object already associated with your Page
* object.
*/
public JavaScript() { public JavaScript() {
registerRpc(new JavaScriptCallbackRpc() { registerRpc(new JavaScriptCallbackRpc() {
public void call(String name, JSONArray arguments) { public void call(String name, JSONArray arguments) {
return (JavaScriptManagerState) super.getState(); return (JavaScriptManagerState) super.getState();
} }


/**
* Add a new function to the global JavaScript namespace (i.e. the window
* object). The <code>call</code> method in the passed
* {@link JavaScriptCallback} object will be invoked with the same
* parameters whenever the JavaScript function is called in the browser.
*
* A callback added with the name <code>"myCallback"</code> can thus be
* invoked with the following JavaScript code:
* <code>window.myCallback(argument1, argument2)</code>.
*
* If the name parameter contains dots, simple objects are created on demand
* to allow calling the function using the same name (e.g.
* <code>window.myObject.myFunction</code>).
*
* @param name
* the name that the callback function should get in the global
* JavaScript namespace.
* @param callback
* the JavaScriptCallback that will be invoked if the JavaScript
* function is called.
*/
public void addCallback(String name, JavaScriptCallback callback) { public void addCallback(String name, JavaScriptCallback callback) {
callbacks.put(name, callback); callbacks.put(name, callback);
if (getState().getNames().add(name)) { if (getState().getNames().add(name)) {
} }
} }


/**
* Removes a JavaScripCallback from the browser's global JavaScript
* namespace.
*
* If the name contains dots and intermediate were created by
* {@link #addCallback(String, JavaScriptCallback)}addCallback, these
* objects will not be removed when the callback is removed.
*
* @param name
* the name of the callback to remove
*/
public void removeCallback(String name) { public void removeCallback(String name) {
callbacks.remove(name); callbacks.remove(name);
if (getState().getNames().remove(name)) { if (getState().getNames().remove(name)) {
} }
} }


/**
* Executes the given JavaScript code in the browser.
*
* @param script
* The JavaScript code to run.
*/
public void execute(String script) { public void execute(String script) {
getRpcProxy(ExecuteJavaScriptRpc.class).executeJavaScript(script); getRpcProxy(ExecuteJavaScriptRpc.class).executeJavaScript(script);
} }


/**
* Get the JavaScript object for the current Page, or null if there is no
* current page.
*
* @see Page#getCurrent()
*
* @return the JavaScript object corresponding to the current Page, or
* <code>null</code> if there is no current page.
*/
public static JavaScript getCurrent() { public static JavaScript getCurrent() {
Page page = Page.getCurrent(); Page page = Page.getCurrent();
if (page == null) { if (page == null) {
return page.getJavaScript(); return page.getJavaScript();
} }


/**
* JavaScript is not designed to be removed.
*
* @throws UnsupportedOperationException
* when invoked
*/
@Override
public void removeFromTarget() {
throw new UnsupportedOperationException(
"JavaScript is not designed to be removed.");
}

} }

+ 27
- 0
src/com/vaadin/ui/JavaScriptCallback.java View File



import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONArray;
import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONException;
import com.vaadin.terminal.AbstractJavaScriptExtension;


/**
* Defines a method that is called by a client-side JavaScript function. When
* the corresponding JavaScript function is called, the {@link #call(JSONArray)}
* method is invoked.
*
* @see JavaScript#addCallback(String, JavaScriptCallback)
* @see AbstractJavaScriptComponent#registerCallback(String, JavaScriptCallback)
* @see AbstractJavaScriptExtension#registerCallback(String, JavaScriptCallback)
*
* @author Vaadin Ltd
* @version @VERSION@
* @since 7.0.0
*/
public interface JavaScriptCallback extends Serializable { public interface JavaScriptCallback extends Serializable {
/**
* Invoked whenever the corresponding JavaScript function is called in the
* browser.
* <p>
* Because of the asynchronous nature of the communication between client
* and server, no return value can be sent back to the browser.
*
* @param arguments
* an array with JSON representations of the arguments with which
* the JavaScript function was called.
* @throws JSONException
* if the arguments can not be interpreted
*/
public void call(JSONArray arguments) throws JSONException; public void call(JSONArray arguments) throws JSONException;
} }

Loading…
Cancel
Save