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(); |
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."); | ||||
} | } | ||||
} | } |
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); | ||||
} | } |
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 | |||||
*/ | |||||
} | } |
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( |
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); | ||||
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) { |
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); | ||||
} | } |
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); | ||||
} | } |
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."); | |||||
} | |||||
} | } |
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; | ||||
} | } |