@@ -31,7 +31,12 @@ import com.vaadin.ui.HasComponents; | |||
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 { | |||
/** | |||
@@ -115,6 +120,7 @@ public abstract class AbstractClientConnector implements ClientConnector { | |||
throw new RuntimeException( | |||
"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]; | |||
registerRpc(implementation, type); | |||
} | |||
@@ -371,6 +377,14 @@ public abstract class AbstractClientConnector implements ClientConnector { | |||
} | |||
} | |||
/** | |||
* 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( | |||
final ClientConnector connector) { | |||
return new AllChildrenIterable(connector); | |||
@@ -380,6 +394,13 @@ public abstract class AbstractClientConnector implements ClientConnector { | |||
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) { | |||
ClientConnector previousParent = extension.getParent(); | |||
if (previousParent == this) { | |||
@@ -439,6 +460,14 @@ public abstract class AbstractClientConnector implements ClientConnector { | |||
} | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* <p> | |||
* The {@link #getApplication()} and {@link #getRoot()} methods might return | |||
* <code>null</code> after this method is called. | |||
* </p> | |||
*/ | |||
public void detach() { | |||
for (ClientConnector connector : getAllChildrenIterable(this)) { | |||
connector.detach(); |
@@ -6,19 +6,51 @@ package com.vaadin.terminal; | |||
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 | |||
implements Extension { | |||
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; | |||
} | |||
/** | |||
* 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) { | |||
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); | |||
} | |||
@@ -29,14 +61,14 @@ public abstract class AbstractExtension extends AbstractClientConnector | |||
"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); | |||
previouslyAttached = true; | |||
} else { | |||
throw new IllegalArgumentException(getClass().getName() | |||
+ " can only be attached to targets of type " | |||
+ acceptedParentType.getName() + " but attach to " | |||
+ supportedParentType.getName() + " but attach to " | |||
+ parent.getClass().getName() + " was attempted."); | |||
} | |||
} |
@@ -7,22 +7,146 @@ package com.vaadin.terminal; | |||
import com.vaadin.terminal.gwt.client.JavaScriptExtensionState; | |||
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( | |||
this); | |||
@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); | |||
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, | |||
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) { | |||
callbackHelper.invokeCallback(name, arguments); | |||
} |
@@ -6,6 +6,22 @@ package com.vaadin.terminal; | |||
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 { | |||
/* | |||
* Currently just an empty marker interface to distinguish between | |||
* extensions and other connectors, e.g. components | |||
*/ | |||
} |
@@ -14,11 +14,25 @@ import java.util.Set; | |||
import com.vaadin.external.json.JSONArray; | |||
import com.vaadin.external.json.JSONException; | |||
import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper; | |||
import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper.JavaScriptConnectorState; | |||
import com.vaadin.tools.ReflectTools; | |||
import com.vaadin.ui.AbstractJavaScriptComponent; | |||
import com.vaadin.ui.JavaScript.JavaScriptCallbackRpc; | |||
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 { | |||
private static final Method CALL_METHOD = ReflectTools.findMethod( |
@@ -10,8 +10,8 @@ import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector; | |||
import com.vaadin.terminal.gwt.client.ui.Connect; | |||
@Connect(AbstractJavaScriptExtension.class) | |||
public class JavaScriptExtension extends AbstractExtensionConnector implements | |||
HasJavaScriptConnectorHelper { | |||
public final class JavaScriptExtension extends AbstractExtensionConnector | |||
implements HasJavaScriptConnectorHelper { | |||
private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper( | |||
this); | |||
@@ -8,8 +8,8 @@ import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper | |||
import com.vaadin.ui.AbstractJavaScriptComponent; | |||
@Connect(AbstractJavaScriptComponent.class) | |||
public class JavaScriptComponentConnector extends AbstractComponentConnector | |||
implements HasJavaScriptConnectorHelper { | |||
public final class JavaScriptComponentConnector extends | |||
AbstractComponentConnector implements HasJavaScriptConnectorHelper { | |||
private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper( | |||
this) { |
@@ -12,7 +12,6 @@ import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.terminal.gwt.client.communication.SharedState; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.ComponentContainer; | |||
import com.vaadin.ui.Root; | |||
/** | |||
* Interface implemented by all connectors that are capable of communicating | |||
@@ -30,8 +29,6 @@ public interface ClientConnector extends Connector, RpcTarget { | |||
* | |||
* @return an unmodifiable ordered list of pending server to client method | |||
* calls (not null) | |||
* | |||
* @since 7.0 | |||
*/ | |||
public List<ClientMethodInvocation> retrievePendingRpcCalls(); | |||
@@ -61,7 +58,7 @@ public interface ClientConnector extends Connector, RpcTarget { | |||
* 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 | |||
* descendant depends on the state of a ancestor. | |||
* descendant depends on the state of an ancestor. | |||
*/ | |||
public void requestRepaintAll(); | |||
@@ -69,18 +66,18 @@ public interface ClientConnector extends Connector, RpcTarget { | |||
* Sets the parent connector of the connector. | |||
* | |||
* <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> | |||
* This method is rarely called directly. One of the | |||
* {@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> | |||
@@ -103,15 +100,13 @@ public interface ClientConnector extends Connector, RpcTarget { | |||
* The caller of this method is {@link #setParent(ClientConnector)} if the | |||
* 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 | |||
* 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> | |||
* The attachment logic is implemented in {@link AbstractClientConnector}. | |||
* </p> | |||
* | |||
* @see #getApplication() | |||
*/ | |||
public void attach(); | |||
@@ -119,25 +114,26 @@ public interface ClientConnector extends Connector, RpcTarget { | |||
* Notifies the component that it is detached from the application. | |||
* | |||
* <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> | |||
*/ | |||
public void detach(); | |||
/** | |||
* Get a read-only collection of all extensions attached to this connector. | |||
* | |||
* @return a collection of extensions | |||
*/ | |||
public Collection<Extension> getExtensions(); | |||
/** | |||
* Remove an extension from this connector. | |||
* | |||
* @param extension | |||
* the extension to remove. | |||
*/ | |||
public void removeExtension(Extension extension); | |||
} |
@@ -5,23 +5,151 @@ package com.vaadin.ui; | |||
import com.vaadin.terminal.JavaScriptCallbackHelper; | |||
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 { | |||
private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( | |||
this); | |||
@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); | |||
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, | |||
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) { | |||
callbackHelper.invokeCallback(name, arguments); | |||
} |
@@ -15,6 +15,16 @@ import com.vaadin.terminal.gwt.client.communication.ServerRpc; | |||
import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.ExecuteJavaScriptRpc; | |||
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 { | |||
private Map<String, JavaScriptCallback> callbacks = new HashMap<String, JavaScriptCallback>(); | |||
@@ -24,6 +34,11 @@ public class JavaScript extends AbstractExtension { | |||
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() { | |||
registerRpc(new JavaScriptCallbackRpc() { | |||
public void call(String name, JSONArray arguments) { | |||
@@ -43,6 +58,27 @@ public class JavaScript extends AbstractExtension { | |||
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) { | |||
callbacks.put(name, callback); | |||
if (getState().getNames().add(name)) { | |||
@@ -50,6 +86,17 @@ public class JavaScript extends AbstractExtension { | |||
} | |||
} | |||
/** | |||
* 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) { | |||
callbacks.remove(name); | |||
if (getState().getNames().remove(name)) { | |||
@@ -57,10 +104,25 @@ public class JavaScript extends AbstractExtension { | |||
} | |||
} | |||
/** | |||
* Executes the given JavaScript code in the browser. | |||
* | |||
* @param script | |||
* The JavaScript code to run. | |||
*/ | |||
public void execute(String 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() { | |||
Page page = Page.getCurrent(); | |||
if (page == null) { | |||
@@ -69,4 +131,16 @@ public class JavaScript extends AbstractExtension { | |||
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."); | |||
} | |||
} |
@@ -8,7 +8,34 @@ import java.io.Serializable; | |||
import com.vaadin.external.json.JSONArray; | |||
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 { | |||
/** | |||
* 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; | |||
} |