aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/vaadin/terminal
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/vaadin/terminal')
-rw-r--r--src/com/vaadin/terminal/AbstractClientConnector.java8
-rw-r--r--src/com/vaadin/terminal/AbstractExtension.java4
-rw-r--r--src/com/vaadin/terminal/AbstractJavascriptExtension.java20
-rw-r--r--src/com/vaadin/terminal/JavascriptRpcHelper.java61
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConnection.java43
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java205
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavascriptExtension.java33
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetSet.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcManager.java59
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java78
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java22
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java60
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java25
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/BootstrapHandler.java57
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientConnector.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/DragAndDropService.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonCodec.java7
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java6
21 files changed, 662 insertions, 53 deletions
diff --git a/src/com/vaadin/terminal/AbstractClientConnector.java b/src/com/vaadin/terminal/AbstractClientConnector.java
index 136ed702fc..a2bf4a0be8 100644
--- a/src/com/vaadin/terminal/AbstractClientConnector.java
+++ b/src/com/vaadin/terminal/AbstractClientConnector.java
@@ -334,7 +334,7 @@ public abstract class AbstractClientConnector implements ClientConnector {
public void requestRepaintAll() {
requestRepaint();
- for (ClientConnector connector : getAllChildrenIteratable(this)) {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.requestRepaintAll();
}
}
@@ -371,7 +371,7 @@ public abstract class AbstractClientConnector implements ClientConnector {
}
}
- public static Iterable<ClientConnector> getAllChildrenIteratable(
+ public static Iterable<ClientConnector> getAllChildrenIterable(
final ClientConnector connector) {
return new AllChildrenIterable(connector);
}
@@ -442,13 +442,13 @@ public abstract class AbstractClientConnector implements ClientConnector {
public void attach() {
requestRepaint();
- for (ClientConnector connector : getAllChildrenIteratable(this)) {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.attach();
}
}
public void detach() {
- for (ClientConnector connector : getAllChildrenIteratable(this)) {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.detach();
}
}
diff --git a/src/com/vaadin/terminal/AbstractExtension.java b/src/com/vaadin/terminal/AbstractExtension.java
index 35123bc488..353db85a18 100644
--- a/src/com/vaadin/terminal/AbstractExtension.java
+++ b/src/com/vaadin/terminal/AbstractExtension.java
@@ -13,6 +13,10 @@ public abstract class AbstractExtension extends AbstractClientConnector
return ClientConnector.class;
}
+ protected void attachTo(AbstractClientConnector parent) {
+ parent.addExtension(this);
+ }
+
@Override
public void setParent(ClientConnector parent) {
Class<? extends ClientConnector> acceptedParentType = getAcceptedParentType();
diff --git a/src/com/vaadin/terminal/AbstractJavascriptExtension.java b/src/com/vaadin/terminal/AbstractJavascriptExtension.java
new file mode 100644
index 0000000000..e741e2af1e
--- /dev/null
+++ b/src/com/vaadin/terminal/AbstractJavascriptExtension.java
@@ -0,0 +1,20 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import com.vaadin.ui.JavascriptCallback;
+
+public class AbstractJavascriptExtension extends AbstractExtension {
+ private JavascriptRpcHelper rpcHelper = new JavascriptRpcHelper(this);
+
+ protected void registerCallback(String functionName,
+ JavascriptCallback javascriptCallback) {
+ rpcHelper.registerCallback(functionName, javascriptCallback);
+ }
+
+ protected void invokeCallback(String name, Object... arguments) {
+ rpcHelper.invokeCallback(name, arguments);
+ }
+}
diff --git a/src/com/vaadin/terminal/JavascriptRpcHelper.java b/src/com/vaadin/terminal/JavascriptRpcHelper.java
new file mode 100644
index 0000000000..b566462833
--- /dev/null
+++ b/src/com/vaadin/terminal/JavascriptRpcHelper.java
@@ -0,0 +1,61 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.JavascriptCallback;
+import com.vaadin.ui.JavascriptManager.JavascriptCallbackRpc;
+
+public class JavascriptRpcHelper {
+
+ private static final Method CALL_METHOD = ReflectTools.findMethod(
+ JavascriptCallbackRpc.class, "call", String.class, JSONArray.class);
+ private AbstractClientConnector connector;
+
+ private Map<String, JavascriptCallback> callbacks = new HashMap<String, JavascriptCallback>();
+ private JavascriptCallbackRpc javascriptCallbackRpc;
+
+ public JavascriptRpcHelper(AbstractClientConnector connector) {
+ this.connector = connector;
+ }
+
+ public void registerCallback(String functionName,
+ JavascriptCallback javascriptCallback) {
+ callbacks.put(functionName, javascriptCallback);
+ ensureRpc();
+ }
+
+ private void ensureRpc() {
+ if (javascriptCallbackRpc == null) {
+ javascriptCallbackRpc = new JavascriptCallbackRpc() {
+ public void call(String name, JSONArray arguments) {
+ JavascriptCallback callback = callbacks.get(name);
+ try {
+ callback.call(arguments);
+ } catch (JSONException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ };
+ connector.registerRpc(javascriptCallbackRpc);
+ }
+ }
+
+ public void invokeCallback(String name, Object... arguments) {
+ JSONArray args = new JSONArray(Arrays.asList(arguments));
+ connector.addMethodInvocationToQueue(
+ JavascriptCallbackRpc.class.getName(), CALL_METHOD,
+ new Object[] { name, args });
+ connector.requestRepaint();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index e8013ccc72..7d11c6245a 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -40,6 +40,7 @@ import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
@@ -1438,12 +1439,19 @@ public class ApplicationConnection {
.getConnector(connectorId);
if (null != connector) {
- JSONObject stateDataAndType = new JSONObject(
+ JSONObject stateJson = new JSONObject(
states.getJavaScriptObject(connectorId));
+ if (connector instanceof HasJavascriptConnectorHelper) {
+ ((HasJavascriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper()
+ .setNativeState(
+ stateJson.getJavaScriptObject());
+ }
+
SharedState state = connector.getState();
JsonDecoder.decodeValue(new Type(state.getClass()
- .getName(), null), stateDataAndType, state,
+ .getName(), null), stateJson, state,
ApplicationConnection.this);
StateChangeEvent event = GWT
@@ -1584,11 +1592,8 @@ public class ApplicationConnection {
for (int i = 0; i < rpcLength; i++) {
try {
JSONArray rpcCall = (JSONArray) rpcCalls.get(i);
- MethodInvocation invocation = parseMethodInvocation(rpcCall);
- VConsole.log("Server to client RPC call: "
- + invocation);
- rpcManager.applyInvocation(invocation,
- getConnectorMap());
+ rpcManager.parseAndApplyInvocation(rpcCall,
+ ApplicationConnection.this);
} catch (final Throwable e) {
VConsole.error(e);
}
@@ -1601,26 +1606,6 @@ public class ApplicationConnection {
ApplicationConfiguration.runWhenWidgetsLoaded(c);
}
- private MethodInvocation parseMethodInvocation(JSONArray rpcCall) {
- String connectorId = ((JSONString) rpcCall.get(0)).stringValue();
- String interfaceName = ((JSONString) rpcCall.get(1)).stringValue();
- String methodName = ((JSONString) rpcCall.get(2)).stringValue();
- JSONArray parametersJson = (JSONArray) rpcCall.get(3);
-
- MethodInvocation methodInvocation = new MethodInvocation(connectorId,
- interfaceName, methodName);
- Type[] parameterTypes = rpcManager.getParameterTypes(methodInvocation);
-
- Object[] parameters = new Object[parametersJson.size()];
- for (int j = 0; j < parametersJson.size(); ++j) {
- parameters[j] = JsonDecoder.decodeValue(parameterTypes[j],
- parametersJson.get(j), null, this);
- }
-
- methodInvocation.setParameters(parameters);
- return methodInvocation;
- }
-
// Redirect browser, null reloads current page
private static native void redirect(String url)
/*-{
@@ -2169,8 +2154,8 @@ public class ApplicationConnection {
private ServerConnector createAndRegisterConnector(String connectorId,
int connectorType) {
// Create and register a new connector with the given type
- ServerConnector p = widgetSet
- .createConnector(connectorType, configuration);
+ ServerConnector p = widgetSet.createConnector(connectorType,
+ configuration);
connectorMap.registerConnector(connectorId, p);
p.doInit(connectorId, this);
diff --git a/src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java b/src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java
new file mode 100644
index 0000000000..ab0e62222c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java
@@ -0,0 +1,205 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.ArrayList;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.json.client.JSONArray;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+
+public class JavascriptConnectorHelper {
+
+ private final ServerConnector connector;
+ private final JavaScriptObject nativeState = JavaScriptObject
+ .createObject();
+ private final JavaScriptObject rpcMap = JavaScriptObject.createObject();
+
+ private JavaScriptObject connectorWrapper;
+ private int tag;
+
+ public JavascriptConnectorHelper(ServerConnector connector) {
+ this.connector = connector;
+ }
+
+ public boolean init() {
+ ApplicationConfiguration conf = connector.getConnection()
+ .getConfiguration();
+ ArrayList<String> attemptedNames = new ArrayList<String>();
+ Integer tag = Integer.valueOf(this.tag);
+ while (tag != null) {
+ String serverSideClassName = conf.getServerSideClassNameForTag(tag);
+ String initFunctionName = serverSideClassName
+ .replaceAll("\\.", "_");
+ if (tryInitJs(initFunctionName, getConnectorWrapper())) {
+ VConsole.log("Javascript connector initialized using "
+ + initFunctionName);
+ return true;
+ } else {
+ VConsole.log("No javascript function " + initFunctionName
+ + " found");
+ attemptedNames.add(initFunctionName);
+ tag = conf.getParentTag(tag.intValue());
+ }
+ }
+ VConsole.log("No javascript init for connector not found");
+ showInitProblem(attemptedNames);
+ return false;
+ }
+
+ protected void showInitProblem(ArrayList<String> attemptedNames) {
+ // Default does nothing
+ }
+
+ private static native boolean tryInitJs(String initFunctionName,
+ JavaScriptObject connectorWrapper)
+ /*-{
+ if (typeof $wnd[initFunctionName] == 'function') {
+ $wnd[initFunctionName].apply(connectorWrapper);
+ return true;
+ } else {
+ return false;
+ }
+ }-*/;
+
+ private JavaScriptObject getConnectorWrapper() {
+ if (connectorWrapper == null) {
+ connectorWrapper = createConnectorWrapper();
+ }
+
+ return connectorWrapper;
+ }
+
+ protected JavaScriptObject createConnectorWrapper() {
+ return createConnectorWrapper(this, nativeState, rpcMap,
+ connector.getConnectorId());
+ }
+
+ public void fireNativeStateChange() {
+ fireNativeStateChange(getConnectorWrapper());
+ }
+
+ private static native void fireNativeStateChange(
+ JavaScriptObject connectorWrapper)
+ /*-{
+ if (typeof connectorWrapper.onStateChange == 'function') {
+ connectorWrapper.onStateChange();
+ }
+ }-*/;
+
+ private static native JavaScriptObject createConnectorWrapper(
+ JavascriptConnectorHelper h, JavaScriptObject nativeState,
+ JavaScriptObject registeredRpc, String connectorId)
+ /*-{
+ return {
+ 'getConnectorId': function() {
+ return connectorId;
+ },
+ 'getState': function() {
+ return nativeState;
+ },
+ 'getRpcProxyFunction': function(iface, method) {
+ return $entry(function() {
+ h.@com.vaadin.terminal.gwt.client.JavascriptConnectorHelper::fireRpc(Ljava/lang/String;Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(iface, method, arguments);
+ });
+ },
+ 'getCallback': function(name) {
+ return $entry(function() {
+ var args = [name, Array.prototype.slice.call(arguments, 0)];
+ var iface = "com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc";
+ var method = "call";
+ h.@com.vaadin.terminal.gwt.client.JavascriptConnectorHelper::fireRpc(Ljava/lang/String;Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(iface, method, args);
+ });
+ },
+ 'registerCallback': function(name, callback) {
+ //TODO maintain separate map
+ if (!registeredRpc[name]) {
+ registeredRpc[name] = [];
+ }
+ registeredRpc[name].push(callback);
+ },
+ 'registerRpc': function(iface, rpcHandler) {
+ if (!registeredRpc[iface]) {
+ registeredRpc[iface] = [];
+ }
+ registeredRpc[iface].push(rpcHandler);
+ },
+ };
+ }-*/;
+
+ private void fireRpc(String iface, String method,
+ JsArray<JavaScriptObject> arguments) {
+ JSONArray argumentsArray = new JSONArray(arguments);
+ Object[] parameters = new Object[arguments.length()];
+ for (int i = 0; i < parameters.length; i++) {
+ parameters[i] = argumentsArray.get(i);
+ }
+ connector.getConnection().addMethodInvocationToQueue(
+ new MethodInvocation(connector.getConnectorId(), iface, method,
+ parameters), true);
+ }
+
+ public void setNativeState(JavaScriptObject state) {
+ updateNativeState(nativeState, state);
+ }
+
+ private static native void updateNativeState(JavaScriptObject state,
+ JavaScriptObject input)
+ /*-{
+ // Copy all fields to existing state object
+ for(var key in state) {
+ if (state.hasOwnProperty(key)) {
+ delete state[key];
+ }
+ }
+
+ for(var key in input) {
+ if (input.hasOwnProperty(key)) {
+ state[key] = input[key];
+ }
+ }
+ }-*/;
+
+ public Object[] decodeRpcParameters(JSONArray parametersJson) {
+ return new Object[] { parametersJson.getJavaScriptObject() };
+ }
+
+ public void setTag(int tag) {
+ this.tag = tag;
+ }
+
+ public void invokeJsRpc(MethodInvocation invocation,
+ JSONArray parametersJson) {
+ if ("com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc"
+ .equals(invocation.getInterfaceName())
+ && "call".equals(invocation.getMethodName())) {
+ invokeJsRpc(rpcMap, parametersJson.get(0).isString().stringValue(),
+ null, parametersJson.get(1).isArray().getJavaScriptObject());
+ } else {
+ invokeJsRpc(rpcMap, invocation.getInterfaceName(),
+ invocation.getMethodName(),
+ parametersJson.getJavaScriptObject());
+ }
+ }
+
+ private static native void invokeJsRpc(JavaScriptObject rpcMap,
+ String interfaceName, String methodName, JavaScriptObject parameters)
+ /*-{
+ var targets = rpcMap[interfaceName];
+ if (!targets) {
+ return;
+ }
+ for(var i = 0; i < targets.length; i++) {
+ var target = targets[i];
+ if (methodName === null && typeof target === 'function') {
+ target.apply($wnd, parameters);
+ } else {
+ target[methodName].apply(target, parameters);
+ }
+ }
+ }-*/;
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/JavascriptExtension.java b/src/com/vaadin/terminal/gwt/client/JavascriptExtension.java
new file mode 100644
index 0000000000..6c098a52f6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavascriptExtension.java
@@ -0,0 +1,33 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import com.vaadin.terminal.AbstractJavascriptExtension;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+
+@Connect(AbstractJavascriptExtension.class)
+public class JavascriptExtension extends AbstractConnector implements
+ HasJavascriptConnectorHelper {
+ private final JavascriptConnectorHelper helper = new JavascriptConnectorHelper(
+ this);
+
+ @Override
+ protected void init() {
+ helper.init();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ helper.fireNativeStateChange();
+ }
+
+ public JavascriptConnectorHelper getJavascriptConnectorHelper() {
+ return helper;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetSet.java b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
index d7cc2df00d..ecbfb0ecc9 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetSet.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
@@ -5,6 +5,7 @@
package com.vaadin.terminal.gwt.client;
import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
public class WidgetSet {
@@ -52,7 +53,12 @@ public class WidgetSet {
/*
* let the auto generated code instantiate this type
*/
- return widgetMap.instantiate(classType);
+ ServerConnector connector = widgetMap.instantiate(classType);
+ if (connector instanceof HasJavascriptConnectorHelper) {
+ ((HasJavascriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper().setTag(tag);
+ }
+ return connector;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java b/src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java
new file mode 100644
index 0000000000..74bc75da66
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.vaadin.terminal.gwt.client.JavascriptConnectorHelper;
+
+public interface HasJavascriptConnectorHelper {
+ public JavascriptConnectorHelper getJavascriptConnectorHelper();
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
index 657f44896d..cb7dbe5e72 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
@@ -59,6 +59,8 @@ public class JsonEncoder {
boolean restrictToInternalTypes, ApplicationConnection connection) {
if (null == value) {
return JSONNull.getInstance();
+ } else if (value instanceof JSONValue) {
+ return (JSONValue) value;
} else if (value instanceof String[]) {
String[] array = (String[]) value;
JSONArray jsonArray = new JSONArray();
diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
index 1d3447687d..e0ffb40125 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
@@ -9,8 +9,12 @@ import java.util.HashMap;
import java.util.Map;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONString;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.VConsole;
/**
* Client side RPC manager that can invoke methods based on RPC calls received
@@ -41,20 +45,10 @@ public class RpcManager {
*
* @param invocation
* method to invoke
- * @param connectorMap
- * mapper used to find Connector for the method call and any
- * connectors referenced in parameters
*/
public void applyInvocation(MethodInvocation invocation,
- ConnectorMap connectorMap) {
- ServerConnector connector = connectorMap.getConnector(invocation
- .getConnectorId());
+ ServerConnector connector) {
String signature = getSignature(invocation);
- if (connector == null) {
- throw new IllegalStateException("Target connector ("
- + invocation.getConnectorId() + ") not found for RCC to "
- + signature);
- }
RpcMethod rpcMethod = getRpcMethod(signature);
Collection<ClientRpc> implementations = connector
@@ -82,4 +76,47 @@ public class RpcManager {
return getRpcMethod(getSignature(invocation)).getParameterTypes();
}
+ public void parseAndApplyInvocation(JSONArray rpcCall,
+ ApplicationConnection connection) {
+ ConnectorMap connectorMap = ConnectorMap.get(connection);
+
+ String connectorId = ((JSONString) rpcCall.get(0)).stringValue();
+ String interfaceName = ((JSONString) rpcCall.get(1)).stringValue();
+ String methodName = ((JSONString) rpcCall.get(2)).stringValue();
+ JSONArray parametersJson = (JSONArray) rpcCall.get(3);
+
+ ServerConnector connector = connectorMap.getConnector(connectorId);
+
+ MethodInvocation invocation = new MethodInvocation(connectorId,
+ interfaceName, methodName);
+ if (connector instanceof HasJavascriptConnectorHelper) {
+ ((HasJavascriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper().invokeJsRpc(invocation,
+ parametersJson);
+ } else {
+ if (connector == null) {
+ throw new IllegalStateException("Target connector ("
+ + connector + ") not found for RCC to "
+ + getSignature(invocation));
+ }
+
+ parseMethodParameters(invocation, parametersJson, connection);
+ VConsole.log("Server to client RPC call: " + invocation);
+ applyInvocation(invocation, connector);
+ }
+ }
+
+ private void parseMethodParameters(MethodInvocation methodInvocation,
+ JSONArray parametersJson, ApplicationConnection connection) {
+ Type[] parameterTypes = getParameterTypes(methodInvocation);
+
+ Object[] parameters = new Object[parametersJson.size()];
+ for (int j = 0; j < parametersJson.size(); ++j) {
+ parameters[j] = JsonDecoder.decodeValue(parameterTypes[j],
+ parametersJson.get(j), null, connection);
+ }
+
+ methodInvocation.setParameters(parameters);
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java
new file mode 100644
index 0000000000..979a6d22c4
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java
@@ -0,0 +1,78 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.json.client.JSONArray;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.JavascriptManager;
+
+@Connect(JavascriptManager.class)
+public class JavascriptManagerConnector extends AbstractConnector {
+ private Set<String> currentNames = new HashSet<String>();
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ Set<String> newNames = getState().getNames();
+
+ // Current names now only contains orphan callbacks
+ currentNames.removeAll(newNames);
+
+ for (String name : currentNames) {
+ removeCallback(name);
+ }
+
+ currentNames = new HashSet<String>(newNames);
+ for (String name : newNames) {
+ addCallback(name);
+ }
+ }
+
+ // TODO Ensure we don't overwrite anything (important) in $wnd
+ private native void addCallback(String name)
+ /*-{
+ var m = this;
+ $wnd[name] = $entry(function() {
+ //Must make a copy because arguments is an array-like object (not instanceof Array), causing suboptimal JSON encoding
+ var args = Array.prototype.slice.call(arguments, 0);
+ m.@com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavascriptManagerConnector::sendRpc(Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(name, args);
+ });
+ }-*/;
+
+ // TODO only remove what we actually added
+ private native void removeCallback(String name)
+ /*-{
+ delete $wnd[name];
+ }-*/;
+
+ public void sendRpc(String name, JsArray<JavaScriptObject> arguments) {
+ Object[] parameters = new Object[] { name, new JSONArray(arguments) };
+
+ /*
+ * Must invoke manually as the RPC interface can't be used in GWT
+ * because of the JSONArray parameter
+ */
+ getConnection()
+ .addMethodInvocationToQueue(
+ new MethodInvocation(
+ getConnectorId(),
+ "com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc",
+ "call", parameters), true);
+ }
+
+ @Override
+ public JavascriptManagerState getState() {
+ return (JavascriptManagerState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java
new file mode 100644
index 0000000000..77794ffdca
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java
@@ -0,0 +1,22 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+
+public class JavascriptManagerState extends SharedState {
+ private Set<String> names = new HashSet<String>();
+
+ public Set<String> getNames() {
+ return names;
+ }
+
+ public void setNames(Set<String> names) {
+ this.names = names;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java
new file mode 100644
index 0000000000..57e65e91c6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java
@@ -0,0 +1,60 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.Element;
+import com.vaadin.terminal.gwt.client.JavascriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.ui.AbstractJavascriptComponent;
+
+@Connect(AbstractJavascriptComponent.class)
+public class JavascriptComponentConnector extends AbstractComponentConnector
+ implements HasJavascriptConnectorHelper {
+
+ private final JavascriptConnectorHelper helper = new JavascriptConnectorHelper(
+ this) {
+ @Override
+ protected void showInitProblem(
+ java.util.ArrayList<String> attemptedNames) {
+ getWidget().showNoInitFound(attemptedNames);
+ }
+
+ @Override
+ protected JavaScriptObject createConnectorWrapper() {
+ JavaScriptObject connectorWrapper = super.createConnectorWrapper();
+ addGetWidgetElement(connectorWrapper, getWidget().getElement());
+ return connectorWrapper;
+ }
+ };
+
+ @Override
+ protected void init() {
+ helper.init();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ helper.fireNativeStateChange();
+ }
+
+ private static native void addGetWidgetElement(
+ JavaScriptObject connectorWrapper, Element element)
+ /*-{
+ connectorWrapper.getWidgetElement = function() {
+ return element;
+ };
+ }-*/;
+
+ @Override
+ public JavascriptWidget getWidget() {
+ return (JavascriptWidget) super.getWidget();
+ }
+
+ public JavascriptConnectorHelper getJavascriptConnectorHelper() {
+ return helper;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java b/src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java
new file mode 100644
index 0000000000..93a4417b1c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.ArrayList;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.ui.Widget;
+
+public class JavascriptWidget extends Widget {
+ public JavascriptWidget() {
+ setElement(Document.get().createDivElement());
+ }
+
+ public void showNoInitFound(ArrayList<String> attemptedNames) {
+ String message = "Could not initialize JavascriptConnector because no javascript init function was found. Make sure one of these functions are defined: <ul>";
+ for (String name : attemptedNames) {
+ message += "<li>" + name + "</li>";
+ }
+ message += "</ul>";
+
+ getElement().setInnerHTML(message);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index b77dc961d6..86bbb4e80b 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -899,7 +899,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
JSONArray children = new JSONArray();
for (ClientConnector child : AbstractClientConnector
- .getAllChildrenIteratable(connector)) {
+ .getAllChildrenIterable(connector)) {
if (isVisible(child)) {
children.put(child.getConnectorId());
}
diff --git a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
index 8a0c700121..ae1fadd91b 100644
--- a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
+++ b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
@@ -9,6 +9,10 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
@@ -16,6 +20,7 @@ import javax.servlet.http.HttpServletResponse;
import com.vaadin.Application;
import com.vaadin.RootRequiresMoreInformationException;
import com.vaadin.Version;
+import com.vaadin.annotations.LoadScripts;
import com.vaadin.external.json.JSONException;
import com.vaadin.external.json.JSONObject;
import com.vaadin.terminal.DeploymentConfiguration;
@@ -467,15 +472,15 @@ public abstract class BootstrapHandler implements RequestHandler {
page.write("<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\"/>\n");
page.write("<style type=\"text/css\">"
- + "html, body {height:100%;margin:0;}</style>");
+ + "html, body {height:100%;margin:0;}</style>\n");
// Add favicon links
if (themeName != null) {
String themeUri = getThemeUri(context, themeName);
page.write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\""
- + themeUri + "/favicon.ico\" />");
+ + themeUri + "/favicon.ico\" />\n");
page.write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\""
- + themeUri + "/favicon.ico\" />");
+ + themeUri + "/favicon.ico\" />\n");
}
Root root = context.getRoot();
@@ -484,7 +489,51 @@ public abstract class BootstrapHandler implements RequestHandler {
page.write("<title>"
+ AbstractApplicationServlet.safeEscapeForHtml(title)
- + "</title>");
+ + "</title>\n");
+
+ if (root != null) {
+ List<LoadScripts> loadScriptsAnnotations = getAnnotationsFor(
+ root.getClass(), LoadScripts.class);
+ Collections.reverse(loadScriptsAnnotations);
+ // Begin from the end as a class might requests scripts that depend
+ // on script loaded by a super class
+ for (int i = loadScriptsAnnotations.size() - 1; i >= 0; i--) {
+ LoadScripts loadScripts = loadScriptsAnnotations.get(i);
+ String[] value = loadScripts.value();
+ if (value != null) {
+ for (String script : value) {
+ page.write("<script type='text/javascript' src='");
+ page.write(script);
+ page.write("'></script>\n");
+ }
+ }
+ }
+
+ }
+ }
+
+ private static <T extends Annotation> List<T> getAnnotationsFor(
+ Class<?> type, Class<T> annotationType) {
+ List<T> list = new ArrayList<T>();
+ // Find from the class hierarchy
+ Class<?> currentType = type;
+ while (currentType != Object.class) {
+ T annotation = currentType.getAnnotation(annotationType);
+ if (annotation != null) {
+ list.add(annotation);
+ }
+ currentType = currentType.getSuperclass();
+ }
+
+ // Find from an implemented interface
+ for (Class<?> iface : type.getInterfaces()) {
+ T annotation = iface.getAnnotation(annotationType);
+ if (annotation != null) {
+ list.add(annotation);
+ }
+ }
+
+ return list;
}
/**
diff --git a/src/com/vaadin/terminal/gwt/server/ClientConnector.java b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
index 6830aa8e14..359e112738 100644
--- a/src/com/vaadin/terminal/gwt/server/ClientConnector.java
+++ b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
@@ -139,5 +139,5 @@ public interface ClientConnector extends Connector, RpcTarget {
public Iterator<Extension> getExtensionIterator();
- public void removeExtension(Extension feature);
+ public void removeExtension(Extension extension);
}
diff --git a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
index f5bef0b568..101e7f0cb3 100644
--- a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
+++ b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
@@ -296,7 +296,7 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
}
@Override
- public void removeExtension(Extension feature) {
+ public void removeExtension(Extension extension) {
// TODO Auto-generated method stub
}
diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
index aa61c68338..71e4727164 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
@@ -123,6 +123,9 @@ public class JsonCodec implements Serializable {
// Try to decode object using fields
if (value == JSONObject.NULL) {
return null;
+ } else if (targetType == JSONObject.class
+ || targetType == JSONArray.class) {
+ return value;
} else {
return decodeObject(targetType, (JSONObject) value, application);
}
@@ -400,7 +403,7 @@ public class JsonCodec implements Serializable {
boolean restrictToInternalTypes, JSONArray jsonArray,
Application application) throws JSONException {
HashSet<Object> set = new HashSet<Object>();
- set.addAll(decodeList(List.class, restrictToInternalTypes, jsonArray,
+ set.addAll(decodeList(targetType, restrictToInternalTypes, jsonArray,
application));
return set;
}
@@ -514,6 +517,8 @@ public class JsonCodec implements Serializable {
return connector.getConnectorId();
} else if (value instanceof Enum) {
return encodeEnum((Enum<?>) value, application);
+ } else if (value instanceof JSONArray || value instanceof JSONObject) {
+ return value;
} else {
// Any object that we do not know how to encode we encode by looping
// through fields
diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
index 6f278f7797..95565c4379 100644
--- a/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
+++ b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
@@ -79,6 +79,12 @@ public class ServerRpcMethodInvocation extends MethodInvocation {
}
}
+ if (invocationMethod == null) {
+ throw new IllegalStateException("Can't find method " + methodName
+ + " with " + parameterCount + " parameters in "
+ + targetType.getName());
+ }
+
return invocationMethod;
}