diff options
Diffstat (limited to 'src/com/vaadin/terminal')
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; } |