aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/vaadin/terminal
diff options
context:
space:
mode:
authorJouni Koivuviita <jouni@vaadin.com>2012-08-07 15:35:55 +0300
committerJouni Koivuviita <jouni@vaadin.com>2012-08-07 15:35:55 +0300
commitd422eba61ba6e8ef82c72fa661ff4991f8918ec0 (patch)
tree1e4f1c8f7f2d8b3543eed1991709a43889466446 /src/com/vaadin/terminal
parentef744edf4a0e849932d30bd9d6870ec15f391225 (diff)
parent23fcb95f1a8b2fda7f6b7d648634ec21463da875 (diff)
downloadvaadin-framework-d422eba61ba6e8ef82c72fa661ff4991f8918ec0.tar.gz
vaadin-framework-d422eba61ba6e8ef82c72fa661ff4991f8918ec0.zip
Boxlayout
Diffstat (limited to 'src/com/vaadin/terminal')
-rw-r--r--src/com/vaadin/terminal/AbstractClientConnector.java490
-rw-r--r--src/com/vaadin/terminal/AbstractErrorMessage.java7
-rw-r--r--src/com/vaadin/terminal/AbstractExtension.java76
-rw-r--r--src/com/vaadin/terminal/AbstractJavaScriptExtension.java158
-rw-r--r--src/com/vaadin/terminal/ClassResource.java8
-rw-r--r--src/com/vaadin/terminal/CombinedRequest.java2
-rw-r--r--src/com/vaadin/terminal/Extension.java27
-rw-r--r--src/com/vaadin/terminal/JavaScriptCallbackHelper.java115
-rw-r--r--src/com/vaadin/terminal/Page.java646
-rw-r--r--src/com/vaadin/terminal/PaintTarget.java26
-rw-r--r--src/com/vaadin/terminal/UserError.java10
-rw-r--r--src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java41
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConnection.java194
-rw-r--r--src/com/vaadin/terminal/gwt/client/BrowserInfo.java20
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentConnector.java23
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentLocator.java28
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentState.java24
-rw-r--r--src/com/vaadin/terminal/gwt/client/Connector.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ConnectorMap.java33
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavaScriptConnectorHelper.java372
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavaScriptExtension.java32
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavaScriptExtensionState.java36
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManager.java111
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java44
-rw-r--r--src/com/vaadin/terminal/gwt/client/ServerConnector.java29
-rw-r--r--src/com/vaadin/terminal/gwt/client/Util.java31
-rw-r--r--src/com/vaadin/terminal/gwt/client/VDebugConsole.java47
-rw-r--r--src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetMap.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetSet.java55
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/DiffJSONSerializer.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/GeneratedRpcMethodProvider.java20
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/HasJavaScriptConnectorHelper.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java13
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java224
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java185
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcManager.java102
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java32
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/SharedState.java26
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/Type.java40
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java24
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/UidlValue.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/AbstractExtensionConnector.java27
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/ExecuteJavaScriptRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerConnector.java119
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerState.java22
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java18
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java104
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java82
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java41
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java66
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java1
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java5
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentConnector.java42
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentState.java37
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavaScriptWidget.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java111
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/VButton.java42
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java37
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/AcceptCriterion.java33
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/DDUtil.java21
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VAcceptAll.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VAnd.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VContainsDataFlavor.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VLazyInitItemIdentifiers.java16
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VNot.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VOr.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VServerAccept.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VTargetDetailIs.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java3
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/form/VForm.java5
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java3
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/GreetAgainRpc.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldExtensionConnector.java48
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldRpc.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldState.java18
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/label/ContentMode.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java23
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java7
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java6
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java6
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java33
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/PageClientRpc.java13
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java21
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java223
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java17
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java9
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java36
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java207
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java242
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java15
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java40
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java44
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java4
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java90
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java94
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java304
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java9
-rw-r--r--src/com/vaadin/terminal/gwt/server/BootstrapHandler.java59
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientConnector.java96
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java7
-rw-r--r--src/com/vaadin/terminal/gwt/server/CommunicationManager.java18
-rw-r--r--src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java13
-rw-r--r--src/com/vaadin/terminal/gwt/server/Constants.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/DragAndDropService.java59
-rw-r--r--src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java36
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonCodec.java456
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java37
-rw-r--r--src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java17
-rw-r--r--src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java6
-rw-r--r--src/com/vaadin/terminal/gwt/server/RpcManager.java33
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServerRpcManager.java9
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java6
-rw-r--r--src/com/vaadin/terminal/gwt/server/WebBrowser.java50
-rw-r--r--src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java2
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java40
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java252
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java4
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java5
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java (renamed from src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java)138
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java5
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java2
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java266
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java30
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java62
146 files changed, 5809 insertions, 1890 deletions
diff --git a/src/com/vaadin/terminal/AbstractClientConnector.java b/src/com/vaadin/terminal/AbstractClientConnector.java
new file mode 100644
index 0000000000..9de444d70e
--- /dev/null
+++ b/src/com/vaadin/terminal/AbstractClientConnector.java
@@ -0,0 +1,490 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Logger;
+
+import com.vaadin.Application;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.server.ClientConnector;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
+import com.vaadin.terminal.gwt.server.RpcManager;
+import com.vaadin.terminal.gwt.server.RpcTarget;
+import com.vaadin.terminal.gwt.server.ServerRpcManager;
+import com.vaadin.ui.HasComponents;
+import com.vaadin.ui.Root;
+
+/**
+ * An abstract base class for ClientConnector implementations. This class
+ * provides all the basic functionality required for connectors.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public abstract class AbstractClientConnector implements ClientConnector {
+ /**
+ * A map from client to server RPC interface class to the RPC call manager
+ * that handles incoming RPC calls for that interface.
+ */
+ private Map<Class<?>, RpcManager> rpcManagerMap = new HashMap<Class<?>, RpcManager>();
+
+ /**
+ * A map from server to client RPC interface class to the RPC proxy that
+ * sends ourgoing RPC calls for that interface.
+ */
+ private Map<Class<?>, ClientRpc> rpcProxyMap = new HashMap<Class<?>, ClientRpc>();
+
+ /**
+ * Shared state object to be communicated from the server to the client when
+ * modified.
+ */
+ private SharedState sharedState;
+
+ /**
+ * Pending RPC method invocations to be sent.
+ */
+ private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
+
+ private String connectorId;
+
+ private ArrayList<Extension> extensions = new ArrayList<Extension>();
+
+ private ClientConnector parent;
+
+ /* Documentation copied from interface */
+ public void requestRepaint() {
+ Root root = getRoot();
+ if (root != null) {
+ root.getConnectorTracker().markDirty(this);
+ }
+ }
+
+ /**
+ * Registers an RPC interface implementation for this component.
+ *
+ * A component can listen to multiple RPC interfaces, and subclasses can
+ * register additional implementations.
+ *
+ * @since 7.0
+ *
+ * @param implementation
+ * RPC interface implementation
+ * @param rpcInterfaceType
+ * RPC interface class for which the implementation should be
+ * registered
+ */
+ protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) {
+ rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>(
+ implementation, rpcInterfaceType));
+ }
+
+ /**
+ * Registers an RPC interface implementation for this component.
+ *
+ * A component can listen to multiple RPC interfaces, and subclasses can
+ * register additional implementations.
+ *
+ * @since 7.0
+ *
+ * @param implementation
+ * RPC interface implementation. Also used to deduce the type.
+ */
+ protected <T extends ServerRpc> void registerRpc(T implementation) {
+ Class<?> cls = implementation.getClass();
+ Class<?>[] interfaces = cls.getInterfaces();
+ while (interfaces.length == 0) {
+ // Search upwards until an interface is found. It must be found as T
+ // extends ServerRpc
+ cls = cls.getSuperclass();
+ interfaces = cls.getInterfaces();
+ }
+ if (interfaces.length != 1
+ || !(ServerRpc.class.isAssignableFrom(interfaces[0]))) {
+ throw new RuntimeException(
+ "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface");
+ }
+ @SuppressWarnings("unchecked")
+ Class<T> type = (Class<T>) interfaces[0];
+ registerRpc(implementation, type);
+ }
+
+ public SharedState getState() {
+ if (null == sharedState) {
+ sharedState = createState();
+ }
+ return sharedState;
+ }
+
+ /**
+ * Creates the shared state bean to be used in server to client
+ * communication.
+ * <p>
+ * By default a state object of the defined return type of
+ * {@link #getState()} is created. Subclasses can override this method and
+ * return a new instance of the correct state class but this should rarely
+ * be necessary.
+ * </p>
+ * <p>
+ * No configuration of the values of the state should be performed in
+ * {@link #createState()}.
+ *
+ * @since 7.0
+ *
+ * @return new shared state object
+ */
+ protected SharedState createState() {
+ try {
+ return getStateType().newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Error creating state of type " + getStateType().getName()
+ + " for " + getClass().getName(), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.server.ClientConnector#getStateType()
+ */
+ public Class<? extends SharedState> getStateType() {
+ try {
+ Method m = getClass().getMethod("getState", (Class[]) null);
+ Class<?> type = m.getReturnType();
+ return type.asSubclass(SharedState.class);
+ } catch (Exception e) {
+ throw new RuntimeException("Error finding state type for "
+ + getClass().getName(), e);
+ }
+ }
+
+ /**
+ * Returns an RPC proxy for a given server to client RPC interface for this
+ * component.
+ *
+ * TODO more javadoc, subclasses, ...
+ *
+ * @param rpcInterface
+ * RPC interface type
+ *
+ * @since 7.0
+ */
+ public <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface) {
+ // create, initialize and return a dynamic proxy for RPC
+ try {
+ if (!rpcProxyMap.containsKey(rpcInterface)) {
+ Class<?> proxyClass = Proxy.getProxyClass(
+ rpcInterface.getClassLoader(), rpcInterface);
+ Constructor<?> constructor = proxyClass
+ .getConstructor(InvocationHandler.class);
+ T rpcProxy = rpcInterface.cast(constructor
+ .newInstance(new RpcInvoicationHandler(rpcInterface)));
+ // cache the proxy
+ rpcProxyMap.put(rpcInterface, rpcProxy);
+ }
+ return (T) rpcProxyMap.get(rpcInterface);
+ } catch (Exception e) {
+ // TODO exception handling?
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static final class AllChildrenIterable implements
+ Iterable<ClientConnector>, Serializable {
+ private final ClientConnector connector;
+
+ private AllChildrenIterable(ClientConnector connector) {
+ this.connector = connector;
+ }
+
+ public Iterator<ClientConnector> iterator() {
+ CombinedIterator<ClientConnector> iterator = new CombinedIterator<ClientConnector>();
+ iterator.addIterator(connector.getExtensions().iterator());
+
+ if (connector instanceof HasComponents) {
+ HasComponents hasComponents = (HasComponents) connector;
+ iterator.addIterator(hasComponents.iterator());
+ }
+
+ return iterator;
+ }
+ }
+
+ private class RpcInvoicationHandler implements InvocationHandler,
+ Serializable {
+
+ private String rpcInterfaceName;
+
+ public RpcInvoicationHandler(Class<?> rpcInterface) {
+ rpcInterfaceName = rpcInterface.getName().replaceAll("\\$", ".");
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ addMethodInvocationToQueue(rpcInterfaceName, method, args);
+ // TODO no need to do full repaint if only RPC calls
+ requestRepaint();
+ return null;
+ }
+
+ }
+
+ /**
+ * For internal use: adds a method invocation to the pending RPC call queue.
+ *
+ * @param interfaceName
+ * RPC interface name
+ * @param method
+ * RPC method
+ * @param parameters
+ * RPC all parameters
+ *
+ * @since 7.0
+ */
+ protected void addMethodInvocationToQueue(String interfaceName,
+ Method method, Object[] parameters) {
+ // add to queue
+ pendingInvocations.add(new ClientMethodInvocation(this, interfaceName,
+ method, parameters));
+ }
+
+ /**
+ * @see RpcTarget#getRpcManager(Class)
+ *
+ * @param rpcInterface
+ * RPC interface for which a call was made
+ * @return RPC Manager handling calls for the interface
+ *
+ * @since 7.0
+ */
+ public RpcManager getRpcManager(Class<?> rpcInterface) {
+ return rpcManagerMap.get(rpcInterface);
+ }
+
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ if (pendingInvocations.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ List<ClientMethodInvocation> result = pendingInvocations;
+ pendingInvocations = new ArrayList<ClientMethodInvocation>();
+ return Collections.unmodifiableList(result);
+ }
+ }
+
+ public String getConnectorId() {
+ if (connectorId == null) {
+ if (getApplication() == null) {
+ throw new RuntimeException(
+ "Component must be attached to an application when getConnectorId() is called for the first time");
+ }
+ connectorId = getApplication().createConnectorId(this);
+ }
+ return connectorId;
+ }
+
+ /**
+ * Finds the Application to which this connector belongs. If the connector
+ * has not been attached, <code>null</code> is returned.
+ *
+ * @return The connector's application, or <code>null</code> if not attached
+ */
+ protected Application getApplication() {
+ Root root = getRoot();
+ if (root == null) {
+ return null;
+ } else {
+ return root.getApplication();
+ }
+ }
+
+ /**
+ * Finds a Root ancestor of this connector. <code>null</code> is returned if
+ * no Root ancestor is found (typically because the connector is not
+ * attached to a proper hierarchy).
+ *
+ * @return the Root ancestor of this connector, or <code>null</code> if none
+ * is found.
+ */
+ protected Root getRoot() {
+ ClientConnector connector = this;
+ while (connector != null) {
+ if (connector instanceof Root) {
+ return (Root) connector;
+ }
+ connector = connector.getParent();
+ }
+ return null;
+ }
+
+ private static Logger getLogger() {
+ return Logger.getLogger(AbstractClientConnector.class.getName());
+ }
+
+ public void requestRepaintAll() {
+ requestRepaint();
+
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
+ connector.requestRepaintAll();
+ }
+ }
+
+ private static final class CombinedIterator<T> implements Iterator<T>,
+ Serializable {
+
+ private final Collection<Iterator<? extends T>> iterators = new ArrayList<Iterator<? extends T>>();
+
+ public void addIterator(Iterator<? extends T> iterator) {
+ iterators.add(iterator);
+ }
+
+ public boolean hasNext() {
+ for (Iterator<? extends T> i : iterators) {
+ if (i.hasNext()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public T next() {
+ for (Iterator<? extends T> i : iterators) {
+ if (i.hasNext()) {
+ return i.next();
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Get an Iterable for iterating over all child connectors, including both
+ * extensions and child components.
+ *
+ * @param connector
+ * the connector to get children for
+ * @return an Iterable giving all child connectors.
+ */
+ public static Iterable<ClientConnector> getAllChildrenIterable(
+ final ClientConnector connector) {
+ return new AllChildrenIterable(connector);
+ }
+
+ public Collection<Extension> getExtensions() {
+ return Collections.unmodifiableCollection(extensions);
+ }
+
+ /**
+ * Add an extension to this connector. This method is protected to allow
+ * extensions to select which targets they can extend.
+ *
+ * @param extension
+ * the extension to add
+ */
+ protected void addExtension(Extension extension) {
+ ClientConnector previousParent = extension.getParent();
+ if (previousParent == this) {
+ // Nothing to do, already attached
+ return;
+ } else if (previousParent != null) {
+ throw new IllegalStateException(
+ "Moving an extension from one parent to another is not supported");
+ }
+
+ extensions.add(extension);
+ extension.setParent(this);
+ requestRepaint();
+ }
+
+ public void removeExtension(Extension extension) {
+ extension.setParent(null);
+ extensions.remove(extension);
+ requestRepaint();
+ }
+
+ public void setParent(ClientConnector parent) {
+
+ // If the parent is not changed, don't do anything
+ if (parent == this.parent) {
+ return;
+ }
+
+ if (parent != null && this.parent != null) {
+ throw new IllegalStateException(getClass().getName()
+ + " already has a parent.");
+ }
+
+ // Send detach event if the component have been connected to a window
+ if (getApplication() != null) {
+ detach();
+ }
+
+ // Connect to new parent
+ this.parent = parent;
+
+ // Send attach event if connected to an application
+ if (getApplication() != null) {
+ attach();
+ }
+ }
+
+ public ClientConnector getParent() {
+ return parent;
+ }
+
+ public void attach() {
+ requestRepaint();
+
+ getRoot().getConnectorTracker().registerConnector(this);
+
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
+ connector.attach();
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>
+ * The {@link #getApplication()} and {@link #getRoot()} methods might return
+ * <code>null</code> after this method is called.
+ * </p>
+ */
+ public void detach() {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
+ connector.detach();
+ }
+
+ getRoot().getConnectorTracker().unregisterConnector(this);
+ }
+
+ public boolean isConnectorEnabled() {
+ if (getParent() == null) {
+ // No parent -> the component cannot receive updates from the client
+ return false;
+ } else {
+ return getParent().isConnectorEnabled();
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/AbstractErrorMessage.java b/src/com/vaadin/terminal/AbstractErrorMessage.java
index 1a625fc0e6..3f526f7339 100644
--- a/src/com/vaadin/terminal/AbstractErrorMessage.java
+++ b/src/com/vaadin/terminal/AbstractErrorMessage.java
@@ -4,6 +4,8 @@
package com.vaadin.terminal;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
@@ -156,7 +158,10 @@ public abstract class AbstractErrorMessage implements ErrorMessage {
}
return error;
} else {
- return new SystemError(t);
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ return new SystemError(sw.toString());
}
}
diff --git a/src/com/vaadin/terminal/AbstractExtension.java b/src/com/vaadin/terminal/AbstractExtension.java
new file mode 100644
index 0000000000..33a60e39ef
--- /dev/null
+++ b/src/com/vaadin/terminal/AbstractExtension.java
@@ -0,0 +1,76 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import com.vaadin.terminal.gwt.server.ClientConnector;
+
+/**
+ * An extension is an entity that is attached to a Component or another
+ * Extension and independently communicates between client and server.
+ * <p>
+ * Extensions can use shared state and RPC in the same way as components.
+ * <p>
+ * AbstractExtension adds a mechanism for adding the extension to any Connector
+ * (extend). To let the Extension determine what kind target it can be added to,
+ * the extend method is declared as protected.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public abstract class AbstractExtension extends AbstractClientConnector
+ implements Extension {
+ private boolean previouslyAttached = false;
+
+ /**
+ * Gets a type that the parent must be an instance of. Override this if the
+ * extension only support certain targets, e.g. if only TextFields can be
+ * extended.
+ *
+ * @return a type that the parent must be an instance of
+ */
+ protected Class<? extends ClientConnector> getSupportedParentType() {
+ return ClientConnector.class;
+ }
+
+ /**
+ * Add this extension to the target connector. This method is protected to
+ * allow subclasses to require a more specific type of target.
+ *
+ * @param target
+ * the connector to attach this extension to
+ */
+ protected void extend(AbstractClientConnector target) {
+ target.addExtension(this);
+ }
+
+ /**
+ * Remove this extension from its target. After an extension has been
+ * removed, it can not be attached again.
+ */
+ public void removeFromTarget() {
+ getParent().removeExtension(this);
+ }
+
+ @Override
+ public void setParent(ClientConnector parent) {
+ if (previouslyAttached && parent != null) {
+ throw new IllegalStateException(
+ "An extension can not be set to extend a new target after getting detached from the previous.");
+ }
+
+ Class<? extends ClientConnector> supportedParentType = getSupportedParentType();
+ if (parent == null || supportedParentType.isInstance(parent)) {
+ super.setParent(parent);
+ previouslyAttached = true;
+ } else {
+ throw new IllegalArgumentException(getClass().getName()
+ + " can only be attached to targets of type "
+ + supportedParentType.getName() + " but attach to "
+ + parent.getClass().getName() + " was attempted.");
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/AbstractJavaScriptExtension.java b/src/com/vaadin/terminal/AbstractJavaScriptExtension.java
new file mode 100644
index 0000000000..bdcd948c74
--- /dev/null
+++ b/src/com/vaadin/terminal/AbstractJavaScriptExtension.java
@@ -0,0 +1,158 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import com.vaadin.terminal.gwt.client.JavaScriptExtensionState;
+import com.vaadin.ui.JavaScriptCallback;
+
+/**
+ * Base class for Extensions with all client-side logic implemented using
+ * JavaScript.
+ * <p>
+ * When a new JavaScript extension is initialized in the browser, the framework
+ * will look for a globally defined JavaScript function that will initialize the
+ * extension. The name of the initialization function is formed by replacing .
+ * with _ in the name of the server-side class. If no such function is defined,
+ * each super class is used in turn until a match is found. The framework will
+ * thus first attempt with <code>com_example_MyExtension</code> for the
+ * server-side
+ * <code>com.example.MyExtension extends AbstractJavaScriptExtension</code>
+ * class. If MyExtension instead extends <code>com.example.SuperExtension</code>
+ * , then <code>com_example_SuperExtension</code> will also be attempted if
+ * <code>com_example_MyExtension</code> has not been defined.
+ * <p>
+ *
+ * The initialization function will be called with <code>this</code> pointing to
+ * a connector wrapper object providing integration to Vaadin with the following
+ * functions:
+ * <ul>
+ * <li><code>getConnectorId()</code> - returns a string with the id of the
+ * connector.</li>
+ * <li><code>getParentId([connectorId])</code> - returns a string with the id of
+ * the connector's parent. If <code>connectorId</code> is provided, the id of
+ * the parent of the corresponding connector with the passed id is returned
+ * instead.</li>
+ * <li><code>getWidgetElement([connectorId])</code> - returns the DOM Element
+ * that is the root of a connector's widget. <code>null</code> is returned if
+ * the connector can not be found or if the connector doesn't have a widget. If
+ * <code>connectorId</code> is not provided, the connector id of the current
+ * connector will be used.</li>
+ * <li><code>getState()</code> - returns an object corresponding to the shared
+ * state defined on the server. The scheme for conversion between Java and
+ * JavaScript types is described bellow.</li>
+ * <li><code>registerRpc([name, ] rpcObject)</code> - registers the
+ * <code>rpcObject</code> as a RPC handler. <code>rpcObject</code> should be an
+ * object with field containing functions for all eligible RPC functions. If
+ * <code>name</code> is provided, the RPC handler will only used for RPC calls
+ * for the RPC interface with the same fully qualified Java name. If no
+ * <code>name</code> is provided, the RPC handler will be used for all incoming
+ * RPC invocations where the RPC method name is defined as a function field in
+ * the handler. The scheme for conversion between Java types in the RPC
+ * interface definition and the JavaScript values passed as arguments to the
+ * handler functions is described bellow.</li>
+ * <li><code>getRpcProxy([name])</code> - returns an RPC proxy object. If
+ * <code>name</code> is provided, the proxy object will contain functions for
+ * all methods in the RPC interface with the same fully qualified name, provided
+ * a RPC handler has been registered by the server-side code. If no
+ * <code>name</code> is provided, the returned RPC proxy object will contain
+ * functions for all methods in all RPC interfaces registered for the connector
+ * on the server. If the same method name is present in multiple registered RPC
+ * interfaces, the corresponding function in the RPC proxy object will throw an
+ * exception when called. The scheme for conversion between Java types in the
+ * RPC interface and the JavaScript values that should be passed to the
+ * functions is described bellow.</li>
+ * </ul>
+ * The connector wrapper also supports these special functions:
+ * <ul>
+ * <li><code>onStateChange</code> - If the JavaScript code assigns a function to
+ * the field, that function is called whenever the contents of the shared state
+ * is changed.</li>
+ * <li>Any field name corresponding to a call to
+ * {@link #registerCallback(String, JavaScriptCallback)} on the server will
+ * automatically be present as a function that triggers the registered callback
+ * on the server.</li>
+ * <li>Any field name referred to using
+ * {@link #invokeCallback(String, Object...)} on the server will be called if a
+ * function has been assigned to the field.</li>
+ * </ul>
+ * <p>
+ *
+ * Values in the Shared State and in RPC calls are converted between Java and
+ * JavaScript using the following conventions:
+ * <ul>
+ * <li>Primitive Java numbers (byte, char, int, long, float, double) and their
+ * boxed types (Byte, Character, Integer, Long, Float, Double) are represented
+ * by JavaScript numbers.</li>
+ * <li>The primitive Java boolean and the boxed Boolean are represented by
+ * JavaScript booleans.</li>
+ * <li>Java Strings are represented by JavaScript strings.</li>
+ * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
+ * <li>Map<String, ?> in Java is represented by JavaScript object with fields
+ * corresponding to the map keys.</li>
+ * <li>Any other Java Map is represented by a JavaScript array containing two
+ * arrays, the first contains the keys and the second contains the values in the
+ * same order.</li>
+ * <li>A Java Bean is represented by a JavaScript object with fields
+ * corresponding to the bean's properties.</li>
+ * <li>A Java Connector is represented by a JavaScript string containing the
+ * connector's id.</li>
+ * <li>A pluggable serialization mechanism is provided for types not described
+ * here. Please refer to the documentation for specific types for serialization
+ * information.</li>
+ * </ul>
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public abstract class AbstractJavaScriptExtension extends AbstractExtension {
+ private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper(
+ this);
+
+ @Override
+ protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) {
+ super.registerRpc(implementation, rpcInterfaceType);
+ callbackHelper.registerRpc(rpcInterfaceType);
+ }
+
+ /**
+ * Register a {@link JavaScriptCallback} that can be called from the
+ * JavaScript using the provided name. A JavaScript function with the
+ * provided name will be added to the connector wrapper object (initially
+ * available as <code>this</code>). Calling that JavaScript function will
+ * cause the call method in the registered {@link JavaScriptCallback} to be
+ * invoked with the same arguments.
+ *
+ * @param functionName
+ * the name that should be used for client-side callback
+ * @param javaScriptCallback
+ * the callback object that will be invoked when the JavaScript
+ * function is called
+ */
+ protected void registerCallback(String functionName,
+ JavaScriptCallback javaScriptCallback) {
+ callbackHelper.registerCallback(functionName, javaScriptCallback);
+ }
+
+ /**
+ * Invoke a named function that the connector JavaScript has added to the
+ * JavaScript connector wrapper object. The arguments should only contain
+ * data types that can be represented in JavaScript, including primitive
+ * boxing types, arrays, String, List, Set, Map, Connector and JavaBeans.
+ *
+ * @param name
+ * the name of the function
+ * @param arguments
+ * function arguments
+ */
+ protected void invokeCallback(String name, Object... arguments) {
+ callbackHelper.invokeCallback(name, arguments);
+ }
+
+ @Override
+ public JavaScriptExtensionState getState() {
+ return (JavaScriptExtensionState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/terminal/ClassResource.java b/src/com/vaadin/terminal/ClassResource.java
index fa196e90d9..e7419576f1 100644
--- a/src/com/vaadin/terminal/ClassResource.java
+++ b/src/com/vaadin/terminal/ClassResource.java
@@ -60,13 +60,7 @@ public class ClassResource implements ApplicationResource, Serializable {
* the application this resource will be added to.
*/
public ClassResource(String resourceName, Application application) {
- associatedClass = application.getClass();
- this.resourceName = resourceName;
- this.application = application;
- if (resourceName == null) {
- throw new NullPointerException();
- }
- application.addResource(this);
+ this(application.getClass(), resourceName, application);
}
/**
diff --git a/src/com/vaadin/terminal/CombinedRequest.java b/src/com/vaadin/terminal/CombinedRequest.java
index ccef6d8963..abf5e0412a 100644
--- a/src/com/vaadin/terminal/CombinedRequest.java
+++ b/src/com/vaadin/terminal/CombinedRequest.java
@@ -129,7 +129,7 @@ public class CombinedRequest implements WrappedRequest {
public WebBrowser getWebBrowser() {
WebApplicationContext context = (WebApplicationContext) Application
- .getCurrentApplication().getContext();
+ .getCurrent().getContext();
return context.getBrowser();
}
};
diff --git a/src/com/vaadin/terminal/Extension.java b/src/com/vaadin/terminal/Extension.java
new file mode 100644
index 0000000000..ef5bb4cf8d
--- /dev/null
+++ b/src/com/vaadin/terminal/Extension.java
@@ -0,0 +1,27 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import com.vaadin.terminal.gwt.server.ClientConnector;
+
+/**
+ * An extension is an entity that is attached to a Component or another
+ * Extension and independently communicates between client and server.
+ * <p>
+ * An extension can only be attached once. It is not supported to move an
+ * extension from one target to another.
+ * <p>
+ * Extensions can use shared state and RPC in the same way as components.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public interface Extension extends ClientConnector {
+ /*
+ * Currently just an empty marker interface to distinguish between
+ * extensions and other connectors, e.g. components
+ */
+}
diff --git a/src/com/vaadin/terminal/JavaScriptCallbackHelper.java b/src/com/vaadin/terminal/JavaScriptCallbackHelper.java
new file mode 100644
index 0000000000..01db0267d9
--- /dev/null
+++ b/src/com/vaadin/terminal/JavaScriptCallbackHelper.java
@@ -0,0 +1,115 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper.JavaScriptConnectorState;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.AbstractJavaScriptComponent;
+import com.vaadin.ui.JavaScript.JavaScriptCallbackRpc;
+import com.vaadin.ui.JavaScriptCallback;
+
+/**
+ * Internal helper class used to implement functionality common to
+ * {@link AbstractJavaScriptComponent} and {@link AbstractJavaScriptExtension}.
+ * Corresponding support in client-side code is in
+ * {@link JavaScriptConnectorHelper}.
+ * <p>
+ * You should most likely no use this class directly.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public class JavaScriptCallbackHelper implements Serializable {
+
+ private static final Method CALL_METHOD = ReflectTools.findMethod(
+ JavaScriptCallbackRpc.class, "call", String.class, JSONArray.class);
+ private AbstractClientConnector connector;
+
+ private Map<String, JavaScriptCallback> callbacks = new HashMap<String, JavaScriptCallback>();
+ private JavaScriptCallbackRpc javascriptCallbackRpc;
+
+ public JavaScriptCallbackHelper(AbstractClientConnector connector) {
+ this.connector = connector;
+ }
+
+ public void registerCallback(String functionName,
+ JavaScriptCallback javaScriptCallback) {
+ callbacks.put(functionName, javaScriptCallback);
+ JavaScriptConnectorState state = getConnectorState();
+ if (state.getCallbackNames().add(functionName)) {
+ connector.requestRepaint();
+ }
+ ensureRpc();
+ }
+
+ private JavaScriptConnectorState getConnectorState() {
+ JavaScriptConnectorState state = (JavaScriptConnectorState) connector
+ .getState();
+ return state;
+ }
+
+ 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) {
+ if (callbacks.containsKey(name)) {
+ throw new IllegalStateException(
+ "Can't call callback "
+ + name
+ + " on the client because a callback with the same name is registered on the server.");
+ }
+ JSONArray args = new JSONArray(Arrays.asList(arguments));
+ connector.addMethodInvocationToQueue(
+ JavaScriptCallbackRpc.class.getName(), CALL_METHOD,
+ new Object[] { name, args });
+ connector.requestRepaint();
+ }
+
+ public void registerRpc(Class<?> rpcInterfaceType) {
+ if (rpcInterfaceType == JavaScriptCallbackRpc.class) {
+ // Ignore
+ return;
+ }
+ Map<String, Set<String>> rpcInterfaces = getConnectorState()
+ .getRpcInterfaces();
+ String interfaceName = rpcInterfaceType.getName();
+ if (!rpcInterfaces.containsKey(interfaceName)) {
+ Set<String> methodNames = new HashSet<String>();
+
+ for (Method method : rpcInterfaceType.getMethods()) {
+ methodNames.add(method.getName());
+ }
+
+ rpcInterfaces.put(interfaceName, methodNames);
+ connector.requestRepaint();
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/Page.java b/src/com/vaadin/terminal/Page.java
new file mode 100644
index 0000000000..8ccb243a1e
--- /dev/null
+++ b/src/com/vaadin/terminal/Page.java
@@ -0,0 +1,646 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.EventObject;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.vaadin.event.EventRouter;
+import com.vaadin.terminal.WrappedRequest.BrowserDetails;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
+import com.vaadin.terminal.gwt.client.ui.root.PageClientRpc;
+import com.vaadin.terminal.gwt.client.ui.root.VRoot;
+import com.vaadin.terminal.gwt.server.WebApplicationContext;
+import com.vaadin.terminal.gwt.server.WebBrowser;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.JavaScript;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.Root;
+
+public class Page implements Serializable {
+
+ /**
+ * Listener that gets notified when the size of the browser window
+ * containing the root has changed.
+ *
+ * @see Root#addListener(BrowserWindowResizeListener)
+ */
+ public interface BrowserWindowResizeListener extends Serializable {
+ /**
+ * Invoked when the browser window containing a Root has been resized.
+ *
+ * @param event
+ * a browser window resize event
+ */
+ public void browserWindowResized(BrowserWindowResizeEvent event);
+ }
+
+ /**
+ * Event that is fired when a browser window containing a root is resized.
+ */
+ public class BrowserWindowResizeEvent extends EventObject {
+
+ private final int width;
+ private final int height;
+
+ /**
+ * Creates a new event
+ *
+ * @param source
+ * the root for which the browser window has been resized
+ * @param width
+ * the new width of the browser window
+ * @param height
+ * the new height of the browser window
+ */
+ public BrowserWindowResizeEvent(Page source, int width, int height) {
+ super(source);
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public Page getSource() {
+ return (Page) super.getSource();
+ }
+
+ /**
+ * Gets the new browser window height
+ *
+ * @return an integer with the new pixel height of the browser window
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Gets the new browser window width
+ *
+ * @return an integer with the new pixel width of the browser window
+ */
+ public int getWidth() {
+ return width;
+ }
+ }
+
+ /**
+ * Private class for storing properties related to opening resources.
+ */
+ private class OpenResource implements Serializable {
+
+ /**
+ * The resource to open
+ */
+ private final Resource resource;
+
+ /**
+ * The name of the target window
+ */
+ private final String name;
+
+ /**
+ * The width of the target window
+ */
+ private final int width;
+
+ /**
+ * The height of the target window
+ */
+ private final int height;
+
+ /**
+ * The border style of the target window
+ */
+ private final int border;
+
+ /**
+ * Creates a new open resource.
+ *
+ * @param resource
+ * The resource to open
+ * @param name
+ * The name of the target window
+ * @param width
+ * The width of the target window
+ * @param height
+ * The height of the target window
+ * @param border
+ * The border style of the target window
+ */
+ private OpenResource(Resource resource, String name, int width,
+ int height, int border) {
+ this.resource = resource;
+ this.name = name;
+ this.width = width;
+ this.height = height;
+ this.border = border;
+ }
+
+ /**
+ * Paints the open request. Should be painted inside the window.
+ *
+ * @param target
+ * the paint target
+ * @throws PaintException
+ * if the paint operation fails
+ */
+ private void paintContent(PaintTarget target) throws PaintException {
+ target.startTag("open");
+ target.addAttribute("src", resource);
+ if (name != null && name.length() > 0) {
+ target.addAttribute("name", name);
+ }
+ if (width >= 0) {
+ target.addAttribute("width", width);
+ }
+ if (height >= 0) {
+ target.addAttribute("height", height);
+ }
+ switch (border) {
+ case BORDER_MINIMAL:
+ target.addAttribute("border", "minimal");
+ break;
+ case BORDER_NONE:
+ target.addAttribute("border", "none");
+ break;
+ }
+
+ target.endTag("open");
+ }
+ }
+
+ private static final Method BROWSWER_RESIZE_METHOD = ReflectTools
+ .findMethod(BrowserWindowResizeListener.class,
+ "browserWindowResized", BrowserWindowResizeEvent.class);
+
+ /**
+ * A border style used for opening resources in a window without a border.
+ */
+ public static final int BORDER_NONE = 0;
+
+ /**
+ * A border style used for opening resources in a window with a minimal
+ * border.
+ */
+ public static final int BORDER_MINIMAL = 1;
+
+ /**
+ * A border style that indicates that the default border style should be
+ * used when opening resources.
+ */
+ public static final int BORDER_DEFAULT = 2;
+
+ /**
+ * Listener that listens changes in URI fragment.
+ */
+ public interface FragmentChangedListener extends Serializable {
+ public void fragmentChanged(FragmentChangedEvent event);
+ }
+
+ private static final Method FRAGMENT_CHANGED_METHOD = ReflectTools
+ .findMethod(Page.FragmentChangedListener.class, "fragmentChanged",
+ FragmentChangedEvent.class);
+
+ /**
+ * Resources to be opened automatically on next repaint. The list is
+ * automatically cleared when it has been sent to the client.
+ */
+ private final LinkedList<OpenResource> openList = new LinkedList<OpenResource>();
+
+ /**
+ * A list of notifications that are waiting to be sent to the client.
+ * Cleared (set to null) when the notifications have been sent.
+ */
+ private List<Notification> notifications;
+
+ /**
+ * Event fired when uri fragment changes.
+ */
+ public class FragmentChangedEvent extends EventObject {
+
+ /**
+ * The new uri fragment
+ */
+ private final String fragment;
+
+ /**
+ * Creates a new instance of UriFragmentReader change event.
+ *
+ * @param source
+ * the Source of the event.
+ */
+ public FragmentChangedEvent(Page source, String fragment) {
+ super(source);
+ this.fragment = fragment;
+ }
+
+ /**
+ * Gets the root in which the fragment has changed.
+ *
+ * @return the root in which the fragment has changed
+ */
+ public Page getPage() {
+ return (Page) getSource();
+ }
+
+ /**
+ * Get the new fragment
+ *
+ * @return the new fragment
+ */
+ public String getFragment() {
+ return fragment;
+ }
+ }
+
+ private EventRouter eventRouter;
+
+ /**
+ * The current URI fragment.
+ */
+ private String fragment;
+
+ private final Root root;
+
+ private int browserWindowWidth = -1;
+ private int browserWindowHeight = -1;
+
+ private JavaScript javaScript;
+
+ public Page(Root root) {
+ this.root = root;
+ }
+
+ private void addListener(Class<?> eventType, Object target, Method method) {
+ if (eventRouter == null) {
+ eventRouter = new EventRouter();
+ }
+ eventRouter.addListener(eventType, target, method);
+ }
+
+ private void removeListener(Class<?> eventType, Object target, Method method) {
+ if (eventRouter != null) {
+ eventRouter.removeListener(eventType, target, method);
+ }
+ }
+
+ public void addListener(Page.FragmentChangedListener listener) {
+ addListener(FragmentChangedEvent.class, listener,
+ FRAGMENT_CHANGED_METHOD);
+ }
+
+ public void removeListener(Page.FragmentChangedListener listener) {
+ removeListener(FragmentChangedEvent.class, listener,
+ FRAGMENT_CHANGED_METHOD);
+ }
+
+ /**
+ * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
+ *
+ * @param newFragment
+ * id of the new fragment
+ * @param fireEvent
+ * true to fire event
+ * @see FragmentChangedEvent
+ * @see Page.FragmentChangedListener
+ */
+ public void setFragment(String newFragment, boolean fireEvents) {
+ if (newFragment == null) {
+ throw new NullPointerException("The fragment may not be null");
+ }
+ if (!newFragment.equals(fragment)) {
+ fragment = newFragment;
+ if (fireEvents) {
+ fireEvent(new FragmentChangedEvent(this, newFragment));
+ }
+ root.requestRepaint();
+ }
+ }
+
+ private void fireEvent(EventObject event) {
+ if (eventRouter != null) {
+ eventRouter.fireEvent(event);
+ }
+ }
+
+ /**
+ * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
+ *
+ * @param newFragment
+ * id of the new fragment
+ * @see FragmentChangedEvent
+ * @see Page.FragmentChangedListener
+ */
+ public void setFragment(String newFragment) {
+ setFragment(newFragment, true);
+ }
+
+ /**
+ * Gets currently set URI fragment.
+ * <p>
+ * To listen changes in fragment, hook a
+ * {@link Page.FragmentChangedListener}.
+ *
+ * @return the current fragment in browser uri or null if not known
+ */
+ public String getFragment() {
+ return fragment;
+ }
+
+ public void init(WrappedRequest request) {
+ BrowserDetails browserDetails = request.getBrowserDetails();
+ if (browserDetails != null) {
+ fragment = browserDetails.getUriFragment();
+ }
+ }
+
+ public WebBrowser getWebBrowser() {
+ return ((WebApplicationContext) root.getApplication().getContext())
+ .getBrowser();
+ }
+
+ public void setBrowserWindowSize(Integer width, Integer height) {
+ boolean fireEvent = false;
+
+ if (width != null) {
+ int newWidth = width.intValue();
+ if (newWidth != browserWindowWidth) {
+ browserWindowWidth = newWidth;
+ fireEvent = true;
+ }
+ }
+
+ if (height != null) {
+ int newHeight = height.intValue();
+ if (newHeight != browserWindowHeight) {
+ browserWindowHeight = newHeight;
+ fireEvent = true;
+ }
+ }
+
+ if (fireEvent) {
+ fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
+ browserWindowHeight));
+ }
+
+ }
+
+ /**
+ * Adds a new {@link BrowserWindowResizeListener} to this root. The listener
+ * will be notified whenever the browser window within which this root
+ * resides is resized.
+ *
+ * @param resizeListener
+ * the listener to add
+ *
+ * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent)
+ * @see #setResizeLazy(boolean)
+ */
+ public void addListener(BrowserWindowResizeListener resizeListener) {
+ addListener(BrowserWindowResizeEvent.class, resizeListener,
+ BROWSWER_RESIZE_METHOD);
+ }
+
+ /**
+ * Removes a {@link BrowserWindowResizeListener} from this root. The
+ * listener will no longer be notified when the browser window is resized.
+ *
+ * @param resizeListener
+ * the listener to remove
+ */
+ public void removeListener(BrowserWindowResizeListener resizeListener) {
+ removeListener(BrowserWindowResizeEvent.class, resizeListener,
+ BROWSWER_RESIZE_METHOD);
+ }
+
+ /**
+ * Gets the last known height of the browser window in which this root
+ * resides.
+ *
+ * @return the browser window height in pixels
+ */
+ public int getBrowserWindowHeight() {
+ return browserWindowHeight;
+ }
+
+ /**
+ * Gets the last known width of the browser window in which this root
+ * resides.
+ *
+ * @return the browser window width in pixels
+ */
+ public int getBrowserWindowWidth() {
+ return browserWindowWidth;
+ }
+
+ public JavaScript getJavaScript() {
+ if (javaScript == null) {
+ // Create and attach on first use
+ javaScript = new JavaScript();
+ javaScript.extend(root);
+ }
+
+ return javaScript;
+ }
+
+ public void paintContent(PaintTarget target) throws PaintException {
+ if (!openList.isEmpty()) {
+ for (final Iterator<OpenResource> i = openList.iterator(); i
+ .hasNext();) {
+ (i.next()).paintContent(target);
+ }
+ openList.clear();
+ }
+
+ // Paint notifications
+ if (notifications != null) {
+ target.startTag("notifications");
+ for (final Iterator<Notification> it = notifications.iterator(); it
+ .hasNext();) {
+ final Notification n = it.next();
+ target.startTag("notification");
+ if (n.getCaption() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_CAPTION,
+ n.getCaption());
+ }
+ if (n.getDescription() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_MESSAGE,
+ n.getDescription());
+ }
+ if (n.getIcon() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_ICON,
+ n.getIcon());
+ }
+ if (!n.isHtmlContentAllowed()) {
+ target.addAttribute(
+ VRoot.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED, true);
+ }
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_POSITION,
+ n.getPosition());
+ target.addAttribute(VNotification.ATTRIBUTE_NOTIFICATION_DELAY,
+ n.getDelayMsec());
+ if (n.getStyleName() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_STYLE,
+ n.getStyleName());
+ }
+ target.endTag("notification");
+ }
+ target.endTag("notifications");
+ notifications = null;
+ }
+
+ if (fragment != null) {
+ target.addAttribute(VRoot.FRAGMENT_VARIABLE, fragment);
+ }
+
+ }
+
+ /**
+ * Opens the given resource in this root. The contents of this Root is
+ * replaced by the {@code Resource}.
+ *
+ * @param resource
+ * the resource to show in this root
+ */
+ public void open(Resource resource) {
+ openList.add(new OpenResource(resource, null, -1, -1, BORDER_DEFAULT));
+ root.requestRepaint();
+ }
+
+ /**
+ * Opens the given resource in a window with the given name.
+ * <p>
+ * The supplied {@code windowName} is used as the target name in a
+ * window.open call in the client. This means that special values such as
+ * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
+ * <code>null</code> window name is also a special case.
+ * </p>
+ * <p>
+ * "", null and "_self" as {@code windowName} all causes the resource to be
+ * opened in the current window, replacing any old contents. For
+ * downloadable content you should avoid "_self" as "_self" causes the
+ * client to skip rendering of any other changes as it considers them
+ * irrelevant (the page will be replaced by the resource). This can speed up
+ * the opening of a resource, but it might also put the client side into an
+ * inconsistent state if the window content is not completely replaced e.g.,
+ * if the resource is downloaded instead of displayed in the browser.
+ * </p>
+ * <p>
+ * "_blank" as {@code windowName} causes the resource to always be opened in
+ * a new window or tab (depends on the browser and browser settings).
+ * </p>
+ * <p>
+ * "_top" and "_parent" as {@code windowName} works as specified by the HTML
+ * standard.
+ * </p>
+ * <p>
+ * Any other {@code windowName} will open the resource in a window with that
+ * name, either by opening a new window/tab in the browser or by replacing
+ * the contents of an existing window with that name.
+ * </p>
+ *
+ * @param resource
+ * the resource.
+ * @param windowName
+ * the name of the window.
+ */
+ public void open(Resource resource, String windowName) {
+ openList.add(new OpenResource(resource, windowName, -1, -1,
+ BORDER_DEFAULT));
+ root.requestRepaint();
+ }
+
+ /**
+ * Opens the given resource in a window with the given size, border and
+ * name. For more information on the meaning of {@code windowName}, see
+ * {@link #open(Resource, String)}.
+ *
+ * @param resource
+ * the resource.
+ * @param windowName
+ * the name of the window.
+ * @param width
+ * the width of the window in pixels
+ * @param height
+ * the height of the window in pixels
+ * @param border
+ * the border style of the window. See {@link #BORDER_NONE
+ * Window.BORDER_* constants}
+ */
+ public void open(Resource resource, String windowName, int width,
+ int height, int border) {
+ openList.add(new OpenResource(resource, windowName, width, height,
+ border));
+ root.requestRepaint();
+ }
+
+ /**
+ * Internal helper method to actually add a notification.
+ *
+ * @param notification
+ * the notification to add
+ */
+ private void addNotification(Notification notification) {
+ if (notifications == null) {
+ notifications = new LinkedList<Notification>();
+ }
+ notifications.add(notification);
+ root.requestRepaint();
+ }
+
+ /**
+ * Shows a notification message.
+ *
+ * @see Notification
+ *
+ * @param notification
+ * The notification message to show
+ *
+ * @deprecated Use Notification.show(Page) instead.
+ */
+ @Deprecated
+ public void showNotification(Notification notification) {
+ addNotification(notification);
+ }
+
+ /**
+ * Gets the Page to which the current root belongs. This is automatically
+ * defined when processing requests to the server. In other cases, (e.g.
+ * from background threads), the current root is not automatically defined.
+ *
+ * @see Root#getCurrent()
+ *
+ * @return the current page instance if available, otherwise
+ * <code>null</code>
+ */
+ public static Page getCurrent() {
+ Root currentRoot = Root.getCurrent();
+ if (currentRoot == null) {
+ return null;
+ }
+ return currentRoot.getPage();
+ }
+
+ /**
+ * Sets the page title. The page title is displayed by the browser e.g. as
+ * the title of the browser window or as the title of the tab.
+ *
+ * @param title
+ * the new page title to set
+ */
+ public void setTitle(String title) {
+ root.getRpcProxy(PageClientRpc.class).setTitle(title);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/PaintTarget.java b/src/com/vaadin/terminal/PaintTarget.java
index 9cfa324133..b658c9f4a3 100644
--- a/src/com/vaadin/terminal/PaintTarget.java
+++ b/src/com/vaadin/terminal/PaintTarget.java
@@ -9,7 +9,9 @@ import java.util.Map;
import com.vaadin.terminal.StreamVariable.StreamingStartEvent;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.server.ClientConnector;
+import com.vaadin.ui.Component;
/**
* This interface defines the methods for painting XML to the UIDL stream.
@@ -39,7 +41,7 @@ public interface PaintTarget extends Serializable {
/**
* Result of starting to paint a Paintable (
- * {@link PaintTarget#startPaintable(ClientConnector, String)}).
+ * {@link PaintTarget#startPaintable(Component, String)}).
*
* @since 7.0
*/
@@ -55,7 +57,7 @@ public interface PaintTarget extends Serializable {
* changes.
*/
CACHED
- };
+ }
/**
* Prints element start tag of a paintable section. Starts a paintable
@@ -73,8 +75,8 @@ public interface PaintTarget extends Serializable {
* </p>
* <p>
* Each paintable being painted should be closed by a matching
- * {@link #endPaintable(ClientConnector)} regardless of the
- * {@link PaintStatus} returned.
+ * {@link #endPaintable(Component)} regardless of the {@link PaintStatus}
+ * returned.
* </p>
*
* @param paintable
@@ -89,15 +91,15 @@ public interface PaintTarget extends Serializable {
* @see #startTag(String)
* @since 7.0 (previously using startTag(Paintable, String))
*/
- public PaintStatus startPaintable(ClientConnector paintable, String tag)
+ public PaintStatus startPaintable(Component paintable, String tag)
throws PaintException;
/**
* Prints paintable element end tag.
*
- * Calls to {@link #startPaintable(ClientConnector, String)}should be
- * matched by {@link #endPaintable(ClientConnector)}. If the parent tag is
- * closed before every child tag is closed a PaintException is raised.
+ * Calls to {@link #startPaintable(Component, String)}should be matched by
+ * {@link #endPaintable(Component)}. If the parent tag is closed before
+ * every child tag is closed a PaintException is raised.
*
* @param paintable
* the paintable to close.
@@ -105,7 +107,7 @@ public interface PaintTarget extends Serializable {
* if the paint operation failed.
* @since 7.0 (previously using engTag(String))
*/
- public void endPaintable(ClientConnector paintable) throws PaintException;
+ public void endPaintable(Component paintable) throws PaintException;
/**
* Prints element start tag.
@@ -289,7 +291,7 @@ public interface PaintTarget extends Serializable {
* the Paintable to be referenced on client side
* @throws PaintException
*/
- public void addAttribute(String name, ClientConnector value)
+ public void addAttribute(String name, Component value)
throws PaintException;
/**
@@ -421,8 +423,8 @@ public interface PaintTarget extends Serializable {
* @throws PaintException
* if the paint oparation fails
*/
- public void addVariable(VariableOwner owner, String name,
- ClientConnector value) throws PaintException;
+ public void addVariable(VariableOwner owner, String name, Component value)
+ throws PaintException;
/**
* Adds a upload stream type variable.
diff --git a/src/com/vaadin/terminal/UserError.java b/src/com/vaadin/terminal/UserError.java
index baaf331fa0..a7a4fd89e2 100644
--- a/src/com/vaadin/terminal/UserError.java
+++ b/src/com/vaadin/terminal/UserError.java
@@ -44,6 +44,16 @@ public class UserError extends AbstractErrorMessage {
super(textErrorMessage);
}
+ /**
+ * Creates an error message with level and content mode.
+ *
+ * @param message
+ * the error message.
+ * @param contentMode
+ * the content Mode.
+ * @param errorLevel
+ * the level of error.
+ */
public UserError(String message, ContentMode contentMode,
ErrorLevel errorLevel) {
super(message);
diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
index 3487a655e5..77e3127745 100644
--- a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
+++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
@@ -71,9 +71,9 @@
<!-- Generate client side RPC manager for server to client RPC -->
<generate-with
- class="com.vaadin.terminal.gwt.widgetsetutils.RpcManagerGenerator">
+ class="com.vaadin.terminal.gwt.widgetsetutils.GeneratedRpcMethodProviderGenerator">
<when-type-assignable
- class="com.vaadin.terminal.gwt.client.communication.RpcManager" />
+ class="com.vaadin.terminal.gwt.client.communication.GeneratedRpcMethodProvider" />
</generate-with>
<generate-with
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
index 8eeccb828d..960b0a8b0e 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
@@ -18,6 +18,7 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
public class ApplicationConfiguration implements EntryPoint {
@@ -209,7 +210,7 @@ public class ApplicationConfiguration implements EntryPoint {
private HashMap<Integer, String> unknownComponents;
- private Class<? extends ComponentConnector>[] classes = new Class[1024];
+ private Class<? extends ServerConnector>[] classes = new Class[1024];
private boolean browserDetailsSent = false;
private boolean widgetsetVersionSent = false;
@@ -346,6 +347,7 @@ public class ApplicationConfiguration implements EntryPoint {
*/
public static void startApplication(final String applicationId) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+
public void execute() {
ApplicationConfiguration appConf = getConfigFromDOM(applicationId);
ApplicationConnection a = GWT
@@ -393,7 +395,7 @@ public class ApplicationConfiguration implements EntryPoint {
return useDebugIdInDom;
}
- public Class<? extends ComponentConnector> getWidgetClassByEncodedTag(
+ public Class<? extends ServerConnector> getConnectorClassByEncodedTag(
int tag) {
try {
return classes[tag];
@@ -508,7 +510,7 @@ public class ApplicationConfiguration implements EntryPoint {
public void run() {
pending = false;
if (!isBusy()) {
- Class<? extends ComponentConnector> nextType = getNextType();
+ Class<? extends ServerConnector> nextType = getNextType();
if (nextType == null) {
// ensured that all widgets are loaded
deferredWidgetLoader = null;
@@ -521,13 +523,13 @@ public class ApplicationConfiguration implements EntryPoint {
}
}
- private Class<? extends ComponentConnector> getNextType() {
- Class<? extends ComponentConnector>[] deferredLoadedWidgets = widgetSet
- .getDeferredLoadedWidgets();
- if (deferredLoadedWidgets.length <= nextWidgetIndex) {
+ private Class<? extends ServerConnector> getNextType() {
+ Class<? extends ServerConnector>[] deferredLoadedConnectors = widgetSet
+ .getDeferredLoadedConnectors();
+ if (deferredLoadedConnectors.length <= nextWidgetIndex) {
return null;
} else {
- return deferredLoadedWidgets[nextWidgetIndex++];
+ return deferredLoadedConnectors[nextWidgetIndex++];
}
}
@@ -568,6 +570,7 @@ public class ApplicationConfiguration implements EntryPoint {
* GWT hosted mode.
*/
GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+
public void onUncaughtException(Throwable e) {
/*
* Note in case of null console (without ?debug) we eat
@@ -602,12 +605,15 @@ public class ApplicationConfiguration implements EntryPoint {
*
* @return true if client side is currently been debugged
*/
- public native static boolean isDebugMode()
+ public static boolean isDebugMode() {
+ return isDebugAvailable()
+ && Window.Location.getParameter("debug") != null;
+ }
+
+ private native static boolean isDebugAvailable()
/*-{
if($wnd.vaadin.debug) {
- var parameters = $wnd.location.search;
- var re = /debug[^\/]*$/;
- return re.test(parameters);
+ return true;
} else {
return false;
}
@@ -618,12 +624,11 @@ public class ApplicationConfiguration implements EntryPoint {
*
* @return <code>true</code> if debug logging should be quiet
*/
- public native static boolean isQuietDebugMode()
- /*-{
- var uri = $wnd.location;
- var re = /debug=q[^\/]*$/;
- return re.test(uri);
- }-*/;
+ public static boolean isQuietDebugMode() {
+ String debugParameter = Window.Location.getParameter("debug");
+ return isDebugAvailable() && debugParameter != null
+ && debugParameter.startsWith("q");
+ }
/**
* Checks whether information from the web browser (e.g. uri fragment and
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index 51a0ec3f02..f0470c8ee8 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -28,6 +28,7 @@ import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
@@ -39,12 +40,17 @@ 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;
import com.vaadin.terminal.gwt.client.communication.RpcManager;
import com.vaadin.terminal.gwt.client.communication.SerializerMap;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.Type;
+import com.vaadin.terminal.gwt.client.communication.UidlValue;
+import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector;
import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
import com.vaadin.terminal.gwt.client.ui.VContextMenu;
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
@@ -365,6 +371,25 @@ public class ApplicationConnection {
}-*/;
/**
+ * If on Liferay and logged in, ask the client side session management
+ * JavaScript to extend the session duration.
+ *
+ * Otherwise, Liferay client side JavaScript will explicitly expire the
+ * session even though the server side considers the session to be active.
+ * See ticket #8305 for more information.
+ */
+ protected native void extendLiferaySession()
+ /*-{
+ if ($wnd.Liferay && $wnd.Liferay.Session) {
+ $wnd.Liferay.Session.extend();
+ // if the extend banner is visible, hide it
+ if ($wnd.Liferay.Session.banner) {
+ $wnd.Liferay.Session.banner.remove();
+ }
+ }
+ }-*/;
+
+ /**
* Get the active Console for writing debug messages. May return an actual
* logging console, or the NullConsole if debugging is not turned on.
*
@@ -846,6 +871,14 @@ public class ApplicationConnection {
public void execute() {
if (!hasActiveRequest()) {
hideLoadingIndicator();
+
+ // If on Liferay and session expiration management is in
+ // use, extend session duration on each request.
+ // Doing it here rather than before the request to improve
+ // responsiveness.
+ // Postponed until the end of the next request if other
+ // requests still pending.
+ extendLiferaySession();
}
}
});
@@ -1271,27 +1304,24 @@ public class ApplicationConnection {
List<ServerConnector> currentConnectors = new ArrayList<ServerConnector>(
connectorMap.getConnectors());
for (ServerConnector c : currentConnectors) {
- if (c instanceof ComponentConnector) {
- ComponentConnector cc = (ComponentConnector) c;
- if (cc.getParent() != null) {
- if (!cc.getParent().getChildren().contains(cc)) {
- VConsole.error("ERROR: Connector is connected to a parent but the parent does not contain the connector");
- }
- } else if ((cc instanceof RootConnector && cc == getRootConnector())) {
- // RootConnector for this connection, leave as-is
- } else if (cc instanceof WindowConnector
- && getRootConnector().hasSubWindow(
- (WindowConnector) cc)) {
- // Sub window attached to this RootConnector, leave
- // as-is
- } else {
- // The connector has been detached from the
- // hierarchy, unregister it and any possible
- // children. The RootConnector should never be
- // unregistered even though it has no parent.
- connectorMap.unregisterConnector(cc);
- unregistered++;
+ if (c.getParent() != null) {
+ if (!c.getParent().getChildren().contains(c)) {
+ VConsole.error("ERROR: Connector is connected to a parent but the parent does not contain the connector");
}
+ } else if ((c instanceof RootConnector && c == getRootConnector())) {
+ // RootConnector for this connection, leave as-is
+ } else if (c instanceof WindowConnector
+ && getRootConnector().hasSubWindow(
+ (WindowConnector) c)) {
+ // Sub window attached to this RootConnector, leave
+ // as-is
+ } else {
+ // The connector has been detached from the
+ // hierarchy, unregister it and any possible
+ // children. The RootConnector should never be
+ // unregistered even though it has no parent.
+ connectorMap.unregisterConnector(c);
+ unregistered++;
}
}
@@ -1319,8 +1349,8 @@ public class ApplicationConnection {
continue;
}
- Class<? extends ComponentConnector> connectorClass = configuration
- .getWidgetClassByEncodedTag(connectorType);
+ Class<? extends ServerConnector> connectorClass = configuration
+ .getConnectorClassByEncodedTag(connectorType);
// Connector does not exist so we must create it
if (connectorClass != RootConnector.class) {
@@ -1410,11 +1440,19 @@ public class ApplicationConnection {
.getConnector(connectorId);
if (null != connector) {
- JSONArray stateDataAndType = new JSONArray(
+ JSONObject stateJson = new JSONObject(
states.getJavaScriptObject(connectorId));
- JsonDecoder.decodeValue(stateDataAndType,
- connector.getState(), connectorMap,
+ if (connector instanceof HasJavaScriptConnectorHelper) {
+ ((HasJavaScriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper()
+ .setNativeState(
+ stateJson.getJavaScriptObject());
+ }
+
+ SharedState state = connector.getState();
+ JsonDecoder.decodeValue(new Type(state.getClass()
+ .getName(), null), stateJson, state,
ApplicationConnection.this);
StateChangeEvent event = GWT
@@ -1454,47 +1492,48 @@ public class ApplicationConnection {
for (int i = 0; i < hierarchyKeys.length(); i++) {
try {
String connectorId = hierarchyKeys.get(i);
- ServerConnector connector = connectorMap
+ ServerConnector parentConnector = connectorMap
.getConnector(connectorId);
- if (!(connector instanceof ComponentContainerConnector)) {
- VConsole.error("Retrieved a hierarchy update for a connector ("
- + connectorId
- + ") that is not a ComponentContainerConnector");
- continue;
- }
- ComponentContainerConnector ccc = (ComponentContainerConnector) connector;
-
JsArrayString childConnectorIds = hierarchies
.getJSStringArray(connectorId);
int childConnectorSize = childConnectorIds.length();
List<ServerConnector> newChildren = new ArrayList<ServerConnector>();
+ List<ComponentConnector> newComponents = new ArrayList<ComponentConnector>();
for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) {
String childConnectorId = childConnectorIds
.get(connectorIndex);
- ComponentConnector childConnector = (ComponentConnector) connectorMap
+ ServerConnector childConnector = connectorMap
.getConnector(childConnectorId);
if (childConnector == null) {
VConsole.error("Hierarchy claims that "
+ childConnectorId + " is a child for "
+ connectorId + " ("
- + connector.getClass().getName()
+ + parentConnector.getClass().getName()
+ ") but no connector with id "
+ childConnectorId
+ " has been registered");
continue;
}
newChildren.add(childConnector);
- if (childConnector.getParent() != ccc) {
+ if (childConnector instanceof ComponentConnector) {
+ newComponents
+ .add((ComponentConnector) childConnector);
+ } else if (!(childConnector instanceof AbstractExtensionConnector)) {
+ throw new IllegalStateException(
+ Util.getConnectorString(childConnector)
+ + " is not a ComponentConnector nor an AbstractExtensionConnector");
+ }
+ if (childConnector.getParent() != parentConnector) {
// Avoid extra calls to setParent
- childConnector.setParent(ccc);
+ childConnector.setParent(parentConnector);
}
}
// TODO This check should be done on the server side in
// the future so the hierarchy update is only sent when
// something actually has changed
- List<ComponentConnector> oldChildren = ccc
+ List<ServerConnector> oldChildren = parentConnector
.getChildren();
boolean actuallyChanged = !Util.collectionsEquals(
oldChildren, newChildren);
@@ -1503,19 +1542,34 @@ public class ApplicationConnection {
continue;
}
- // Fire change event if the hierarchy has changed
- ConnectorHierarchyChangeEvent event = GWT
- .create(ConnectorHierarchyChangeEvent.class);
- event.setOldChildren(oldChildren);
- event.setConnector(ccc);
- ccc.setChildren((List) newChildren);
- events.add(event);
+ if (parentConnector instanceof ComponentContainerConnector) {
+ ComponentContainerConnector ccc = (ComponentContainerConnector) parentConnector;
+ List<ComponentConnector> oldComponents = ccc
+ .getChildComponents();
+ if (!Util.collectionsEquals(oldComponents,
+ newComponents)) {
+ // Fire change event if the hierarchy has
+ // changed
+ ConnectorHierarchyChangeEvent event = GWT
+ .create(ConnectorHierarchyChangeEvent.class);
+ event.setOldChildren(oldComponents);
+ event.setConnector(parentConnector);
+ ccc.setChildComponents(newComponents);
+ events.add(event);
+ }
+ } else if (!newComponents.isEmpty()) {
+ VConsole.error("Hierachy claims "
+ + Util.getConnectorString(parentConnector)
+ + " has component children even though it isn't a ComponentContainerConnector");
+ }
+
+ parentConnector.setChildren(newChildren);
// Remove parent for children that are no longer
// attached to this (avoid updating children if they
// have already been assigned to a new parent)
- for (ComponentConnector oldChild : oldChildren) {
- if (oldChild.getParent() != ccc) {
+ for (ServerConnector oldChild : oldChildren) {
+ if (oldChild.getParent() != parentConnector) {
continue;
}
@@ -1543,11 +1597,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);
}
@@ -1560,21 +1611,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);
- Object[] parameters = new Object[parametersJson.size()];
- for (int j = 0; j < parametersJson.size(); ++j) {
- parameters[j] = JsonDecoder.decodeValue(
- (JSONArray) parametersJson.get(j), null, getConnectorMap(),
- this);
- }
- return new MethodInvocation(connectorId, interfaceName, methodName,
- parameters);
- }
-
// Redirect browser, null reloads current page
private static native void redirect(String url)
/*-{
@@ -1591,7 +1627,7 @@ public class ApplicationConnection {
// TODO could eliminate invocations of same shared variable setter
addMethodInvocationToQueue(new MethodInvocation(connectorId,
UPDATE_VARIABLE_INTERFACE, UPDATE_VARIABLE_METHOD,
- new Object[] { variableName, value }), immediate);
+ new Object[] { variableName, new UidlValue(value) }), immediate);
}
/**
@@ -1692,7 +1728,7 @@ public class ApplicationConnection {
// TODO non-static encoder? type registration?
paramJson.set(i, JsonEncoder.encode(
invocation.getParameters()[i],
- restrictToInternalTypes, getConnectorMap(), this));
+ restrictToInternalTypes, this));
}
invocationJson.set(3, paramJson);
reqJson.set(reqJson.size(), invocationJson);
@@ -2079,7 +2115,9 @@ public class ApplicationConnection {
@Deprecated
public ComponentConnector getPaintable(UIDL uidl) {
- return getConnector(uidl.getId(), Integer.parseInt(uidl.getTag()));
+ // Non-component connectors shouldn't be painted from legacy connectors
+ return (ComponentConnector) getConnector(uidl.getId(),
+ Integer.parseInt(uidl.getTag()));
}
/**
@@ -2098,17 +2136,17 @@ public class ApplicationConnection {
* @return Either an existing ComponentConnector or a new ComponentConnector
* of the given type
*/
- public ComponentConnector getConnector(String connectorId, int connectorType) {
+ public ServerConnector getConnector(String connectorId, int connectorType) {
if (!connectorMap.hasConnector(connectorId)) {
return createAndRegisterConnector(connectorId, connectorType);
}
- return (ComponentConnector) connectorMap.getConnector(connectorId);
+ return connectorMap.getConnector(connectorId);
}
/**
- * Creates a new ComponentConnector with the given type and id.
+ * Creates a new ServerConnector with the given type and id.
*
- * Creates and registers a new ComponentConnector of the given type. Should
+ * Creates and registers a new ServerConnector of the given type. Should
* never be called with the connector id of an existing connector.
*
* @param connectorId
@@ -2116,12 +2154,12 @@ public class ApplicationConnection {
* @param connectorType
* Type of the connector, as passed from the server side
*
- * @return A new ComponentConnector of the given type
+ * @return A new ServerConnector of the given type
*/
- private ComponentConnector createAndRegisterConnector(String connectorId,
+ private ServerConnector createAndRegisterConnector(String connectorId,
int connectorType) {
// Create and register a new connector with the given type
- ComponentConnector p = widgetSet.createWidget(connectorType,
+ ServerConnector p = widgetSet.createConnector(connectorType,
configuration);
connectorMap.registerConnector(connectorId, p);
p.doInit(connectorId, this);
diff --git a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java
index e12c002930..82cf925ec1 100644
--- a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java
+++ b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java
@@ -31,6 +31,9 @@ public class BrowserInfo {
private static final String OS_ANDROID = "android";
private static final String OS_IOS = "ios";
+ // Common CSS class for all touch devices
+ private static final String UI_TOUCH = "touch";
+
private static BrowserInfo instance;
private static String cssClass = null;
@@ -169,7 +172,9 @@ public class BrowserInfo {
if (osClass != null) {
cssClass = cssClass + " " + prefix + osClass;
}
-
+ if (isTouchDevice()) {
+ cssClass = cssClass + " " + prefix + UI_TOUCH;
+ }
}
return cssClass;
@@ -344,15 +349,14 @@ public class BrowserInfo {
if (!isTouchDevice()) {
return false;
}
-
- if (isAndroid() && isWebkit() && getWebkitVersion() < 534) {
- return true;
+ if (isAndroid() && isWebkit() && getWebkitVersion() >= 534) {
+ return false;
}
- // if (isIOS() && isWebkit() && getWebkitVersion() < ???) {
- // return true;
+ // Cannot enable native touch scrolling on iOS 5 until #8792 is resolved
+ // if (isIOS() && isWebkit() && getWebkitVersion() >= 534) {
+ // return false;
// }
-
- return false;
+ return true;
}
/**
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ComponentConnector.java
index 5f9171084e..4e6a690a3c 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentConnector.java
@@ -71,29 +71,6 @@ public interface ComponentConnector extends ServerConnector {
public boolean isRelativeHeight();
/**
- * Returns the parent of this connector. Can be null for only the root
- * connector.
- *
- * @return The parent of this connector, as set by
- * {@link #setParent(ComponentContainerConnector)}.
- */
- public ComponentContainerConnector getParent();
-
- /**
- * Sets the parent for this connector. This method should only be called by
- * the framework to ensure that the connector hierarchy on the client side
- * and the server side are in sync.
- * <p>
- * Note that calling this method does not fire a
- * {@link ConnectorHierarchyChangeEvent}. The event is fired only when the
- * whole hierarchy has been updated.
- *
- * @param parent
- * The new parent of the connector
- */
- public void setParent(ComponentContainerConnector parent);
-
- /**
* Checks if the connector is read only.
*
* @deprecated This belongs in AbstractFieldConnector, see #8514
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java b/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java
index 05334e8049..08ce3d31dc 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java
@@ -14,7 +14,7 @@ import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent.ConnectorHie
* An interface used by client-side connectors whose widget is a component
* container (implements {@link HasWidgets}).
*/
-public interface ComponentContainerConnector extends ComponentConnector {
+public interface ComponentContainerConnector extends ServerConnector {
/**
* Update child components caption, description and error message.
@@ -42,7 +42,7 @@ public interface ComponentContainerConnector extends ComponentConnector {
* @return A collection of children for this connector. An empty collection
* if there are no children. Never returns null.
*/
- public List<ComponentConnector> getChildren();
+ public List<ComponentConnector> getChildComponents();
/**
* Sets the children for this connector. This method should only be called
@@ -50,14 +50,14 @@ public interface ComponentContainerConnector extends ComponentConnector {
* side and the server side are in sync.
* <p>
* Note that calling this method does not call
- * {@link #connectorHierarchyChanged(ConnectorHierarchyChangeEvent)}. The
- * event method is called only when the hierarchy has been updated for all
- * connectors.
+ * {@link ConnectorHierarchyChangeHandler#onConnectorHierarchyChange(ConnectorHierarchyChangeEvent)}
+ * . The event method is called only when the hierarchy has been updated for
+ * all connectors.
*
* @param children
* The new child connectors
*/
- public void setChildren(List<ComponentConnector> children);
+ public void setChildComponents(List<ComponentConnector> children);
/**
* Adds a handler that is called whenever the child hierarchy of this
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
index a4df6c6cc4..0e7a0c1d1c 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
@@ -12,6 +12,7 @@ import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.client.ui.SubPartAware;
import com.vaadin.terminal.gwt.client.ui.VBoxLayout;
import com.vaadin.terminal.gwt.client.ui.gridlayout.VGridLayout;
@@ -441,8 +442,8 @@ public class ComponentLocator {
} else if (w == null) {
String id = part;
// Must be old static pid (PID_S*)
- ComponentConnector connector = (ComponentConnector) ConnectorMap
- .get(client).getConnector(id);
+ ServerConnector connector = ConnectorMap.get(client)
+ .getConnector(id);
if (connector == null) {
// Lookup by debugId
// TODO Optimize this
@@ -450,8 +451,8 @@ public class ComponentLocator {
id.substring(5));
}
- if (connector != null) {
- w = connector.getWidget();
+ if (connector instanceof ComponentConnector) {
+ w = ((ComponentConnector) connector).getWidget();
} else {
// Not found
return null;
@@ -597,19 +598,16 @@ public class ComponentLocator {
return w;
}
- private ComponentConnector findConnectorById(ComponentConnector root,
- String id) {
- if (root instanceof ComponentConnector
- && id.equals(root.getState().getDebugId())) {
+ private ServerConnector findConnectorById(ServerConnector root, String id) {
+ SharedState state = root.getState();
+ if (state instanceof ComponentState
+ && id.equals(((ComponentState) state).getDebugId())) {
return root;
}
- if (root instanceof ComponentContainerConnector) {
- ComponentContainerConnector ccc = (ComponentContainerConnector) root;
- for (ComponentConnector child : ccc.getChildren()) {
- ComponentConnector found = findConnectorById(child, id);
- if (found != null) {
- return found;
- }
+ for (ServerConnector child : root.getChildren()) {
+ ServerConnector found = findConnectorById(child, id);
+ if (found != null) {
+ return found;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentState.java b/src/com/vaadin/terminal/gwt/client/ComponentState.java
index 7f01300d68..a603368f44 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentState.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentState.java
@@ -24,7 +24,6 @@ public class ComponentState extends SharedState {
private String width = "";
private boolean readOnly = false;
private boolean immediate = false;
- private boolean enabled = true;
private String description = "";
// Note: for the caption, there is a difference between null and an empty
// string!
@@ -195,29 +194,6 @@ public class ComponentState extends SharedState {
}
/**
- * Returns true if the component is enabled.
- *
- * @see com.vaadin.ui.Component#isEnabled()
- *
- * @return true if the component is enabled
- */
- public boolean isEnabled() {
- return enabled;
- }
-
- /**
- * Enables or disables the component.
- *
- * @see com.vaadin.ui.Component#setEnabled(boolean)
- *
- * @param enabled
- * new mode for the component
- */
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- /**
* Gets the description of the component (typically shown as tooltip).
*
* @see com.vaadin.ui.AbstractComponent#getDescription()
diff --git a/src/com/vaadin/terminal/gwt/client/Connector.java b/src/com/vaadin/terminal/gwt/client/Connector.java
index 1c61052735..9b2fcf61f1 100644
--- a/src/com/vaadin/terminal/gwt/client/Connector.java
+++ b/src/com/vaadin/terminal/gwt/client/Connector.java
@@ -46,4 +46,12 @@ public interface Connector extends Serializable {
*/
public String getConnectorId();
+ /**
+ * Gets the parent connector of this connector, or <code>null</code> if the
+ * connector is not attached to any parent.
+ *
+ * @return the parent connector, or <code>null</code> if there is no parent.
+ */
+ public Connector getParent();
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ConnectorMap.java b/src/com/vaadin/terminal/gwt/client/ConnectorMap.java
index e57776cf1c..efb50b5e00 100644
--- a/src/com/vaadin/terminal/gwt/client/ConnectorMap.java
+++ b/src/com/vaadin/terminal/gwt/client/ConnectorMap.java
@@ -159,17 +159,16 @@ public class ConnectorMap {
idToConnector.remove(connectorId);
connector.onUnregister();
- if (connector instanceof ComponentContainerConnector) {
- for (ComponentConnector child : ((ComponentContainerConnector) connector)
- .getChildren()) {
- if (child.getParent() == connector) {
- // Only unregister children that are actually connected to
- // this parent. For instance when moving connectors from one
- // layout to another and removing the first layout it will
- // still contain references to its old children, which are
- // now attached to another connector.
- unregisterConnector(child);
- }
+ for (ServerConnector child : connector.getChildren()) {
+ if (child.getParent() == connector) {
+ /*
+ * Only unregister children that are actually connected to this
+ * parent. For instance when moving connectors from one layout
+ * to another and removing the first layout it will still
+ * contain references to its old children, which are now
+ * attached to another connector.
+ */
+ unregisterConnector(child);
}
}
}
@@ -209,12 +208,20 @@ public class ConnectorMap {
*/
@Deprecated
public TooltipInfo getTooltipInfo(ComponentConnector paintable, Object key) {
- return getComponentDetail(paintable).getTooltipInfo(key);
+ ComponentDetail componentDetail = getComponentDetail(paintable);
+ if (componentDetail == null) {
+ return null;
+ }
+ return componentDetail.getTooltipInfo(key);
}
@Deprecated
public TooltipInfo getWidgetTooltipInfo(Widget widget, Object key) {
- return getTooltipInfo(getConnector(widget), key);
+ ComponentConnector connector = getConnector(widget);
+ if (connector == null) {
+ return null;
+ }
+ return getTooltipInfo(connector, key);
}
public Collection<? extends ServerConnector> getConnectors() {
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..bd62a759cb
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavaScriptConnectorHelper.java
@@ -0,0 +1,372 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+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.google.gwt.user.client.Element;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler;
+
+public class JavaScriptConnectorHelper {
+
+ public interface JavaScriptConnectorState {
+ public Set<String> getCallbackNames();
+
+ public Map<String, Set<String>> getRpcInterfaces();
+ }
+
+ private final ServerConnector connector;
+ private final JavaScriptObject nativeState = JavaScriptObject
+ .createObject();
+ private final JavaScriptObject rpcMap = JavaScriptObject.createObject();
+
+ private final Map<String, JavaScriptObject> rpcObjects = new HashMap<String, JavaScriptObject>();
+ private final Map<String, Set<String>> rpcMethods = new HashMap<String, Set<String>>();
+
+ private JavaScriptObject connectorWrapper;
+ private int tag;
+
+ private boolean inited = false;
+
+ public JavaScriptConnectorHelper(ServerConnector connector) {
+ this.connector = connector;
+
+ // Wildcard rpc object
+ rpcObjects.put("", JavaScriptObject.createObject());
+ }
+
+ public void init() {
+ connector.addStateChangeHandler(new StateChangeHandler() {
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ JavaScriptObject wrapper = getConnectorWrapper();
+ JavaScriptConnectorState state = getConnectorState();
+
+ for (String callback : state.getCallbackNames()) {
+ ensureCallback(JavaScriptConnectorHelper.this, wrapper,
+ callback);
+ }
+
+ for (Entry<String, Set<String>> entry : state
+ .getRpcInterfaces().entrySet()) {
+ String rpcName = entry.getKey();
+ String jsName = getJsInterfaceName(rpcName);
+ if (!rpcObjects.containsKey(jsName)) {
+ Set<String> methods = entry.getValue();
+ rpcObjects.put(jsName,
+ createRpcObject(rpcName, methods));
+
+ // Init all methods for wildcard rpc
+ for (String method : methods) {
+ JavaScriptObject wildcardRpcObject = rpcObjects
+ .get("");
+ Set<String> interfaces = rpcMethods.get(method);
+ if (interfaces == null) {
+ interfaces = new HashSet<String>();
+ rpcMethods.put(method, interfaces);
+ attachRpcMethod(wildcardRpcObject, null, method);
+ }
+ interfaces.add(rpcName);
+ }
+ }
+ }
+
+ // Init after setting up callbacks & rpc
+ if (!inited) {
+ initJavaScript();
+ inited = true;
+ }
+
+ fireNativeStateChange(wrapper);
+ }
+ });
+ }
+
+ private static String getJsInterfaceName(String rpcName) {
+ return rpcName.replace('$', '.');
+ }
+
+ protected JavaScriptObject createRpcObject(String iface, Set<String> methods) {
+ JavaScriptObject object = JavaScriptObject.createObject();
+
+ for (String method : methods) {
+ attachRpcMethod(object, iface, method);
+ }
+
+ return object;
+ }
+
+ private boolean initJavaScript() {
+ 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(this, nativeState,
+ rpcMap, connector.getConnectorId(), rpcObjects);
+ }
+
+ return connectorWrapper;
+ }
+
+ 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,
+ Map<String, JavaScriptObject> rpcObjects)
+ /*-{
+ return {
+ 'getConnectorId': function() {
+ return connectorId;
+ },
+ 'getParentId': $entry(function(connectorId) {
+ return h.@com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper::getParentId(Ljava/lang/String;)(connectorId);
+ }),
+ 'getState': function() {
+ return nativeState;
+ },
+ 'getRpcProxy': $entry(function(iface) {
+ if (!iface) {
+ iface = '';
+ }
+ return rpcObjects.@java.util.Map::get(Ljava/lang/Object;)(iface);
+ }),
+ 'getWidgetElement': $entry(function(connectorId) {
+ return h.@com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper::getWidgetElement(Ljava/lang/String;)(connectorId);
+ }),
+ 'registerRpc': function(iface, rpcHandler) {
+ //registerRpc(handler) -> registerRpc('', handler);
+ if (!rpcHandler) {
+ rpcHandler = iface;
+ iface = '';
+ }
+ if (!registeredRpc[iface]) {
+ registeredRpc[iface] = [];
+ }
+ registeredRpc[iface].push(rpcHandler);
+ },
+ };
+ }-*/;
+
+ private native void attachRpcMethod(JavaScriptObject rpc, String iface,
+ String method)
+ /*-{
+ var self = this;
+ rpc[method] = $entry(function() {
+ self.@com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper::fireRpc(Ljava/lang/String;Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(iface, method, arguments);
+ });
+ }-*/;
+
+ private String getParentId(String connectorId) {
+ ServerConnector target = getConnector(connectorId);
+ if (target == null) {
+ return null;
+ }
+ ServerConnector parent = target.getParent();
+ if (parent == null) {
+ return null;
+ } else {
+ return parent.getConnectorId();
+ }
+ }
+
+ private Element getWidgetElement(String connectorId) {
+ ServerConnector target = getConnector(connectorId);
+ if (target instanceof ComponentConnector) {
+ return ((ComponentConnector) target).getWidget().getElement();
+ } else {
+ return null;
+ }
+ }
+
+ private ServerConnector getConnector(String connectorId) {
+ if (connectorId == null || connectorId.length() == 0) {
+ return connector;
+ }
+
+ return ConnectorMap.get(connector.getConnection())
+ .getConnector(connectorId);
+ }
+
+ private void fireRpc(String iface, String method,
+ JsArray<JavaScriptObject> arguments) {
+ if (iface == null) {
+ iface = findWildcardInterface(method);
+ }
+
+ 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);
+ }
+
+ private String findWildcardInterface(String method) {
+ Set<String> interfaces = rpcMethods.get(method);
+ if (interfaces.size() == 1) {
+ return interfaces.iterator().next();
+ } else {
+ // TODO Resolve conflicts using argument count and types
+ String interfaceList = "";
+ for (String iface : interfaces) {
+ if (interfaceList.length() != 0) {
+ interfaceList += ", ";
+ }
+ interfaceList += getJsInterfaceName(iface);
+ }
+
+ throw new IllegalStateException(
+ "Can not call method "
+ + method
+ + " for wildcard rpc proxy because the function is defined for multiple rpc interfaces: "
+ + interfaceList
+ + ". Retrieve a rpc proxy for a specific interface using getRpcProxy(interfaceName) to use the function.");
+ }
+ }
+
+ private void fireCallback(String name, JsArray<JavaScriptObject> arguments) {
+ MethodInvocation invocation = new MethodInvocation(
+ connector.getConnectorId(),
+ "com.vaadin.ui.JavaScript$JavaScriptCallbackRpc", "call",
+ new Object[] { name, new JSONArray(arguments) });
+ connector.getConnection().addMethodInvocationToQueue(invocation, 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) {
+ String iface = invocation.getInterfaceName();
+ String method = invocation.getMethodName();
+ if ("com.vaadin.ui.JavaScript$JavaScriptCallbackRpc".equals(iface)
+ && "call".equals(method)) {
+ String callbackName = parametersJson.get(0).isString()
+ .stringValue();
+ JavaScriptObject arguments = parametersJson.get(1).isArray()
+ .getJavaScriptObject();
+ invokeCallback(getConnectorWrapper(), callbackName, arguments);
+ } else {
+ JavaScriptObject arguments = parametersJson.getJavaScriptObject();
+ invokeJsRpc(rpcMap, iface, method, arguments);
+ // Also invoke wildcard interface
+ invokeJsRpc(rpcMap, "", method, arguments);
+ }
+ }
+
+ private static native void invokeCallback(JavaScriptObject connector,
+ String name, JavaScriptObject arguments)
+ /*-{
+ connector[name].apply(connector, arguments);
+ }-*/;
+
+ 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];
+ target[methodName].apply(target, parameters);
+ }
+ }-*/;
+
+ private static native void ensureCallback(JavaScriptConnectorHelper h,
+ JavaScriptObject connector, String name)
+ /*-{
+ connector[name] = $entry(function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ h.@com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper::fireCallback(Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(name, args);
+ });
+ }-*/;
+
+ private JavaScriptConnectorState getConnectorState() {
+ return (JavaScriptConnectorState) connector.getState();
+ }
+
+}
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..2a97e4a770
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavaScriptExtension.java
@@ -0,0 +1,32 @@
+/*
+@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.extensions.AbstractExtensionConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+
+@Connect(AbstractJavaScriptExtension.class)
+public final class JavaScriptExtension extends AbstractExtensionConnector
+ implements HasJavaScriptConnectorHelper {
+ private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper(
+ this);
+
+ @Override
+ protected void init() {
+ super.init();
+ helper.init();
+ }
+
+ public JavaScriptConnectorHelper getJavascriptConnectorHelper() {
+ return helper;
+ }
+
+ @Override
+ public JavaScriptExtensionState getState() {
+ return (JavaScriptExtensionState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/JavaScriptExtensionState.java b/src/com/vaadin/terminal/gwt/client/JavaScriptExtensionState.java
new file mode 100644
index 0000000000..e7bfbc4bb2
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavaScriptExtensionState.java
@@ -0,0 +1,36 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper.JavaScriptConnectorState;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+
+public class JavaScriptExtensionState extends SharedState implements
+ JavaScriptConnectorState {
+
+ private Set<String> callbackNames = new HashSet<String>();
+ private Map<String, Set<String>> rpcInterfaces = new HashMap<String, Set<String>>();
+
+ public Set<String> getCallbackNames() {
+ return callbackNames;
+ }
+
+ public void setCallbackNames(Set<String> callbackNames) {
+ this.callbackNames = callbackNames;
+ }
+
+ public Map<String, Set<String>> getRpcInterfaces() {
+ return rpcInterfaces;
+ }
+
+ public void setRpcInterfaces(Map<String, Set<String>> rpcInterfaces) {
+ this.rpcInterfaces = rpcInterfaces;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
index 239a948a10..41ad0e9f47 100644
--- a/src/com/vaadin/terminal/gwt/client/LayoutManager.java
+++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
@@ -120,7 +120,7 @@ public class LayoutManager {
/**
* Assigns a measured size to an element. Method defined as protected to
- * allow separate implementation for IE8 in which delete not always works.
+ * allow separate implementation for IE8.
*
* @param element
* the dom element to attach the measured size to
@@ -139,18 +139,14 @@ public class LayoutManager {
}-*/;
/**
- * Get the measured size of the given element. If no size is set, use the
- * default size instead.
- *
- * Method defined as protected to allow separate implementation for IE8
- * (performance reason: storing any data in the DOM causes a reflow).
+ * Gets the measured size for an element. Method defined as protected to
+ * allow separate implementation for IE8.
*
* @param element
- * the dom element whose measured size to get
+ * The element to get measured size for
* @param defaultSize
- * a fallback size if the element doesn't have a measured size
- * stored
- * @return
+ * The size to return if no measured size could be found
+ * @return The measured size for the element or {@literal defaultSize}
*/
protected native MeasuredSize getMeasuredSize(Element element,
MeasuredSize defaultSize)
@@ -185,7 +181,7 @@ public class LayoutManager {
return;
}
measuredSize.removeDependent(owner.getConnectorId());
- stopMeasuringIfUnnecessary(element);
+ stopMeasuringIfUnecessary(element);
}
public boolean isLayoutRunning() {
@@ -270,16 +266,10 @@ public class LayoutManager {
VConsole.log(" Measured " + measuredConnectorCount
+ " elements in " + measureTime + " ms");
- VConsole.log(" Total of " + measureCount
- + " measurement operations");
-
if (!listenersToFire.isEmpty()) {
for (Element element : listenersToFire) {
Collection<ElementResizeListener> listeners = elementResizeListeners
.get(element);
- if (listeners == null) {
- continue;
- }
ElementResizeListener[] array = listeners
.toArray(new ElementResizeListener[listeners.size()]);
ElementResizeEvent event = new ElementResizeEvent(this,
@@ -409,8 +399,13 @@ public class LayoutManager {
((PostLayoutListener) connector).postLayout();
}
}
- VConsole.log("Invoke post layout listeners in "
- + (totalDuration.elapsedMillis() - postLayoutStart) + " ms");
+ int postLayoutDone = (totalDuration.elapsedMillis() - postLayoutStart);
+ VConsole.log("Invoke post layout listeners in " + postLayoutDone
+ + " ms");
+
+ cleanMeasuredSizes();
+ int cleaningDone = (totalDuration.elapsedMillis() - postLayoutDone);
+ VConsole.log("Cleaned old measured sizes in " + cleaningDone + "ms");
VConsole.log("Total layout phase time: "
+ totalDuration.elapsedMillis() + "ms");
@@ -436,11 +431,13 @@ public class LayoutManager {
for (ComponentConnector componentConnector : pendingOverflowFixes) {
// Delay the overflow fix if the involved connectors might still
// change
- if (!currentDependencyTree
- .noMoreChangesExpected(componentConnector)
- || !currentDependencyTree
- .noMoreChangesExpected(componentConnector
- .getParent())) {
+ boolean connectorChangesExpected = !currentDependencyTree
+ .noMoreChangesExpected(componentConnector);
+ boolean parentChangesExcpected = componentConnector.getParent() instanceof ComponentConnector
+ && !currentDependencyTree
+ .noMoreChangesExpected((ComponentConnector) componentConnector
+ .getParent());
+ if (connectorChangesExpected || parentChangesExcpected) {
delayedOverflowFixes.add(componentConnector);
continue;
}
@@ -521,15 +518,8 @@ public class LayoutManager {
}
private void measureConnector(ComponentConnector connector) {
- MeasuredSize measuredSize = getMeasuredSize(connector);
- if (!isManagedLayout(connector)
- && !isManagedLayout(connector.getParent())
- && elementResizeListeners.get(connector.getWidget()
- .getElement()) == null && !measuredSize.hasDependents()) {
- return;
- }
-
Element element = connector.getWidget().getElement();
+ MeasuredSize measuredSize = getMeasuredSize(connector);
MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
if (measureResult.isChanged()) {
@@ -569,11 +559,8 @@ public class LayoutManager {
+ " non connector elements");
}
- int measureCount = 0;
-
private MeasureResult measuredAndUpdate(Element element,
MeasuredSize measuredSize) {
- measureCount++;
MeasureResult measureResult = measuredSize.measure(element);
if (measureResult.isChanged()) {
notifyListenersAndDepdendents(element,
@@ -1032,12 +1019,48 @@ public class LayoutManager {
return getMeasuredSize(element, nullSize).getMarginLeft();
}
- public int getMarginWidth(Element element) {
- return getMeasuredSize(element, nullSize).getMarginWidth();
+ /**
+ * Gets the combined top & bottom margin of the given element, provided that
+ * they have been measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured margin for
+ * @return the measured top+bottom margin of the element in pixels.
+ */
+ public int getMarginHeight(Element element) {
+ return getMarginTop(element) + getMarginBottom(element);
}
- public int getMarginHeight(Element element) {
- return getMeasuredSize(element, nullSize).getMarginHeight();
+ /**
+ * Gets the combined left & right margin of the given element, provided that
+ * they have been measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured margin for
+ * @return the measured left+right margin of the element in pixels.
+ */
+ public int getMarginWidth(Element element) {
+ return getMarginLeft(element) + getMarginRight(element);
}
/**
@@ -1192,12 +1215,12 @@ public class LayoutManager {
listeners.remove(listener);
if (listeners.isEmpty()) {
elementResizeListeners.remove(element);
- stopMeasuringIfUnnecessary(element);
+ stopMeasuringIfUnecessary(element);
}
}
}
- private void stopMeasuringIfUnnecessary(Element element) {
+ private void stopMeasuringIfUnecessary(Element element) {
if (!needsMeasure(element)) {
measuredNonConnectorElements.remove(element);
setMeasuredSize(element, null);
@@ -1227,4 +1250,10 @@ public class LayoutManager {
everythingNeedsMeasure = true;
}
+ /**
+ * Clean measured sizes which are no longer needed. Only for IE8.
+ */
+ protected void cleanMeasuredSizes() {
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java b/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
index fc55007969..ea130779ea 100644
--- a/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
+++ b/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
@@ -4,43 +4,47 @@
package com.vaadin.terminal.gwt.client;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.ui.RootPanel;
public class LayoutManagerIE8 extends LayoutManager {
- protected HashMap<Element, MeasuredSize> sizes = new HashMap<Element, MeasuredSize>();
+ private Map<Element, MeasuredSize> measuredSizes = new HashMap<Element, MeasuredSize>();
@Override
protected void setMeasuredSize(Element element, MeasuredSize measuredSize) {
if (measuredSize != null) {
- sizes.put(element, measuredSize);
+ measuredSizes.put(element, measuredSize);
} else {
- sizes.remove(element);
+ measuredSizes.remove(element);
}
}
- // @Override
- // protected native void setMeasuredSize(Element element,
- // MeasuredSize measuredSize)
- // IE8 cannot do delete element.vMeasuredSize, at least in the case when
- // element is not attached to the document (e.g. when a caption is removed)
- /*-{
- if (measuredSize) {
- element.vMeasuredSize = measuredSize;
- } else {
- element.vMeasuredSize = undefined;
- }
- // }-*/;
-
@Override
protected MeasuredSize getMeasuredSize(Element element,
MeasuredSize defaultSize) {
- MeasuredSize size = sizes.get(element);
- if (size != null) {
- return size;
+ MeasuredSize measured = measuredSizes.get(element);
+ if (measured != null) {
+ return measured;
+ } else {
+ return defaultSize;
}
- return defaultSize;
}
+ @Override
+ protected void cleanMeasuredSizes() {
+ Document document = RootPanel.get().getElement().getOwnerDocument();
+
+ Iterator<Element> i = measuredSizes.keySet().iterator();
+ while (i.hasNext()) {
+ Element e = i.next();
+ if (e.getOwnerDocument() != document) {
+ i.remove();
+ }
+ }
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ServerConnector.java b/src/com/vaadin/terminal/gwt/client/ServerConnector.java
index 02b894e831..fcf5100c8e 100644
--- a/src/com/vaadin/terminal/gwt/client/ServerConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ServerConnector.java
@@ -4,6 +4,7 @@
package com.vaadin.terminal.gwt.client;
import java.util.Collection;
+import java.util.List;
import com.google.gwt.event.shared.GwtEvent;
import com.google.web.bindery.event.shared.HandlerRegistration;
@@ -94,4 +95,32 @@ public interface ServerConnector extends Connector {
*/
public void onUnregister();
+ /**
+ * Returns the parent of this connector. Can be null for only the root
+ * connector.
+ *
+ * @return The parent of this connector, as set by
+ * {@link #setParent(ServerConnector)}.
+ */
+ public ServerConnector getParent();
+
+ /**
+ * Sets the parent for this connector. This method should only be called by
+ * the framework to ensure that the connector hierarchy on the client side
+ * and the server side are in sync.
+ * <p>
+ * Note that calling this method does not fire a
+ * {@link ConnectorHierarchyChangeEvent}. The event is fired only when the
+ * whole hierarchy has been updated.
+ *
+ * @param parent
+ * The new parent of the connector
+ */
+ public void setParent(ServerConnector parent);
+
+ public void updateEnabledState(boolean enabledState);
+
+ public void setChildren(List<ServerConnector> children);
+
+ public List<ServerConnector> getChildren();
}
diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java
index c392a0ba9c..d3cb54160d 100644
--- a/src/com/vaadin/terminal/gwt/client/Util.java
+++ b/src/com/vaadin/terminal/gwt/client/Util.java
@@ -812,13 +812,12 @@ public class Util {
return idx;
}
- private static void printPaintablesInvocations(
+ private static void printConnectorInvocations(
ArrayList<MethodInvocation> invocations, String id,
ApplicationConnection c) {
- ComponentConnector paintable = (ComponentConnector) ConnectorMap.get(c)
- .getConnector(id);
- if (paintable != null) {
- VConsole.log("\t" + id + " (" + paintable.getClass() + ") :");
+ ServerConnector connector = ConnectorMap.get(c).getConnector(id);
+ if (connector != null) {
+ VConsole.log("\t" + id + " (" + connector.getClass() + ") :");
for (MethodInvocation invocation : invocations) {
Object[] parameters = invocation.getParameters();
String formattedParams = null;
@@ -842,7 +841,7 @@ public class Util {
+ ")");
}
} else {
- VConsole.log("\t" + id + ": Warning: no corresponding paintable!");
+ VConsole.log("\t" + id + ": Warning: no corresponding connector!");
}
}
@@ -858,14 +857,14 @@ public class Util {
if (curId == null) {
curId = id;
} else if (!curId.equals(id)) {
- printPaintablesInvocations(invocations, curId, c);
+ printConnectorInvocations(invocations, curId, c);
invocations.clear();
curId = id;
}
invocations.add(loggedBurst.get(i));
}
if (!invocations.isEmpty()) {
- printPaintablesInvocations(invocations, curId, c);
+ printConnectorInvocations(invocations, curId, c);
}
} catch (Exception e) {
VConsole.error(e);
@@ -912,6 +911,22 @@ public class Util {
}
/**
+ * Find the element corresponding to the coordinates in the passed mouse
+ * event. Please note that this is not always the same as the target of the
+ * event e.g. if event capture is used.
+ *
+ * @param event
+ * the mouse event to get coordinates from
+ * @return the element at the coordinates of the event
+ */
+ public static Element getElementUnderMouse(NativeEvent event) {
+ int pageX = getTouchOrMouseClientX(event);
+ int pageY = getTouchOrMouseClientY(event);
+
+ return getElementFromPoint(pageX, pageY);
+ }
+
+ /**
* A helper method to return the client position from an event. Returns
* position from either first changed touch (if touch event) or from the
* event itself.
diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
index 5eaf78f255..09e939336e 100644
--- a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
+++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
@@ -560,23 +560,27 @@ public class VDebugConsole extends VOverlay implements Console {
Set<ComponentConnector> zeroHeightComponents,
ApplicationConnection ac) {
for (final ComponentConnector paintable : zeroHeightComponents) {
- final Widget layout = paintable.getParent().getWidget();
+ final ServerConnector parent = paintable.getParent();
VerticalPanel errorDetails = new VerticalPanel();
errorDetails.add(new Label("" + Util.getSimpleName(paintable)
- + " inside " + Util.getSimpleName(layout)));
- final CheckBox emphasisInUi = new CheckBox(
- "Emphasize components parent in UI (the actual component is not visible)");
- emphasisInUi.addClickHandler(new ClickHandler() {
- public void onClick(ClickEvent event) {
- if (paintable != null) {
+ + " inside " + Util.getSimpleName(parent)));
+ if (parent instanceof ComponentConnector) {
+ ComponentConnector parentComponent = (ComponentConnector) parent;
+ final Widget layout = parentComponent.getWidget();
+
+ final CheckBox emphasisInUi = new CheckBox(
+ "Emphasize components parent in UI (the actual component is not visible)");
+ emphasisInUi.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
Element element2 = layout.getElement();
Widget.setStyleName(element2, "invalidlayout",
- emphasisInUi.getValue());
+ emphasisInUi.getValue().booleanValue());
}
- }
- });
- errorDetails.add(emphasisInUi);
+ });
+
+ errorDetails.add(emphasisInUi);
+ }
panel.add(errorDetails);
}
}
@@ -862,7 +866,7 @@ public class VDebugConsole extends VOverlay implements Console {
log("================");
log("Connector hierarchy for Root: " + root.getState().getCaption()
+ " (" + root.getConnectorId() + ")");
- Set<ComponentConnector> connectorsInHierarchy = new HashSet<ComponentConnector>();
+ Set<ServerConnector> connectorsInHierarchy = new HashSet<ServerConnector>();
SimpleTree rootHierachy = dumpConnectorHierarchy(root, "",
connectorsInHierarchy);
if (panel.isAttached()) {
@@ -874,7 +878,7 @@ public class VDebugConsole extends VOverlay implements Console {
Collection<? extends ServerConnector> registeredConnectors = connectorMap
.getConnectors();
log("Sub windows:");
- Set<ComponentConnector> subWindowHierarchyConnectors = new HashSet<ComponentConnector>();
+ Set<ServerConnector> subWindowHierarchyConnectors = new HashSet<ServerConnector>();
for (WindowConnector wc : root.getSubWindows()) {
SimpleTree windowHierachy = dumpConnectorHierarchy(wc, "",
subWindowHierarchyConnectors);
@@ -908,14 +912,15 @@ public class VDebugConsole extends VOverlay implements Console {
}
- private SimpleTree dumpConnectorHierarchy(
- final ComponentConnector connector, String indent,
- Set<ComponentConnector> connectors) {
+ private SimpleTree dumpConnectorHierarchy(final ServerConnector connector,
+ String indent, Set<ServerConnector> connectors) {
SimpleTree simpleTree = new SimpleTree(getConnectorString(connector)) {
@Override
protected void select(ClickEvent event) {
super.select(event);
- VUIDLBrowser.highlight(connector);
+ if (connector instanceof ComponentConnector) {
+ VUIDLBrowser.highlight((ComponentConnector) connector);
+ }
}
};
simpleTree.addDomHandler(new MouseOutHandler() {
@@ -930,12 +935,8 @@ public class VDebugConsole extends VOverlay implements Console {
consoleLog(msg);
System.out.println(msg);
- if (connector instanceof ComponentContainerConnector) {
- for (ComponentConnector c : ((ComponentContainerConnector) connector)
- .getChildren()) {
- simpleTree.add(dumpConnectorHierarchy(c, indent + " ",
- connectors));
- }
+ for (ServerConnector c : connector.getChildren()) {
+ simpleTree.add(dumpConnectorHierarchy(c, indent + " ", connectors));
}
return simpleTree;
}
diff --git a/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java b/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java
index 9fa973dc29..4230eda298 100644
--- a/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java
+++ b/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java
@@ -98,8 +98,8 @@ public class VUIDLBrowser extends SimpleTree {
private String getNodeName(UIDL uidl, ApplicationConfiguration conf,
int tag) {
- Class<? extends ComponentConnector> widgetClassByDecodedTag = conf
- .getWidgetClassByEncodedTag(tag);
+ Class<? extends ServerConnector> widgetClassByDecodedTag = conf
+ .getConnectorClassByEncodedTag(tag);
if (widgetClassByDecodedTag == UnknownComponentConnector.class) {
return conf.getUnknownServerClassNameByTag(tag)
+ "(NO CLIENT IMPLEMENTATION FOUND)";
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java b/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java
index dd69883d58..0a4f92bc79 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java
@@ -7,5 +7,5 @@ package com.vaadin.terminal.gwt.client;
* A helper class used by WidgetMap implementation. Used by the generated code.
*/
interface WidgetInstantiator {
- public ComponentConnector get();
+ public ServerConnector get();
}
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetMap.java b/src/com/vaadin/terminal/gwt/client/WidgetMap.java
index af84a11ced..b770414457 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetMap.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetMap.java
@@ -17,7 +17,7 @@ import com.vaadin.terminal.gwt.widgetsetutils.WidgetMapGenerator;
*/
abstract class WidgetMap {
- protected static HashMap<Class, WidgetInstantiator> instmap = new HashMap<Class, WidgetInstantiator>();
+ protected static HashMap<Class<? extends ServerConnector>, WidgetInstantiator> instmap = new HashMap<Class<? extends ServerConnector>, WidgetInstantiator>();
/**
* Create a new instance of a connector based on its type.
@@ -26,8 +26,8 @@ abstract class WidgetMap {
* {@link ComponentConnector} class to instantiate
* @return new instance of the connector
*/
- public ComponentConnector instantiate(
- Class<? extends ComponentConnector> classType) {
+ public ServerConnector instantiate(
+ Class<? extends ServerConnector> classType) {
return instmap.get(classType).get();
}
@@ -39,7 +39,7 @@ abstract class WidgetMap {
* fully qualified name of the server side component class
* @return component connector class to use
*/
- public abstract Class<? extends ComponentConnector> getConnectorClassForServerSideClassName(
+ public abstract Class<? extends ServerConnector> getConnectorClassForServerSideClassName(
String fullyqualifiedName);
/**
@@ -49,7 +49,7 @@ abstract class WidgetMap {
* @return component connector class to load after the initial widgetset
* loading
*/
- public abstract Class<? extends ComponentConnector>[] getDeferredLoadedWidgets();
+ public abstract Class<? extends ServerConnector>[] getDeferredLoadedConnectors();
/**
* Make sure the code for a (deferred or lazy) component connector type has
@@ -60,6 +60,6 @@ abstract class WidgetMap {
* component connector class
*/
public abstract void ensureInstantiator(
- Class<? extends ComponentConnector> classType);
+ Class<? extends ServerConnector> classType);
}
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetSet.java b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
index e47837296d..3d7e838c62 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetSet.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
@@ -5,7 +5,7 @@
package com.vaadin.terminal.gwt.client;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper;
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
public class WidgetSet {
@@ -18,29 +18,29 @@ public class WidgetSet {
private WidgetMap widgetMap = GWT.create(WidgetMap.class);
/**
- * Create an uninitialized component that best matches given UIDL. The
- * component must be a {@link Widget} that implements
- * {@link ComponentConnector}.
+ * Create an uninitialized connector that best matches given UIDL. The
+ * connector must implement {@link ServerConnector}.
*
* @param tag
- * component type tag for the component to create
- * @param client
- * the application connection that whishes to instantiate widget
+ * connector type tag for the connector to create
+ * @param conf
+ * the application configuration to use when creating the
+ * connector
*
- * @return New uninitialized and unregistered component that can paint given
+ * @return New uninitialized and unregistered connector that can paint given
* UIDL.
*/
- public ComponentConnector createWidget(int tag,
+ public ServerConnector createConnector(int tag,
ApplicationConfiguration conf) {
/*
* Yes, this (including the generated code in WidgetMap) may look very
* odd code, but due the nature of GWT, we cannot do this any cleaner.
* Luckily this is mostly written by WidgetSetGenerator, here are just
- * some hacks. Extra instantiation code is needed if client side widget
- * has no "native" counterpart on client side.
+ * some hacks. Extra instantiation code is needed if client side
+ * connector has no "native" counterpart on client side.
*/
- Class<? extends ComponentConnector> classType = resolveInheritedWidgetType(
+ Class<? extends ServerConnector> classType = resolveInheritedConnectorType(
conf, tag);
if (classType == null || classType == UnknownComponentConnector.class) {
@@ -53,27 +53,32 @@ 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;
}
}
- private Class<? extends ComponentConnector> resolveInheritedWidgetType(
+ private Class<? extends ServerConnector> resolveInheritedConnectorType(
ApplicationConfiguration conf, int tag) {
- Class<? extends ComponentConnector> classType = null;
+ Class<? extends ServerConnector> classType = null;
Integer t = tag;
do {
- classType = resolveWidgetType(t, conf);
+ classType = resolveConnectorType(t, conf);
t = conf.getParentTag(t);
} while (classType == null && t != null);
return classType;
}
- protected Class<? extends ComponentConnector> resolveWidgetType(int tag,
+ protected Class<? extends ServerConnector> resolveConnectorType(int tag,
ApplicationConfiguration conf) {
- Class<? extends ComponentConnector> widgetClass = conf
- .getWidgetClassByEncodedTag(tag);
+ Class<? extends ServerConnector> connectorClass = conf
+ .getConnectorClassByEncodedTag(tag);
- return widgetClass;
+ return connectorClass;
}
/**
@@ -85,9 +90,9 @@ public class WidgetSet {
* @param applicationConfiguration
* @return
*/
- public Class<? extends ComponentConnector> getConnectorClassByTag(int tag,
+ public Class<? extends ServerConnector> getConnectorClassByTag(int tag,
ApplicationConfiguration conf) {
- Class<? extends ComponentConnector> connectorClass = null;
+ Class<? extends ServerConnector> connectorClass = null;
Integer t = tag;
do {
String serverSideClassName = conf.getServerSideClassNameForTag(t);
@@ -99,11 +104,11 @@ public class WidgetSet {
return connectorClass;
}
- public Class<? extends ComponentConnector>[] getDeferredLoadedWidgets() {
- return widgetMap.getDeferredLoadedWidgets();
+ public Class<? extends ServerConnector>[] getDeferredLoadedConnectors() {
+ return widgetMap.getDeferredLoadedConnectors();
}
- public void loadImplementation(Class<? extends ComponentConnector> nextType) {
+ public void loadImplementation(Class<? extends ServerConnector> nextType) {
widgetMap.ensureInstantiator(nextType);
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/DiffJSONSerializer.java b/src/com/vaadin/terminal/gwt/client/communication/DiffJSONSerializer.java
new file mode 100644
index 0000000000..29cb714828
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/DiffJSONSerializer.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.google.gwt.json.client.JSONValue;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+
+public interface DiffJSONSerializer<T> extends JSONSerializer<T> {
+ /**
+ * Update the target object in place based on the passed JSON data.
+ *
+ * @param target
+ * @param jsonValue
+ * @param connection
+ */
+ public void update(T target, Type type, JSONValue jsonValue,
+ ApplicationConnection connection);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/GeneratedRpcMethodProvider.java b/src/com/vaadin/terminal/gwt/client/communication/GeneratedRpcMethodProvider.java
new file mode 100644
index 0000000000..c92466084c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/GeneratedRpcMethodProvider.java
@@ -0,0 +1,20 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.util.Collection;
+
+/**
+ * Provides runtime data about client side RPC calls received from the server to
+ * the client-side code.
+ *
+ * A GWT generator is used to create an implementation of this class at
+ * run-time.
+ *
+ * @since 7.0
+ */
+public interface GeneratedRpcMethodProvider {
+
+ public Collection<RpcMethod> getGeneratedRpcMethods();
+}
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..a5191a5fed
--- /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/JSONSerializer.java b/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java
index f7b3df6b05..9820b6a895 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java
@@ -34,14 +34,9 @@ public interface JSONSerializer<T> {
*
* @param jsonValue
* JSON map from property name to property value
- * @param target
- * The object to write the deserialized values to
- * @param idMapper
- * mapper from paintable id to paintable, used to decode
- * references to paintables
* @return A deserialized object
*/
- T deserialize(JSONValue jsonValue, T target, ConnectorMap idMapper,
+ T deserialize(Type type, JSONValue jsonValue,
ApplicationConnection connection);
/**
@@ -52,12 +47,8 @@ public interface JSONSerializer<T> {
*
* @param value
* The object to serialize
- * @param idMapper
- * mapper from paintable id to paintable, used to decode
- * references to paintables
* @return A JSON serialized version of the object
*/
- JSONValue serialize(T value, ConnectorMap idMapper,
- ApplicationConnection connection);
+ JSONValue serialize(T value, ApplicationConnection connection);
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
index 9ed20b6c79..23a2c30cd0 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
@@ -5,21 +5,20 @@
package com.vaadin.terminal.gwt.client.communication;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.ConnectorMap;
-import com.vaadin.terminal.gwt.client.ServerConnector;
/**
* Client side decoder for decodeing shared state and other values from JSON
@@ -38,128 +37,177 @@ public class JsonDecoder {
* Decode a JSON array with two elements (type and value) into a client-side
* type, recursively if necessary.
*
- * @param jsonArray
- * JSON array with two elements
- * @param idMapper
- * mapper between connector ID and {@link ServerConnector}
- * objects
+ * @param jsonValue
+ * JSON value with encoded data
* @param connection
* reference to the current ApplicationConnection
* @return decoded value (does not contain JSON types)
*/
- public static Object decodeValue(JSONArray jsonArray, Object target,
- ConnectorMap idMapper, ApplicationConnection connection) {
- String type = ((JSONString) jsonArray.get(0)).stringValue();
- return decodeValue(type, jsonArray.get(1), target, idMapper, connection);
- }
+ public static Object decodeValue(Type type, JSONValue jsonValue,
+ Object target, ApplicationConnection connection) {
- private static Object decodeValue(String variableType, JSONValue value,
- Object target, ConnectorMap idMapper,
- ApplicationConnection connection) {
- Object val = null;
- // TODO type checks etc.
- if (JsonEncoder.VTYPE_NULL.equals(variableType)) {
- val = null;
- } else if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) {
- val = decodeArray((JSONArray) value, idMapper, connection);
- } else if (JsonEncoder.VTYPE_MAP.equals(variableType)) {
- val = decodeMap((JSONObject) value, idMapper, connection);
- } else if (JsonEncoder.VTYPE_LIST.equals(variableType)) {
- val = decodeList((JSONArray) value, idMapper, connection);
- } else if (JsonEncoder.VTYPE_SET.equals(variableType)) {
- val = decodeSet((JSONArray) value, idMapper, connection);
- } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) {
- val = decodeStringArray((JSONArray) value);
- } else if (JsonEncoder.VTYPE_STRING.equals(variableType)) {
- val = ((JSONString) value).stringValue();
- } else if (JsonEncoder.VTYPE_INTEGER.equals(variableType)) {
+ // Null is null, regardless of type
+ if (jsonValue.isNull() != null) {
+ return null;
+ }
+
+ String baseTypeName = type.getBaseTypeName();
+ if (Map.class.getName().equals(baseTypeName)
+ || HashMap.class.getName().equals(baseTypeName)) {
+ return decodeMap(type, jsonValue, connection);
+ } else if (List.class.getName().equals(baseTypeName)
+ || ArrayList.class.getName().equals(baseTypeName)) {
+ return decodeList(type, (JSONArray) jsonValue, connection);
+ } else if (Set.class.getName().equals(baseTypeName)) {
+ return decodeSet(type, (JSONArray) jsonValue, connection);
+ } else if (String.class.getName().equals(baseTypeName)) {
+ return ((JSONString) jsonValue).stringValue();
+ } else if (Integer.class.getName().equals(baseTypeName)) {
+ return Integer.valueOf(String.valueOf(jsonValue));
+ } else if (Long.class.getName().equals(baseTypeName)) {
+ // TODO handle properly
+ return Long.valueOf(String.valueOf(jsonValue));
+ } else if (Float.class.getName().equals(baseTypeName)) {
// TODO handle properly
- val = Integer.valueOf(String.valueOf(value));
- } else if (JsonEncoder.VTYPE_LONG.equals(variableType)) {
+ return Float.valueOf(String.valueOf(jsonValue));
+ } else if (Double.class.getName().equals(baseTypeName)) {
// TODO handle properly
- val = Long.valueOf(String.valueOf(value));
- } else if (JsonEncoder.VTYPE_FLOAT.equals(variableType)) {
+ return Double.valueOf(String.valueOf(jsonValue));
+ } else if (Boolean.class.getName().equals(baseTypeName)) {
// TODO handle properly
- val = Float.valueOf(String.valueOf(value));
- } else if (JsonEncoder.VTYPE_DOUBLE.equals(variableType)) {
+ return Boolean.valueOf(String.valueOf(jsonValue));
+ } else if (Byte.class.getName().equals(baseTypeName)) {
// TODO handle properly
- val = Double.valueOf(String.valueOf(value));
- } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) {
+ return Byte.valueOf(String.valueOf(jsonValue));
+ } else if (Character.class.getName().equals(baseTypeName)) {
// TODO handle properly
- val = Boolean.valueOf(String.valueOf(value));
- } else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) {
- val = idMapper.getConnector(((JSONString) value).stringValue());
+ return Character.valueOf(((JSONString) jsonValue).stringValue()
+ .charAt(0));
+ } else if (Connector.class.getName().equals(baseTypeName)) {
+ return ConnectorMap.get(connection).getConnector(
+ ((JSONString) jsonValue).stringValue());
} else {
- return decodeObject(variableType, value, target, idMapper,
- connection);
+ return decodeObject(type, jsonValue, target, connection);
}
-
- return val;
}
- private static Object decodeObject(String variableType,
- JSONValue encodedValue, Object target, ConnectorMap idMapper,
- ApplicationConnection connection) {
- // object, class name as type
+ private static Object decodeObject(Type type, JSONValue jsonValue,
+ Object target, ApplicationConnection connection) {
JSONSerializer<Object> serializer = connection.getSerializerMap()
- .getSerializer(variableType);
+ .getSerializer(type.getBaseTypeName());
// TODO handle case with no serializer found
- Object object = serializer.deserialize(encodedValue, target, idMapper,
- connection);
- return object;
+ // Currently getSerializer throws exception if not found
+
+ if (target != null && serializer instanceof DiffJSONSerializer<?>) {
+ DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer;
+ diffSerializer.update(target, type, jsonValue, connection);
+ return target;
+ } else {
+ Object object = serializer.deserialize(type, jsonValue, connection);
+ return object;
+ }
}
- private static Map<Object, Object> decodeMap(JSONObject jsonMap,
- ConnectorMap idMapper, ApplicationConnection connection) {
- HashMap<Object, Object> map = new HashMap<Object, Object>();
- Iterator<String> it = jsonMap.keySet().iterator();
- while (it.hasNext()) {
- String key = it.next();
- JSONArray encodedKey = (JSONArray) JSONParser.parseStrict(key);
- JSONArray encodedValue = (JSONArray) jsonMap.get(key);
- Object decodedKey = decodeValue(encodedKey, null, idMapper,
+ private static Map<Object, Object> decodeMap(Type type, JSONValue jsonMap,
+ ApplicationConnection connection) {
+ // Client -> server encodes empty map as an empty array because of
+ // #8906. Do the same for server -> client to maintain symmetry.
+ if (jsonMap instanceof JSONArray) {
+ JSONArray array = (JSONArray) jsonMap;
+ if (array.size() == 0) {
+ return new HashMap<Object, Object>();
+ }
+ }
+
+ Type keyType = type.getParameterTypes()[0];
+ Type valueType = type.getParameterTypes()[1];
+
+ if (keyType.getBaseTypeName().equals(String.class.getName())) {
+ return decodeStringMap(valueType, jsonMap, connection);
+ } else if (keyType.getBaseTypeName().equals(Connector.class.getName())) {
+ return decodeConnectorMap(valueType, jsonMap, connection);
+ } else {
+ return decodeObjectMap(keyType, valueType, jsonMap, connection);
+ }
+ }
+
+ private static Map<Object, Object> decodeObjectMap(Type keyType,
+ Type valueType, JSONValue jsonValue,
+ ApplicationConnection connection) {
+ Map<Object, Object> map = new HashMap<Object, Object>();
+
+ JSONArray mapArray = (JSONArray) jsonValue;
+ JSONArray keys = (JSONArray) mapArray.get(0);
+ JSONArray values = (JSONArray) mapArray.get(1);
+
+ assert (keys.size() == values.size());
+
+ for (int i = 0; i < keys.size(); i++) {
+ Object decodedKey = decodeValue(keyType, keys.get(i), null,
connection);
- Object decodedValue = decodeValue(encodedValue, null, idMapper,
+ Object decodedValue = decodeValue(valueType, values.get(i), null,
connection);
+
map.put(decodedKey, decodedValue);
}
+
return map;
}
- private static String[] decodeStringArray(JSONArray jsonArray) {
- int size = jsonArray.size();
- List<String> tokens = new ArrayList<String>(size);
- for (int i = 0; i < size; ++i) {
- tokens.add(String.valueOf(jsonArray.get(i)));
+ private static Map<Object, Object> decodeConnectorMap(Type valueType,
+ JSONValue jsonValue, ApplicationConnection connection) {
+ Map<Object, Object> map = new HashMap<Object, Object>();
+
+ JSONObject jsonMap = (JSONObject) jsonValue;
+ ConnectorMap connectorMap = ConnectorMap.get(connection);
+
+ for (String connectorId : jsonMap.keySet()) {
+ Object value = decodeValue(valueType, jsonMap.get(connectorId),
+ null, connection);
+ map.put(connectorMap.getConnector(connectorId), value);
}
- return tokens.toArray(new String[tokens.size()]);
+
+ return map;
}
- private static Object[] decodeArray(JSONArray jsonArray,
- ConnectorMap idMapper, ApplicationConnection connection) {
- List<Object> list = decodeList(jsonArray, idMapper, connection);
- return list.toArray(new Object[list.size()]);
+ private static Map<Object, Object> decodeStringMap(Type valueType,
+ JSONValue jsonValue, ApplicationConnection connection) {
+ Map<Object, Object> map = new HashMap<Object, Object>();
+
+ JSONObject jsonMap = (JSONObject) jsonValue;
+
+ for (String key : jsonMap.keySet()) {
+ Object value = decodeValue(valueType, jsonMap.get(key), null,
+ connection);
+ map.put(key, value);
+ }
+
+ return map;
}
- private static List<Object> decodeList(JSONArray jsonArray,
- ConnectorMap idMapper, ApplicationConnection connection) {
+ private static List<Object> decodeList(Type type, JSONArray jsonArray,
+ ApplicationConnection connection) {
List<Object> tokens = new ArrayList<Object>();
- for (int i = 0; i < jsonArray.size(); ++i) {
- // each entry always has two elements: type and value
- JSONArray entryArray = (JSONArray) jsonArray.get(i);
- tokens.add(decodeValue(entryArray, null, idMapper, connection));
- }
+ decodeIntoCollection(type.getParameterTypes()[0], jsonArray,
+ connection, tokens);
return tokens;
}
- private static Set<Object> decodeSet(JSONArray jsonArray,
- ConnectorMap idMapper, ApplicationConnection connection) {
+ private static Set<Object> decodeSet(Type type, JSONArray jsonArray,
+ ApplicationConnection connection) {
Set<Object> tokens = new HashSet<Object>();
+ decodeIntoCollection(type.getParameterTypes()[0], jsonArray,
+ connection, tokens);
+ return tokens;
+ }
+
+ private static void decodeIntoCollection(Type childType,
+ JSONArray jsonArray, ApplicationConnection connection,
+ Collection<Object> tokens) {
for (int i = 0; i < jsonArray.size(); ++i) {
// each entry always has two elements: type and value
- JSONArray entryArray = (JSONArray) jsonArray.get(i);
- tokens.add(decodeValue(entryArray, null, idMapper, connection));
+ JSONValue entryValue = jsonArray.get(i);
+ tokens.add(decodeValue(childType, entryValue, null, connection));
}
- return tokens;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
index f09536a9f7..925f0b6272 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
@@ -7,17 +7,18 @@ package com.vaadin.terminal.gwt.client.communication;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNull;
+import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Connector;
-import com.vaadin.terminal.gwt.client.ConnectorMap;
/**
* Encoder for converting RPC parameters and other values to JSON for transfer
@@ -52,57 +53,49 @@ public class JsonEncoder {
*
* @param value
* value to convert
- * @param connectorMap
- * mapper from connectors to connector IDs
* @param connection
* @return JSON representation of the value
*/
public static JSONValue encode(Object value,
- boolean restrictToInternalTypes, ConnectorMap connectorMap,
- ApplicationConnection connection) {
+ boolean restrictToInternalTypes, ApplicationConnection connection) {
if (null == value) {
- return combineTypeAndValue(VTYPE_NULL, JSONNull.getInstance());
+ return JSONNull.getInstance();
+ } else if (value instanceof JSONValue) {
+ return (JSONValue) value;
} else if (value instanceof String[]) {
String[] array = (String[]) value;
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < array.length; ++i) {
jsonArray.set(i, new JSONString(array[i]));
}
- return combineTypeAndValue(VTYPE_STRINGARRAY, jsonArray);
+ return jsonArray;
} else if (value instanceof String) {
- return combineTypeAndValue(VTYPE_STRING, new JSONString(
- (String) value));
+ return new JSONString((String) value);
} else if (value instanceof Boolean) {
- return combineTypeAndValue(VTYPE_BOOLEAN,
- JSONBoolean.getInstance((Boolean) value));
+ return JSONBoolean.getInstance((Boolean) value);
+ } else if (value instanceof Byte) {
+ return new JSONNumber((Byte) value);
+ } else if (value instanceof Character) {
+ return new JSONString(String.valueOf(value));
} else if (value instanceof Object[]) {
return encodeObjectArray((Object[]) value, restrictToInternalTypes,
- connectorMap, connection);
+ connection);
} else if (value instanceof Enum) {
- if (restrictToInternalTypes) {
- // Enums are encoded as strings in Vaadin 6 so we still do that
- // for backwards copmatibility.
- return encode(value.toString(), restrictToInternalTypes,
- connectorMap, connection);
- } else {
- Enum e = (Enum) value;
- return encodeEnum(e, connectorMap, connection);
- }
+ return encodeEnum((Enum<?>) value, connection);
} else if (value instanceof Map) {
- return encodeMap((Map) value, restrictToInternalTypes,
- connectorMap, connection);
+ return encodeMap((Map) value, restrictToInternalTypes, connection);
} else if (value instanceof Connector) {
Connector connector = (Connector) value;
- return combineTypeAndValue(VTYPE_CONNECTOR, new JSONString(
- connector.getConnectorId()));
+ return new JSONString(connector.getConnectorId());
} else if (value instanceof Collection) {
return encodeCollection((Collection) value,
- restrictToInternalTypes, connectorMap, connection);
+ restrictToInternalTypes, connection);
+ } else if (value instanceof UidlValue) {
+ return encodeVariableChange((UidlValue) value, connection);
} else {
String transportType = getTransportType(value);
if (transportType != null) {
- return combineTypeAndValue(transportType,
- new JSONString(String.valueOf(value)));
+ return new JSONString(String.valueOf(value));
} else {
// Try to find a generated serializer object, class name is the
// type
@@ -111,61 +104,135 @@ public class JsonEncoder {
.getSerializer(transportType);
// TODO handle case with no serializer found
- return combineTypeAndValue(transportType,
- serializer.serialize(value, connectorMap, connection));
+ return serializer.serialize(value, connection);
}
}
}
+ private static JSONValue encodeVariableChange(UidlValue uidlValue,
+ ApplicationConnection connection) {
+ Object value = uidlValue.getValue();
+
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.set(0, new JSONString(getTransportType(value)));
+ jsonArray.set(1, encode(value, true, connection));
+
+ return jsonArray;
+ }
+
private static JSONValue encodeMap(Map<Object, Object> map,
- boolean restrictToInternalTypes, ConnectorMap connectorMap,
+ boolean restrictToInternalTypes, ApplicationConnection connection) {
+ /*
+ * As we have no info about declared types, we instead select encoding
+ * scheme based on actual type of first key. We can't do this if there's
+ * no first key, so instead we send some special value that the
+ * server-side decoding must check for. (see #8906)
+ */
+ if (map.isEmpty()) {
+ return new JSONArray();
+ }
+
+ Object firstKey = map.keySet().iterator().next();
+ if (firstKey instanceof String) {
+ return encodeStringMap(map, restrictToInternalTypes, connection);
+ } else if (restrictToInternalTypes) {
+ throw new IllegalStateException(
+ "Only string keys supported for legacy maps");
+ } else if (firstKey instanceof Connector) {
+ return encodeConenctorMap(map, connection);
+ } else {
+ return encodeObjectMap(map, connection);
+ }
+ }
+
+ private static JSONValue encodeObjectMap(Map<Object, Object> map,
+ ApplicationConnection connection) {
+ JSONArray keys = new JSONArray();
+ JSONArray values = new JSONArray();
+ for (Entry<?, ?> entry : map.entrySet()) {
+ // restrictToInternalTypes always false if we end up here
+ keys.set(keys.size(), encode(entry.getKey(), false, connection));
+ values.set(values.size(),
+ encode(entry.getValue(), false, connection));
+ }
+
+ JSONArray keysAndValues = new JSONArray();
+ keysAndValues.set(0, keys);
+ keysAndValues.set(1, values);
+
+ return keysAndValues;
+ }
+
+ private static JSONValue encodeConenctorMap(Map<Object, Object> map,
ApplicationConnection connection) {
JSONObject jsonMap = new JSONObject();
- for (Object mapKey : map.keySet()) {
- Object mapValue = map.get(mapKey);
- JSONValue encodedKey = encode(mapKey, restrictToInternalTypes,
- connectorMap, connection);
- JSONValue encodedValue = encode(mapValue, restrictToInternalTypes,
- connectorMap, connection);
- jsonMap.put(encodedKey.toString(), encodedValue);
+
+ for (Entry<?, ?> entry : map.entrySet()) {
+ Connector connector = (Connector) entry.getKey();
+
+ // restrictToInternalTypes always false if we end up here
+ JSONValue encodedValue = encode(entry.getValue(), false, connection);
+
+ jsonMap.put(connector.getConnectorId(), encodedValue);
+ }
+
+ return jsonMap;
+ }
+
+ private static JSONValue encodeStringMap(Map<Object, Object> map,
+ boolean restrictToInternalTypes, ApplicationConnection connection) {
+ JSONObject jsonMap = new JSONObject();
+
+ for (Entry<?, ?> entry : map.entrySet()) {
+ String key = (String) entry.getKey();
+ Object value = entry.getValue();
+
+ if (restrictToInternalTypes) {
+ value = new UidlValue(value);
+ }
+
+ JSONValue encodedValue = encode(value, restrictToInternalTypes,
+ connection);
+
+ jsonMap.put(key, encodedValue);
}
- return combineTypeAndValue(VTYPE_MAP, jsonMap);
+
+ return jsonMap;
}
- private static JSONValue encodeEnum(Enum e, ConnectorMap connectorMap,
+ private static JSONValue encodeEnum(Enum<?> e,
ApplicationConnection connection) {
- return combineTypeAndValue(e.getClass().getName(),
- new JSONString(e.toString()));
+ return new JSONString(e.toString());
}
private static JSONValue encodeObjectArray(Object[] array,
- boolean restrictToInternalTypes, ConnectorMap connectorMap,
- ApplicationConnection connection) {
+ boolean restrictToInternalTypes, ApplicationConnection connection) {
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < array.length; ++i) {
// TODO handle object graph loops?
- jsonArray.set(
- i,
- encode(array[i], restrictToInternalTypes, connectorMap,
- connection));
+ Object value = array[i];
+ if (restrictToInternalTypes) {
+ value = new UidlValue(value);
+ }
+ jsonArray
+ .set(i, encode(value, restrictToInternalTypes, connection));
}
- return combineTypeAndValue(VTYPE_ARRAY, jsonArray);
+ return jsonArray;
}
private static JSONValue encodeCollection(Collection collection,
- boolean restrictToInternalTypes, ConnectorMap connectorMap,
- ApplicationConnection connection) {
+ boolean restrictToInternalTypes, ApplicationConnection connection) {
JSONArray jsonArray = new JSONArray();
int idx = 0;
for (Object o : collection) {
JSONValue encodedObject = encode(o, restrictToInternalTypes,
- connectorMap, connection);
+ connection);
jsonArray.set(idx++, encodedObject);
}
if (collection instanceof Set) {
- return combineTypeAndValue(VTYPE_SET, jsonArray);
+ return jsonArray;
} else if (collection instanceof List) {
- return combineTypeAndValue(VTYPE_LIST, jsonArray);
+ return jsonArray;
} else {
throw new RuntimeException("Unsupport collection type: "
+ collection.getClass().getName());
@@ -173,13 +240,6 @@ public class JsonEncoder {
}
- private static JSONValue combineTypeAndValue(String type, JSONValue value) {
- JSONArray outerArray = new JSONArray();
- outerArray.set(0, new JSONString(type));
- outerArray.set(1, value);
- return outerArray;
- }
-
/**
* Returns the transport type for the given value. Only returns a transport
* type for internally handled values.
@@ -216,6 +276,9 @@ public class JsonEncoder {
return VTYPE_ARRAY;
} else if (value instanceof Map) {
return VTYPE_MAP;
+ } else if (value instanceof Enum<?>) {
+ // Enum value is processed as a string
+ return VTYPE_STRING;
}
return null;
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
index 302e6eaa55..07d6292ce2 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
@@ -4,9 +4,17 @@
package com.vaadin.terminal.gwt.client.communication;
-import java.io.Serializable;
+import java.util.Collection;
+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
@@ -17,16 +25,98 @@ import com.vaadin.terminal.gwt.client.ConnectorMap;
*
* @since 7.0
*/
-public interface RpcManager extends Serializable {
+public class RpcManager {
+
+ private final Map<String, RpcMethod> methodMap = new HashMap<String, RpcMethod>();
+
+ public RpcManager() {
+ GeneratedRpcMethodProvider provider = GWT
+ .create(GeneratedRpcMethodProvider.class);
+ Collection<RpcMethod> methods = provider.getGeneratedRpcMethods();
+ for (RpcMethod rpcMethod : methods) {
+ methodMap.put(
+ rpcMethod.getInterfaceName() + "."
+ + rpcMethod.getMethodName(), rpcMethod);
+ }
+ }
+
/**
* Perform server to client RPC invocation.
*
* @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) {
+ String signature = getSignature(invocation);
+
+ RpcMethod rpcMethod = getRpcMethod(signature);
+ Collection<ClientRpc> implementations = connector
+ .getRpcImplementations(invocation.getInterfaceName());
+ for (ClientRpc clientRpc : implementations) {
+ rpcMethod.applyInvocation(clientRpc, invocation.getParameters());
+ }
+ }
+
+ private RpcMethod getRpcMethod(String signature) {
+ RpcMethod rpcMethod = methodMap.get(signature);
+ if (rpcMethod == null) {
+ throw new IllegalStateException("There is no information about "
+ + signature
+ + ". Did you remember to compile the right widgetset?");
+ }
+ return rpcMethod;
+ }
+
+ private static String getSignature(MethodInvocation invocation) {
+ return invocation.getInterfaceName() + "." + invocation.getMethodName();
+ }
+
+ public Type[] getParameterTypes(MethodInvocation invocation) {
+ 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/communication/RpcMethod.java b/src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java
new file mode 100644
index 0000000000..abdcf73e2c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java
@@ -0,0 +1,32 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+public abstract class RpcMethod {
+ private String interfaceName;
+ private String methodName;
+ private Type[] parameterTypes;
+
+ public RpcMethod(String interfaceName, String methodName,
+ Type... parameterTypes) {
+ this.interfaceName = interfaceName;
+ this.methodName = methodName;
+ this.parameterTypes = parameterTypes;
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public Type[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public abstract void applyInvocation(ClientRpc target, Object... parameters);
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/SharedState.java b/src/com/vaadin/terminal/gwt/client/communication/SharedState.java
index 266d6bcbf2..b087907f9e 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/SharedState.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/SharedState.java
@@ -38,4 +38,30 @@ import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
* @since 7.0
*/
public class SharedState implements Serializable {
+
+ private boolean enabled = true;
+
+ /**
+ * Returns true if the component is enabled.
+ *
+ * @see com.vaadin.ui.Component#isEnabled()
+ *
+ * @return true if the component is enabled
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Enables or disables the component.
+ *
+ * @see com.vaadin.ui.Component#setEnabled(boolean)
+ *
+ * @param enabled
+ * new mode for the component
+ */
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/Type.java b/src/com/vaadin/terminal/gwt/client/communication/Type.java
new file mode 100644
index 0000000000..dc33f760ff
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/Type.java
@@ -0,0 +1,40 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+public class Type {
+ private final String baseTypeName;
+ private final Type[] parameterTypes;
+
+ public Type(String baseTypeName, Type[] parameterTypes) {
+ this.baseTypeName = baseTypeName;
+ this.parameterTypes = parameterTypes;
+ }
+
+ public String getBaseTypeName() {
+ return baseTypeName;
+ }
+
+ public Type[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ @Override
+ public String toString() {
+ String string = baseTypeName;
+ if (parameterTypes != null) {
+ string += '<';
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (i != 0) {
+ string += ',';
+ }
+ string += parameterTypes[i].toString();
+ }
+ string += '>';
+ }
+
+ return string;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java b/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java
index 2ee7df7f6d..bab0f385ed 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java
@@ -4,32 +4,34 @@
package com.vaadin.terminal.gwt.client.communication;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.ConnectorMap;
public class URLReference_Serializer implements JSONSerializer<URLReference> {
- public URLReference deserialize(JSONValue jsonValue, URLReference target,
- ConnectorMap idMapper, ApplicationConnection connection) {
+ // setURL() -> uRL as first char becomes lower case...
+ private static final String URL_FIELD = "uRL";
+
+ public URLReference deserialize(Type type, JSONValue jsonValue,
+ ApplicationConnection connection) {
URLReference reference = GWT.create(URLReference.class);
JSONObject json = (JSONObject) jsonValue;
- if (json.containsKey("URL")) {
- JSONArray jsonURL = (JSONArray) json.get("URL");
- String URL = (String) JsonDecoder.decodeValue(jsonURL, null,
- idMapper, connection);
+ if (json.containsKey(URL_FIELD)) {
+ JSONValue jsonURL = json.get(URL_FIELD);
+ String URL = (String) JsonDecoder.decodeValue(
+ new Type(String.class.getName(), null), jsonURL, null,
+ connection);
reference.setURL(connection.translateVaadinUri(URL));
}
return reference;
}
- public JSONValue serialize(URLReference value, ConnectorMap idMapper,
+ public JSONValue serialize(URLReference value,
ApplicationConnection connection) {
JSONObject json = new JSONObject();
- json.put("URL",
- JsonEncoder.encode(value.getURL(), true, idMapper, connection));
+ json.put(URL_FIELD,
+ JsonEncoder.encode(value.getURL(), true, connection));
return json;
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/UidlValue.java b/src/com/vaadin/terminal/gwt/client/communication/UidlValue.java
new file mode 100644
index 0000000000..2a21074037
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/UidlValue.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+public class UidlValue implements Serializable {
+ private Object value;
+
+ public UidlValue(Object value) {
+ this.value = value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "" + value;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/AbstractExtensionConnector.java b/src/com/vaadin/terminal/gwt/client/extensions/AbstractExtensionConnector.java
new file mode 100644
index 0000000000..bcefcf05cb
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/AbstractExtensionConnector.java
@@ -0,0 +1,27 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions;
+
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+
+public abstract class AbstractExtensionConnector extends AbstractConnector {
+ @Override
+ public void setParent(ServerConnector parent) {
+ ServerConnector oldParent = getParent();
+ if (oldParent != null && oldParent != parent) {
+ throw new IllegalStateException(
+ "An extension can not be moved from one parent to another.");
+ }
+
+ super.setParent(parent);
+
+ extend(parent);
+ }
+
+ protected void extend(ServerConnector target) {
+ // Default does nothing
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/ExecuteJavaScriptRpc.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/ExecuteJavaScriptRpc.java
new file mode 100644
index 0000000000..f1185586d5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/ExecuteJavaScriptRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+
+public interface ExecuteJavaScriptRpc extends ClientRpc {
+ public void executeJavaScript(String script);
+}
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..8656783a86
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerConnector.java
@@ -0,0 +1,119 @@
+/*
+@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.extensions.AbstractExtensionConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.JavaScript;
+
+@Connect(JavaScript.class)
+public class JavaScriptManagerConnector extends AbstractExtensionConnector {
+ private Set<String> currentNames = new HashSet<String>();
+
+ @Override
+ protected void init() {
+ registerRpc(ExecuteJavaScriptRpc.class, new ExecuteJavaScriptRpc() {
+ public void executeJavaScript(String Script) {
+ eval(Script);
+ }
+ });
+ }
+
+ @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;
+ var target = $wnd;
+ var parts = name.split('.');
+
+ for(var i = 0; i < parts.length - 1; i++) {
+ var part = parts[i];
+ if (target[part] === undefined) {
+ target[part] = {};
+ }
+ target = target[part];
+ }
+
+ target[parts[parts.length - 1]] = $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
+ // TODO We might leave empty objects behind, but there's no good way of
+ // knowing whether they are unused
+ private native void removeCallback(String name)
+ /*-{
+ var target = $wnd;
+ var parts = name.split('.');
+
+ for(var i = 0; i < parts.length - 1; i++) {
+ var part = parts[i];
+ if (target[part] === undefined) {
+ $wnd.console.log(part,'not defined in',target);
+ // No longer attached -> nothing more to do
+ return;
+ }
+ target = target[part];
+ }
+
+ $wnd.console.log('removing',parts[parts.length - 1],'from',target);
+ delete target[parts[parts.length - 1]];
+ }-*/;
+
+ private static native void eval(String script)
+ /*-{
+ if(script) {
+ (new $wnd.Function(script)).apply($wnd);
+ }
+ }-*/;
+
+ 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.JavaScript$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..fc246aff04
--- /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/AbstractBoxLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java
index 854b02018f..413c605a88 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java
@@ -7,10 +7,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.AbstractFieldState;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
@@ -58,11 +56,6 @@ public abstract class AbstractBoxLayoutConnector extends
}
@Override
- protected Widget createWidget() {
- return GWT.create(VBoxLayout.class);
- }
-
- @Override
public VBoxLayout getWidget() {
return (VBoxLayout) super.getWidget();
}
@@ -119,7 +112,6 @@ public abstract class AbstractBoxLayoutConnector extends
required = ((AbstractFieldConnector) child).isRequired();
}
boolean enabled = child.getState().isEnabled();
- // TODO Description is handled from somewhere else?
slot.setCaption(caption, iconUrl, styles, error, showError, required,
enabled);
@@ -157,7 +149,7 @@ public abstract class AbstractBoxLayoutConnector extends
int currentIndex = 0;
VBoxLayout layout = getWidget();
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
Slot slot = layout.getSlot(child);
if (slot.getParent() != layout) {
child.addStateChangeHandler(childStateChangeHandler);
@@ -218,7 +210,7 @@ public abstract class AbstractBoxLayoutConnector extends
boolean equalExpandRatio = getWidget().vertical ? !isUndefinedHeight()
: !isUndefinedWidth();
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
double expandRatio = getState().getChildData().get(child)
.getExpandRatio();
if (expandRatio > 0) {
@@ -227,7 +219,7 @@ public abstract class AbstractBoxLayoutConnector extends
}
}
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
Slot slot = getWidget().getSlot(child);
AlignmentInfo alignment = new AlignmentInfo(getState()
@@ -313,7 +305,7 @@ public abstract class AbstractBoxLayoutConnector extends
}
private void updateAllSlotListeners() {
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
updateSlotListeners(child);
}
// if (needsFixedHeight()) {
@@ -553,7 +545,7 @@ public abstract class AbstractBoxLayoutConnector extends
// dontListen(getWidget().getElement(), layoutResizeListener);
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
Slot slot = getWidget().getSlot(child);
if (slot.hasCaption()) {
dontListen(slot.getCaptionElement(), slotCaptionResizeListener);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java
index 31204aa0c6..e0ca798682 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java
@@ -3,29 +3,76 @@
*/
package com.vaadin.terminal.gwt.client.ui;
+import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
+import com.google.gwt.event.dom.client.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.Util;
-public abstract class AbstractClickEventHandler implements DoubleClickHandler,
- ContextMenuHandler, MouseUpHandler {
+public abstract class AbstractClickEventHandler implements MouseDownHandler,
+ MouseUpHandler, DoubleClickHandler, ContextMenuHandler {
- private HandlerRegistration doubleClickHandlerRegistration;
+ private HandlerRegistration mouseDownHandlerRegistration;
private HandlerRegistration mouseUpHandlerRegistration;
+ private HandlerRegistration doubleClickHandlerRegistration;
private HandlerRegistration contextMenuHandlerRegistration;
protected ComponentConnector connector;
private String clickEventIdentifier;
+ /**
+ * The element where the last mouse down event was registered.
+ */
+ private JavaScriptObject lastMouseDownTarget;
+
+ /**
+ * Set to true by {@link #mouseUpPreviewHandler} if it gets a mouseup at the
+ * same element as {@link #lastMouseDownTarget}.
+ */
+ private boolean mouseUpPreviewMatched = false;
+
+ private HandlerRegistration mouseUpEventPreviewRegistration;
+
+ /**
+ * Previews events after a mousedown to detect where the following mouseup
+ * hits.
+ */
+ private final NativePreviewHandler mouseUpPreviewHandler = new NativePreviewHandler() {
+
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ if (event.getTypeInt() == Event.ONMOUSEUP) {
+ mouseUpEventPreviewRegistration.removeHandler();
+
+ // Event's reported target not always correct if event
+ // capture is in use
+ Element elementUnderMouse = Util.getElementUnderMouse(event
+ .getNativeEvent());
+ if (lastMouseDownTarget != null
+ && elementUnderMouse.cast() == lastMouseDownTarget) {
+ mouseUpPreviewMatched = true;
+ } else {
+ System.out.println("Ignoring mouseup from "
+ + elementUnderMouse + " when mousedown was on "
+ + lastMouseDownTarget);
+ }
+ }
+ }
+ };
+
public AbstractClickEventHandler(ComponentConnector connector,
String clickEventIdentifier) {
this.connector = connector;
@@ -36,25 +83,28 @@ public abstract class AbstractClickEventHandler implements DoubleClickHandler,
// Handle registering/unregistering of click handler depending on if
// server side listeners have been added or removed.
if (hasEventListener()) {
- if (mouseUpHandlerRegistration == null) {
+ if (mouseDownHandlerRegistration == null) {
+ mouseDownHandlerRegistration = registerHandler(this,
+ MouseDownEvent.getType());
mouseUpHandlerRegistration = registerHandler(this,
MouseUpEvent.getType());
- contextMenuHandlerRegistration = registerHandler(this,
- ContextMenuEvent.getType());
doubleClickHandlerRegistration = registerHandler(this,
DoubleClickEvent.getType());
+ contextMenuHandlerRegistration = registerHandler(this,
+ ContextMenuEvent.getType());
}
} else {
- if (mouseUpHandlerRegistration != null) {
+ if (mouseDownHandlerRegistration != null) {
// Remove existing handlers
- doubleClickHandlerRegistration.removeHandler();
+ mouseDownHandlerRegistration.removeHandler();
mouseUpHandlerRegistration.removeHandler();
+ doubleClickHandlerRegistration.removeHandler();
contextMenuHandlerRegistration.removeHandler();
- contextMenuHandlerRegistration = null;
+ mouseDownHandlerRegistration = null;
mouseUpHandlerRegistration = null;
doubleClickHandlerRegistration = null;
-
+ contextMenuHandlerRegistration = null;
}
}
@@ -93,6 +143,7 @@ public abstract class AbstractClickEventHandler implements DoubleClickHandler,
* Event handler for context menu. Prevents the browser context menu from
* popping up if there is a listener for right clicks.
*/
+
public void onContextMenu(ContextMenuEvent event) {
if (hasEventListener() && shouldFireEvent(event)) {
// Prevent showing the browser's context menu when there is a right
@@ -101,18 +152,34 @@ public abstract class AbstractClickEventHandler implements DoubleClickHandler,
}
}
- /**
- * Event handler for mouse up. This is used to detect all single click
- * events.
- */
+ public void onMouseDown(MouseDownEvent event) {
+ /*
+ * When getting a mousedown event, we must detect where the
+ * corresponding mouseup event if it's on a different part of the page.
+ */
+ lastMouseDownTarget = event.getNativeEvent().getEventTarget();
+ mouseUpPreviewMatched = false;
+ mouseUpEventPreviewRegistration = Event
+ .addNativePreviewHandler(mouseUpPreviewHandler);
+ }
+
public void onMouseUp(MouseUpEvent event) {
- // TODO For perfect accuracy we should check that a mousedown has
- // occured on this element before this mouseup and that no mouseup
- // has occured anywhere after that.
- if (hasEventListener() && shouldFireEvent(event)) {
+ /*
+ * Only fire a click if the mouseup hits the same element as the
+ * corresponding mousedown. This is first checked in the event preview
+ * but we can't fire the even there as the event might get canceled
+ * before it gets here.
+ */
+ if (hasEventListener()
+ && mouseUpPreviewMatched
+ && lastMouseDownTarget != null
+ && Util.getElementUnderMouse(event.getNativeEvent()) == lastMouseDownTarget
+ && shouldFireEvent(event)) {
// "Click" with left, right or middle button
fireClick(event.getNativeEvent());
}
+ mouseUpPreviewMatched = false;
+ lastMouseDownTarget = null;
}
/**
@@ -140,6 +207,7 @@ public abstract class AbstractClickEventHandler implements DoubleClickHandler,
* that browsers typically fail to prevent the second click event so a
* double click will result in two click events and one double click event.
*/
+
public void onDoubleClick(DoubleClickEvent event) {
if (hasEventListener() && shouldFireEvent(event)) {
fireClick(event.getNativeEvent());
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java
index 1caec0428e..a621c488be 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java
@@ -14,8 +14,10 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.TooltipInfo;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
@@ -26,8 +28,6 @@ import com.vaadin.terminal.gwt.client.ui.root.RootConnector;
public abstract class AbstractComponentConnector extends AbstractConnector
implements ComponentConnector {
- private ComponentContainerConnector parent;
-
private Widget widget;
private String lastKnownWidth = "";
@@ -77,8 +77,6 @@ public abstract class AbstractComponentConnector extends AbstractConnector
@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
- super.onStateChanged(stateChangeEvent);
-
ConnectorMap paintableMap = ConnectorMap.get(getConnection());
if (getState().getDebugId() != null) {
@@ -90,7 +88,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector
/*
* Disabled state may affect (override) tabindex so the order must be
- * first setting tabindex, then enabled state.
+ * first setting tabindex, then enabled state (through super
+ * implementation).
*/
if (getState() instanceof TabIndexState
&& getWidget() instanceof Focusable) {
@@ -98,7 +97,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector
.getTabIndex());
}
- setWidgetEnabled(isEnabled());
+ super.onStateChanged(stateChangeEvent);
// Style names
// String styleName = getStyleNames(getWidget().getStylePrimaryName());
@@ -117,10 +116,10 @@ public abstract class AbstractComponentConnector extends AbstractConnector
// Set captions
if (delegateCaptionHandling()) {
- ComponentContainerConnector parent = getParent();
- if (parent != null) {
- parent.updateCaption(this);
- } else if (!(this instanceof RootConnector)) {
+ ServerConnector parent = getParent();
+ if (parent instanceof ComponentContainerConnector) {
+ ((ComponentContainerConnector) parent).updateCaption(this);
+ } else if (parent == null && !(this instanceof RootConnector)) {
VConsole.error("Parent of connector "
+ Util.getConnectorString(this)
+ " is null. This is typically an indication of a broken component hierarchy");
@@ -137,7 +136,22 @@ public abstract class AbstractComponentConnector extends AbstractConnector
public void setWidgetEnabled(boolean widgetEnabled) {
if (getWidget() instanceof HasEnabled) {
+ // set widget specific enabled state
((HasEnabled) getWidget()).setEnabled(widgetEnabled);
+ // add or remove v-disabled style name from the widget
+ getWidget().setStyleName(ApplicationConnection.DISABLED_CLASSNAME,
+ !widgetEnabled);
+ // make sure the caption has or has not v-disabled style
+ if (delegateCaptionHandling()) {
+ ServerConnector parent = getParent();
+ if (parent instanceof ComponentContainerConnector) {
+ ((ComponentContainerConnector) parent).updateCaption(this);
+ } else if (parent == null && !(this instanceof RootConnector)) {
+ VConsole.error("Parent of connector "
+ + Util.getConnectorString(this)
+ + " is null. This is typically an indication of a broken component hierarchy");
+ }
+ }
}
}
@@ -148,7 +162,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector
// Parent should be updated if either dimension changed between relative
// and non-relative
if (newWidth.endsWith("%") != lastKnownWidth.endsWith("%")) {
- ComponentContainerConnector parent = getParent();
+ Connector parent = getParent();
if (parent instanceof ManagedLayout) {
getLayoutManager().setNeedsHorizontalLayout(
(ManagedLayout) parent);
@@ -156,7 +170,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector
}
if (newHeight.endsWith("%") != lastKnownHeight.endsWith("%")) {
- ComponentContainerConnector parent = getParent();
+ Connector parent = getParent();
if (parent instanceof ManagedLayout) {
getLayoutManager().setNeedsVerticalLayout(
(ManagedLayout) parent);
@@ -195,23 +209,6 @@ public abstract class AbstractComponentConnector extends AbstractConnector
/*
* (non-Javadoc)
*
- * @see com.vaadin.terminal.gwt.client.Connector#isEnabled()
- */
- public boolean isEnabled() {
- if (!getState().isEnabled()) {
- return false;
- }
-
- if (getParent() == null) {
- return true;
- } else {
- return getParent().isEnabled();
- }
- }
-
- /*
- * (non-Javadoc)
- *
* @see
* com.vaadin.terminal.gwt.client.ComponentConnector#delegateCaptionHandling
* ()
@@ -337,26 +334,6 @@ public abstract class AbstractComponentConnector extends AbstractConnector
return LayoutManager.get(getConnection());
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.gwt.client.Connector#getParent()
- */
- public ComponentContainerConnector getParent() {
- return parent;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.terminal.gwt.client.Connector#setParent(com.vaadin.terminal
- * .gwt.client.ComponentContainerConnector)
- */
- public void setParent(ComponentContainerConnector parent) {
- this.parent = parent;
- }
-
/**
* Checks if there is a registered server side listener for the given event
* identifier.
@@ -372,6 +349,13 @@ public abstract class AbstractComponentConnector extends AbstractConnector
}
@Override
+ public void updateEnabledState(boolean enabledState) {
+ super.updateEnabledState(enabledState);
+
+ setWidgetEnabled(isEnabled());
+ }
+
+ @Override
public void onUnregister() {
super.onUnregister();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java
index 526631e4b2..c6bfba5023 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java
@@ -3,7 +3,7 @@
*/
package com.vaadin.terminal.gwt.client.ui;
-import java.util.LinkedList;
+import java.util.Collections;
import java.util.List;
import com.google.gwt.event.shared.HandlerRegistration;
@@ -18,19 +18,11 @@ public abstract class AbstractComponentContainerConnector extends
AbstractComponentConnector implements ComponentContainerConnector,
ConnectorHierarchyChangeHandler {
- List<ComponentConnector> children;
+ List<ComponentConnector> childComponents;
private final boolean debugLogging = false;
/**
- * Temporary storage for last enabled state to be able to see if it has
- * changed. Can be removed once we are able to listen specifically for
- * enabled changes in the state. Widget.isEnabled() cannot be used as all
- * Widgets do not implement HasEnabled
- */
- private boolean lastWidgetEnabledState = true;
-
- /**
* Default constructor
*/
public AbstractComponentContainerConnector() {
@@ -43,12 +35,12 @@ public abstract class AbstractComponentContainerConnector extends
* @see
* com.vaadin.terminal.gwt.client.ComponentContainerConnector#getChildren()
*/
- public List<ComponentConnector> getChildren() {
- if (children == null) {
- return new LinkedList<ComponentConnector>();
+ public List<ComponentConnector> getChildComponents() {
+ if (childComponents == null) {
+ return Collections.emptyList();
}
- return children;
+ return childComponents;
}
/*
@@ -58,8 +50,8 @@ public abstract class AbstractComponentContainerConnector extends
* com.vaadin.terminal.gwt.client.ComponentContainerConnector#setChildren
* (java.util.Collection)
*/
- public void setChildren(List<ComponentConnector> children) {
- this.children = children;
+ public void setChildComponents(List<ComponentConnector> childComponents) {
+ this.childComponents = childComponents;
}
/*
@@ -80,7 +72,7 @@ public abstract class AbstractComponentContainerConnector extends
VConsole.log(oldChildren);
String newChildren = "* New children: ";
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
newChildren += Util.getConnectorString(child) + " ";
}
VConsole.log(newChildren);
@@ -92,19 +84,4 @@ public abstract class AbstractComponentContainerConnector extends
return ensureHandlerManager().addHandler(
ConnectorHierarchyChangeEvent.TYPE, handler);
}
-
- @Override
- public void setWidgetEnabled(boolean widgetEnabled) {
- if (lastWidgetEnabledState == widgetEnabled) {
- return;
- }
- lastWidgetEnabledState = widgetEnabled;
-
- super.setWidgetEnabled(widgetEnabled);
- for (ComponentConnector c : getChildren()) {
- // Update children as they might be affected by the enabled state of
- // their parent
- c.setWidgetEnabled(c.isEnabled());
- }
- }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
index 54812a7a42..d34529ee4e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
@@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import com.google.gwt.event.shared.GwtEvent;
@@ -40,6 +41,16 @@ public abstract class AbstractConnector implements ServerConnector,
private final boolean debugLogging = false;
private SharedState state;
+ private ServerConnector parent;
+
+ /**
+ * Temporary storage for last enabled state to be able to see if it has
+ * changed. Can be removed once we are able to listen specifically for
+ * enabled changes in the state. Widget.isEnabled() cannot be used as all
+ * Widgets do not implement HasEnabled
+ */
+ private boolean lastEnabledState = true;
+ private List<ServerConnector> children;
/*
* (non-Javadoc)
@@ -137,16 +148,6 @@ public abstract class AbstractConnector implements ServerConnector,
return (Collection<T>) rpcImplementations.get(rpcInterfaceId);
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled()
- */
- public boolean isConnectorEnabled() {
- // Client side can always receive message from the server
- return true;
- }
-
public void fireEvent(GwtEvent<?> event) {
if (handlerManager != null) {
handlerManager.fireEvent(event);
@@ -176,6 +177,8 @@ public abstract class AbstractConnector implements ServerConnector,
+ Util.getConnectorString(stateChangeEvent.getConnector())
+ " received by " + Util.getConnectorString(this));
}
+
+ updateEnabledState(isEnabled());
}
/*
@@ -218,4 +221,47 @@ public abstract class AbstractConnector implements ServerConnector,
return ConnectorStateFactory.createState(getClass());
}
+ public ServerConnector getParent() {
+ return parent;
+ }
+
+ public void setParent(ServerConnector parent) {
+ this.parent = parent;
+ }
+
+ public List<ServerConnector> getChildren() {
+ if (children == null) {
+ return Collections.emptyList();
+ }
+ return children;
+ }
+
+ public void setChildren(List<ServerConnector> children) {
+ this.children = children;
+ }
+
+ public boolean isEnabled() {
+ if (!getState().isEnabled()) {
+ return false;
+ }
+
+ if (getParent() == null) {
+ return true;
+ } else {
+ return getParent().isEnabled();
+ }
+ }
+
+ public void updateEnabledState(boolean enabledState) {
+ if (lastEnabledState == enabledState) {
+ return;
+ }
+ lastEnabledState = enabledState;
+
+ for (ServerConnector c : getChildren()) {
+ // Update children as they might be affected by the enabled state of
+ // their parent
+ c.updateEnabledState(c.isEnabled());
+ }
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java b/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java
index cffdb1e68a..758f798ef2 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java
@@ -28,6 +28,7 @@ public abstract class ClickEventHandler extends AbstractClickEventHandler {
* @param event
* The native event that caused this click event
*/
+ @Override
protected void fireClick(NativeEvent event) {
MouseEventDetails mouseDetails = MouseEventDetailsBuilder
.buildMouseEventDetails(event, getRelativeToElement());
diff --git a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
index 62697c4d98..ef1ea8521b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
@@ -34,8 +34,8 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
public FocusableScrollPanel() {
// Prevent IE standard mode bug when a AbsolutePanel is contained.
+ TouchScrollDelegate.enableTouchScrolling(this, getElement());
Style style = getElement().getStyle();
- style.setOverflow(Overflow.AUTO);
style.setProperty("zoom", "1");
style.setPosition(Position.RELATIVE);
}
@@ -153,7 +153,8 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
* the new vertical scroll position, in pixels
*/
public void setScrollPosition(int position) {
- if (BrowserInfo.get().isAndroidWithBrokenScrollTop()) {
+ if (BrowserInfo.get().isAndroidWithBrokenScrollTop()
+ && BrowserInfo.get().requiresTouchScrollDelegate()) {
ArrayList<com.google.gwt.dom.client.Element> elements = TouchScrollDelegate
.getElements(getElement());
for (com.google.gwt.dom.client.Element el : elements) {
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..bb062a6677
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentConnector.java
@@ -0,0 +1,42 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper;
+import com.vaadin.ui.AbstractJavaScriptComponent;
+
+@Connect(AbstractJavaScriptComponent.class)
+public final 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
+ public JavaScriptWidget getWidget() {
+ return (JavaScriptWidget) super.getWidget();
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ helper.init();
+ }
+
+ public JavaScriptConnectorHelper getJavascriptConnectorHelper() {
+ return helper;
+ }
+
+ @Override
+ public JavaScriptComponentState getState() {
+ return (JavaScriptComponentState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentState.java b/src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentState.java
new file mode 100644
index 0000000000..6728f85ec9
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/JavaScriptComponentState.java
@@ -0,0 +1,37 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.JavaScriptConnectorHelper.JavaScriptConnectorState;
+
+public class JavaScriptComponentState extends ComponentState implements
+ JavaScriptConnectorState {
+
+ private Set<String> callbackNames = new HashSet<String>();
+ private Map<String, Set<String>> rpcInterfaces = new HashMap<String, Set<String>>();
+
+ public Set<String> getCallbackNames() {
+ return callbackNames;
+ }
+
+ public void setCallbackNames(Set<String> callbackNames) {
+ this.callbackNames = callbackNames;
+ }
+
+ public Map<String, Set<String>> getRpcInterfaces() {
+ return rpcInterfaces;
+ }
+
+ public void setRpcInterfaces(Map<String, Set<String>> rpcInterfaces) {
+ this.rpcInterfaces = rpcInterfaces;
+ }
+
+}
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..e6c3323893
--- /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/client/ui/TouchScrollDelegate.java b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
index 8b2248aff6..7302f9f2ac 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
@@ -4,6 +4,8 @@
package com.vaadin.terminal.gwt.client.ui;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.Duration;
@@ -15,10 +17,12 @@ import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
+import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.VConsole;
@@ -68,7 +72,7 @@ public class TouchScrollDelegate implements NativePreviewHandler {
private static final double DECELERATION = 0.002;
private static final int MAX_DURATION = 1500;
private int origY;
- private Element[] scrollableElements;
+ private HashSet<Element> scrollableElements;
private Element scrolledElement;
private int origScrollTop;
private HandlerRegistration handlerRegistration;
@@ -86,8 +90,107 @@ public class TouchScrollDelegate implements NativePreviewHandler {
private static final boolean androidWithBrokenScrollTop = BrowserInfo.get()
.isAndroidWithBrokenScrollTop();
+ /**
+ * A helper class for making a widget scrollable. Uses native scrolling if
+ * supported by the browser, otherwise registers a touch start handler
+ * delegating to a TouchScrollDelegate instance.
+ */
+ public static class TouchScrollHandler implements TouchStartHandler {
+
+ private static final String SCROLLABLE_CLASSNAME = "v-scrollable";
+
+ private final TouchScrollDelegate delegate;
+ private final boolean requiresDelegate = BrowserInfo.get()
+ .requiresTouchScrollDelegate();
+
+ /**
+ * Constructs a scroll handler for the given widget.
+ *
+ * @param widget
+ * The widget that contains scrollable elements
+ * @param scrollables
+ * The elements of the widget that should be scrollable.
+ */
+ public TouchScrollHandler(Widget widget, Element... scrollables) {
+ if (requiresDelegate) {
+ delegate = new TouchScrollDelegate();
+ widget.addDomHandler(this, TouchStartEvent.getType());
+ } else {
+ delegate = null;
+ }
+ setElements(scrollables);
+ }
+
+ public void onTouchStart(TouchStartEvent event) {
+ assert delegate != null;
+ delegate.onTouchStart(event);
+ }
+
+ public void debug(Element e) {
+ VConsole.log("Classes: " + e.getClassName() + " overflow: "
+ + e.getStyle().getProperty("overflow") + " w-o-s: "
+ + e.getStyle().getProperty("WebkitOverflowScrolling"));
+ }
+
+ /**
+ * Registers the given element as scrollable.
+ */
+ public void addElement(Element scrollable) {
+ scrollable.addClassName(SCROLLABLE_CLASSNAME);
+ if (requiresDelegate) {
+ delegate.scrollableElements.add(scrollable);
+ }
+ }
+
+ /**
+ * Unregisters the given element as scrollable. Should be called when a
+ * previously-registered element is removed from the DOM to prevent
+ * memory leaks.
+ */
+ public void removeElement(Element scrollable) {
+ scrollable.removeClassName(SCROLLABLE_CLASSNAME);
+ if (requiresDelegate) {
+ delegate.scrollableElements.remove(scrollable);
+ }
+ }
+
+ /**
+ * Registers the given elements as scrollable, removing previously
+ * registered scrollables from this handler.
+ *
+ * @param scrollables
+ * The elements that should be scrollable
+ */
+ public void setElements(Element... scrollables) {
+ if (requiresDelegate) {
+ for (Element e : delegate.scrollableElements) {
+ e.removeClassName(SCROLLABLE_CLASSNAME);
+ }
+ delegate.scrollableElements.clear();
+ }
+ for (Element e : scrollables) {
+ addElement(e);
+ }
+ }
+ }
+
+ /**
+ * Makes the given elements scrollable, either natively or by using a
+ * TouchScrollDelegate, depending on platform capabilities.
+ *
+ * @param widget
+ * The widget that contains scrollable elements
+ * @param scrollables
+ * The elements inside the widget that should be scrollable
+ * @return A scroll handler for the given widget.
+ */
+ public static TouchScrollHandler enableTouchScrolling(Widget widget,
+ Element... scrollables) {
+ return new TouchScrollHandler(widget, scrollables);
+ }
+
public TouchScrollDelegate(Element... elements) {
- scrollableElements = elements;
+ setElements(elements);
}
public void setScrollHandler(ScrollHandler scrollHandler) {
@@ -535,8 +638,8 @@ public class TouchScrollDelegate implements NativePreviewHandler {
}
}
- public void setElements(com.google.gwt.user.client.Element[] elements) {
- scrollableElements = elements;
+ public void setElements(Element[] elements) {
+ scrollableElements = new HashSet<Element>(Arrays.asList(elements));
}
/**
diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java
index b42c0acea7..91436f5353 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java
@@ -109,7 +109,7 @@ public class AbsoluteLayoutConnector extends
// TODO Margin handling
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
getWrapper(child).setPosition(
getState().getConnectorPosition(child));
}
@@ -133,7 +133,7 @@ public class AbsoluteLayoutConnector extends
public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
super.onConnectorHierarchyChange(event);
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
getWrapper(child);
}
@@ -149,7 +149,7 @@ public class AbsoluteLayoutConnector extends
public void layoutVertically() {
VAbsoluteLayout layout = getWidget();
- for (ComponentConnector paintable : getChildren()) {
+ for (ComponentConnector paintable : getChildComponents()) {
Widget widget = paintable.getWidget();
AbsoluteWrapper wrapper = (AbsoluteWrapper) widget.getParent();
Style wrapperStyle = wrapper.getElement().getStyle();
@@ -181,7 +181,7 @@ public class AbsoluteLayoutConnector extends
public void layoutHorizontally() {
VAbsoluteLayout layout = getWidget();
- for (ComponentConnector paintable : getChildren()) {
+ for (ComponentConnector paintable : getChildComponents()) {
AbsoluteWrapper wrapper = getWrapper(paintable);
Style wrapperStyle = wrapper.getElement().getStyle();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java
index dcd520bbb3..b83d5afb00 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java
@@ -20,6 +20,8 @@ import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler;
import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector;
import com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheetBase;
@@ -35,8 +37,11 @@ public class VAccordion extends VTabsheetBase {
int selectedUIDLItemIndex = -1;
+ private final TouchScrollHandler touchScrollHandler;
+
public VAccordion() {
super(CLASSNAME);
+ touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
}
@Override
@@ -145,7 +150,6 @@ public class VAccordion extends VTabsheetBase {
}
}
}
-
if (!alreadyOpen) {
item.open();
activeTabIndex = itemIndex;
@@ -342,11 +346,13 @@ public class VAccordion extends VTabsheetBase {
DOM.appendChild(captionNode, caption.getElement());
DOM.appendChild(getElement(), captionNode);
DOM.appendChild(getElement(), content);
- setStyleName(CLASSNAME + "-item");
- DOM.setElementProperty(content, "className", CLASSNAME
- + "-item-content");
- DOM.setElementProperty(captionNode, "className", CLASSNAME
- + "-item-caption");
+
+ getElement().addClassName(CLASSNAME + "-item");
+ captionNode.addClassName(CLASSNAME + "-item-caption");
+ content.addClassName(CLASSNAME + "-item-content");
+
+ touchScrollHandler.addElement(getContainerElement());
+
close();
}
@@ -488,6 +494,7 @@ public class VAccordion extends VTabsheetBase {
protected void removeTab(int index) {
StackItem item = getStackItem(index);
remove(item);
+ touchScrollHandler.removeElement(item.getContainerElement());
}
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java
index fdc053b3ae..2daceea0e8 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java
@@ -5,6 +5,7 @@
package com.vaadin.terminal.gwt.client.ui.button;
import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.ui.TabIndexState;
import com.vaadin.ui.Button;
/**
@@ -14,10 +15,14 @@ import com.vaadin.ui.Button;
*
* @since 7.0
*/
-public class ButtonState extends ComponentState {
+public class ButtonState extends ComponentState implements TabIndexState {
private boolean disableOnClick = false;
private int clickShortcutKeyCode = 0;
/**
+ * The tab order number of this field.
+ */
+ private int tabIndex = 0;
+ /**
* If caption should be rendered in HTML
*/
private boolean htmlContentAllowed = false;
@@ -92,4 +97,22 @@ public class ButtonState extends ComponentState {
return htmlContentAllowed;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ui.TabIndexState#getTabIndex()
+ */
+ public int getTabIndex() {
+ return tabIndex;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ui.TabIndexState#setTabIndex(int)
+ */
+ public void setTabIndex(int tabIndex) {
+ this.tabIndex = tabIndex;
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
index e5e7dbba8b..0cd8bc54f4 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
@@ -10,6 +10,7 @@ import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Accessibility;
@@ -61,7 +62,9 @@ public class VButton extends FocusWidget implements ClickHandler {
private boolean isCapturing;
/**
- * If <code>true</code>, this widget has focus with the space bar down.
+ * If <code>true</code>, this widget has focus with the space bar down. This
+ * means that we will get events when the button is released, but we should
+ * trigger the button only if the button is still focused at that point.
*/
private boolean isFocusing;
@@ -74,6 +77,14 @@ public class VButton extends FocusWidget implements ClickHandler {
protected int clickShortcut = 0;
+ private HandlerRegistration focusHandlerRegistration;
+ private HandlerRegistration blurHandlerRegistration;
+
+ /**
+ * If caption should be rendered in HTML
+ */
+ protected boolean htmlCaption = false;
+
public VButton() {
super(DOM.createDiv());
setTabIndex(0);
@@ -229,37 +240,28 @@ public class VButton extends FocusWidget implements ClickHandler {
if ((event.getTypeInt() & Event.KEYEVENTS) != 0) {
switch (type) {
case Event.ONKEYDOWN:
+ // Stop propagation when the user starts pressing a button that
+ // we are handling to prevent actions from getting triggered
if (event.getKeyCode() == 32 /* space */) {
isFocusing = true;
event.preventDefault();
+ event.stopPropagation();
+ } else if (event.getKeyCode() == KeyCodes.KEY_ENTER) {
+ event.stopPropagation();
}
break;
case Event.ONKEYUP:
if (isFocusing && event.getKeyCode() == 32 /* space */) {
isFocusing = false;
-
- /*
- * If click shortcut is space then the shortcut handler will
- * take care of the click.
- */
- if (clickShortcut != 32 /* space */) {
- onClick();
- }
-
+ onClick();
+ event.stopPropagation();
event.preventDefault();
}
break;
case Event.ONKEYPRESS:
if (event.getKeyCode() == KeyCodes.KEY_ENTER) {
-
- /*
- * If click shortcut is enter then the shortcut handler will
- * take care of the click.
- */
- if (clickShortcut != KeyCodes.KEY_ENTER) {
- onClick();
- }
-
+ onClick();
+ event.stopPropagation();
event.preventDefault();
}
break;
@@ -336,12 +338,10 @@ public class VButton extends FocusWidget implements ClickHandler {
Accessibility.removeState(getElement(),
Accessibility.STATE_PRESSED);
super.setTabIndex(-1);
- addStyleName(ApplicationConnection.DISABLED_CLASSNAME);
} else {
Accessibility.setState(getElement(),
Accessibility.STATE_PRESSED, "false");
super.setTabIndex(tabIndex);
- removeStyleName(ApplicationConnection.DISABLED_CLASSNAME);
}
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java
index d29eda0d6a..8c5d521445 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java
@@ -69,7 +69,8 @@ import com.vaadin.terminal.gwt.client.ui.menubar.MenuItem;
*/
@SuppressWarnings("deprecation")
public class VFilterSelect extends Composite implements Field, KeyDownHandler,
- KeyUpHandler, ClickHandler, FocusHandler, BlurHandler, Focusable {
+ KeyUpHandler, ClickHandler, FocusHandler, BlurHandler, Focusable,
+ SubPartAware {
/**
* Represents a suggestion in the suggestion popup box
@@ -100,6 +101,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* contains an image tag with the rows icon (if an icon has been
* specified) and the caption of the item
*/
+
public String getDisplayString() {
final StringBuffer sb = new StringBuffer();
if (iconUri != null) {
@@ -122,6 +124,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Get a string that represents this item. This is used in the text box.
*/
+
public String getReplacementString() {
return caption;
}
@@ -147,6 +150,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Executes a selection of this item.
*/
+
public void execute() {
onSuggestionSelected(this);
}
@@ -392,6 +396,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt
* .user.client.Event)
*/
+
@Override
public void onBrowserEvent(Event event) {
if (event.getTypeInt() == Event.ONCLICK) {
@@ -449,6 +454,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.user.client.ui.PopupPanel$PositionCallback#setPosition
* (int, int)
*/
+
public void setPosition(int offsetWidth, int offsetHeight) {
int top = -1;
@@ -541,6 +547,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google
* .gwt.event.logical.shared.CloseEvent)
*/
+
@Override
public void onClose(CloseEvent<PopupPanel> event) {
if (event.isAutoClosed()) {
@@ -824,6 +831,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.user.client.ui.TextBoxBase#onBrowserEvent(com.google
* .gwt.user.client.Event)
*/
+
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
@@ -832,8 +840,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
}
- @Override
// Overridden to avoid selecting text when text input is disabled
+ @Override
public void setSelectionRange(int pos, int length) {
if (textInputEnabled) {
super.setSelectionRange(pos, length);
@@ -857,6 +865,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt
* .user.client.Event)
*/
+
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
@@ -951,6 +960,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
public VFilterSelect() {
selectedItemIcon.setStyleName("v-icon");
selectedItemIcon.addLoadHandler(new LoadHandler() {
+
public void onLoad(LoadEvent event) {
if (BrowserInfo.get().isIE8()) {
// IE8 needs some help to discover it should reposition the
@@ -1203,6 +1213,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt
* .event.dom.client.KeyDownEvent)
*/
+
public void onKeyDown(KeyDownEvent event) {
if (enabled && !readonly) {
int keyCode = event.getNativeKeyCode();
@@ -1364,6 +1375,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* @param event
* The KeyUpEvent of the key depressed
*/
+
public void onKeyUp(KeyUpEvent event) {
if (enabled && !readonly) {
switch (event.getNativeKeyCode()) {
@@ -1411,6 +1423,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Listener for popupopener
*/
+
public void onClick(ClickEvent event) {
if (textInputEnabled
&& event.getNativeEvent().getEventTarget().cast() == tb
@@ -1474,6 +1487,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event
* .dom.client.FocusEvent)
*/
+
public void onFocus(FocusEvent event) {
/*
@@ -1567,6 +1581,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
*
* @see com.vaadin.terminal.gwt.client.Focusable#focus()
*/
+
public void focus() {
focused = true;
if (prompting && !readonly) {
@@ -1674,4 +1689,22 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
super.onDetach();
suggestionPopup.hide();
}
+
+ public Element getSubPartElement(String subPart) {
+ if ("textbox".equals(subPart)) {
+ return this.tb.getElement();
+ } else if ("button".equals(subPart)) {
+ return this.popupOpener.getElement();
+ }
+ return null;
+ }
+
+ public String getSubPartName(Element subElement) {
+ if (tb.getElement().isOrHasChild(subElement)) {
+ return "textbox";
+ } else if (popupOpener.getElement().isOrHasChild(subElement)) {
+ return "button";
+ }
+ return null;
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java
index 71d670fe9d..4d341bddfc 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java
@@ -64,7 +64,7 @@ public class CssLayoutConnector extends AbstractLayoutConnector {
getWidget().setMarginStyles(
new VMarginInfo(getState().getMarginsBitmask()));
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
if (!getState().getChildCss().containsKey(child)) {
continue;
}
@@ -92,7 +92,7 @@ public class CssLayoutConnector extends AbstractLayoutConnector {
int index = 0;
FlowPane cssLayoutWidgetContainer = getWidget().panel;
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
VCaption childCaption = childToCaption.get(child);
if (childCaption != null) {
cssLayoutWidgetContainer.addOrMove(childCaption, index++);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java
index 981818fc69..5001711d6c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java
@@ -28,8 +28,8 @@ public class CustomComponentConnector extends
super.onConnectorHierarchyChange(event);
ComponentConnector newChild = null;
- if (getChildren().size() == 1) {
- newChild = getChildren().get(0);
+ if (getChildComponents().size() == 1) {
+ newChild = getChildComponents().get(0);
}
VCustomComponent customComponent = getWidget();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java
index ca24cfc91a..f8861caf92 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java
@@ -75,7 +75,7 @@ public class CustomLayoutConnector extends AbstractLayoutConnector implements
updateHtmlTemplate();
// For all contained widgets
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
String location = getState().getChildLocations().get(child);
try {
getWidget().setWidget(child.getWidget(), location);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/AcceptCriterion.java b/src/com/vaadin/terminal/gwt/client/ui/dd/AcceptCriterion.java
new file mode 100644
index 0000000000..8a026e4d2e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/AcceptCriterion.java
@@ -0,0 +1,33 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation type used to point the server side counterpart for client side
+ * a {@link VAcceptCriterion} class.
+ * <p>
+ * Annotations are used at GWT compilation phase, so remember to rebuild your
+ * widgetset if you do changes for {@link AcceptCriterion} mappings.
+ *
+ * Prior to Vaadin 7, the mapping was done with an annotation on server side
+ * classes.
+ *
+ * @since 7.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface AcceptCriterion {
+ /**
+ * @return the class of the server side counterpart for the annotated
+ * criterion
+ */
+ Class<?> value();
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/DDUtil.java b/src/com/vaadin/terminal/gwt/client/ui/dd/DDUtil.java
index 02c1fe5061..97f5eb86fd 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/DDUtil.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/DDUtil.java
@@ -5,6 +5,7 @@ package com.vaadin.terminal.gwt.client.ui.dd;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Window;
import com.vaadin.terminal.gwt.client.Util;
public class DDUtil {
@@ -43,8 +44,11 @@ public class DDUtil {
public static VerticalDropLocation getVerticalDropLocation(Element element,
int offsetHeight, int clientY, double topBottomRatio) {
- int absoluteTop = element.getAbsoluteTop();
- int fromTop = clientY - absoluteTop;
+ // Event coordinates are relative to the viewport, element absolute
+ // position is relative to the document. Make element position relative
+ // to viewport by adjusting for viewport scrolling. See #6021
+ int elementTop = element.getAbsoluteTop() - Window.getScrollTop();
+ int fromTop = clientY - elementTop;
float percentageFromTop = (fromTop / (float) offsetHeight);
if (percentageFromTop < topBottomRatio) {
@@ -74,14 +78,17 @@ public class DDUtil {
public static HorizontalDropLocation getHorizontalDropLocation(
Element element, int clientX, double leftRightRatio) {
- int absoluteLeft = element.getAbsoluteLeft();
+ // Event coordinates are relative to the viewport, element absolute
+ // position is relative to the document. Make element position relative
+ // to viewport by adjusting for viewport scrolling. See #6021
+ int elementLeft = element.getAbsoluteLeft() - Window.getScrollLeft();
int offsetWidth = element.getOffsetWidth();
- int fromTop = clientX - absoluteLeft;
+ int fromLeft = clientX - elementLeft;
- float percentageFromTop = (fromTop / (float) offsetWidth);
- if (percentageFromTop < leftRightRatio) {
+ float percentageFromLeft = (fromLeft / (float) offsetWidth);
+ if (percentageFromLeft < leftRightRatio) {
return HorizontalDropLocation.LEFT;
- } else if (percentageFromTop > 1 - leftRightRatio) {
+ } else if (percentageFromLeft > 1 - leftRightRatio) {
return HorizontalDropLocation.RIGHT;
} else {
return HorizontalDropLocation.CENTER;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VAcceptAll.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VAcceptAll.java
index 51782c002d..a864a93c2a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VAcceptAll.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VAcceptAll.java
@@ -6,8 +6,10 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.AcceptAll;
import com.vaadin.terminal.gwt.client.UIDL;
+@AcceptCriterion(AcceptAll.class)
final public class VAcceptAll extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VAnd.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VAnd.java
index 65c67e6b8a..19399d7d4a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VAnd.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VAnd.java
@@ -6,8 +6,10 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.And;
import com.vaadin.terminal.gwt.client.UIDL;
+@AcceptCriterion(And.class)
final public class VAnd extends VAcceptCriterion implements VAcceptCallback {
private boolean b1;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VContainsDataFlavor.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VContainsDataFlavor.java
index 4a95034d4a..3cd341eefd 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VContainsDataFlavor.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VContainsDataFlavor.java
@@ -6,8 +6,10 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor;
import com.vaadin.terminal.gwt.client.UIDL;
+@AcceptCriterion(ContainsDataFlavor.class)
final public class VContainsDataFlavor extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java
index aabbf58b24..b6af81085f 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java
@@ -3,6 +3,7 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.SourceIs;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.UIDL;
@@ -12,6 +13,7 @@ import com.vaadin.terminal.gwt.client.UIDL;
*
* @since 6.3
*/
+@AcceptCriterion(SourceIs.class)
final public class VDragSourceIs extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java
index 90e2b033c9..5dad4873ea 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java
@@ -9,7 +9,9 @@ package com.vaadin.terminal.gwt.client.ui.dd;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.ui.AbstractSelect;
+@AcceptCriterion(AbstractSelect.TargetItemIs.class)
final public class VIsOverId extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java
index eb55c1a91c..ca4d0e900d 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java
@@ -8,7 +8,9 @@ package com.vaadin.terminal.gwt.client.ui.dd;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.ui.AbstractSelect;
+@AcceptCriterion(AbstractSelect.AcceptItem.class)
final public class VItemIdIs extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VLazyInitItemIdentifiers.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VLazyInitItemIdentifiers.java
index b824c1cb65..e3bed02642 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VLazyInitItemIdentifiers.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VLazyInitItemIdentifiers.java
@@ -9,15 +9,29 @@ package com.vaadin.terminal.gwt.client.ui.dd;
import java.util.HashSet;
import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Tree;
/**
*
*/
-final public class VLazyInitItemIdentifiers extends VAcceptCriterion {
+public class VLazyInitItemIdentifiers extends VAcceptCriterion {
private boolean loaded = false;
private HashSet<String> hashSet;
private VDragEvent lastDragEvent;
+ @AcceptCriterion(Table.TableDropCriterion.class)
+ final public static class VTableLazyInitItemIdentifiers extends
+ VLazyInitItemIdentifiers {
+ // all logic in superclass
+ }
+
+ @AcceptCriterion(Tree.TreeDropCriterion.class)
+ final public static class VTreeLazyInitItemIdentifiers extends
+ VLazyInitItemIdentifiers {
+ // all logic in superclass
+ }
+
@Override
public void accept(final VDragEvent drag, UIDL configuration,
final VAcceptCallback callback) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VNot.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VNot.java
index f4ba868497..e4d2dff606 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VNot.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VNot.java
@@ -6,6 +6,7 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.Not;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
@@ -13,6 +14,7 @@ import com.vaadin.terminal.gwt.client.VConsole;
* TODO implementation could now be simplified/optimized
*
*/
+@AcceptCriterion(Not.class)
final public class VNot extends VAcceptCriterion {
private boolean b1;
private VAcceptCriterion crit1;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VOr.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VOr.java
index e47447b7fc..91ba4bf0c4 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VOr.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VOr.java
@@ -6,11 +6,13 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.Or;
import com.vaadin.terminal.gwt.client.UIDL;
/**
*
*/
+@AcceptCriterion(Or.class)
final public class VOr extends VAcceptCriterion implements VAcceptCallback {
private boolean accepted;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VServerAccept.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VServerAccept.java
index bccb6656aa..64c2da5320 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VServerAccept.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VServerAccept.java
@@ -6,8 +6,10 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
import com.vaadin.terminal.gwt.client.UIDL;
+@AcceptCriterion(ServerSideCriterion.class)
final public class VServerAccept extends VAcceptCriterion {
@Override
public void accept(final VDragEvent drag, UIDL configuration,
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java
index 430b422b34..2365eabe22 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java
@@ -6,9 +6,11 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.SourceIsTarget;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
+@AcceptCriterion(SourceIsTarget.class)
final public class VSourceIsTarget extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetDetailIs.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetDetailIs.java
index 713a0d6646..610d555745 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetDetailIs.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetDetailIs.java
@@ -6,8 +6,10 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
+import com.vaadin.event.dd.acceptcriteria.TargetDetailIs;
import com.vaadin.terminal.gwt.client.UIDL;
+@AcceptCriterion(TargetDetailIs.class)
final public class VTargetDetailIs extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java
index f69fa85290..56478b2b95 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java
@@ -10,7 +10,9 @@ import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.ui.tree.VTree;
import com.vaadin.terminal.gwt.client.ui.tree.VTree.TreeNode;
+import com.vaadin.ui.Tree;
+@AcceptCriterion(Tree.TargetInSubtree.class)
final public class VTargetInSubtree extends VAcceptCriterion {
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
index 57f6835bcc..2a78cc4433 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
@@ -57,6 +57,9 @@ public class DragAndDropWrapperConnector extends CustomComponentConnector
getWidget().initDragStartMode();
getWidget().html5DataFlavors = uidl
.getMapAttribute(VDragAndDropWrapper.HTML5_DATA_FLAVORS);
+
+ // Used to prevent wrapper from stealing tooltips when not defined
+ getWidget().hasTooltip = getState().hasDescription();
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
index d09b81e1e1..4c36e92bbb 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
@@ -60,6 +60,8 @@ public class VDragAndDropWrapper extends VCustomComponent implements
private static final String CLASSNAME = "v-ddwrapper";
protected static final String DRAGGABLE = "draggable";
+ boolean hasTooltip = false;
+
public VDragAndDropWrapper() {
super();
sinkEvents(VTooltip.TOOLTIP_EVENTS);
@@ -67,6 +69,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
hookHtml5Events(getElement());
setStyleName(CLASSNAME);
addDomHandler(new MouseDownHandler() {
+
public void onMouseDown(MouseDownEvent event) {
if (startDrag(event.getNativeEvent())) {
event.preventDefault(); // prevent text selection
@@ -75,6 +78,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}, MouseDownEvent.getType());
addDomHandler(new TouchStartHandler() {
+
public void onTouchStart(TouchStartEvent event) {
if (startDrag(event.getNativeEvent())) {
/*
@@ -92,7 +96,8 @@ public class VDragAndDropWrapper extends VCustomComponent implements
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
- if (client != null) {
+ if (hasTooltip && client != null) {
+ // Override child tooltips if the wrapper has a tooltip defined
client.handleTooltipEvent(event, this);
}
}
@@ -172,6 +177,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
private boolean uploading;
private ReadyStateChangeHandler readyStateChangeHandler = new ReadyStateChangeHandler() {
+
public void onReadyStateChange(XMLHttpRequest xhr) {
if (xhr.getReadyState() == XMLHttpRequest.DONE) {
// visit server for possible
@@ -269,6 +275,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
try {
dragleavetimer = new Timer() {
+
@Override
public void run() {
// Yes, dragleave happens before drop. Makes no sense to me.
@@ -455,6 +462,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
if (detailsChanged) {
currentlyValid = false;
validate(new VAcceptCallback() {
+
public void accepted(VDragEvent event) {
dragAccepted(drag);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java b/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java
index e3a0c9b321..81f24a8e7e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java
@@ -4,7 +4,6 @@
package com.vaadin.terminal.gwt.client.ui.form;
-import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.shared.HandlerRegistration;
@@ -27,7 +26,6 @@ public class VForm extends ComplexPanel implements KeyDownHandler {
Widget lo;
Element legend = DOM.createLegend();
Element caption = DOM.createSpan();
- private Element errorIndicatorElement = DOM.createDiv();
Element desc = DOM.createDiv();
Icon icon;
VErrorMessage errorMessage = new VErrorMessage();
@@ -52,9 +50,6 @@ public class VForm extends ComplexPanel implements KeyDownHandler {
setStyleName(CLASSNAME);
fieldSet.appendChild(legend);
legend.appendChild(caption);
- errorIndicatorElement.setClassName("v-errorindicator");
- errorIndicatorElement.getStyle().setDisplay(Display.NONE);
- errorIndicatorElement.setInnerText(" "); // needed for IE
desc.setClassName("v-form-description");
fieldSet.appendChild(desc); // Adding description for initial padding
// measurements, removed later if no
diff --git a/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java
index cdac73a771..ca21947a6c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java
@@ -46,9 +46,9 @@ public class FormLayoutConnector extends AbstractLayoutConnector {
int childId = 0;
- formLayoutTable.setRowCount(getChildren().size());
+ formLayoutTable.setRowCount(getChildComponents().size());
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
Widget childWidget = child.getWidget();
Caption caption = formLayoutTable.getCaption(childWidget);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java b/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java
index 85584755a6..8a859c409c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java
@@ -35,7 +35,6 @@ public class VFormLayout extends SimplePanel {
private final static String CLASSNAME = "v-formlayout";
- ApplicationConnection client;
VFormLayoutTable table;
public VFormLayout() {
@@ -371,7 +370,7 @@ public class VFormLayout extends SimplePanel {
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
if (owner != null) {
- client.handleTooltipEvent(event, owner);
+ owner.getConnection().handleTooltipEvent(event, owner);
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java
index 50afbc5913..2cd82313c2 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java
@@ -63,7 +63,7 @@ public class GridLayoutConnector extends AbstractComponentContainerConnector
layout.spacingMeasureElement);
// Unregister caption size dependencies
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
Cell cell = layout.widgetToCell.get(child.getWidget());
cell.slot.setCaption(null);
}
@@ -155,7 +155,7 @@ public class GridLayoutConnector extends AbstractComponentContainerConnector
if (needCaptionUpdate) {
needCaptionUpdate = false;
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
updateCaption(child);
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/GreetAgainRpc.java b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/GreetAgainRpc.java
new file mode 100644
index 0000000000..0bfb8f3c32
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/GreetAgainRpc.java
@@ -0,0 +1,12 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.helloworldfeature;
+
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+
+public interface GreetAgainRpc extends ClientRpc {
+
+ public void greetAgain();
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldExtensionConnector.java b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldExtensionConnector.java
new file mode 100644
index 0000000000..ff444fece5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldExtensionConnector.java
@@ -0,0 +1,48 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.helloworldfeature;
+
+import com.google.gwt.user.client.Window;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.HelloWorldExtension;
+
+@Connect(HelloWorldExtension.class)
+public class HelloWorldExtensionConnector extends AbstractConnector {
+ HelloWorldRpc rpc = RpcProxy.create(HelloWorldRpc.class, this);
+
+ @Override
+ public HelloWorldState getState() {
+ return (HelloWorldState) super.getState();
+ }
+
+ @Override
+ protected void init() {
+ registerRpc(GreetAgainRpc.class, new GreetAgainRpc() {
+ public void greetAgain() {
+ greet();
+ }
+ });
+ }
+
+ @Override
+ public void setParent(ServerConnector parent) {
+ super.setParent(parent);
+ greet();
+ }
+
+ private void greet() {
+ String msg = getState().getGreeting() + " from "
+ + Util.getConnectorString(this) + " attached to "
+ + Util.getConnectorString(getParent());
+ VConsole.log(msg);
+
+ String response = Window.prompt(msg, "");
+ rpc.onMessageSent(response);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldRpc.java b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldRpc.java
new file mode 100644
index 0000000000..0289713390
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldRpc.java
@@ -0,0 +1,10 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.helloworldfeature;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public interface HelloWorldRpc extends ServerRpc {
+ public void onMessageSent(String message);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldState.java b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldState.java
new file mode 100644
index 0000000000..9524a5e9aa
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/helloworldfeature/HelloWorldState.java
@@ -0,0 +1,18 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.helloworldfeature;
+
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+
+public class HelloWorldState extends SharedState {
+ private String greeting = "Hello world";
+
+ public String getGreeting() {
+ return greeting;
+ }
+
+ public void setGreeting(String greeting) {
+ this.greeting = greeting;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/label/ContentMode.java b/src/com/vaadin/terminal/gwt/client/ui/label/ContentMode.java
index 2c232c9c38..4892c7e6bd 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/label/ContentMode.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/label/ContentMode.java
@@ -6,7 +6,7 @@ package com.vaadin.terminal.gwt.client.ui.label;
/**
* Content modes defining how the client should interpret a Label's value.
*
- * @sine 7.0
+ * @since 7.0.0
*/
public enum ContentMode {
/**
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java b/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java
index 1ca8933ff2..18843057f3 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java
@@ -13,6 +13,7 @@ import java.util.Set;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
@@ -154,9 +155,9 @@ public class LayoutDependencyTree {
needsSize.add(connector);
}
if (!isRelativeInDirection(connector, direction)) {
- ComponentConnector parent = connector.getParent();
- if (parent != null) {
- needsSize.add(parent);
+ ServerConnector parent = connector.getParent();
+ if (parent instanceof ComponentConnector) {
+ needsSize.add((ComponentConnector) parent);
}
}
@@ -193,7 +194,7 @@ public class LayoutDependencyTree {
if (connector instanceof ComponentContainerConnector) {
ComponentContainerConnector container = (ComponentContainerConnector) connector;
- for (ComponentConnector child : container.getChildren()) {
+ for (ComponentConnector child : container.getChildComponents()) {
if (isRelativeInDirection(child, direction)) {
resized.add(child);
}
@@ -246,16 +247,15 @@ public class LayoutDependencyTree {
private LayoutDependency findPotentiallyChangedScrollbar() {
ComponentConnector currentConnector = connector;
while (true) {
- ComponentContainerConnector parent = currentConnector
- .getParent();
- if (parent == null) {
+ ServerConnector parent = currentConnector.getParent();
+ if (!(parent instanceof ComponentConnector)) {
return null;
}
if (parent instanceof MayScrollChildren) {
return getDependency(currentConnector,
getOppositeDirection());
}
- currentConnector = parent;
+ currentConnector = (ComponentConnector) parent;
}
}
@@ -504,12 +504,11 @@ public class LayoutDependencyTree {
public ComponentConnector getScrollingBoundary(ComponentConnector connector) {
LayoutDependency dependency = getDependency(connector, HORIZONTAL);
if (!dependency.scrollingParentCached) {
- ComponentContainerConnector parent = dependency.connector
- .getParent();
+ ServerConnector parent = dependency.connector.getParent();
if (parent instanceof MayScrollChildren) {
dependency.scrollingBoundary = connector;
- } else if (parent != null) {
- dependency.scrollingBoundary = getScrollingBoundary(parent);
+ } else if (parent instanceof ComponentConnector) {
+ dependency.scrollingBoundary = getScrollingBoundary((ComponentConnector) parent);
} else {
// No scrolling parent
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java
index 01ac4fab6a..dd6e741126 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java
@@ -127,11 +127,4 @@ public class VNativeButton extends Button implements ClickHandler {
clickPending = false;
}
- @Override
- public void setEnabled(boolean enabled) {
- if (isEnabled() != enabled) {
- super.setEnabled(enabled);
- setStyleName(ApplicationConnection.DISABLED_CLASSNAME, !enabled);
- }
- }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java b/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java
index 0d222044ba..fb853b8a55 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java
@@ -43,6 +43,8 @@ public class VNotification extends VOverlay {
public static final String STYLE_SYSTEM = "system";
private static final int FADE_ANIMATION_INTERVAL = 50; // == 20 fps
+ private static final ArrayList<VNotification> notifications = new ArrayList<VNotification>();
+
private int startOpacity = 90;
private int fadeMsec = 400;
private int delayMsec = 1000;
@@ -159,6 +161,7 @@ public class VNotification extends VOverlay {
addStyleDependentName(style);
}
super.show();
+ notifications.add(this);
setPosition(position);
/**
* Android 4 fails to render notifications correctly without a little
@@ -180,6 +183,7 @@ public class VNotification extends VOverlay {
temporaryStyle = null;
}
super.hide();
+ notifications.remove(this);
fireEvent(new HideEvent(this));
}
@@ -435,4 +439,19 @@ public class VNotification extends VOverlay {
public interface EventListener extends java.util.EventListener {
public void notificationHidden(HideEvent event);
}
+
+ /**
+ * Moves currently visible notifications to the top of the event preview
+ * stack. Can be called when opening other overlays such as subwindows to
+ * ensure the notifications receive the events they need and don't linger
+ * indefinitely. See #7136.
+ *
+ * TODO Should this be a generic Overlay feature instead?
+ */
+ public static void bringNotificationsToFront() {
+ for (VNotification notification : notifications) {
+ DOM.removeEventPreview(notification);
+ DOM.addEventPreview(notification);
+ }
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
index d15766db21..9a89553fd2 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
@@ -61,7 +61,7 @@ public abstract class AbstractOrderedLayoutConnector extends
lm.unregisterDependency(this, layout.spacingMeasureElement);
// Unregister child caption listeners
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
VLayoutSlot slot = layout.getSlotForChild(child.getWidget());
slot.setCaption(null);
}
@@ -105,7 +105,7 @@ public abstract class AbstractOrderedLayoutConnector extends
VMeasuringOrderedLayout layout = getWidget();
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
VLayoutSlot slot = layout.getSlotForChild(child.getWidget());
AlignmentInfo alignment = new AlignmentInfo(getState()
@@ -285,7 +285,7 @@ public abstract class AbstractOrderedLayoutConnector extends
int currentIndex = 0;
VMeasuringOrderedLayout layout = getWidget();
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
Widget childWidget = child.getWidget();
VLayoutSlot slot = layout.getSlotForChild(childWidget);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java
index 35a0ede390..d9096526f3 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java
@@ -111,6 +111,8 @@ public class PanelConnector extends AbstractComponentContainerConnector
getWidget().captionNode.setClassName(captionClass);
getWidget().contentNode.setClassName(contentClass);
getWidget().bottomDecoration.setClassName(decoClass);
+
+ getWidget().makeScrollable();
}
if (!isRealUpdate(uidl)) {
@@ -226,8 +228,8 @@ public class PanelConnector extends AbstractComponentContainerConnector
super.onConnectorHierarchyChange(event);
// We always have 1 child, unless the child is hidden
Widget newChildWidget = null;
- if (getChildren().size() == 1) {
- ComponentConnector newChild = getChildren().get(0);
+ if (getChildComponents().size() == 1) {
+ ComponentConnector newChild = getChildComponents().get(0);
newChildWidget = newChild.getWidget();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java
index e2d3d443a0..6a06367acd 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java
@@ -6,8 +6,6 @@ package com.vaadin.terminal.gwt.client.ui.panel;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
-import com.google.gwt.event.dom.client.TouchStartEvent;
-import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
@@ -18,6 +16,7 @@ import com.vaadin.terminal.gwt.client.ui.Icon;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler;
public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
Focusable {
@@ -46,7 +45,7 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
int scrollLeft;
- private TouchScrollDelegate touchScrollDelegate;
+ private TouchScrollHandler touchScrollHandler;
public VPanel() {
super();
@@ -74,11 +73,11 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
setStyleName(CLASSNAME);
DOM.sinkEvents(getElement(), Event.ONKEYDOWN);
DOM.sinkEvents(contentNode, Event.ONSCROLL | Event.TOUCHEVENTS);
- addHandler(new TouchStartHandler() {
- public void onTouchStart(TouchStartEvent event) {
- getTouchScrollDelegate().onTouchStart(event);
- }
- }, TouchStartEvent.getType());
+
+ contentNode.getStyle().setProperty("position", "relative");
+ getElement().getStyle().setProperty("overflow", "hidden");
+
+ makeScrollable();
}
/**
@@ -100,6 +99,7 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
*
* @see com.vaadin.terminal.gwt.client.Focusable#focus()
*/
+
public void focus() {
setFocus(true);
@@ -174,16 +174,17 @@ public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
}
}
- protected TouchScrollDelegate getTouchScrollDelegate() {
- if (touchScrollDelegate == null) {
- touchScrollDelegate = new TouchScrollDelegate(contentNode);
- }
- return touchScrollDelegate;
-
- }
-
public ShortcutActionHandler getShortcutActionHandler() {
return shortcutHandler;
}
+ /**
+ * Ensures the panel is scrollable eg. after style name changes
+ */
+ void makeScrollable() {
+ if (touchScrollHandler == null) {
+ touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
+ }
+ touchScrollHandler.addElement(contentNode);
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/PageClientRpc.java b/src/com/vaadin/terminal/gwt/client/ui/root/PageClientRpc.java
new file mode 100644
index 0000000000..a02ecc8ded
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/PageClientRpc.java
@@ -0,0 +1,13 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.root;
+
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+
+public interface PageClientRpc extends ClientRpc {
+
+ public void setTitle(String title);
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java
index e02bcc9330..1a62e566ad 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java
@@ -61,6 +61,11 @@ public class RootConnector extends AbstractComponentContainerConnector
@Override
protected void init() {
super.init();
+ registerRpc(PageClientRpc.class, new PageClientRpc() {
+ public void setTitle(String title) {
+ com.google.gwt.user.client.Window.setTitle(title);
+ }
+ });
}
public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
@@ -93,12 +98,9 @@ public class RootConnector extends AbstractComponentContainerConnector
}
getWidget().setStyleName(styles.trim());
- clickEventHandler.handleEventHandlerRegistration();
+ getWidget().makeScrollable();
- if (!getWidget().isEmbedded() && getState().getCaption() != null) {
- // only change window title if we're in charge of the whole page
- com.google.gwt.user.client.Window.setTitle(getState().getCaption());
- }
+ clickEventHandler.handleEventHandlerRegistration();
// Process children
int childIndex = 0;
@@ -168,9 +170,6 @@ public class RootConnector extends AbstractComponentContainerConnector
getWidget().id, client);
}
getWidget().actionHandler.updateActionMap(childUidl);
- } else if (tag == "execJS") {
- String script = childUidl.getStringAttribute("script");
- VRoot.eval(script);
} else if (tag == "notifications") {
for (final Iterator<?> it = childUidl.getChildIterator(); it
.hasNext();) {
@@ -329,7 +328,7 @@ public class RootConnector extends AbstractComponentContainerConnector
*/
@Deprecated
public boolean hasSubWindow(WindowConnector wc) {
- return getChildren().contains(wc);
+ return getChildComponents().contains(wc);
}
/**
@@ -340,7 +339,7 @@ public class RootConnector extends AbstractComponentContainerConnector
*/
public List<WindowConnector> getSubWindows() {
ArrayList<WindowConnector> windows = new ArrayList<WindowConnector>();
- for (ComponentConnector child : getChildren()) {
+ for (ComponentConnector child : getChildComponents()) {
if (child instanceof WindowConnector) {
windows.add((WindowConnector) child);
}
@@ -380,7 +379,7 @@ public class RootConnector extends AbstractComponentContainerConnector
onChildSizeChange();
}
- for (ComponentConnector c : getChildren()) {
+ for (ComponentConnector c : getChildComponents()) {
if (c instanceof WindowConnector) {
WindowConnector wc = (WindowConnector) c;
wc.setWindowOrderAndPosition();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java
index 12a69d5556..0af8919280 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java
@@ -7,6 +7,7 @@ package com.vaadin.terminal.gwt.client.ui.root;
import java.util.ArrayList;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
@@ -26,6 +27,8 @@ import com.vaadin.terminal.gwt.client.Focusable;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler;
import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
@@ -37,21 +40,36 @@ public class VRoot extends SimplePanel implements ResizeHandler,
public static final String FRAGMENT_VARIABLE = "fragment";
+ public static final String BROWSER_HEIGHT_VAR = "browserHeight";
+
+ public static final String BROWSER_WIDTH_VAR = "browserWidth";
+
private static final String CLASSNAME = "v-view";
public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain";
+ private static int MONITOR_PARENT_TIMER_INTERVAL = 1000;
+
String theme;
String id;
ShortcutActionHandler actionHandler;
- /** stored width for IE resize optimization */
- private int width;
+ /*
+ * Last known window size used to detect whether VView should be layouted
+ * again. Detection must check window size, because the VView size might be
+ * fixed and thus not automatically adapt to changed window sizes.
+ */
+ private int windowWidth;
+ private int windowHeight;
- /** stored height for IE resize optimization */
- private int height;
+ /*
+ * Last know view size used to detect whether new dimensions should be sent
+ * to the server.
+ */
+ private int viewWidth;
+ private int viewHeight;
ApplicationConnection connection;
@@ -59,13 +77,19 @@ public class VRoot extends SimplePanel implements ResizeHandler,
public static final String CLICK_EVENT_ID = "click";
/**
- * We are postponing resize process with IE. IE bugs with scrollbars in some
- * situations, that causes false onWindowResized calls. With Timer we will
- * give IE some time to decide if it really wants to keep current size
- * (scrollbars).
+ * Keep track of possible parent size changes when an embedded application.
+ *
+ * Uses {@link #parentWidth} and {@link #parentHeight} as an optimization to
+ * keep track of when there is a real change.
*/
private Timer resizeTimer;
+ /** stored width of parent for embedded application auto-resize */
+ private int parentWidth;
+
+ /** stored height of parent for embedded application auto-resize */
+ private int parentHeight;
+
int scrollTop;
int scrollLeft;
@@ -85,6 +109,8 @@ public class VRoot extends SimplePanel implements ResizeHandler,
private HandlerRegistration historyHandlerRegistration;
+ private TouchScrollHandler touchScrollHandler;
+
/**
* The current URI fragment, used to avoid sending updates if nothing has
* changed.
@@ -96,6 +122,7 @@ public class VRoot extends SimplePanel implements ResizeHandler,
* whenever the value changes.
*/
private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {
+
public void onValueChange(ValueChangeEvent<String> event) {
String newFragment = event.getValue();
@@ -110,9 +137,9 @@ public class VRoot extends SimplePanel implements ResizeHandler,
private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
new ScheduledCommand() {
+
public void execute() {
- windowSizeMaybeChanged(Window.getClientWidth(),
- Window.getClientHeight());
+ performSizeCheck();
}
});
@@ -124,6 +151,29 @@ public class VRoot extends SimplePanel implements ResizeHandler,
// Allow focusing the view by using the focus() method, the view
// should not be in the document focus flow
getElement().setTabIndex(-1);
+ makeScrollable();
+ }
+
+ /**
+ * Start to periodically monitor for parent element resizes if embedded
+ * application (e.g. portlet).
+ */
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ if (isMonitoringParentSize()) {
+ resizeTimer = new Timer() {
+
+ @Override
+ public void run() {
+ // trigger check to see if parent size has changed,
+ // recalculate layouts
+ performSizeCheck();
+ resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
+ }
+ };
+ resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
+ }
}
@Override
@@ -142,32 +192,95 @@ public class VRoot extends SimplePanel implements ResizeHandler,
}
/**
- * Called when the window might have been resized.
+ * Stop monitoring for parent element resizes.
+ */
+
+ @Override
+ protected void onUnload() {
+ if (resizeTimer != null) {
+ resizeTimer.cancel();
+ resizeTimer = null;
+ }
+ super.onUnload();
+ }
+
+ /**
+ * Called when the window or parent div might have been resized.
*
- * @param newWidth
+ * This immediately checks the sizes of the window and the parent div (if
+ * monitoring it) and triggers layout recalculation if they have changed.
+ */
+ protected void performSizeCheck() {
+ windowSizeMaybeChanged(Window.getClientWidth(),
+ Window.getClientHeight());
+ }
+
+ /**
+ * Called when the window or parent div might have been resized.
+ *
+ * This immediately checks the sizes of the window and the parent div (if
+ * monitoring it) and triggers layout recalculation if they have changed.
+ *
+ * @param newWindowWidth
* The new width of the window
- * @param newHeight
+ * @param newWindowHeight
* The new height of the window
+ *
+ * @deprecated use {@link #performSizeCheck()}
*/
- protected void windowSizeMaybeChanged(int newWidth, int newHeight) {
+ @Deprecated
+ protected void windowSizeMaybeChanged(int newWindowWidth,
+ int newWindowHeight) {
boolean changed = false;
ComponentConnector connector = ConnectorMap.get(connection)
.getConnector(this);
- if (width != newWidth) {
- width = newWidth;
+ if (windowWidth != newWindowWidth) {
+ windowWidth = newWindowWidth;
changed = true;
- connector.getLayoutManager().reportOuterWidth(connector, newWidth);
- VConsole.log("New window width: " + width);
+ connector.getLayoutManager().reportOuterWidth(connector,
+ newWindowWidth);
+ VConsole.log("New window width: " + windowWidth);
}
- if (height != newHeight) {
- height = newHeight;
+ if (windowHeight != newWindowHeight) {
+ windowHeight = newWindowHeight;
changed = true;
- connector.getLayoutManager()
- .reportOuterHeight(connector, newHeight);
- VConsole.log("New window height: " + height);
+ connector.getLayoutManager().reportOuterHeight(connector,
+ newWindowHeight);
+ VConsole.log("New window height: " + windowHeight);
+ }
+ Element parentElement = getElement().getParentElement();
+ if (isMonitoringParentSize() && parentElement != null) {
+ // check also for parent size changes
+ int newParentWidth = parentElement.getClientWidth();
+ int newParentHeight = parentElement.getClientHeight();
+ if (parentWidth != newParentWidth) {
+ parentWidth = newParentWidth;
+ changed = true;
+ VConsole.log("New parent width: " + parentWidth);
+ }
+ if (parentHeight != newParentHeight) {
+ parentHeight = newParentHeight;
+ changed = true;
+ VConsole.log("New parent height: " + parentHeight);
+ }
}
if (changed) {
- VConsole.log("Running layout functions due to window resize");
+ /*
+ * If the window size has changed, layout the VView again and send
+ * new size to the server if the size changed. (Just checking VView
+ * size would cause us to ignore cases when a relatively sized VView
+ * should shrink as the content's size is fixed and would thus not
+ * automatically shrink.)
+ */
+ VConsole.log("Running layout functions due to window or parent resize");
+
+ // update size to avoid (most) redundant re-layout passes
+ // there can still be an extra layout recalculation if webkit
+ // overflow fix updates the size in a deferred block
+ if (isMonitoringParentSize() && parentElement != null) {
+ parentWidth = parentElement.getClientWidth();
+ parentHeight = parentElement.getClientHeight();
+ }
sendClientResized();
@@ -188,21 +301,6 @@ public class VRoot extends SimplePanel implements ResizeHandler,
}-*/;
/**
- * Evaluate the given script in the browser document.
- *
- * @param script
- * Script to be executed.
- */
- static native void eval(String script)
- /*-{
- try {
- if (script == null) return;
- $wnd.eval(script);
- } catch (e) {
- }
- }-*/;
-
- /**
* Returns true if the body is NOT generated, i.e if someone else has made
* the page that we're running in. Otherwise we're in charge of the whole
* page.
@@ -214,6 +312,17 @@ public class VRoot extends SimplePanel implements ResizeHandler,
.contains(ApplicationConnection.GENERATED_BODY_CLASSNAME);
}
+ /**
+ * Returns true if the size of the parent should be checked periodically and
+ * the application should react to its changes.
+ *
+ * @return true if size of parent should be tracked
+ */
+ protected boolean isMonitoringParentSize() {
+ // could also perform a more specific check (Liferay portlet)
+ return isEmbedded();
+ }
+
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
@@ -251,14 +360,19 @@ public class VRoot extends SimplePanel implements ResizeHandler,
* com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google
* .gwt.event.logical.shared.ResizeEvent)
*/
+
public void onResize(ResizeEvent event) {
- onResize();
+ triggerSizeChangeCheck();
}
/**
* Called when a resize event is received.
+ *
+ * This may trigger a lazy refresh or perform the size check immediately
+ * depending on the browser used and whether the server side requests
+ * resizes to be lazy.
*/
- void onResize() {
+ private void triggerSizeChangeCheck() {
/*
* IE (pre IE9 at least) will give us some false resize events due to
* problems with scrollbars. Firefox 3 might also produce some extra
@@ -274,8 +388,7 @@ public class VRoot extends SimplePanel implements ResizeHandler,
if (lazy) {
delayedResizeExecutor.trigger();
} else {
- windowSizeMaybeChanged(Window.getClientWidth(),
- Window.getClientHeight());
+ performSizeCheck();
}
}
@@ -283,8 +396,19 @@ public class VRoot extends SimplePanel implements ResizeHandler,
* Send new dimensions to the server.
*/
private void sendClientResized() {
- connection.updateVariable(id, "height", height, false);
- connection.updateVariable(id, "width", width, immediate);
+ Element parentElement = getElement().getParentElement();
+ int viewHeight = parentElement.getClientHeight();
+ int viewWidth = parentElement.getClientWidth();
+
+ connection.updateVariable(id, "height", viewHeight, false);
+ connection.updateVariable(id, "width", viewWidth, false);
+
+ int windowWidth = Window.getClientWidth();
+ int windowHeight = Window.getClientHeight();
+
+ connection.updateVariable(id, BROWSER_WIDTH_VAR, windowWidth, false);
+ connection.updateVariable(id, BROWSER_HEIGHT_VAR, windowHeight,
+ immediate);
}
public native static void goTo(String url)
@@ -319,4 +443,13 @@ public class VRoot extends SimplePanel implements ResizeHandler,
getElement().focus();
}
+ /**
+ * Ensures the root is scrollable eg. after style name changes.
+ */
+ void makeScrollable() {
+ if (touchScrollHandler == null) {
+ touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
+ }
+ touchScrollHandler.addElement(getElement());
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java b/src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java
index 9ff614252d..5c7ee7a784 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java
@@ -18,6 +18,7 @@ import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.ContainerResizedListener;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.VTooltip;
import com.vaadin.terminal.gwt.client.ui.Field;
import com.vaadin.terminal.gwt.client.ui.SimpleFocusablePanel;
import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
@@ -51,6 +52,7 @@ public class VSlider extends SimpleFocusablePanel implements Field,
private final HTML feedback = new HTML("", false);
private final VOverlay feedbackPopup = new VOverlay(true, false, true) {
+
@Override
public void show() {
super.show();
@@ -112,6 +114,8 @@ public class VSlider extends SimpleFocusablePanel implements Field,
feedbackPopup.addStyleName(CLASSNAME + "-feedback");
feedbackPopup.setWidget(feedback);
+
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
}
void setFeedbackValue(double value) {
@@ -139,8 +143,12 @@ public class VSlider extends SimpleFocusablePanel implements Field,
void buildBase() {
final String styleAttribute = vertical ? "height" : "width";
+ final String oppositeStyleAttribute = vertical ? "width" : "height";
final String domProperty = vertical ? "offsetHeight" : "offsetWidth";
+ // clear unnecessary opposite style attribute
+ DOM.setStyleAttribute(base, oppositeStyleAttribute, "");
+
final Element p = DOM.getParent(getElement());
if (DOM.getElementPropertyInt(p, domProperty) > 50) {
if (vertical) {
@@ -153,6 +161,7 @@ public class VSlider extends SimpleFocusablePanel implements Field,
// (supposedly) been drawn completely.
DOM.setStyleAttribute(base, styleAttribute, MIN_SIZE + "px");
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
final Element p = DOM.getParent(getElement());
if (DOM.getElementPropertyInt(p, domProperty) > (MIN_SIZE + 5)) {
@@ -173,9 +182,14 @@ public class VSlider extends SimpleFocusablePanel implements Field,
void buildHandle() {
final String handleAttribute = vertical ? "marginTop" : "marginLeft";
+ final String oppositeHandleAttribute = vertical ? "marginLeft"
+ : "marginTop";
DOM.setStyleAttribute(handle, handleAttribute, "0");
+ // clear unnecessary opposite handle attribute
+ DOM.setStyleAttribute(handle, oppositeHandleAttribute, "");
+
// Restore visibility
DOM.setStyleAttribute(handle, "visibility", "visible");
@@ -277,6 +291,9 @@ public class VSlider extends SimpleFocusablePanel implements Field,
event.preventDefault(); // avoid simulated events
event.stopPropagation();
}
+ if (client != null) {
+ client.handleTooltipEvent(event, this);
+ }
}
private void processMouseWheelEvent(final Event event) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java
index a2f1f26c15..e33755bc9b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java
@@ -127,6 +127,12 @@ public abstract class AbstractSplitPanelConnector extends
getWidget().setStylenames();
+ getWidget().minimumPosition = splitterState.getMinPosition()
+ + splitterState.getMinPositionUnit();
+
+ getWidget().maximumPosition = splitterState.getMaxPosition()
+ + splitterState.getMaxPositionUnit();
+
getWidget().position = splitterState.getPosition()
+ splitterState.getPositionUnit();
@@ -136,6 +142,7 @@ public abstract class AbstractSplitPanelConnector extends
getLayoutManager().setNeedsLayout(this);
+ getWidget().makeScrollable();
}
public void layout() {
@@ -143,7 +150,7 @@ public abstract class AbstractSplitPanelConnector extends
splitPanel.setSplitPosition(splitPanel.position);
splitPanel.updateSizes();
// Report relative sizes in other direction for quicker propagation
- List<ComponentConnector> children = getChildren();
+ List<ComponentConnector> children = getChildComponents();
for (ComponentConnector child : children) {
reportOtherDimension(child);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java
index 8b80eed840..db3a39d3a5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java
@@ -49,6 +49,10 @@ public class AbstractSplitPanelState extends ComponentState {
public static class SplitterState implements Serializable {
private float position;
private String positionUnit;
+ private float minPosition;
+ private String minPositionUnit;
+ private float maxPosition;
+ private String maxPositionUnit;
private boolean positionReversed = false;
private boolean locked = false;
@@ -68,6 +72,38 @@ public class AbstractSplitPanelState extends ComponentState {
this.positionUnit = positionUnit;
}
+ public float getMinPosition() {
+ return minPosition;
+ }
+
+ public void setMinPosition(float minPosition) {
+ this.minPosition = minPosition;
+ }
+
+ public String getMinPositionUnit() {
+ return minPositionUnit;
+ }
+
+ public void setMinPositionUnit(String minPositionUnit) {
+ this.minPositionUnit = minPositionUnit;
+ }
+
+ public float getMaxPosition() {
+ return maxPosition;
+ }
+
+ public void setMaxPosition(float maxPosition) {
+ this.maxPosition = maxPosition;
+ }
+
+ public String getMaxPositionUnit() {
+ return maxPositionUnit;
+ }
+
+ public void setMaxPositionUnit(String maxPositionUnit) {
+ this.maxPositionUnit = maxPositionUnit;
+ }
+
public boolean isPositionReversed() {
return positionReversed;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java
index 166e79e92e..e2f30c6676 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java
@@ -4,6 +4,7 @@
package com.vaadin.terminal.gwt.client.ui.splitpanel;
+import java.util.Collections;
import java.util.List;
import com.google.gwt.dom.client.Node;
@@ -31,6 +32,7 @@ import com.vaadin.terminal.gwt.client.LayoutManager;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler;
import com.vaadin.terminal.gwt.client.ui.VOverlay;
import com.vaadin.terminal.gwt.client.ui.splitpanel.VAbstractSplitPanel.SplitterMoveHandler.SplitterMoveEvent;
@@ -76,7 +78,7 @@ public class VAbstractSplitPanel extends ComplexPanel {
private boolean positionReversed = false;
- List<String> componentStyleNames;
+ List<String> componentStyleNames = Collections.emptyList();
private Element draggingCurtain;
@@ -87,12 +89,16 @@ public class VAbstractSplitPanel extends ComplexPanel {
/* The current position of the split handle in either percentages or pixels */
String position;
+ String maximumPosition;
+
+ String minimumPosition;
+
+ private TouchScrollHandler touchScrollHandler;
+
protected Element scrolledContainer;
protected int origScrollTop;
- private TouchScrollDelegate touchScrollDelegate;
-
public VAbstractSplitPanel() {
this(ORIENTATION_HORIZONTAL);
}
@@ -116,6 +122,8 @@ public class VAbstractSplitPanel extends ComplexPanel {
setOrientation(orientation);
sinkEvents(Event.MOUSEEVENTS);
+ makeScrollable();
+
addDomHandler(new TouchCancelHandler() {
public void onTouchCancel(TouchCancelEvent event) {
// TODO When does this actually happen??
@@ -127,11 +135,8 @@ public class VAbstractSplitPanel extends ComplexPanel {
Node target = event.getTouches().get(0).getTarget().cast();
if (splitter.isOrHasChild(target)) {
onMouseDown(Event.as(event.getNativeEvent()));
- } else {
- getTouchScrollDelegate().onTouchStart(event);
}
}
-
}, TouchStartEvent.getType());
addDomHandler(new TouchMoveHandler() {
public void onTouchMove(TouchMoveEvent event) {
@@ -150,14 +155,6 @@ public class VAbstractSplitPanel extends ComplexPanel {
}
- private TouchScrollDelegate getTouchScrollDelegate() {
- if (touchScrollDelegate == null) {
- touchScrollDelegate = new TouchScrollDelegate(firstContainer,
- secondContainer);
- }
- return touchScrollDelegate;
- }
-
protected void constructDom() {
DOM.appendChild(splitter, DOM.createDiv()); // for styling
DOM.appendChild(getElement(), wrapper);
@@ -172,9 +169,7 @@ public class VAbstractSplitPanel extends ComplexPanel {
DOM.setStyleAttribute(splitter, "position", "absolute");
DOM.setStyleAttribute(secondContainer, "position", "absolute");
- DOM.setStyleAttribute(firstContainer, "overflow", "auto");
- DOM.setStyleAttribute(secondContainer, "overflow", "auto");
-
+ setStylenames();
}
private void setOrientation(int orientation) {
@@ -190,11 +185,6 @@ public class VAbstractSplitPanel extends ComplexPanel {
DOM.setStyleAttribute(firstContainer, "width", "100%");
DOM.setStyleAttribute(secondContainer, "width", "100%");
}
-
- DOM.setElementProperty(firstContainer, "className", CLASSNAME
- + "-first-container");
- DOM.setElementProperty(secondContainer, "className", CLASSNAME
- + "-second-container");
}
@Override
@@ -232,11 +222,109 @@ public class VAbstractSplitPanel extends ComplexPanel {
}
}
+ /**
+ * Converts given split position string (in pixels or percentage) to a
+ * floating point pixel value.
+ *
+ * @param pos
+ * @return
+ */
+ private float convertToPixels(String pos) {
+ float posAsFloat;
+ if (pos.indexOf("%") > 0) {
+ posAsFloat = Math.round(Float.parseFloat(pos.substring(0,
+ pos.length() - 1))
+ / 100
+ * (orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth()
+ : getOffsetHeight()));
+ } else {
+ posAsFloat = Float.parseFloat(pos.substring(0, pos.length() - 2));
+ }
+ return posAsFloat;
+ }
+
+ /**
+ * Converts given split position string (in pixels or percentage) to a float
+ * percentage value.
+ *
+ * @param pos
+ * @return
+ */
+ private float convertToPercentage(String pos) {
+ float posAsFloat = 0;
+
+ if (pos.indexOf("px") > 0) {
+ int posAsInt = Integer.parseInt(pos.substring(0, pos.length() - 2));
+ int offsetLength = orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth()
+ : getOffsetHeight();
+
+ // 100% needs special handling
+ if (posAsInt + getSplitterSize() >= offsetLength) {
+ posAsInt = offsetLength;
+ }
+ posAsFloat = ((float) posAsInt / (float) offsetLength * 100);
+
+ } else {
+ posAsFloat = Float.parseFloat(pos.substring(0, pos.length() - 1));
+ }
+ return posAsFloat;
+ }
+
+ /**
+ * Returns the given position clamped to the range between current minimum
+ * and maximum positions.
+ *
+ * TODO Should this be in the connector?
+ *
+ * @param pos
+ * Position of the splitter as a CSS string, either pixels or a
+ * percentage.
+ * @return minimumPosition if pos is less than minimumPosition;
+ * maximumPosition if pos is greater than maximumPosition; pos
+ * otherwise.
+ */
+ private String checkSplitPositionLimits(String pos) {
+ float positionAsFloat = convertToPixels(pos);
+
+ if (maximumPosition != null
+ && convertToPixels(maximumPosition) < positionAsFloat) {
+ pos = maximumPosition;
+ } else if (minimumPosition != null
+ && convertToPixels(minimumPosition) > positionAsFloat) {
+ pos = minimumPosition;
+ }
+ return pos;
+ }
+
+ /**
+ * Converts given string to the same units as the split position is.
+ *
+ * @param pos
+ * position to be converted
+ * @return converted position string
+ */
+ private String convertToPositionUnits(String pos) {
+ if (position.indexOf("%") != -1 && pos.indexOf("%") == -1) {
+ // position is in percentage, pos in pixels
+ pos = convertToPercentage(pos) + "%";
+ } else if (position.indexOf("px") > 0 && pos.indexOf("px") == -1) {
+ // position is in pixels and pos in percentage
+ pos = convertToPixels(pos) + "px";
+ }
+
+ return pos;
+ }
+
void setSplitPosition(String pos) {
if (pos == null) {
return;
}
+ pos = checkSplitPositionLimits(pos);
+ if (!pos.equals(position)) {
+ position = convertToPositionUnits(pos);
+ }
+
// Convert percentage values to pixels
if (pos.indexOf("%") > 0) {
int size = orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth()
@@ -375,7 +463,6 @@ public class VAbstractSplitPanel extends ComplexPanel {
}
break;
}
-
}
void setFirstWidget(Widget w) {
@@ -481,16 +568,7 @@ public class VAbstractSplitPanel extends ComplexPanel {
}
if (position.indexOf("%") > 0) {
- float pos = newX;
- // 100% needs special handling
- if (newX + getSplitterSize() >= getOffsetWidth()) {
- pos = getOffsetWidth();
- }
- // Reversed position
- if (positionReversed) {
- pos = getOffsetWidth() - pos - getSplitterSize();
- }
- position = (pos / getOffsetWidth() * 100) + "%";
+ position = convertToPositionUnits(newX + "px");
} else {
// Reversed position
if (positionReversed) {
@@ -523,16 +601,7 @@ public class VAbstractSplitPanel extends ComplexPanel {
}
if (position.indexOf("%") > 0) {
- float pos = newY;
- // 100% needs special handling
- if (newY + getSplitterSize() >= getOffsetHeight()) {
- pos = getOffsetHeight();
- }
- // Reversed position
- if (positionReversed) {
- pos = getOffsetHeight() - pos - getSplitterSize();
- }
- position = pos / getOffsetHeight() * 100 + "%";
+ position = convertToPositionUnits(newY + "px");
} else {
// Reversed position
if (positionReversed) {
@@ -659,30 +728,24 @@ public class VAbstractSplitPanel extends ComplexPanel {
}
void setStylenames() {
- final String splitterSuffix = (orientation == ORIENTATION_HORIZONTAL ? "-hsplitter"
- : "-vsplitter");
- final String firstContainerSuffix = "-first-container";
- final String secondContainerSuffix = "-second-container";
- String lockedSuffix = "";
-
- String splitterStyle = CLASSNAME + splitterSuffix;
- String firstStyle = CLASSNAME + firstContainerSuffix;
- String secondStyle = CLASSNAME + secondContainerSuffix;
-
- if (locked) {
- splitterStyle = CLASSNAME + splitterSuffix + "-locked";
- lockedSuffix = "-locked";
- }
- for (String style : componentStyleNames) {
- splitterStyle += " " + CLASSNAME + splitterSuffix + "-" + style
- + lockedSuffix;
- firstStyle += " " + CLASSNAME + firstContainerSuffix + "-" + style;
- secondStyle += " " + CLASSNAME + secondContainerSuffix + "-"
- + style;
- }
- DOM.setElementProperty(splitter, "className", splitterStyle);
- DOM.setElementProperty(firstContainer, "className", firstStyle);
- DOM.setElementProperty(secondContainer, "className", secondStyle);
+ final String splitterClass = CLASSNAME
+ + (orientation == ORIENTATION_HORIZONTAL ? "-hsplitter"
+ : "-vsplitter");
+ final String firstContainerClass = CLASSNAME + "-first-container";
+ final String secondContainerClass = CLASSNAME + "-second-container";
+ final String lockedSuffix = locked ? "-locked" : "";
+
+ splitter.setClassName(splitterClass + lockedSuffix);
+ firstContainer.setClassName(firstContainerClass);
+ secondContainer.setClassName(secondContainerClass);
+
+ for (String styleName : componentStyleNames) {
+ splitter.addClassName(splitterClass + "-" + styleName
+ + lockedSuffix);
+ firstContainer.addClassName(firstContainerClass + "-" + styleName);
+ secondContainer
+ .addClassName(secondContainerClass + "-" + styleName);
+ }
}
public void setEnabled(boolean enabled) {
@@ -693,4 +756,14 @@ public class VAbstractSplitPanel extends ComplexPanel {
return enabled;
}
+ /**
+ * Ensures the panels are scrollable eg. after style name changes
+ */
+ void makeScrollable() {
+ if (touchScrollHandler == null) {
+ touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
+ }
+ touchScrollHandler.addElement(firstContainer);
+ touchScrollHandler.addElement(secondContainer);
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java b/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java
index 73bc5da83f..ada0f2424f 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java
@@ -16,6 +16,7 @@ import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
@@ -283,8 +284,11 @@ public class TableConnector extends AbstractComponentContainerConnector
Scheduler.get().scheduleFinally(new ScheduledCommand() {
public void execute() {
getLayoutManager().setNeedsMeasure(TableConnector.this);
- getLayoutManager().setNeedsMeasure(
- TableConnector.this.getParent());
+ ServerConnector parent = getParent();
+ if (parent instanceof ComponentConnector) {
+ getLayoutManager().setNeedsMeasure(
+ (ComponentConnector) parent);
+ }
getLayoutManager().setNeedsVerticalLayout(
TableConnector.this);
getLayoutManager().layoutNow();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
index c45c26c4ac..c4a57f5c8b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
@@ -44,8 +44,6 @@ import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
-import com.google.gwt.event.dom.client.TouchStartEvent;
-import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Command;
@@ -236,6 +234,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private boolean enableDebug = false;
+ private static final boolean hasNativeTouchScrolling = BrowserInfo.get()
+ .isTouchDevice()
+ && !BrowserInfo.get().requiresTouchScrollDelegate();
+
+ private Set<String> noncollapsibleColumns;
+
/**
* Represents a select range of rows
*/
@@ -268,6 +272,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
*
* @see java.lang.Object#toString()
*/
+
@Override
public String toString() {
return startRow.getKey() + "-" + length;
@@ -321,6 +326,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
final FocusableScrollPanel scrollBodyPanel = new FocusableScrollPanel(true);
private KeyPressHandler navKeyPressHandler = new KeyPressHandler() {
+
public void onKeyPress(KeyPressEvent keyPressEvent) {
// This is used for Firefox only, since Firefox auto-repeat
// works correctly only if we use a key press handler, other
@@ -489,12 +495,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
public VScrollTable() {
setMultiSelectMode(MULTISELECT_MODE_DEFAULT);
- scrollBodyPanel.setStyleName(CLASSNAME + "-body-wrapper");
+ scrollBodyPanel.addStyleName(CLASSNAME + "-body-wrapper");
scrollBodyPanel.addFocusHandler(this);
scrollBodyPanel.addBlurHandler(this);
scrollBodyPanel.addScrollHandler(this);
- scrollBodyPanel.setStyleName(CLASSNAME + "-body");
+ scrollBodyPanel.addStyleName(CLASSNAME + "-body");
/*
* Firefox auto-repeat works correctly only if we use a key press
@@ -509,14 +515,10 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
scrollBodyPanel.addKeyUpHandler(navKeyUpHandler);
scrollBodyPanel.sinkEvents(Event.TOUCHEVENTS);
- scrollBodyPanel.addDomHandler(new TouchStartHandler() {
- public void onTouchStart(TouchStartEvent event) {
- getTouchScrollDelegate().onTouchStart(event);
- }
- }, TouchStartEvent.getType());
scrollBodyPanel.sinkEvents(Event.ONCONTEXTMENU);
scrollBodyPanel.addDomHandler(new ContextMenuHandler() {
+
public void onContextMenu(ContextMenuEvent event) {
handleBodyContextMenu(event);
}
@@ -536,22 +538,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
// Add a handler to clear saved context menu details when the menu
// closes. See #8526.
client.getContextMenu().addCloseHandler(new CloseHandler<PopupPanel>() {
+
public void onClose(CloseEvent<PopupPanel> event) {
contextMenu = null;
}
});
}
- protected TouchScrollDelegate getTouchScrollDelegate() {
- if (touchScrollDelegate == null) {
- touchScrollDelegate = new TouchScrollDelegate(
- scrollBodyPanel.getElement());
- touchScrollDelegate.setScrollHandler(this);
- }
- return touchScrollDelegate;
-
- }
-
private void handleBodyContextMenu(ContextMenuEvent event) {
if (enabled && bodyActionKeys != null) {
int left = Util.getTouchOrMouseClientX(event.getNativeEvent());
@@ -896,6 +889,10 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
updateHeader(uidl.getStringArrayAttribute("vcolorder"));
updateFooter(uidl.getStringArrayAttribute("vcolorder"));
+ if (uidl.hasVariable("noncollapsiblecolumns")) {
+ noncollapsibleColumns = uidl
+ .getStringArrayVariableAsSet("noncollapsiblecolumns");
+ }
}
private void updateCollapsedColumns(UIDL uidl) {
@@ -1848,12 +1845,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
isNewBody = false;
if (firstvisible > 0) {
- // FIXME #7607
- // Originally deferred due to Firefox oddities which should not
- // occur any more. Currently deferring breaks Webkit scrolling with
- // relative-height tables, but not deferring instead breaks tables
- // with explicit page length.
+ // Deferred due to some Firefox oddities
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
scrollBodyPanel
.setScrollPosition(measureRowHeightOffset(firstvisible));
@@ -1888,6 +1882,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
*/
scrollBody.reLayoutComponents();
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
}
@@ -2218,6 +2213,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
setWidth(tdWidth + "px");
} else {
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
int tdWidth = width
+ scrollBody.getCellExtraWidth();
@@ -2271,6 +2267,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
/**
* Handle column reordering.
*/
+
@Override
public void onBrowserEvent(Event event) {
if (enabled && event != null) {
@@ -2814,6 +2811,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
final int newWidth = width;
Scheduler.get().scheduleDeferred(
new ScheduledCommand() {
+
public void execute() {
setColWidth(colIx, newWidth, true);
}
@@ -2841,6 +2839,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
if (refreshContentWidths) {
// Recalculate the column sizings if any column has changed
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+
public void execute() {
triggerLazyColumnAdjustment(true);
}
@@ -3029,6 +3028,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
String colKey;
private boolean collapsed;
+ private boolean noncollapsible = false;
private VScrollTableRow currentlyFocusedRow;
public VisibleColumnAction(String colKey) {
@@ -3040,6 +3040,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
@Override
public void execute() {
+ if (noncollapsible) {
+ return;
+ }
client.getContextMenu().hide();
// toggle selected column
if (collapsedColumns.contains(colKey)) {
@@ -3063,17 +3066,28 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
collapsed = b;
}
+ public void setNoncollapsible(boolean b) {
+ noncollapsible = b;
+ }
+
/**
* Override default method to distinguish on/off columns
*/
+
@Override
public String getHTML() {
final StringBuffer buf = new StringBuffer();
+ buf.append("<span class=\"");
if (collapsed) {
- buf.append("<span class=\"v-off\">");
+ buf.append("v-off");
} else {
- buf.append("<span class=\"v-on\">");
+ buf.append("v-on");
+ }
+ if (noncollapsible) {
+ buf.append(" v-disabled");
}
+ buf.append("\">");
+
buf.append(super.getHTML());
buf.append("</span>");
@@ -3085,6 +3099,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
/*
* Returns columns as Action array for column select popup
*/
+
public Action[] getActions() {
Object[] cols;
if (columnReordering && columnOrder != null) {
@@ -3115,6 +3130,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
if (!c.isEnabled()) {
a.setCollapsed(true);
}
+ if (noncollapsibleColumns.contains(cid)) {
+ a.setNoncollapsible(true);
+ }
actions[i] = a;
}
return actions;
@@ -3200,6 +3218,10 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* The text in the footer
*/
public void setText(String footerText) {
+ if (footerText == null || footerText.equals("")) {
+ footerText = "&nbsp;";
+ }
+
DOM.setInnerHTML(captionContainer, footerText);
}
@@ -3295,6 +3317,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
setWidth(tdWidth + "px");
} else {
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
int borderWidths = 1;
int tdWidth = width
@@ -3541,6 +3564,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* com.google.gwt.user.client.ui.Panel#remove(com.google.gwt.user.client
* .ui.Widget)
*/
+
@Override
public boolean remove(Widget w) {
if (visibleCells.contains(w)) {
@@ -3557,6 +3581,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
*
* @see com.google.gwt.user.client.ui.HasWidgets#iterator()
*/
+
public Iterator<Widget> iterator() {
return visibleCells.iterator();
}
@@ -3835,7 +3860,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
DOM.appendChild(container, preSpacer);
DOM.appendChild(container, table);
DOM.appendChild(container, postSpacer);
- if (BrowserInfo.get().isTouchDevice()) {
+ if (BrowserInfo.get().requiresTouchScrollDelegate()) {
NodeList<Node> childNodes = container.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Element item = (Element) childNodes.getItem(i);
@@ -4388,7 +4413,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private String[] actionKeys = null;
private final TableRowElement rowElement;
- private boolean mDown;
private int index;
private Event touchStart;
private static final String ROW_CLASSNAME_EVEN = CLASSNAME + "-row";
@@ -4396,8 +4420,10 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
+ "-row-odd";
private static final int TOUCH_CONTEXT_MENU_TIMEOUT = 500;
private Timer contextTouchTimeout;
+ private Timer dragTouchTimeout;
private int touchStartY;
private int touchStartX;
+ private boolean isDragging = false;
private VScrollTableRow(int rowKey) {
this.rowKey = rowKey;
@@ -4785,12 +4811,128 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
}
+ /**
+ * Special handler for touch devices that support native scrolling
+ *
+ * @return Whether the event was handled by this method.
+ */
+ private boolean handleTouchEvent(final Event event) {
+
+ boolean touchEventHandled = false;
+
+ if (enabled && hasNativeTouchScrolling) {
+ final Element targetTdOrTr = getEventTargetTdOrTr(event);
+ final int type = event.getTypeInt();
+
+ switch (type) {
+ case Event.ONTOUCHSTART:
+ touchEventHandled = true;
+ touchStart = event;
+ isDragging = false;
+ Touch touch = event.getChangedTouches().get(0);
+ // save position to fields, touches in events are same
+ // instance during the operation.
+ touchStartX = touch.getClientX();
+ touchStartY = touch.getClientY();
+
+ if (dragmode != 0) {
+ if (dragTouchTimeout == null) {
+ dragTouchTimeout = new Timer() {
+
+ @Override
+ public void run() {
+ if (touchStart != null) {
+ // Start a drag if a finger is held
+ // in place long enough, then moved
+ isDragging = true;
+ }
+ }
+ };
+ }
+ dragTouchTimeout.schedule(TOUCHSCROLL_TIMEOUT);
+ }
+
+ if (actionKeys != null) {
+ if (contextTouchTimeout == null) {
+ contextTouchTimeout = new Timer() {
+
+ @Override
+ public void run() {
+ if (touchStart != null) {
+ // Open the context menu if finger
+ // is held in place long enough.
+ showContextMenu(touchStart);
+ event.preventDefault();
+ touchStart = null;
+ }
+ }
+ };
+ }
+ contextTouchTimeout
+ .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
+ }
+ break;
+ case Event.ONTOUCHMOVE:
+ touchEventHandled = true;
+ if (isSignificantMove(event)) {
+ if (contextTouchTimeout != null) {
+ // Moved finger before the context menu timer
+ // expired, so let the browser handle this as a
+ // scroll.
+ contextTouchTimeout.cancel();
+ contextTouchTimeout = null;
+ }
+ if (!isDragging && dragTouchTimeout != null) {
+ // Moved finger before the drag timer expired,
+ // so let the browser handle this as a scroll.
+ dragTouchTimeout.cancel();
+ dragTouchTimeout = null;
+ }
+
+ if (dragmode != 0 && touchStart != null
+ && isDragging) {
+ event.preventDefault();
+ event.stopPropagation();
+ startRowDrag(touchStart, type, targetTdOrTr);
+ }
+ touchStart = null;
+ }
+ break;
+ case Event.ONTOUCHEND:
+ case Event.ONTOUCHCANCEL:
+ touchEventHandled = true;
+ if (contextTouchTimeout != null) {
+ contextTouchTimeout.cancel();
+ }
+ if (dragTouchTimeout != null) {
+ dragTouchTimeout.cancel();
+ }
+ if (touchStart != null) {
+ event.preventDefault();
+ event.stopPropagation();
+ if (!BrowserInfo.get().isAndroid()) {
+ Util.simulateClickFromTouchEvent(touchStart,
+ this);
+ }
+ touchStart = null;
+ }
+ isDragging = false;
+ break;
+ }
+ }
+ return touchEventHandled;
+ }
+
/*
* React on click that occur on content cells only
*/
+
@Override
public void onBrowserEvent(final Event event) {
- if (enabled) {
+
+ final boolean touchEventHandled = handleTouchEvent(event);
+
+ if (enabled && !touchEventHandled) {
final int type = event.getTypeInt();
final Element targetTdOrTr = getEventTargetTdOrTr(event);
if (type == Event.ONCONTEXTMENU) {
@@ -4823,7 +4965,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
break;
case Event.ONMOUSEUP:
if (targetCellOrRowFound) {
- mDown = false;
/*
* Queue here, send at the same time as the
* corresponding value change event - see #7127
@@ -4983,6 +5124,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
touchStart.preventDefault();
if (dragmode != 0 || actionKeys != null) {
new Timer() {
+
@Override
public void run() {
TouchScrollDelegate activeScrollDelegate = TouchScrollDelegate
@@ -5021,6 +5163,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
if (contextTouchTimeout == null
&& actionKeys != null) {
contextTouchTimeout = new Timer() {
+
@Override
public void run() {
if (touchStart != null) {
@@ -5066,9 +5209,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
break;
case Event.ONMOUSEOUT:
- if (targetCellOrRowFound) {
- mDown = false;
- }
break;
default:
break;
@@ -5098,7 +5238,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
protected void startRowDrag(Event event, final int type,
Element targetTdOrTr) {
- mDown = true;
VTransferable transferable = new VTransferable();
transferable.setDragSource(ConnectorMap.get(client)
.getConnector(VScrollTable.this));
@@ -5290,6 +5429,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
*
* @see com.vaadin.terminal.gwt.client.ui.IActionOwner#getActions ()
*/
+
public Action[] getActions() {
if (actionKeys == null) {
return new Action[] {};
@@ -5299,6 +5439,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
final String actionKey = actionKeys[i];
final TreeAction a = new TreeAction(this,
String.valueOf(rowKey), actionKey) {
+
@Override
public void execute() {
super.execute();
@@ -5586,6 +5727,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* column widths "optimally". Doing this lazily to avoid expensive
* calculation when resizing is not yet finished.
*/
+
@Override
public void run() {
if (scrollBody == null) {
@@ -5682,7 +5824,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
if (isDynamicHeight() && totalRows == pageLength) {
// fix body height (may vary if lazy loading is offhorizontal
// scrollbar appears/disappears)
- int bodyHeight = scrollBody.getRequiredHeight();
+ int bodyHeight = Util.getRequiredHeight(scrollBody);
boolean needsSpaceForHorizontalScrollbar = (availW < usedMinimumWidth);
if (needsSpaceForHorizontalScrollbar) {
bodyHeight += Util.getNativeScrollbarSize();
@@ -5695,6 +5837,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
scrollBody.reLayoutComponents();
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
}
@@ -5804,15 +5947,26 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
void updateHeight() {
setContainerHeight();
- updatePageLength();
-
+ if (initializedAndAttached) {
+ updatePageLength();
+ }
if (!rendering) {
// Webkit may sometimes get an odd rendering bug (white space
// between header and body), see bug #3875. Running
// overflow hack here to shake body element a bit.
- Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
+ // We must run the fix as a deferred command to prevent it from
+ // overwriting the scroll position with an outdated value, see
+ // #7607.
+ Scheduler.get().scheduleDeferred(new Command() {
+
+ public void execute() {
+ Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
+ }
+ });
}
+ triggerLazyColumnAdjustment(false);
+
/*
* setting height may affect wheter the component has scrollbars ->
* needs scrolling or not
@@ -5826,6 +5980,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* lost). Example ITabPanel just set contained components invisible and back
* when changing tabs.
*/
+
@Override
public void setVisible(boolean visible) {
if (isVisible() != visible) {
@@ -5833,6 +5988,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
if (initializedAndAttached) {
if (visible) {
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
scrollBodyPanel
.setScrollPosition(measureRowHeightOffset(firstRowInViewPort));
@@ -5866,6 +6022,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* This method has logic which rows needs to be requested from server when
* user scrolls
*/
+
public void onScroll(ScrollEvent event) {
scrollLeft = scrollBodyPanel.getElement().getScrollLeft();
scrollTop = scrollBodyPanel.getScrollPosition();
@@ -5894,6 +6051,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
// correct
// value available soon.
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
onScroll(null);
}
@@ -5994,7 +6152,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
return false;
}
- // @Override
+ //
// public int hashCode() {
// return overkey;
// }
@@ -6053,6 +6211,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
deEmphasis();
final TableDDDetails newDetails = dropDetails;
VAcceptCallback cb = new VAcceptCallback() {
+
public void accepted(VDragEvent event) {
if (newDetails.equals(dropDetails)) {
dragAccepted(event);
@@ -6459,6 +6618,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event
* .dom.client.FocusEvent)
*/
+
public void onFocus(FocusEvent event) {
if (isFocusable()) {
hasFocus = true;
@@ -6479,6 +6639,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event
* .dom.client.BlurEvent)
*/
+
public void onBlur(BlurEvent event) {
hasFocus = false;
navKeyDown = false;
@@ -6551,6 +6712,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
*
* @see com.vaadin.terminal.gwt.client.Focusable#focus()
*/
+
public void focus() {
if (isFocusable()) {
scrollBodyPanel.focus();
@@ -6594,6 +6756,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
public void startScrollingVelocityTimer() {
if (scrollingVelocityTimer == null) {
scrollingVelocityTimer = new Timer() {
+
@Override
public void run() {
scrollingVelocity++;
@@ -6630,6 +6793,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
public void lazyRevertFocusToRow(final VScrollTableRow currentlyFocusedRow) {
Scheduler.get().scheduleFinally(new ScheduledCommand() {
+
public void execute() {
if (currentlyFocusedRow != null) {
setRowFocus(currentlyFocusedRow);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java
index c97ede1252..aba5a41f9a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java
@@ -331,6 +331,11 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
return width;
}
+
+ public Element getCloseButton() {
+ return closeButton;
+ }
+
}
static class TabBar extends ComplexPanel implements ClickHandler,
@@ -393,7 +398,14 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
public void onClick(ClickEvent event) {
- Widget caption = (Widget) event.getSource();
+ TabCaption caption = (TabCaption) event.getSource();
+ Element targetElement = event.getNativeEvent().getEventTarget()
+ .cast();
+ // the tab should not be focused if the close button was clicked
+ if (targetElement == caption.getCloseButton()) {
+ return;
+ }
+
int index = getWidgetIndex(caption.getParent());
// IE needs explicit focus()
if (BrowserInfo.get().isIE()) {
@@ -1021,6 +1033,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
final Style style = scroller.getStyle();
style.setProperty("whiteSpace", "normal");
Scheduler.get().scheduleDeferred(new Command() {
+
public void execute() {
style.setProperty("whiteSpace", "");
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java
index f2b37c3a1c..bd6cddb682 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java
@@ -4,16 +4,12 @@
package com.vaadin.terminal.gwt.client.ui.tabsheet;
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.event.dom.client.TouchStartEvent;
-import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler;
/**
* A panel that displays all of its child widgets in a 'deck', where only one
@@ -27,38 +23,15 @@ import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
public class VTabsheetPanel extends ComplexPanel {
private Widget visibleWidget;
- private TouchScrollDelegate touchScrollDelegate;
+
+ private final TouchScrollHandler touchScrollHandler;
/**
* Creates an empty tabsheet panel.
*/
public VTabsheetPanel() {
setElement(DOM.createDiv());
- sinkEvents(Event.TOUCHEVENTS);
- addDomHandler(new TouchStartHandler() {
- public void onTouchStart(TouchStartEvent event) {
- /*
- * All container elements needs to be scrollable by one finger.
- * Update the scrollable element list of touch delegate on each
- * touch start.
- */
- NodeList<Node> childNodes = getElement().getChildNodes();
- Element[] elements = new Element[childNodes.getLength()];
- for (int i = 0; i < elements.length; i++) {
- elements[i] = (Element) childNodes.getItem(i);
- }
- getTouchScrollDelegate().setElements(elements);
- getTouchScrollDelegate().onTouchStart(event);
- }
- }, TouchStartEvent.getType());
- }
-
- protected TouchScrollDelegate getTouchScrollDelegate() {
- if (touchScrollDelegate == null) {
- touchScrollDelegate = new TouchScrollDelegate();
- }
- return touchScrollDelegate;
-
+ touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
}
/**
@@ -77,8 +50,8 @@ public class VTabsheetPanel extends ComplexPanel {
private Element createContainerElement() {
Element el = DOM.createDiv();
DOM.setStyleAttribute(el, "position", "absolute");
- DOM.setStyleAttribute(el, "overflow", "auto");
hide(el);
+ touchScrollHandler.addElement(el);
return el;
}
@@ -122,6 +95,7 @@ public class VTabsheetPanel extends ComplexPanel {
if (parent != null) {
DOM.removeChild(getElement(), parent);
}
+ touchScrollHandler.removeElement(parent);
}
return removed;
}
@@ -141,6 +115,8 @@ public class VTabsheetPanel extends ComplexPanel {
hide(DOM.getParent(visibleWidget.getElement()));
}
visibleWidget = newVisible;
+ touchScrollHandler.setElements(visibleWidget.getElement()
+ .getParentElement());
}
// Always ensure the selected tab is visible. If server prevents a tab
// change we might end up here with visibleWidget == newVisible but its
diff --git a/src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java b/src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java
index f7cd9d133e..9a8e0e9ce1 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java
@@ -379,26 +379,34 @@ public class VTreeTable extends VScrollTable {
rowsToDelete.add(row);
}
}
- RowCollapseAnimation anim = new RowCollapseAnimation(rowsToDelete) {
- @Override
- protected void onComplete() {
- super.onComplete();
- // Actually unlink the rows and update the cache after the
- // animation is done.
- unlinkAndReindexRows(firstIndex, rows);
- discardRowsOutsideCacheWindow();
- ensureCacheFilled();
- }
- };
- anim.run(150);
+ if (!rowsToDelete.isEmpty()) {
+ // #8810 Only animate if there's something to animate
+ RowCollapseAnimation anim = new RowCollapseAnimation(
+ rowsToDelete) {
+ @Override
+ protected void onComplete() {
+ super.onComplete();
+ // Actually unlink the rows and update the cache after
+ // the
+ // animation is done.
+ unlinkAndReindexRows(firstIndex, rows);
+ discardRowsOutsideCacheWindow();
+ ensureCacheFilled();
+ }
+ };
+ anim.run(150);
+ }
}
protected List<VScrollTableRow> insertRowsAnimated(UIDL rowData,
int firstIndex, int rows) {
List<VScrollTableRow> insertedRows = insertAndReindexRows(rowData,
firstIndex, rows);
- RowExpandAnimation anim = new RowExpandAnimation(insertedRows);
- anim.run(150);
+ if (!insertedRows.isEmpty()) {
+ // Only animate if there's something to animate (#8810)
+ RowExpandAnimation anim = new RowExpandAnimation(insertedRows);
+ anim.run(150);
+ }
return insertedRows;
}
@@ -521,6 +529,10 @@ public class VTreeTable extends VScrollTable {
private Element cloneTable;
private AnimationPreparator preparator;
+ /**
+ * @param rows
+ * List of rows to animate. Must not be empty.
+ */
public RowExpandAnimation(List<VScrollTableRow> rows) {
this.rows = rows;
buildAndInsertAnimatingDiv();
@@ -641,6 +653,10 @@ public class VTreeTable extends VScrollTable {
private final List<VScrollTableRow> rows;
+ /**
+ * @param rows
+ * List of rows to animate. Must not be empty.
+ */
public RowCollapseAnimation(List<VScrollTableRow> rows) {
super(rows);
this.rows = rows;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java b/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java
index d08387fc6d..8fd84a9ea6 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java
@@ -40,6 +40,7 @@ import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
import com.vaadin.terminal.gwt.client.ui.VOverlay;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
/**
* "Sub window" component.
@@ -264,8 +265,10 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
if (!orderingDefered) {
orderingDefered = true;
Scheduler.get().scheduleFinally(new Command() {
+
public void execute() {
doServerSideOrdering();
+ VNotification.bringNotificationsToFront();
}
});
}
@@ -275,6 +278,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
orderingDefered = false;
VWindow[] array = windowOrder.toArray(new VWindow[windowOrder.size()]);
Arrays.sort(array, new Comparator<VWindow>() {
+
public int compare(VWindow o1, VWindow o2) {
/*
* Order by modality, then by bringtofront sequence.
diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java
index 3a37baafbb..83de039f0b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java
@@ -201,8 +201,8 @@ public class WindowConnector extends AbstractComponentContainerConnector
// We always have 1 child, unless the child is hidden
Widget newChildWidget = null;
ComponentConnector newChild = null;
- if (getChildren().size() == 1) {
- newChild = getChildren().get(0);
+ if (getChildComponents().size() == 1) {
+ newChild = getChildComponents().get(0);
newChildWidget = newChild.getWidget();
}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
index 1acc9d128a..c2f887674a 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
@@ -64,10 +64,7 @@ import com.vaadin.ui.Root;
public abstract class AbstractApplicationPortlet extends GenericPortlet
implements Constants {
- private static final Logger logger = Logger
- .getLogger(AbstractApplicationPortlet.class.getName());
-
- private static class WrappedHttpAndPortletRequest extends
+ public static class WrappedHttpAndPortletRequest extends
WrappedPortletRequest {
public WrappedHttpAndPortletRequest(PortletRequest request,
@@ -112,7 +109,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
}
- private static class WrappedGateinRequest extends
+ public static class WrappedGateinRequest extends
WrappedHttpAndPortletRequest {
public WrappedGateinRequest(PortletRequest request,
DeploymentConfiguration deploymentConfiguration) {
@@ -134,7 +131,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
}
- private static class WrappedLiferayRequest extends
+ public static class WrappedLiferayRequest extends
WrappedHttpAndPortletRequest {
public WrappedLiferayRequest(PortletRequest request,
@@ -169,7 +166,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
- private static class AbstractApplicationPortletWrapper implements Callback {
+ public static class AbstractApplicationPortletWrapper implements Callback {
private final AbstractApplicationPortlet portlet;
@@ -203,6 +200,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
*/
public static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme";
+ public static final String WRITE_AJAX_PAGE_SCRIPT_WIDGETSET_SHOULD_WRITE = "writeAjaxPageScriptWidgetsetShouldWrite";
+
// TODO some parts could be shared with AbstractApplicationServlet
// TODO Can we close the application when the portlet is removed? Do we know
@@ -213,6 +212,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
private boolean productionMode = false;
private DeploymentConfiguration deploymentConfiguration = new DeploymentConfiguration() {
+
public String getConfiguredWidgetset(WrappedRequest request) {
String widgetset = getApplicationOrSystemProperty(
@@ -271,6 +271,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
* @return The location of static resources (inside which there should
* be a VAADIN directory). Does not end with a slash (/).
*/
+
public String getStaticFileLocation(WrappedRequest request) {
String staticFileLocation = WrappedPortletRequest.cast(request)
.getPortalProperty(
@@ -329,7 +330,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
* Print an information/warning message about running with xsrf
* protection disabled
*/
- logger.warning(WARNING_XSRF_PROTECTION_DISABLED);
+ getLogger().warning(WARNING_XSRF_PROTECTION_DISABLED);
}
}
@@ -345,7 +346,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
if (!productionMode) {
/* Print an information/warning message about running in debug mode */
// TODO Maybe we need a different message for portlets?
- logger.warning(NOT_PRODUCTION_MODE_INFO);
+ getLogger().warning(NOT_PRODUCTION_MODE_INFO);
}
}
@@ -500,20 +501,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper(
this);
- WrappedPortletRequest wrappedRequest;
-
- String portalInfo = request.getPortalContext().getPortalInfo()
- .toLowerCase();
- if (portalInfo.contains("liferay")) {
- wrappedRequest = new WrappedLiferayRequest(request,
- getDeploymentConfiguration());
- } else if (portalInfo.contains("gatein")) {
- wrappedRequest = new WrappedGateinRequest(request,
- getDeploymentConfiguration());
- } else {
- wrappedRequest = new WrappedPortletRequest(request,
- getDeploymentConfiguration());
- }
+ WrappedPortletRequest wrappedRequest = createWrappedRequest(request);
WrappedPortletResponse wrappedResponse = new WrappedPortletResponse(
response, getDeploymentConfiguration());
@@ -552,7 +540,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
if (application == null) {
return;
}
- Application.setCurrentApplication(application);
+ Application.setCurrent(application);
/*
* Get or create an application context and an application
@@ -650,7 +638,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
/* Handle the request */
if (requestType == RequestType.FILE_UPLOAD) {
- applicationManager.handleFileUpload(wrappedRequest,
+ applicationManager.handleFileUpload(root, wrappedRequest,
wrappedResponse);
return;
} else if (requestType == RequestType.BROWSER_DETAILS) {
@@ -678,11 +666,12 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
} catch (final SessionExpiredException e) {
// TODO Figure out a better way to deal with
// SessionExpiredExceptions
- logger.finest("A user session has expired");
+ getLogger().finest("A user session has expired");
} catch (final GeneralSecurityException e) {
// TODO Figure out a better way to deal with
// GeneralSecurityExceptions
- logger.fine("General security exception, the security key was probably incorrect.");
+ getLogger()
+ .fine("General security exception, the security key was probably incorrect.");
} catch (final Throwable e) {
handleServiceException(request, response, application, e);
} finally {
@@ -700,25 +689,51 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
} finally {
- Root.setCurrentRoot(null);
- Application.setCurrentApplication(null);
+ Root.setCurrent(null);
+ Application.setCurrent(null);
- requestTimer
- .stop((AbstractWebApplicationContext) application
- .getContext());
+ PortletSession session = request
+ .getPortletSession(false);
+ if (session != null) {
+ requestTimer.stop(getApplicationContext(session));
+ }
}
}
}
}
}
+ /**
+ * Wraps the request in a (possibly portal specific) wrapped portlet
+ * request.
+ *
+ * @param request
+ * The original PortletRequest
+ * @return A wrapped version of the PorletRequest
+ */
+ protected WrappedPortletRequest createWrappedRequest(PortletRequest request) {
+ String portalInfo = request.getPortalContext().getPortalInfo()
+ .toLowerCase();
+ if (portalInfo.contains("liferay")) {
+ return new WrappedLiferayRequest(request,
+ getDeploymentConfiguration());
+ } else if (portalInfo.contains("gatein")) {
+ return new WrappedGateinRequest(request,
+ getDeploymentConfiguration());
+ } else {
+ return new WrappedPortletRequest(request,
+ getDeploymentConfiguration());
+ }
+
+ }
+
private DeploymentConfiguration getDeploymentConfiguration() {
return deploymentConfiguration;
}
private void handleUnknownRequest(PortletRequest request,
PortletResponse response) {
- logger.warning("Unknown request type");
+ getLogger().warning("Unknown request type");
}
/**
@@ -784,8 +799,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
os.write(buffer, 0, bytes);
}
} else {
- logger.info("Requested resource [" + resourceID
- + "] could not be found");
+ getLogger().info(
+ "Requested resource [" + resourceID
+ + "] could not be found");
response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
Integer.toString(HttpServletResponse.SC_NOT_FOUND));
}
@@ -1130,4 +1146,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
return PortletApplicationContext2.getApplicationContext(portletSession);
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(AbstractApplicationPortlet.class.getName());
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
index 905cfe7e3c..f7e46a7ca9 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
@@ -87,9 +87,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
// TODO Move some (all?) of the constants to a separate interface (shared
// with portlet)
- private static final Logger logger = Logger
- .getLogger(AbstractApplicationServlet.class.getName());
-
private Properties applicationProperties;
private boolean productionMode = false;
@@ -99,6 +96,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
private int resourceCacheTime = 3600;
private DeploymentConfiguration deploymentConfiguration = new DeploymentConfiguration() {
+
public String getStaticFileLocation(WrappedRequest request) {
HttpServletRequest servletRequest = WrappedHttpServletRequest
.cast(request);
@@ -149,8 +147,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* if an exception has occurred that interferes with the
* servlet's normal operation.
*/
- @SuppressWarnings("unchecked")
@Override
+ @SuppressWarnings("unchecked")
public void init(javax.servlet.ServletConfig servletConfig)
throws javax.servlet.ServletException {
super.init(servletConfig);
@@ -186,7 +184,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* Print an information/warning message about running with xsrf
* protection disabled
*/
- logger.warning(WARNING_XSRF_PROTECTION_DISABLED);
+ getLogger().warning(WARNING_XSRF_PROTECTION_DISABLED);
}
}
@@ -200,7 +198,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
if (!productionMode) {
/* Print an information/warning message about running in debug mode */
- logger.warning(NOT_PRODUCTION_MODE_INFO);
+ getLogger().warning(NOT_PRODUCTION_MODE_INFO);
}
}
@@ -214,7 +212,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
} catch (NumberFormatException nfe) {
// Default is 1h
resourceCacheTime = 3600;
- logger.warning(WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC);
+ getLogger().warning(WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC);
}
}
@@ -333,6 +331,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* @throws IOException
* if the request for the TRACE cannot be handled.
*/
+
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
@@ -386,7 +385,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
if (application == null) {
return;
}
- Application.setCurrentApplication(application);
+ Application.setCurrent(application);
/*
* Get or create a WebApplicationContext and an ApplicationManager
@@ -422,25 +421,29 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
/* Handle the request */
if (requestType == RequestType.FILE_UPLOAD) {
- applicationManager.handleFileUpload(application, request,
- response);
+ Root root = application.getRootForRequest(request);
+ if (root == null) {
+ throw new ServletException(ERROR_NO_ROOT_FOUND);
+ }
+ applicationManager.handleFileUpload(root, request, response);
return;
} else if (requestType == RequestType.UIDL) {
- // Handles AJAX UIDL requests
Root root = application.getRootForRequest(request);
if (root == null) {
- throw new ServletException(ERROR_NO_WINDOW_FOUND);
+ throw new ServletException(ERROR_NO_ROOT_FOUND);
}
+ // Handles AJAX UIDL requests
applicationManager.handleUidlRequest(request, response,
servletWrapper, root);
return;
} else if (requestType == RequestType.BROWSER_DETAILS) {
+ // Browser details - not related to a specific root
applicationManager.handleBrowserDetailsRequest(request,
response, application);
return;
}
- // Removes application if it has stopped (mayby by thread or
+ // Removes application if it has stopped (maybe by thread or
// transactionlistener)
if (!application.isRunning()) {
endApplication(request, response, application);
@@ -475,12 +478,13 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
.onRequestEnd(request, response);
}
} finally {
- Root.setCurrentRoot(null);
- Application.setCurrentApplication(null);
+ Root.setCurrent(null);
+ Application.setCurrent(null);
- requestTimer
- .stop((AbstractWebApplicationContext) application
- .getContext());
+ HttpSession session = request.getSession(false);
+ if (session != null) {
+ requestTimer.stop(getApplicationContext(session));
+ }
}
}
@@ -796,8 +800,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
resultPath = url.getFile();
} catch (final Exception e) {
// FIXME: Handle exception
- logger.log(Level.INFO, "Could not find resource path " + path,
- e);
+ getLogger().log(Level.INFO,
+ "Could not find resource path " + path, e);
}
}
return resultPath;
@@ -1054,10 +1058,11 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
if (resourceUrl == null) {
// cannot serve requested file
- logger.info("Requested resource ["
- + filename
- + "] not found from filesystem or through class loader."
- + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder.");
+ getLogger()
+ .info("Requested resource ["
+ + filename
+ + "] not found from filesystem or through class loader."
+ + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder.");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
@@ -1065,9 +1070,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
// security check: do not permit navigation out of the VAADIN
// directory
if (!isAllowedVAADINResourceUrl(request, resourceUrl)) {
- logger.info("Requested resource ["
- + filename
- + "] not accessible in the VAADIN directory or access to it is forbidden.");
+ getLogger()
+ .info("Requested resource ["
+ + filename
+ + "] not accessible in the VAADIN directory or access to it is forbidden.");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
@@ -1090,10 +1096,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
} catch (Exception e) {
// Failed to find out last modified timestamp. Continue without it.
- logger.log(
- Level.FINEST,
- "Failed to find out last modified timestamp. Continuing without it.",
- e);
+ getLogger()
+ .log(Level.FINEST,
+ "Failed to find out last modified timestamp. Continuing without it.",
+ e);
} finally {
if (connection instanceof URLConnection) {
try {
@@ -1105,7 +1111,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
is.close();
}
} catch (IOException e) {
- logger.log(Level.INFO,
+ getLogger().log(Level.INFO,
"Error closing URLConnection input stream", e);
}
}
@@ -1130,7 +1136,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* parameter in web.xml
*/
response.setHeader("Cache-Control",
- "max-age: " + String.valueOf(resourceCacheTime));
+ "max-age= " + String.valueOf(resourceCacheTime));
}
// Write the resource to the client.
@@ -1173,12 +1179,14 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
// loader sees it.
if (!resourceUrl.getPath().contains("!/VAADIN/")) {
- logger.info("Blocked attempt to access a JAR entry not starting with /VAADIN/: "
- + resourceUrl);
+ getLogger().info(
+ "Blocked attempt to access a JAR entry not starting with /VAADIN/: "
+ + resourceUrl);
return false;
}
- logger.fine("Accepted access to a JAR entry using a class loader: "
- + resourceUrl);
+ getLogger().fine(
+ "Accepted access to a JAR entry using a class loader: "
+ + resourceUrl);
return true;
} else {
// Some servers such as GlassFish extract files from JARs (file:)
@@ -1188,11 +1196,13 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
// "/../"
if (!resourceUrl.getPath().contains("/VAADIN/")
|| resourceUrl.getPath().contains("/../")) {
- logger.info("Blocked attempt to access file: " + resourceUrl);
+ getLogger().info(
+ "Blocked attempt to access file: " + resourceUrl);
return false;
}
- logger.fine("Accepted access to a file using a class loader: "
- + resourceUrl);
+ getLogger().fine(
+ "Accepted access to a file using a class loader: "
+ + resourceUrl);
return true;
}
}
@@ -1733,4 +1743,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
c > 96 && c < 123 // a-z
;
}
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(AbstractApplicationServlet.class.getName());
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index c08d70aa37..7cad8e3a33 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -49,6 +49,7 @@ import com.vaadin.Version;
import com.vaadin.external.json.JSONArray;
import com.vaadin.external.json.JSONException;
import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.AbstractClientConnector;
import com.vaadin.terminal.CombinedRequest;
import com.vaadin.terminal.LegacyPaint;
import com.vaadin.terminal.PaintException;
@@ -67,14 +68,15 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.communication.UidlValue;
import com.vaadin.terminal.gwt.server.BootstrapHandler.BootstrapContext;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout;
+import com.vaadin.terminal.gwt.server.RpcManager.RpcInvocationException;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Component;
-import com.vaadin.ui.DirtyConnectorTracker;
+import com.vaadin.ui.ConnectorTracker;
import com.vaadin.ui.HasComponents;
-import com.vaadin.ui.Panel;
import com.vaadin.ui.Root;
import com.vaadin.ui.Window;
@@ -91,9 +93,6 @@ public abstract class AbstractCommunicationManager implements Serializable {
private static final String DASHDASH = "--";
- private static final Logger logger = Logger
- .getLogger(AbstractCommunicationManager.class.getName());
-
private static final RequestHandler APP_RESOURCE_HANDLER = new ApplicationResourceHandler();
private static final RequestHandler UNSUPPORTED_BROWSER_HANDLER = new UnsupportedBrowserHandler();
@@ -136,6 +135,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
+ /**
+ * The application this communication manager is used for
+ */
private final Application application;
private List<String> locales;
@@ -519,7 +521,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) {
String pid = request
.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT);
- highlightedConnector = root.getApplication().getConnector(pid);
+ highlightedConnector = root.getConnectorTracker().getConnector(
+ pid);
highlightConnector(highlightedConnector);
}
}
@@ -538,7 +541,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (root == null) {
// This should not happen, no windows exists but
// application is still open.
- logger.warning("Could not get root for application");
+ getLogger().warning("Could not get root for application");
return;
}
} else {
@@ -561,7 +564,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
// FIXME: Handle exception
// Not critical, but something is still wrong; print
// stacktrace
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"getSystemMessages() failed - continuing", e2);
}
if (ci != null) {
@@ -603,8 +606,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
if (!Version.getFullVersion().equals(widgetsetVersion)) {
- logger.warning(String.format(Constants.WIDGETSET_MISMATCH_INFO,
- Version.getFullVersion(), widgetsetVersion));
+ getLogger().warning(
+ String.format(Constants.WIDGETSET_MISMATCH_INFO,
+ Version.getFullVersion(), widgetsetVersion));
}
}
@@ -618,7 +622,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
protected void postPaint(Root root) {
// Remove connectors that have been detached from the application during
// handling of the request
- root.getApplication().cleanConnectorMap();
+ root.getConnectorTracker().cleanConnectorMap();
}
protected void highlightConnector(Connector highlightedConnector) {
@@ -637,7 +641,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
printHighlightedComponentHierarchy(sb, component);
}
- logger.info(sb.toString());
+ getLogger().info(sb.toString());
}
protected void printHighlightedComponentHierarchy(StringBuilder sb,
@@ -764,12 +768,11 @@ public abstract class AbstractCommunicationManager implements Serializable {
ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>();
Application application = root.getApplication();
// Paints components
- DirtyConnectorTracker rootConnectorTracker = root
- .getDirtyConnectorTracker();
- logger.log(Level.FINE, "* Creating response to client");
+ ConnectorTracker rootConnectorTracker = root.getConnectorTracker();
+ getLogger().log(Level.FINE, "* Creating response to client");
if (repaintAll) {
getClientCache(root).clear();
- rootConnectorTracker.markAllComponentsDirty();
+ rootConnectorTracker.markAllConnectorsDirty();
// Reset sent locales
locales = null;
@@ -777,16 +780,18 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
dirtyVisibleConnectors
- .addAll(getDirtyVisibleComponents(rootConnectorTracker));
+ .addAll(getDirtyVisibleConnectors(rootConnectorTracker));
- logger.log(Level.FINE, "Found " + dirtyVisibleConnectors.size()
- + " dirty connectors to paint");
+ getLogger().log(
+ Level.FINE,
+ "Found " + dirtyVisibleConnectors.size()
+ + " dirty connectors to paint");
for (ClientConnector connector : dirtyVisibleConnectors) {
if (connector instanceof Component) {
((Component) connector).updateState();
}
}
- rootConnectorTracker.markAllComponentsClean();
+ rootConnectorTracker.markAllConnectorsClean();
outWriter.print("\"changes\":[");
@@ -840,16 +845,16 @@ public abstract class AbstractCommunicationManager implements Serializable {
try {
referenceState = stateType.newInstance();
} catch (Exception e) {
- logger.log(Level.WARNING,
+ getLogger().log(
+ Level.WARNING,
"Error creating reference object for state of type "
+ stateType.getName());
}
}
- JSONArray stateJsonArray = JsonCodec.encode(state,
- referenceState, stateType, application);
+ Object stateJson = JsonCodec.encode(state, referenceState,
+ stateType, root.getConnectorTracker());
- sharedStates
- .put(connector.getConnectorId(), stateJsonArray);
+ sharedStates.put(connector.getConnectorId(), stateJson);
} catch (JSONException e) {
throw new PaintException(
"Failed to serialize shared state for connector "
@@ -893,26 +898,24 @@ public abstract class AbstractCommunicationManager implements Serializable {
outWriter.print("\"hierarchy\":");
JSONObject hierarchyInfo = new JSONObject();
- for (Connector connector : dirtyVisibleConnectors) {
- if (connector instanceof HasComponents) {
- HasComponents parent = (HasComponents) connector;
- String parentConnectorId = parent.getConnectorId();
- JSONArray children = new JSONArray();
-
- for (Component child : getChildComponents(parent)) {
- if (isVisible(child)) {
- children.put(child.getConnectorId());
- }
- }
- try {
- hierarchyInfo.put(parentConnectorId, children);
- } catch (JSONException e) {
- throw new PaintException(
- "Failed to send hierarchy information about "
- + parentConnectorId + " to the client: "
- + e.getMessage(), e);
+ for (ClientConnector connector : dirtyVisibleConnectors) {
+ String connectorId = connector.getConnectorId();
+ JSONArray children = new JSONArray();
+
+ for (ClientConnector child : AbstractClientConnector
+ .getAllChildrenIterable(connector)) {
+ if (isVisible(child)) {
+ children.put(child.getConnectorId());
}
}
+ try {
+ hierarchyInfo.put(connectorId, children);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to send hierarchy information about "
+ + connectorId + " to the client: "
+ + e.getMessage(), e);
+ }
}
outWriter.append(hierarchyInfo.toString());
outWriter.print(", "); // close hierarchy
@@ -937,7 +940,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
invocationJson.put(invocation.getMethodName());
JSONArray paramJson = new JSONArray();
for (int i = 0; i < invocation.getParameterTypes().length; ++i) {
- Class<?> parameterType = invocation.getParameterTypes()[i];
+ Type parameterType = invocation.getParameterTypes()[i];
Object referenceParameter = null;
// TODO Use default values for RPC parameter types
// if (!JsonCodec.isInternalType(parameterType)) {
@@ -951,7 +954,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
// }
paramJson.put(JsonCodec.encode(
invocation.getParameters()[i], referenceParameter,
- parameterType, application));
+ parameterType, root.getConnectorTracker()));
}
invocationJson.put(paramJson);
rpcCalls.put(invocationJson);
@@ -1008,16 +1011,16 @@ public abstract class AbstractCommunicationManager implements Serializable {
(Class[]) null);
ci = (Application.SystemMessages) m.invoke(null, (Object[]) null);
} catch (NoSuchMethodException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"getSystemMessages() failed - continuing", e);
} catch (IllegalArgumentException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"getSystemMessages() failed - continuing", e);
} catch (IllegalAccessException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"getSystemMessages() failed - continuing", e);
} catch (InvocationTargetException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"getSystemMessages() failed - continuing", e);
}
@@ -1056,8 +1059,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
is = getThemeResourceAsStream(root, getTheme(root), resource);
} catch (final Exception e) {
// FIXME: Handle exception
- logger.log(Level.FINER, "Failed to get theme resource stream.",
- e);
+ getLogger().log(Level.FINER,
+ "Failed to get theme resource stream.", e);
}
if (is != null) {
@@ -1076,13 +1079,13 @@ public abstract class AbstractCommunicationManager implements Serializable {
r.close();
} catch (final java.io.IOException e) {
// FIXME: Handle exception
- logger.log(Level.INFO, "Resource transfer failed", e);
+ getLogger().log(Level.INFO, "Resource transfer failed", e);
}
outWriter.print("\""
+ JsonPaintTarget.escapeJSON(layout.toString()) + "\"");
} else {
// FIXME: Handle exception
- logger.severe("CustomLayout not found: " + resource);
+ getLogger().severe("CustomLayout not found: " + resource);
}
}
outWriter.print("}");
@@ -1173,8 +1176,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
sortByHierarchy((List) legacyComponents);
for (Vaadin6Component c : legacyComponents) {
- logger.fine("Painting Vaadin6Component " + c.getClass().getName()
- + "@" + Integer.toHexString(c.hashCode()));
+ getLogger().fine(
+ "Painting Vaadin6Component " + c.getClass().getName() + "@"
+ + Integer.toHexString(c.hashCode()));
paintTarget.startTag("change");
final String pid = c.getConnectorId();
paintTarget.addAttribute("pid", pid);
@@ -1189,6 +1193,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
// containers rely on that their updateFromUIDL method has been called
// before children start calling e.g. updateCaption
Collections.sort(paintables, new Comparator<Component>() {
+
public int compare(Component c1, Component c2) {
int depth1 = 0;
while (c1.getParent() != null) {
@@ -1223,6 +1228,30 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
/**
+ * Checks if the connector is visible in context. For Components,
+ * {@link #isVisible(Component)} is used. For other types of connectors, the
+ * contextual visibility of its first Component ancestor is used. If no
+ * Component ancestor is found, the connector is not visible.
+ *
+ * @param connector
+ * The connector to check
+ * @return <code>true</code> if the connector is visible to the client,
+ * <code>false</code> otherwise
+ */
+ static boolean isVisible(ClientConnector connector) {
+ if (connector instanceof Component) {
+ return isVisible((Component) connector);
+ } else {
+ ClientConnector parent = connector.getParent();
+ if (parent == null) {
+ return false;
+ } else {
+ return isVisible(parent);
+ }
+ }
+ }
+
+ /**
* Checks if the component is visible in context, i.e. returns false if the
* child is hidden, the parent is hidden or the parent says the child should
* not be rendered (using
@@ -1233,9 +1262,17 @@ public abstract class AbstractCommunicationManager implements Serializable {
* @return true if the child is visible to the client, false otherwise
*/
static boolean isVisible(Component child) {
+ if (!child.isVisible()) {
+ return false;
+ }
+
HasComponents parent = child.getParent();
- if (parent == null || !child.isVisible()) {
- return child.isVisible();
+ if (parent == null) {
+ if (child instanceof Root) {
+ return child.isVisible();
+ } else {
+ return false;
+ }
}
return parent.isComponentVisible(child) && isVisible(parent);
@@ -1256,30 +1293,6 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
- public static Iterable<Component> getChildComponents(HasComponents cc) {
- // TODO This must be moved to Root/Panel
- if (cc instanceof Root) {
- Root root = (Root) cc;
- List<Component> children = new ArrayList<Component>();
- if (root.getContent() != null) {
- children.add(root.getContent());
- }
- for (Window w : root.getWindows()) {
- children.add(w);
- }
- return children;
- } else if (cc instanceof Panel) {
- // This is so wrong.. (#2924)
- if (((Panel) cc).getContent() == null) {
- return Collections.emptyList();
- } else {
- return Collections.singleton((Component) ((Panel) cc)
- .getContent());
- }
- }
- return cc;
- }
-
/**
* Collects all pending RPC calls from listed {@link ClientConnector}s and
* clears their RPC queues.
@@ -1402,7 +1415,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
for (int bi = 1; bi < bursts.length; bi++) {
// unescape any encoded separator characters in the burst
final String burst = unescapeBurst(bursts[bi]);
- success &= handleBurst(request, application2, burst);
+ success &= handleBurst(request, root, burst);
// In case that there were multiple bursts, we know that this is
// a special synchronous case for closing window. Thus we are
@@ -1444,36 +1457,38 @@ public abstract class AbstractCommunicationManager implements Serializable {
* directly.
*
* @param source
- * @param app
- * application receiving the burst
+ * @param root
+ * the root receiving the burst
* @param burst
* the content of the burst as a String to be parsed
* @return true if the processing of the burst was successful and there were
* no messages to non-existent components
*/
- public boolean handleBurst(Object source, Application app,
+ public boolean handleBurst(WrappedRequest source, Root root,
final String burst) {
boolean success = true;
try {
Set<Connector> enabledConnectors = new HashSet<Connector>();
- List<MethodInvocation> invocations = parseInvocations(burst);
+ List<MethodInvocation> invocations = parseInvocations(
+ root.getConnectorTracker(), burst);
for (MethodInvocation invocation : invocations) {
- final ClientConnector connector = getConnector(app,
+ final ClientConnector connector = getConnector(root,
invocation.getConnectorId());
if (connector != null && connector.isConnectorEnabled()) {
enabledConnectors.add(connector);
}
}
+
for (int i = 0; i < invocations.size(); i++) {
MethodInvocation invocation = invocations.get(i);
- final ClientConnector connector = getConnector(app,
+ final ClientConnector connector = getConnector(root,
invocation.getConnectorId());
if (connector == null) {
- logger.log(
+ getLogger().log(
Level.WARNING,
"RPC call to " + invocation.getInterfaceName()
+ "." + invocation.getMethodName()
@@ -1511,13 +1526,23 @@ public abstract class AbstractCommunicationManager implements Serializable {
msg += ", caption=" + caption;
}
}
- logger.warning(msg);
+ getLogger().warning(msg);
continue;
}
if (invocation instanceof ServerRpcMethodInvocation) {
- ServerRpcManager.applyInvocation(connector,
- (ServerRpcMethodInvocation) invocation);
+ try {
+ ServerRpcManager.applyInvocation(connector,
+ (ServerRpcMethodInvocation) invocation);
+ } catch (RpcInvocationException e) {
+ Throwable realException = e.getCause();
+ Component errorComponent = null;
+ if (connector instanceof Component) {
+ errorComponent = (Component) connector;
+ }
+ handleChangeVariablesError(root.getApplication(),
+ errorComponent, realException, null);
+ }
} else {
// All code below is for legacy variable changes
@@ -1525,8 +1550,18 @@ public abstract class AbstractCommunicationManager implements Serializable {
Map<String, Object> changes = legacyInvocation
.getVariableChanges();
try {
- changeVariables(source, (VariableOwner) connector,
- changes);
+ if (connector instanceof VariableOwner) {
+ changeVariables(source, (VariableOwner) connector,
+ changes);
+ } else {
+ throw new IllegalStateException(
+ "Received legacy variable change for "
+ + connector.getClass().getName()
+ + " ("
+ + connector.getConnectorId()
+ + ") which is not a VariableOwner. The client-side connector sent these legacy varaibles: "
+ + changes.keySet());
+ }
} catch (Exception e) {
Component errorComponent = null;
if (connector instanceof Component) {
@@ -1537,16 +1572,15 @@ public abstract class AbstractCommunicationManager implements Serializable {
errorComponent = (Component) dropHandlerOwner;
}
}
- handleChangeVariablesError(app, errorComponent, e,
- changes);
-
+ handleChangeVariablesError(root.getApplication(),
+ errorComponent, e, changes);
}
}
}
-
} catch (JSONException e) {
- logger.warning("Unable to parse RPC call from the client: "
- + e.getMessage());
+ getLogger().warning(
+ "Unable to parse RPC call from the client: "
+ + e.getMessage());
// TODO or return success = false?
throw new RuntimeException(e);
}
@@ -1558,12 +1592,15 @@ public abstract class AbstractCommunicationManager implements Serializable {
* Parse a message burst from the client into a list of MethodInvocation
* instances.
*
+ * @param connectorTracker
+ * The ConnectorTracker used to lookup connectors
* @param burst
* message string (JSON)
* @return list of MethodInvocation to perform
* @throws JSONException
*/
- private List<MethodInvocation> parseInvocations(final String burst)
+ private List<MethodInvocation> parseInvocations(
+ ConnectorTracker connectorTracker, final String burst)
throws JSONException {
JSONArray invocationsJson = new JSONArray(burst);
@@ -1576,7 +1613,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
JSONArray invocationJson = invocationsJson.getJSONArray(i);
MethodInvocation invocation = parseInvocation(invocationJson,
- previousInvocation);
+ previousInvocation, connectorTracker);
if (invocation != null) {
// Can be null iff the invocation was a legacy invocation and it
// was merged with the previous one
@@ -1588,7 +1625,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
private MethodInvocation parseInvocation(JSONArray invocationJson,
- MethodInvocation previousInvocation) throws JSONException {
+ MethodInvocation previousInvocation,
+ ConnectorTracker connectorTracker) throws JSONException {
String connectorId = invocationJson.getString(0);
String interfaceName = invocationJson.getString(1);
String methodName = invocationJson.getString(2);
@@ -1604,10 +1642,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
return parseLegacyChangeVariablesInvocation(connectorId,
interfaceName, methodName,
(LegacyChangeVariablesInvocation) previousInvocation,
- parametersJson);
+ parametersJson, connectorTracker);
} else {
return parseServerRpcInvocation(connectorId, interfaceName,
- methodName, parametersJson);
+ methodName, parametersJson, connectorTracker);
}
}
@@ -1615,17 +1653,18 @@ public abstract class AbstractCommunicationManager implements Serializable {
private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation(
String connectorId, String interfaceName, String methodName,
LegacyChangeVariablesInvocation previousInvocation,
- JSONArray parametersJson) throws JSONException {
+ JSONArray parametersJson, ConnectorTracker connectorTracker)
+ throws JSONException {
if (parametersJson.length() != 2) {
throw new JSONException(
"Invalid parameters in legacy change variables call. Expected 2, was "
+ parametersJson.length());
}
- String variableName = (String) JsonCodec
- .decodeInternalType(String.class, true,
- parametersJson.getJSONArray(0), application);
- Object value = JsonCodec.decodeInternalType(
- parametersJson.getJSONArray(1), application);
+ String variableName = parametersJson.getString(0);
+ UidlValue uidlValue = (UidlValue) JsonCodec.decodeInternalType(
+ UidlValue.class, true, parametersJson.get(1), connectorTracker);
+
+ Object value = uidlValue.getValue();
if (previousInvocation != null
&& previousInvocation.getConnectorId().equals(connectorId)) {
@@ -1639,7 +1678,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
private ServerRpcMethodInvocation parseServerRpcInvocation(
String connectorId, String interfaceName, String methodName,
- JSONArray parametersJson) throws JSONException {
+ JSONArray parametersJson, ConnectorTracker connectorTracker)
+ throws JSONException {
ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
connectorId, interfaceName, methodName, parametersJson.length());
@@ -1648,10 +1688,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
.getGenericParameterTypes();
for (int j = 0; j < parametersJson.length(); ++j) {
- JSONArray parameterJson = parametersJson.getJSONArray(j);
+ Object parameterValue = parametersJson.get(j);
Type parameterType = declaredRpcMethodParameterTypes[j];
parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType,
- parameterJson, application);
+ parameterValue, connectorTracker);
}
invocation.setParameters(parameters);
return invocation;
@@ -1662,8 +1702,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
owner.changeVariables(source, m);
}
- protected ClientConnector getConnector(Application app, String connectorId) {
- ClientConnector c = app.getConnector(connectorId);
+ protected ClientConnector getConnector(Root root, String connectorId) {
+ ClientConnector c = root.getConnectorTracker()
+ .getConnector(connectorId);
if (c == null
&& connectorId.equals(getDragAndDropService().getConnectorId())) {
return getDragAndDropService();
@@ -1745,10 +1786,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
* map from variable names to values
*/
private void handleChangeVariablesError(Application application,
- Component owner, Exception e, Map<String, Object> m) {
+ Component owner, Throwable t, Map<String, Object> m) {
boolean handled = false;
ChangeVariablesErrorEvent errorEvent = new ChangeVariablesErrorEvent(
- owner, e, m);
+ owner, t, m);
if (owner instanceof AbstractField) {
try {
@@ -1889,8 +1930,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
DateFormat dateFormat = DateFormat.getDateTimeInstance(
DateFormat.SHORT, DateFormat.SHORT, l);
if (!(dateFormat instanceof SimpleDateFormat)) {
- logger.warning("Unable to get default date pattern for locale "
- + l.toString());
+ getLogger().warning(
+ "Unable to get default date pattern for locale "
+ + l.toString());
dateFormat = new SimpleDateFormat();
}
final String df = ((SimpleDateFormat) dateFormat).toPattern();
@@ -2016,16 +2058,16 @@ public abstract class AbstractCommunicationManager implements Serializable {
* root window for which dirty components is to be fetched
* @return
*/
- private ArrayList<Component> getDirtyVisibleComponents(
- DirtyConnectorTracker dirtyConnectorTracker) {
- ArrayList<Component> dirtyComponents = new ArrayList<Component>();
- for (Component c : dirtyConnectorTracker.getDirtyComponents()) {
+ private ArrayList<ClientConnector> getDirtyVisibleConnectors(
+ ConnectorTracker connectorTracker) {
+ ArrayList<ClientConnector> dirtyConnectors = new ArrayList<ClientConnector>();
+ for (ClientConnector c : connectorTracker.getDirtyConnectors()) {
if (isVisible(c)) {
- dirtyComponents.add(c);
+ dirtyConnectors.add(c);
}
}
- return dirtyComponents;
+ return dirtyConnectors;
}
/**
@@ -2089,7 +2131,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (id == null) {
id = nextTypeKey++;
typeToKey.put(class1, id);
- logger.log(Level.FINE, "Mapping " + class1.getName() + " to " + id);
+ getLogger().log(Level.FINE,
+ "Mapping " + class1.getName() + " to " + id);
}
return id.toString();
}
@@ -2151,7 +2194,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
// if we do not yet have a currentRoot, it should be initialized
// shortly, and we should send the initial UIDL
- boolean sendUIDL = Root.getCurrentRoot() == null;
+ boolean sendUIDL = Root.getCurrent() == null;
try {
CombinedRequest combinedRequest = new CombinedRequest(request);
@@ -2226,7 +2269,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
writeUidlResponse(request, true, pWriter, root, false);
pWriter.print("}");
String initialUIDL = sWriter.toString();
- logger.log(Level.FINE, "Initial UIDL:" + initialUIDL);
+ getLogger().log(Level.FINE, "Initial UIDL:" + initialUIDL);
return initialUIDL;
}
@@ -2380,4 +2423,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(AbstractCommunicationManager.class.getName());
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java b/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java
index c0ae0afc26..bf4ea860a8 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java
@@ -32,9 +32,6 @@ import com.vaadin.terminal.ApplicationResource;
public abstract class AbstractWebApplicationContext implements
ApplicationContext, HttpSessionBindingListener, Serializable {
- private static final Logger logger = Logger
- .getLogger(AbstractWebApplicationContext.class.getName());
-
protected Collection<TransactionListener> listeners = Collections
.synchronizedList(new LinkedList<TransactionListener>());
@@ -145,7 +142,7 @@ public abstract class AbstractWebApplicationContext implements
// remove same application here. Possible if you got e.g. session
// lifetime 1 min but socket write may take longer than 1 min.
// FIXME: Handle exception
- logger.log(Level.SEVERE,
+ getLogger().log(Level.SEVERE,
"Could not remove application, leaking memory.", e);
}
}
@@ -252,4 +249,8 @@ public abstract class AbstractWebApplicationContext implements
return lastRequestTime;
}
+ private Logger getLogger() {
+ return Logger.getLogger(AbstractWebApplicationContext.class.getName());
+ }
+
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
index 8a0c700121..69f033c8cd 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;
@@ -78,7 +83,7 @@ public abstract class BootstrapHandler implements RequestHandler {
public Root getRoot() {
if (!rootFetched) {
- root = Root.getCurrentRoot();
+ root = Root.getCurrent();
rootFetched = true;
}
return root;
@@ -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 7e74c26fb1..dfdd58879d 100644
--- a/src/com/vaadin/terminal/gwt/server/ClientConnector.java
+++ b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
@@ -3,10 +3,15 @@
*/
package com.vaadin.terminal.gwt.server;
+import java.util.Collection;
import java.util.List;
+import com.vaadin.terminal.AbstractClientConnector;
+import com.vaadin.terminal.Extension;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.ComponentContainer;
/**
* Interface implemented by all connectors that are capable of communicating
@@ -24,8 +29,6 @@ public interface ClientConnector extends Connector, RpcTarget {
*
* @return an unmodifiable ordered list of pending server to client method
* calls (not null)
- *
- * @since 7.0
*/
public List<ClientMethodInvocation> retrievePendingRpcCalls();
@@ -44,4 +47,93 @@ public interface ClientConnector extends Connector, RpcTarget {
*/
public Class<? extends SharedState> getStateType();
+ public ClientConnector getParent();
+
+ /**
+ * Requests that the connector should be repainted as soon as possible.
+ */
+ public void requestRepaint();
+
+ /**
+ * Causes a repaint of this connector, and all connectors below it.
+ *
+ * This should only be used in special cases, e.g when the state of a
+ * descendant depends on the state of an ancestor.
+ */
+ public void requestRepaintAll();
+
+ /**
+ * Sets the parent connector of the connector.
+ *
+ * <p>
+ * This method automatically calls {@link #attach()} if the connector
+ * becomes attached to the application, regardless of whether it was
+ * attached previously. Conversely, if the parent is {@code null} and the
+ * connector is attached to the application, {@link #detach()} is called for
+ * the connector.
+ * </p>
+ * <p>
+ * This method is rarely called directly. One of the
+ * {@link ComponentContainer#addComponent(Component)} or
+ * {@link AbstractClientConnector#addExtension(Extension)} methods are
+ * normally used for adding connectors to a parent and they will call this
+ * method implicitly.
+ * </p>
+ *
+ * <p>
+ * It is not possible to change the parent without first setting the parent
+ * to {@code null}.
+ * </p>
+ *
+ * @param parent
+ * the parent connector
+ * @throws IllegalStateException
+ * if a parent is given even though the connector already has a
+ * parent
+ */
+ public void setParent(ClientConnector parent);
+
+ /**
+ * Notifies the connector that it is connected to an application.
+ *
+ * <p>
+ * The caller of this method is {@link #setParent(ClientConnector)} if the
+ * parent is itself already attached to the application. If not, the parent
+ * will call the {@link #attach()} for all its children when it is attached
+ * to the application. This method is always called before the connector's
+ * data is sent to the client-side for the first time.
+ * </p>
+ *
+ * <p>
+ * The attachment logic is implemented in {@link AbstractClientConnector}.
+ * </p>
+ */
+ public void attach();
+
+ /**
+ * Notifies the component that it is detached from the application.
+ *
+ * <p>
+ * The caller of this method is {@link #setParent(ClientConnector)} if the
+ * parent is in the application. When the parent is detached from the
+ * application it is its response to call {@link #detach()} for all the
+ * children and to detach itself from the terminal.
+ * </p>
+ */
+ public void detach();
+
+ /**
+ * Get a read-only collection of all extensions attached to this connector.
+ *
+ * @return a collection of extensions
+ */
+ public Collection<Extension> getExtensions();
+
+ /**
+ * Remove an extension from this connector.
+ *
+ * @param extension
+ * the extension to remove.
+ */
+ public void removeExtension(Extension extension);
}
diff --git a/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
index 99633a13d6..ad9484723b 100644
--- a/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
+++ b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
@@ -6,6 +6,7 @@ package com.vaadin.terminal.gwt.server;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
/**
* Internal class for keeping track of pending server to client method
@@ -19,7 +20,7 @@ public class ClientMethodInvocation implements Serializable,
private final String interfaceName;
private final String methodName;
private final Object[] parameters;
- private Class<?>[] parameterTypes;
+ private Type[] parameterTypes;
// used for sorting calls between different connectors in the same Root
private final long sequenceNumber;
@@ -31,12 +32,12 @@ public class ClientMethodInvocation implements Serializable,
this.connector = connector;
this.interfaceName = interfaceName;
methodName = method.getName();
- parameterTypes = method.getParameterTypes();
+ parameterTypes = method.getGenericParameterTypes();
this.parameters = (null != parameters) ? parameters : new Object[0];
sequenceNumber = ++counter;
}
- public Class<?>[] getParameterTypes() {
+ public Type[] getParameterTypes() {
return parameterTypes;
}
diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java
index 3dd2eb97fd..cc2981dc45 100644
--- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java
@@ -61,7 +61,8 @@ public class CommunicationManager extends AbstractCommunicationManager {
/**
* Handles file upload request submitted via Upload component.
*
- * @param application
+ * @param root
+ * The root for this request
*
* @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
*
@@ -70,9 +71,9 @@ public class CommunicationManager extends AbstractCommunicationManager {
* @throws IOException
* @throws InvalidUIDLSecurityKeyException
*/
- public void handleFileUpload(Application application,
- WrappedRequest request, WrappedResponse response)
- throws IOException, InvalidUIDLSecurityKeyException {
+ public void handleFileUpload(Root root, WrappedRequest request,
+ WrappedResponse response) throws IOException,
+ InvalidUIDLSecurityKeyException {
/*
* URI pattern: APP/UPLOAD/[PID]/[NAME]/[SECKEY] See #createReceiverUrl
@@ -86,14 +87,14 @@ public class CommunicationManager extends AbstractCommunicationManager {
String uppUri = pathInfo.substring(startOfData);
String[] parts = uppUri.split("/", 3); // 0 = pid, 1= name, 2 = sec key
String variableName = parts[1];
- String paintableId = parts[0];
+ String connectorId = parts[0];
StreamVariable streamVariable = pidToNameToStreamVariable.get(
- paintableId).get(variableName);
+ connectorId).get(variableName);
String secKey = streamVariableToSeckey.get(streamVariable);
if (secKey.equals(parts[2])) {
- Connector source = getConnector(application, paintableId);
+ Connector source = getConnector(root, connectorId);
String contentType = request.getContentType();
if (contentType.contains("boundary")) {
// Multipart requests contain boundary string
@@ -117,13 +118,12 @@ public class CommunicationManager extends AbstractCommunicationManager {
protected void postPaint(Root root) {
super.postPaint(root);
- Application application = root.getApplication();
if (pidToNameToStreamVariable != null) {
Iterator<String> iterator = pidToNameToStreamVariable.keySet()
.iterator();
while (iterator.hasNext()) {
String connectorId = iterator.next();
- if (application.getConnector(connectorId) == null) {
+ if (root.getConnectorTracker().getConnector(connectorId) == null) {
// Owner is no longer attached to the application
Map<String, StreamVariable> removed = pidToNameToStreamVariable
.get(connectorId);
diff --git a/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java b/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
index 335067ca7a..171d440796 100644
--- a/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
+++ b/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
@@ -34,9 +34,6 @@ import com.vaadin.ui.Window;
@SuppressWarnings({ "serial", "deprecation" })
public class ComponentSizeValidator implements Serializable {
- private final static Logger logger = Logger
- .getLogger(ComponentSizeValidator.class.getName());
-
private final static int LAYERS_SHOWN = 4;
/**
@@ -134,7 +131,7 @@ public class ComponentSizeValidator implements Serializable {
return parentCanDefineHeight(component);
} catch (Exception e) {
- logger.log(Level.FINER,
+ getLogger().log(Level.FINER,
"An exception occurred while validating sizes.", e);
return true;
}
@@ -154,7 +151,7 @@ public class ComponentSizeValidator implements Serializable {
return parentCanDefineWidth(component);
} catch (Exception e) {
- logger.log(Level.FINER,
+ getLogger().log(Level.FINER,
"An exception occurred while validating sizes.", e);
return true;
}
@@ -653,11 +650,15 @@ public class ComponentSizeValidator implements Serializable {
return;
} catch (Exception e) {
// TODO Auto-generated catch block
- logger.log(Level.FINER,
+ getLogger().log(Level.FINER,
"An exception occurred while validating sizes.", e);
}
}
}
+ private static Logger getLogger() {
+ return Logger.getLogger(ComponentSizeValidator.class.getName());
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/server/Constants.java b/src/com/vaadin/terminal/gwt/server/Constants.java
index 7c467aa7f4..9e6b2c775b 100644
--- a/src/com/vaadin/terminal/gwt/server/Constants.java
+++ b/src/com/vaadin/terminal/gwt/server/Constants.java
@@ -68,7 +68,7 @@ public interface Constants {
// Widget set parameter name
static final String PARAMETER_WIDGETSET = "widgetset";
- static final String ERROR_NO_WINDOW_FOUND = "No window found. Did you remember to setMainWindow()?";
+ static final String ERROR_NO_ROOT_FOUND = "Application did not return a root for the request and did not request extra information either. Something is wrong.";
static final String DEFAULT_THEME_NAME = "reindeer";
diff --git a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
index f6c96557ea..0e8d1c0152 100644
--- a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
+++ b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
@@ -4,6 +4,8 @@
package com.vaadin.terminal.gwt.server;
import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@@ -17,6 +19,7 @@ import com.vaadin.event.dd.DropTarget;
import com.vaadin.event.dd.TargetDetails;
import com.vaadin.event.dd.TargetDetailsImpl;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
+import com.vaadin.terminal.Extension;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.gwt.client.communication.SharedState;
@@ -26,9 +29,6 @@ import com.vaadin.ui.Component;
public class DragAndDropService implements VariableOwner, ClientConnector {
- private static final Logger logger = Logger
- .getLogger(DragAndDropService.class.getName());
-
private int lastVisitId;
private boolean lastVisitAccepted = false;
@@ -48,8 +48,9 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
// Validate drop handler owner
if (!(owner instanceof DropTarget)) {
- logger.severe("DropHandler owner " + owner
- + " must implement DropTarget");
+ getLogger()
+ .severe("DropHandler owner " + owner
+ + " must implement DropTarget");
return;
}
// owner cannot be null here
@@ -79,8 +80,9 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
DropHandler dropHandler = (dropTarget).getDropHandler();
if (dropHandler == null) {
// No dropHandler returned so no drop can be performed.
- logger.fine("DropTarget.getDropHandler() returned null for owner: "
- + dropTarget);
+ getLogger().fine(
+ "DropTarget.getDropHandler() returned null for owner: "
+ + dropTarget);
return;
}
@@ -242,4 +244,47 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
public Class<? extends SharedState> getStateType() {
return SharedState.class;
}
+
+ public void requestRepaint() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public ClientConnector getParent() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void requestRepaintAll() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setParent(ClientConnector parent) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void attach() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void detach() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Collection<Extension> getExtensions() {
+ // TODO Auto-generated method stub
+ return Collections.emptySet();
+ }
+
+ public void removeExtension(Extension extension) {
+ // TODO Auto-generated method stub
+ }
+
+ private Logger getLogger() {
+ return Logger.getLogger(DragAndDropService.class.getName());
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java
index 485c98f036..a6032fa98d 100644
--- a/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java
+++ b/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java
@@ -94,9 +94,6 @@ import com.vaadin.service.ApplicationContext;
*/
public class GAEApplicationServlet extends ApplicationServlet {
- private static final Logger logger = Logger
- .getLogger(GAEApplicationServlet.class.getName());
-
// memcache mutex is MUTEX_BASE + sessio id
private static final String MUTEX_BASE = "_vmutex";
@@ -209,8 +206,9 @@ public class GAEApplicationServlet extends ApplicationServlet {
try {
Thread.sleep(RETRY_AFTER_MILLISECONDS);
} catch (InterruptedException e) {
- logger.finer("Thread.sleep() interrupted while waiting for lock. Trying again. "
- + e);
+ getLogger().finer(
+ "Thread.sleep() interrupted while waiting for lock. Trying again. "
+ + e);
}
}
@@ -252,16 +250,16 @@ public class GAEApplicationServlet extends ApplicationServlet {
ds.put(entity);
} catch (DeadlineExceededException e) {
- logger.warning("DeadlineExceeded for " + session.getId());
+ getLogger().warning("DeadlineExceeded for " + session.getId());
sendDeadlineExceededNotification(request, response);
} catch (NotSerializableException e) {
- logger.log(Level.SEVERE, "Not serializable!", e);
+ getLogger().log(Level.SEVERE, "Not serializable!", e);
// TODO this notification is usually not shown - should we redirect
// in some other way - can we?
sendNotSerializableNotification(request, response);
} catch (Exception e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"An exception occurred while servicing request.", e);
sendCriticalErrorNotification(request, response);
@@ -308,12 +306,14 @@ public class GAEApplicationServlet extends ApplicationServlet {
session.setAttribute(WebApplicationContext.class.getName(),
applicationContext);
} catch (IOException e) {
- logger.log(Level.WARNING,
+ getLogger().log(
+ Level.WARNING,
"Could not de-serialize ApplicationContext for "
+ session.getId()
+ " A new one will be created. ", e);
} catch (ClassNotFoundException e) {
- logger.log(Level.WARNING,
+ getLogger().log(
+ Level.WARNING,
"Could not de-serialize ApplicationContext for "
+ session.getId()
+ " A new one will be created. ", e);
@@ -368,8 +368,9 @@ public class GAEApplicationServlet extends ApplicationServlet {
List<Entity> entities = pq.asList(Builder
.withLimit(CLEANUP_LIMIT));
if (entities != null) {
- logger.info("Vaadin cleanup deleting " + entities.size()
- + " expired Vaadin sessions.");
+ getLogger().info(
+ "Vaadin cleanup deleting " + entities.size()
+ + " expired Vaadin sessions.");
List<Key> keys = new ArrayList<Key>();
for (Entity e : entities) {
keys.add(e.getKey());
@@ -387,8 +388,9 @@ public class GAEApplicationServlet extends ApplicationServlet {
List<Entity> entities = pq.asList(Builder
.withLimit(CLEANUP_LIMIT));
if (entities != null) {
- logger.info("Vaadin cleanup deleting " + entities.size()
- + " expired appengine sessions.");
+ getLogger().info(
+ "Vaadin cleanup deleting " + entities.size()
+ + " expired appengine sessions.");
List<Key> keys = new ArrayList<Key>();
for (Entity e : entities) {
keys.add(e.getKey());
@@ -397,7 +399,11 @@ public class GAEApplicationServlet extends ApplicationServlet {
}
}
} catch (Exception e) {
- logger.log(Level.WARNING, "Exception while cleaning.", e);
+ getLogger().log(Level.WARNING, "Exception while cleaning.", e);
}
}
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(GAEApplicationServlet.class.getName());
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
index e082eca47e..d3a2ef56f8 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
@@ -8,26 +8,30 @@ import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
+import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
-import com.vaadin.Application;
import com.vaadin.external.json.JSONArray;
import com.vaadin.external.json.JSONException;
import com.vaadin.external.json.JSONObject;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
+import com.vaadin.terminal.gwt.client.communication.UidlValue;
import com.vaadin.ui.Component;
+import com.vaadin.ui.ConnectorTracker;
/**
* Decoder for converting RPC parameters and other values from JSON in transfer
@@ -79,8 +83,16 @@ public class JsonCodec implements Serializable {
public static boolean isInternalType(Type type) {
if (type instanceof Class && ((Class<?>) type).isPrimitive()) {
+ if (type == byte.class || type == char.class) {
+ // Almost all primitive types are handled internally
+ return false;
+ }
// All primitive types are handled internally
return true;
+ } else if (type == UidlValue.class) {
+ // UidlValue is a special internal type wrapping type info and a
+ // value
+ return true;
}
return typeToTransportType.containsKey(getClassForType(type));
}
@@ -93,65 +105,62 @@ public class JsonCodec implements Serializable {
}
}
- public static String getTransportType(JSONArray encodedValue)
- throws JSONException {
- return encodedValue.getString(0);
- }
-
private static Class<?> getType(String transportType) {
return transportTypeToType.get(transportType);
}
- /**
- * Decodes the given value and type, restricted to using only internal
- * types.
- *
- * @param valueAndType
- * @param application
- * @throws JSONException
- */
- @Deprecated
- public static Object decodeInternalType(JSONArray valueAndType,
- Application application) throws JSONException {
- String transportType = getTransportType(valueAndType);
- return decodeInternalType(getType(transportType), true, valueAndType,
- application);
- }
-
public static Object decodeInternalOrCustomType(Type targetType,
- JSONArray valueAndType, Application application)
+ Object value, ConnectorTracker connectorTracker)
throws JSONException {
if (isInternalType(targetType)) {
- return decodeInternalType(targetType, false, valueAndType,
- application);
+ return decodeInternalType(targetType, false, value,
+ connectorTracker);
} else {
- return decodeCustomType(targetType, valueAndType, application);
+ return decodeCustomType(targetType, value, connectorTracker);
}
}
- public static Object decodeCustomType(Type targetType,
- JSONArray valueAndType, Application application)
- throws JSONException {
+ public static Object decodeCustomType(Type targetType, Object value,
+ ConnectorTracker connectorTracker) throws JSONException {
if (isInternalType(targetType)) {
throw new JSONException("decodeCustomType cannot be used for "
+ targetType + ", which is an internal type");
}
- String transportType = getCustomTransportType(getClassForType(targetType));
- String encodedTransportType = valueAndType.getString(0);
- if (!transportTypesCompatible(encodedTransportType, transportType)) {
- throw new JSONException("Expected a value of type " + transportType
- + ", received " + encodedTransportType);
- }
// Try to decode object using fields
- Object value = valueAndType.get(1);
if (value == JSONObject.NULL) {
return null;
+ } else if (targetType == byte.class || targetType == Byte.class) {
+ return Byte.valueOf(String.valueOf(value));
+ } else if (targetType == char.class || targetType == Character.class) {
+ return Character.valueOf(String.valueOf(value).charAt(0));
+ } else if (targetType instanceof Class<?>
+ && ((Class<?>) targetType).isArray()) {
+ // Legacy Object[] and String[] handled elsewhere, this takes care
+ // of generic arrays
+ return decodeArray((Class<?>) targetType, (JSONArray) value,
+ connectorTracker);
+ } else if (targetType == JSONObject.class
+ || targetType == JSONArray.class) {
+ return value;
} else {
- return decodeObject(targetType, (JSONObject) value, application);
+ return decodeObject(targetType, (JSONObject) value,
+ connectorTracker);
}
}
+ private static Object decodeArray(Class<?> targetType, JSONArray value,
+ ConnectorTracker connectorTracker) throws JSONException {
+ Class<?> componentType = targetType.getComponentType();
+ Object array = Array.newInstance(componentType, value.length());
+ for (int i = 0; i < value.length(); i++) {
+ Object decodedValue = decodeInternalOrCustomType(componentType,
+ value.get(i), connectorTracker);
+ Array.set(array, i, decodedValue);
+ }
+ return array;
+ }
+
/**
* Decodes a value that is of an internal type.
* <p>
@@ -177,41 +186,41 @@ public class JsonCodec implements Serializable {
* @throws JSONException
*/
public static Object decodeInternalType(Type targetType,
- boolean restrictToInternalTypes, JSONArray valueAndType,
- Application application) throws JSONException {
- String encodedTransportType = valueAndType.getString(0);
+ boolean restrictToInternalTypes, Object encodedJsonValue,
+ ConnectorTracker connectorTracker) throws JSONException {
if (!isInternalType(targetType)) {
throw new JSONException("Type " + targetType
+ " is not a supported internal type.");
}
String transportType = getInternalTransportType(targetType);
- if (!transportTypesCompatible(encodedTransportType, transportType)) {
- throw new JSONException("Expected a value of type " + targetType
- + ", received " + getType(encodedTransportType));
- }
-
- Object encodedJsonValue = valueAndType.get(1);
- if (JsonEncoder.VTYPE_NULL.equals(encodedTransportType)) {
+ if (encodedJsonValue == JSONObject.NULL) {
return null;
}
+
+ // UidlValue
+ if (targetType == UidlValue.class) {
+ return decodeUidlValue((JSONArray) encodedJsonValue,
+ connectorTracker);
+ }
+
// Collections
if (JsonEncoder.VTYPE_LIST.equals(transportType)) {
return decodeList(targetType, restrictToInternalTypes,
- (JSONArray) encodedJsonValue, application);
+ (JSONArray) encodedJsonValue, connectorTracker);
} else if (JsonEncoder.VTYPE_SET.equals(transportType)) {
return decodeSet(targetType, restrictToInternalTypes,
- (JSONArray) encodedJsonValue, application);
+ (JSONArray) encodedJsonValue, connectorTracker);
} else if (JsonEncoder.VTYPE_MAP.equals(transportType)) {
return decodeMap(targetType, restrictToInternalTypes,
- (JSONObject) encodedJsonValue, application);
+ encodedJsonValue, connectorTracker);
}
// Arrays
if (JsonEncoder.VTYPE_ARRAY.equals(transportType)) {
return decodeObjectArray(targetType, (JSONArray) encodedJsonValue,
- application);
+ connectorTracker);
} else if (JsonEncoder.VTYPE_STRINGARRAY.equals(transportType)) {
return decodeStringArray((JSONArray) encodedJsonValue);
@@ -222,10 +231,10 @@ public class JsonCodec implements Serializable {
String stringValue = String.valueOf(encodedJsonValue);
if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) {
- return application.getConnector(stringValue);
+ return connectorTracker.getConnector(stringValue);
}
- // Standard Java types
+ // Legacy types
if (JsonEncoder.VTYPE_STRING.equals(transportType)) {
return stringValue;
@@ -244,6 +253,15 @@ public class JsonCodec implements Serializable {
throw new JSONException("Unknown type " + transportType);
}
+ private static UidlValue decodeUidlValue(JSONArray encodedJsonValue,
+ ConnectorTracker connectorTracker) throws JSONException {
+ String type = encodedJsonValue.getString(0);
+
+ Object decodedValue = decodeInternalType(getType(type), true,
+ encodedJsonValue.get(1), connectorTracker);
+ return new UidlValue(decodedValue);
+ }
+
private static boolean transportTypesCompatible(
String encodedTransportType, String transportType) {
if (encodedTransportType == null) {
@@ -260,22 +278,94 @@ public class JsonCodec implements Serializable {
}
private static Map<Object, Object> decodeMap(Type targetType,
- boolean restrictToInternalTypes, JSONObject jsonMap,
- Application application) throws JSONException {
- HashMap<Object, Object> map = new HashMap<Object, Object>();
-
- Iterator<String> it = jsonMap.keys();
- while (it.hasNext()) {
- String key = it.next();
- JSONArray encodedKey = new JSONArray(key);
- JSONArray encodedValue = jsonMap.getJSONArray(key);
-
- Object decodedKey = decodeParametrizedType(targetType,
- restrictToInternalTypes, 0, encodedKey, application);
- Object decodedValue = decodeParametrizedType(targetType,
- restrictToInternalTypes, 1, encodedValue, application);
- map.put(decodedKey, decodedValue);
+ boolean restrictToInternalTypes, Object jsonMap,
+ ConnectorTracker connectorTracker) throws JSONException {
+ if (jsonMap instanceof JSONArray) {
+ // Client-side has no declared type information to determine
+ // encoding method for empty maps, so these are handled separately.
+ // See #8906.
+ JSONArray jsonArray = (JSONArray) jsonMap;
+ if (jsonArray.length() == 0) {
+ return new HashMap<Object, Object>();
+ }
}
+
+ if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
+ Type keyType = ((ParameterizedType) targetType)
+ .getActualTypeArguments()[0];
+ Type valueType = ((ParameterizedType) targetType)
+ .getActualTypeArguments()[1];
+ if (keyType == String.class) {
+ return decodeStringMap(valueType, (JSONObject) jsonMap,
+ connectorTracker);
+ } else if (keyType == Connector.class) {
+ return decodeConnectorMap(valueType, (JSONObject) jsonMap,
+ connectorTracker);
+ } else {
+ return decodeObjectMap(keyType, valueType, (JSONArray) jsonMap,
+ connectorTracker);
+ }
+ } else {
+ return decodeStringMap(UidlValue.class, (JSONObject) jsonMap,
+ connectorTracker);
+ }
+ }
+
+ private static Map<Object, Object> decodeObjectMap(Type keyType,
+ Type valueType, JSONArray jsonMap, ConnectorTracker connectorTracker)
+ throws JSONException {
+ Map<Object, Object> map = new HashMap<Object, Object>();
+
+ JSONArray keys = jsonMap.getJSONArray(0);
+ JSONArray values = jsonMap.getJSONArray(1);
+
+ assert (keys.length() == values.length());
+
+ for (int i = 0; i < keys.length(); i++) {
+ Object key = decodeInternalOrCustomType(keyType, keys.get(i),
+ connectorTracker);
+ Object value = decodeInternalOrCustomType(valueType, values.get(i),
+ connectorTracker);
+
+ map.put(key, value);
+ }
+
+ return map;
+ }
+
+ private static Map<Object, Object> decodeConnectorMap(Type valueType,
+ JSONObject jsonMap, ConnectorTracker connectorTracker)
+ throws JSONException {
+ Map<Object, Object> map = new HashMap<Object, Object>();
+
+ for (Iterator<?> iter = jsonMap.keys(); iter.hasNext();) {
+ String key = (String) iter.next();
+ Object value = decodeInternalOrCustomType(valueType,
+ jsonMap.get(key), connectorTracker);
+ if (valueType == UidlValue.class) {
+ value = ((UidlValue) value).getValue();
+ }
+ map.put(connectorTracker.getConnector(key), value);
+ }
+
+ return map;
+ }
+
+ private static Map<Object, Object> decodeStringMap(Type valueType,
+ JSONObject jsonMap, ConnectorTracker connectorTracker)
+ throws JSONException {
+ Map<Object, Object> map = new HashMap<Object, Object>();
+
+ for (Iterator<?> iter = jsonMap.keys(); iter.hasNext();) {
+ String key = (String) iter.next();
+ Object value = decodeInternalOrCustomType(valueType,
+ jsonMap.get(key), connectorTracker);
+ if (valueType == UidlValue.class) {
+ value = ((UidlValue) value).getValue();
+ }
+ map.put(key, value);
+ }
+
return map;
}
@@ -291,19 +381,20 @@ public class JsonCodec implements Serializable {
* @throws JSONException
*/
private static Object decodeParametrizedType(Type targetType,
- boolean restrictToInternalTypes, int typeIndex,
- JSONArray encodedValueAndType, Application application)
- throws JSONException {
+ boolean restrictToInternalTypes, int typeIndex, Object value,
+ ConnectorTracker connectorTracker) throws JSONException {
if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
Type childType = ((ParameterizedType) targetType)
.getActualTypeArguments()[typeIndex];
// Only decode the given type
- return decodeInternalOrCustomType(childType, encodedValueAndType,
- application);
+ return decodeInternalOrCustomType(childType, value,
+ connectorTracker);
} else {
- // Only internal types when not enforcing a given type to avoid
- // security issues
- return decodeInternalType(encodedValueAndType, application);
+ // Only UidlValue when not enforcing a given type to avoid security
+ // issues
+ UidlValue decodeInternalType = (UidlValue) decodeInternalType(
+ UidlValue.class, true, value, connectorTracker);
+ return decodeInternalType.getValue();
}
}
@@ -323,21 +414,21 @@ public class JsonCodec implements Serializable {
}
private static Object[] decodeObjectArray(Type targetType,
- JSONArray jsonArray, Application application) throws JSONException {
- List list = decodeList(List.class, true, jsonArray, application);
+ JSONArray jsonArray, ConnectorTracker connectorTracker)
+ throws JSONException {
+ List list = decodeList(List.class, true, jsonArray, connectorTracker);
return list.toArray(new Object[list.size()]);
}
private static List<Object> decodeList(Type targetType,
boolean restrictToInternalTypes, JSONArray jsonArray,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
List<Object> list = new ArrayList<Object>();
for (int i = 0; i < jsonArray.length(); ++i) {
// each entry always has two elements: type and value
- JSONArray encodedValueAndType = jsonArray.getJSONArray(i);
+ Object encodedValue = jsonArray.get(i);
Object decodedChild = decodeParametrizedType(targetType,
- restrictToInternalTypes, 0, encodedValueAndType,
- application);
+ restrictToInternalTypes, 0, encodedValue, connectorTracker);
list.add(decodedChild);
}
return list;
@@ -345,10 +436,10 @@ public class JsonCodec implements Serializable {
private static Set<Object> decodeSet(Type targetType,
boolean restrictToInternalTypes, JSONArray jsonArray,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
HashSet<Object> set = new HashSet<Object>();
- set.addAll(decodeList(List.class, restrictToInternalTypes, jsonArray,
- application));
+ set.addAll(decodeList(targetType, restrictToInternalTypes, jsonArray,
+ connectorTracker));
return set;
}
@@ -356,7 +447,7 @@ public class JsonCodec implements Serializable {
* Returns the name that should be used as field name in the JSON. We strip
* "set" from the setter, keeping the result - this is easy to do on both
* server and client, avoiding some issues with cASE. E.g setZIndex()
- * becomes "ZIndex". Also ensures that both getter and setter are present,
+ * becomes "zIndex". Also ensures that both getter and setter are present,
* returning null otherwise.
*
* @param pd
@@ -367,11 +458,14 @@ public class JsonCodec implements Serializable {
if (pd.getReadMethod() == null || pd.getWriteMethod() == null) {
return null;
}
- return pd.getWriteMethod().getName().substring(3);
+ String fieldName = pd.getWriteMethod().getName().substring(3);
+ fieldName = Character.toLowerCase(fieldName.charAt(0))
+ + fieldName.substring(1);
+ return fieldName;
}
private static Object decodeObject(Type targetType,
- JSONObject serializedObject, Application application)
+ JSONObject serializedObject, ConnectorTracker connectorTracker)
throws JSONException {
Class<?> targetClass = getClassForType(targetType);
@@ -389,11 +483,10 @@ public class JsonCodec implements Serializable {
if (fieldName == null) {
continue;
}
- JSONArray encodedFieldValue = serializedObject
- .getJSONArray(fieldName);
+ Object encodedFieldValue = serializedObject.get(fieldName);
Type fieldType = pd.getReadMethod().getGenericReturnType();
Object decodedFieldValue = decodeInternalOrCustomType(
- fieldType, encodedFieldValue, application);
+ fieldType, encodedFieldValue, connectorTracker);
pd.getWriteMethod().invoke(decodedObject, decodedFieldValue);
}
@@ -412,55 +505,47 @@ public class JsonCodec implements Serializable {
}
}
- @Deprecated
- private static JSONArray encode(Object value, Application application)
+ public static Object encode(Object value, Object referenceValue,
+ Type valueType, ConnectorTracker connectorTracker)
throws JSONException {
- return encode(value, null, null, application);
- }
- public static JSONArray encode(Object value, Object referenceValue,
- Type valueType, Application application) throws JSONException {
+ if (valueType == null) {
+ throw new IllegalArgumentException("type must be defined");
+ }
if (null == value) {
return encodeNull();
}
- if (valueType == null) {
- valueType = value.getClass();
- }
-
- String internalTransportType = getInternalTransportType(valueType);
if (value instanceof String[]) {
String[] array = (String[]) value;
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < array.length; ++i) {
jsonArray.put(array[i]);
}
- return combineTypeAndValue(JsonEncoder.VTYPE_STRINGARRAY, jsonArray);
+ return jsonArray;
} else if (value instanceof String) {
- return combineTypeAndValue(JsonEncoder.VTYPE_STRING, value);
+ return value;
} else if (value instanceof Boolean) {
- return combineTypeAndValue(JsonEncoder.VTYPE_BOOLEAN, value);
+ return value;
} else if (value instanceof Number) {
- return combineTypeAndValue(internalTransportType, value);
+ return value;
+ } else if (value instanceof Character) {
+ // Character is not a Number
+ return value;
} else if (value instanceof Collection) {
- if (internalTransportType == null) {
- throw new RuntimeException(
- "Unable to serialize unsupported type: " + valueType);
- }
Collection<?> collection = (Collection<?>) value;
JSONArray jsonArray = encodeCollection(valueType, collection,
- application);
-
- return combineTypeAndValue(internalTransportType, jsonArray);
- } else if (value instanceof Object[]) {
- Object[] array = (Object[]) value;
- JSONArray jsonArray = encodeArrayContents(array, application);
- return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray);
+ connectorTracker);
+ return jsonArray;
+ } else if (valueType instanceof Class<?>
+ && ((Class<?>) valueType).isArray()) {
+ JSONArray jsonArray = encodeArrayContents(value, connectorTracker);
+ return jsonArray;
} else if (value instanceof Map) {
- JSONObject jsonMap = encodeMap(valueType, (Map<?, ?>) value,
- application);
- return combineTypeAndValue(JsonEncoder.VTYPE_MAP, jsonMap);
+ Object jsonMap = encodeMap(valueType, (Map<?, ?>) value,
+ connectorTracker);
+ return jsonMap;
} else if (value instanceof Connector) {
Connector connector = (Connector) value;
if (value instanceof Component
@@ -468,28 +553,24 @@ public class JsonCodec implements Serializable {
.isVisible((Component) value))) {
return encodeNull();
}
- return combineTypeAndValue(JsonEncoder.VTYPE_CONNECTOR,
- connector.getConnectorId());
- } else if (internalTransportType != null) {
- return combineTypeAndValue(internalTransportType,
- String.valueOf(value));
+ return connector.getConnectorId();
} else if (value instanceof Enum) {
- return encodeEnum((Enum) value, application);
+ return encodeEnum((Enum<?>) value, connectorTracker);
+ } 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
- return combineTypeAndValue(
- getCustomTransportType((Class<?>) valueType),
- encodeObject(value, referenceValue, application));
+ return encodeObject(value, referenceValue, connectorTracker);
}
}
- private static JSONArray encodeNull() {
- return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL);
+ private static Object encodeNull() {
+ return JSONObject.NULL;
}
private static Object encodeObject(Object value, Object referenceValue,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
JSONObject jsonMap = new JSONObject();
try {
@@ -512,10 +593,18 @@ public class JsonCodec implements Serializable {
equals = equals(fieldValue, referenceFieldValue);
}
if (!equals) {
+ if (jsonMap.has(fieldName)) {
+ throw new RuntimeException(
+ "Can't encode "
+ + value.getClass().getName()
+ + " as it has multiple fields with the name "
+ + fieldName.toLowerCase()
+ + ". This can happen if only casing distinguishes one property name from another.");
+ }
jsonMap.put(
fieldName,
encode(fieldValue, referenceFieldValue, fieldType,
- application));
+ connectorTracker));
// } else {
// System.out.println("Skipping field " + fieldName
// + " of type " + fieldType.getName()
@@ -549,45 +638,46 @@ public class JsonCodec implements Serializable {
return false;
}
- private static JSONArray encodeEnum(Enum e, Application application)
- throws JSONException {
- String enumIdentifier = e.name();
- return combineTypeAndValue(e.getClass().getName(), enumIdentifier);
+ private static String encodeEnum(Enum<?> e,
+ ConnectorTracker connectorTracker) throws JSONException {
+ return e.name();
}
- private static JSONArray encodeArrayContents(Object[] array,
- Application application) throws JSONException {
+ private static JSONArray encodeArrayContents(Object array,
+ ConnectorTracker connectorTracker) throws JSONException {
JSONArray jsonArray = new JSONArray();
- for (Object o : array) {
- jsonArray.put(encode(o, null, null, application));
+ Class<?> componentType = array.getClass().getComponentType();
+ for (int i = 0; i < Array.getLength(array); i++) {
+ jsonArray.put(encode(Array.get(array, i), null, componentType,
+ connectorTracker));
}
return jsonArray;
}
private static JSONArray encodeCollection(Type targetType,
- Collection collection, Application application)
+ Collection collection, ConnectorTracker connectorTracker)
throws JSONException {
JSONArray jsonArray = new JSONArray();
for (Object o : collection) {
- jsonArray.put(encodeChild(targetType, 0, o, application));
+ jsonArray.put(encodeChild(targetType, 0, o, connectorTracker));
}
return jsonArray;
}
- private static JSONArray encodeChild(Type targetType, int typeIndex,
- Object o, Application application) throws JSONException {
+ private static Object encodeChild(Type targetType, int typeIndex, Object o,
+ ConnectorTracker connectorTracker) throws JSONException {
if (targetType instanceof ParameterizedType) {
Type childType = ((ParameterizedType) targetType)
.getActualTypeArguments()[typeIndex];
// Encode using the given type
- return encode(o, null, childType, application);
+ return encode(o, null, childType, connectorTracker);
} else {
- return encode(o, application);
+ throw new JSONException("Collection is missing generics");
}
}
- private static JSONObject encodeMap(Type mapType, Map<?, ?> map,
- Application application) throws JSONException {
+ private static Object encodeMap(Type mapType, Map<?, ?> map,
+ ConnectorTracker connectorTracker) throws JSONException {
Type keyType, valueType;
if (mapType instanceof ParameterizedType) {
@@ -597,26 +687,66 @@ public class JsonCodec implements Serializable {
throw new JSONException("Map is missing generics");
}
+ if (map.isEmpty()) {
+ // Client -> server encodes empty map as an empty array because of
+ // #8906. Do the same for server -> client to maintain symmetry.
+ return new JSONArray();
+ }
+
+ if (keyType == String.class) {
+ return encodeStringMap(valueType, map, connectorTracker);
+ } else if (keyType == Connector.class) {
+ return encodeConnectorMap(valueType, map, connectorTracker);
+ } else {
+ return encodeObjectMap(keyType, valueType, map, connectorTracker);
+ }
+ }
+
+ private static JSONArray encodeObjectMap(Type keyType, Type valueType,
+ Map<?, ?> map, ConnectorTracker connectorTracker)
+ throws JSONException {
+ JSONArray keys = new JSONArray();
+ JSONArray values = new JSONArray();
+
+ for (Entry<?, ?> entry : map.entrySet()) {
+ Object encodedKey = encode(entry.getKey(), null, keyType,
+ connectorTracker);
+ Object encodedValue = encode(entry.getValue(), null, valueType,
+ connectorTracker);
+
+ keys.put(encodedKey);
+ values.put(encodedValue);
+ }
+
+ return new JSONArray(Arrays.asList(keys, values));
+ }
+
+ private static JSONObject encodeConnectorMap(Type valueType, Map<?, ?> map,
+ ConnectorTracker connectorTracker) throws JSONException {
JSONObject jsonMap = new JSONObject();
- for (Object mapKey : map.keySet()) {
- Object mapValue = map.get(mapKey);
- JSONArray encodedKey = encode(mapKey, null, keyType, application);
- JSONArray encodedValue = encode(mapValue, null, valueType,
- application);
- jsonMap.put(encodedKey.toString(), encodedValue);
+
+ for (Entry<?, ?> entry : map.entrySet()) {
+ Connector key = (Connector) entry.getKey();
+ Object encodedValue = encode(entry.getValue(), null, valueType,
+ connectorTracker);
+ jsonMap.put(key.getConnectorId(), encodedValue);
}
+
return jsonMap;
}
- private static JSONArray combineTypeAndValue(String type, Object value) {
- if (type == null) {
- throw new RuntimeException("Type for value " + value
- + " cannot be null!");
+ private static JSONObject encodeStringMap(Type valueType, Map<?, ?> map,
+ ConnectorTracker connectorTracker) throws JSONException {
+ JSONObject jsonMap = new JSONObject();
+
+ for (Entry<?, ?> entry : map.entrySet()) {
+ String key = (String) entry.getKey();
+ Object encodedValue = encode(entry.getValue(), null, valueType,
+ connectorTracker);
+ jsonMap.put(key, encodedValue);
}
- JSONArray outerArray = new JSONArray();
- outerArray.put(type);
- outerArray.put(value);
- return outerArray;
+
+ return jsonMap;
}
/**
diff --git a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
index def334290e..70ab452e4e 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
@@ -26,6 +26,7 @@ import com.vaadin.terminal.ThemeResource;
import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Component;
import com.vaadin.ui.CustomLayout;
/**
@@ -42,9 +43,6 @@ import com.vaadin.ui.CustomLayout;
@SuppressWarnings("serial")
public class JsonPaintTarget implements PaintTarget {
- private static final Logger logger = Logger.getLogger(JsonPaintTarget.class
- .getName());
-
/* Document type declarations */
private final static String UIDL_ARG_NAME = "name";
@@ -161,6 +159,7 @@ public class JsonPaintTarget implements PaintTarget {
* @throws Paintexception
* if the paint operation failed.
*/
+
public void endTag(String tagName) throws PaintException {
// In case of null data output nothing:
if (tagName == null) {
@@ -327,6 +326,7 @@ public class JsonPaintTarget implements PaintTarget {
* if the paint operation failed.
*
*/
+
public void addText(String str) throws PaintException {
tag.addData("\"" + escapeJSON(str) + "\"");
}
@@ -399,7 +399,7 @@ public class JsonPaintTarget implements PaintTarget {
}
- public void addAttribute(String name, ClientConnector value)
+ public void addAttribute(String name, Component value)
throws PaintException {
final String id = value.getConnectorId();
addAttribute(name, id);
@@ -467,8 +467,8 @@ public class JsonPaintTarget implements PaintTarget {
tag.addVariable(new StringVariable(owner, name, escapeJSON(value)));
}
- public void addVariable(VariableOwner owner, String name,
- ClientConnector value) throws PaintException {
+ public void addVariable(VariableOwner owner, String name, Component value)
+ throws PaintException {
tag.addVariable(new StringVariable(owner, name, value.getConnectorId()));
}
@@ -515,6 +515,7 @@ public class JsonPaintTarget implements PaintTarget {
* @throws PaintException
* if the paint operation failed.
*/
+
public void addUploadStreamVariable(VariableOwner owner, String name)
throws PaintException {
startTag("uploadstream");
@@ -534,6 +535,7 @@ public class JsonPaintTarget implements PaintTarget {
* @throws PaintException
* if the paint operation failed.
*/
+
public void addSection(String sectionTagName, String sectionData)
throws PaintException {
tag.addData("{\"" + sectionTagName + "\":\"" + escapeJSON(sectionData)
@@ -548,6 +550,7 @@ public class JsonPaintTarget implements PaintTarget {
* @throws PaintException
* if the paint operation failed.
*/
+
public void addUIDL(String xml) throws PaintException {
// Ensure that the target is open
@@ -581,6 +584,7 @@ public class JsonPaintTarget implements PaintTarget {
* @see com.vaadin.terminal.PaintTarget#addXMLSection(String, String,
* String)
*/
+
public void addXMLSection(String sectionTagName, String sectionData,
String namespace) throws PaintException {
@@ -645,12 +649,14 @@ public class JsonPaintTarget implements PaintTarget {
* @see com.vaadin.terminal.PaintTarget#startPaintable(com.vaadin.terminal
* .Paintable, java.lang.String)
*/
- public PaintStatus startPaintable(ClientConnector connector, String tagName)
+
+ public PaintStatus startPaintable(Component connector, String tagName)
throws PaintException {
boolean topLevelPaintable = openPaintables.isEmpty();
- logger.fine("startPaintable for " + connector.getClass().getName()
- + "@" + Integer.toHexString(connector.hashCode()));
+ getLogger().fine(
+ "startPaintable for " + connector.getClass().getName() + "@"
+ + Integer.toHexString(connector.hashCode()));
startTag(tagName, true);
openPaintables.push(connector);
@@ -670,9 +676,10 @@ public class JsonPaintTarget implements PaintTarget {
return PaintStatus.PAINTING;
}
- public void endPaintable(ClientConnector paintable) throws PaintException {
- logger.fine("endPaintable for " + paintable.getClass().getName() + "@"
- + Integer.toHexString(paintable.hashCode()));
+ public void endPaintable(Component paintable) throws PaintException {
+ getLogger().fine(
+ "endPaintable for " + paintable.getClass().getName() + "@"
+ + Integer.toHexString(paintable.hashCode()));
ClientConnector openPaintable = openPaintables.peek();
if (paintable != openPaintable) {
@@ -691,6 +698,7 @@ public class JsonPaintTarget implements PaintTarget {
*
* @see com.vaadin.terminal.PaintTarget#addCharacterData(java.lang.String )
*/
+
public void addCharacterData(String text) throws PaintException {
if (text != null) {
tag.addData(text);
@@ -997,8 +1005,13 @@ public class JsonPaintTarget implements PaintTarget {
*
* @see com.vaadin.terminal.PaintTarget#isFullRepaint()
*/
+
public boolean isFullRepaint() {
return !cacheEnabled;
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(JsonPaintTarget.class.getName());
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java
index 661da57af6..de4f918b75 100644
--- a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java
+++ b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java
@@ -48,9 +48,6 @@ import com.vaadin.ui.Root;
@SuppressWarnings("serial")
public class PortletApplicationContext2 extends AbstractWebApplicationContext {
- private static final Logger logger = Logger
- .getLogger(PortletApplicationContext2.class.getName());
-
protected Map<Application, Set<PortletListener>> portletListeners = new HashMap<Application, Set<PortletListener>>();
protected transient PortletSession session;
@@ -76,11 +73,11 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
return new File(url.getFile());
} catch (final Exception e) {
// FIXME: Handle exception
- logger.log(
- Level.INFO,
- "Cannot access base directory, possible security issue "
- + "with Application Server or Servlet Container",
- e);
+ getLogger()
+ .log(Level.INFO,
+ "Cannot access base directory, possible security issue "
+ + "with Application Server or Servlet Container",
+ e);
}
}
return null;
@@ -419,4 +416,8 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
"Portlet mode can only be changed from a portlet request");
}
}
+
+ private Logger getLogger() {
+ return Logger.getLogger(PortletApplicationContext2.class.getName());
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
index 55f15da3d9..d3fbf4d988 100644
--- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
@@ -43,12 +43,12 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
super(application);
}
- public void handleFileUpload(WrappedRequest request,
+ public void handleFileUpload(Root root, WrappedRequest request,
WrappedResponse response) throws IOException {
String contentType = request.getContentType();
String name = request.getParameter("name");
String ownerId = request.getParameter("rec-owner");
- Connector owner = getConnector(getApplication(), ownerId);
+ Connector owner = getConnector(root, ownerId);
StreamVariable streamVariable = ownerToNameToStreamVariable.get(owner)
.get(name);
@@ -73,7 +73,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
.iterator();
while (iterator.hasNext()) {
Connector owner = iterator.next();
- if (application.getConnector(owner.getConnectorId()) == null) {
+ if (getConnector(root, owner.getConnectorId()) == null) {
// Owner is no longer attached to the application
iterator.remove();
}
diff --git a/src/com/vaadin/terminal/gwt/server/RpcManager.java b/src/com/vaadin/terminal/gwt/server/RpcManager.java
index d240ab8467..026c847e2b 100644
--- a/src/com/vaadin/terminal/gwt/server/RpcManager.java
+++ b/src/com/vaadin/terminal/gwt/server/RpcManager.java
@@ -13,5 +13,36 @@ import java.io.Serializable;
* @since 7.0
*/
public interface RpcManager extends Serializable {
- public void applyInvocation(ServerRpcMethodInvocation invocation);
+ public void applyInvocation(ServerRpcMethodInvocation invocation)
+ throws RpcInvocationException;
+
+ /**
+ * Wrapper exception for exceptions which occur during invocation of an RPC
+ * call
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0
+ *
+ */
+ public static class RpcInvocationException extends Exception {
+
+ public RpcInvocationException() {
+ super();
+ }
+
+ public RpcInvocationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RpcInvocationException(String message) {
+ super(message);
+ }
+
+ public RpcInvocationException(Throwable cause) {
+ super(cause);
+ }
+
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java
index 07f83864c2..d9931a9610 100644
--- a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java
+++ b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java
@@ -5,6 +5,7 @@
package com.vaadin.terminal.gwt.server;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@@ -66,9 +67,10 @@ public class ServerRpcManager<T> implements RpcManager {
* non-null target of the RPC call
* @param invocation
* method invocation to perform
+ * @throws RpcInvocationException
*/
public static void applyInvocation(RpcTarget target,
- ServerRpcMethodInvocation invocation) {
+ ServerRpcMethodInvocation invocation) throws RpcInvocationException {
RpcManager manager = target.getRpcManager(invocation
.getInterfaceClass());
if (manager != null) {
@@ -109,7 +111,8 @@ public class ServerRpcManager<T> implements RpcManager {
* @param invocation
* method invocation to perform
*/
- public void applyInvocation(ServerRpcMethodInvocation invocation) {
+ public void applyInvocation(ServerRpcMethodInvocation invocation)
+ throws RpcInvocationException {
Method method = invocation.getMethod();
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] args = new Object[parameterTypes.length];
@@ -125,7 +128,7 @@ public class ServerRpcManager<T> implements RpcManager {
try {
method.invoke(implementation, args);
} catch (Exception e) {
- throw new RuntimeException("Unable to invoke method "
+ throw new RpcInvocationException("Unable to invoke method "
+ invocation.getMethodName() + " in "
+ invocation.getInterfaceName(), e);
}
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;
}
diff --git a/src/com/vaadin/terminal/gwt/server/WebBrowser.java b/src/com/vaadin/terminal/gwt/server/WebBrowser.java
index 358f6f38fb..38b5409594 100644
--- a/src/com/vaadin/terminal/gwt/server/WebBrowser.java
+++ b/src/com/vaadin/terminal/gwt/server/WebBrowser.java
@@ -23,8 +23,6 @@ public class WebBrowser implements Terminal {
private int screenHeight = 0;
private int screenWidth = 0;
- private int clientHeight = 0;
- private int clientWidth = 0;
private String browserApplication = null;
private Locale locale;
private String address;
@@ -43,6 +41,7 @@ public class WebBrowser implements Terminal {
*
* @return Always returns null.
*/
+
public String getDefaultTheme() {
return null;
}
@@ -52,6 +51,7 @@ public class WebBrowser implements Terminal {
*
* @see com.vaadin.terminal.Terminal#getScreenHeight()
*/
+
public int getScreenHeight() {
return screenHeight;
}
@@ -61,35 +61,12 @@ public class WebBrowser implements Terminal {
*
* @see com.vaadin.terminal.Terminal#getScreenWidth()
*/
+
public int getScreenWidth() {
return screenWidth;
}
/**
- * Gets the height of the client (browser window).
- * <p>
- * Note that the client size is only updated on a full repaint, not when the
- * browser window size changes
- *
- * @return The height of the client or 0 if unknown.
- */
- public int getClientHeight() {
- return clientHeight;
- }
-
- /**
- * Gets the width of the client (browser window)
- * <p>
- * Note that the client size is only updated on a full repaint, not when the
- * browser window size changes
- *
- * @return The width of the client or 0 if unknown.
- */
- public int getClientWidth() {
- return clientWidth;
- }
-
- /**
* Get the browser user-agent string.
*
* @return The raw browser userAgent string
@@ -367,10 +344,6 @@ public class WebBrowser implements Terminal {
* Screen width
* @param sh
* Screen height
- * @param cw
- * Client width
- * @param ch
- * Client height
* @param tzo
* TimeZone offset in minutes from GMT
* @param rtzo
@@ -383,9 +356,9 @@ public class WebBrowser implements Terminal {
* the current date in milliseconds since the epoch
* @param touchDevice
*/
- void updateClientSideDetails(String sw, String sh, String cw, String ch,
- String tzo, String rtzo, String dstSavings, String dstInEffect,
- String curDate, boolean touchDevice) {
+ void updateClientSideDetails(String sw, String sh, String tzo, String rtzo,
+ String dstSavings, String dstInEffect, String curDate,
+ boolean touchDevice) {
if (sw != null) {
try {
screenHeight = Integer.parseInt(sh);
@@ -394,14 +367,6 @@ public class WebBrowser implements Terminal {
screenHeight = screenWidth = 0;
}
}
- if (cw != null) {
- try {
- clientHeight = Integer.parseInt(ch);
- clientWidth = Integer.parseInt(cw);
- } catch (final NumberFormatException e) {
- clientHeight = clientWidth = 0;
- }
- }
if (tzo != null) {
try {
// browser->java conversion: min->ms, reverse sign
@@ -462,8 +427,7 @@ public class WebBrowser implements Terminal {
if (request.getParameter("sw") != null) {
updateClientSideDetails(request.getParameter("sw"),
- request.getParameter("sh"), request.getParameter("cw"),
- request.getParameter("ch"), request.getParameter("tzo"),
+ request.getParameter("sh"), request.getParameter("tzo"),
request.getParameter("rtzo"), request.getParameter("dstd"),
request.getParameter("dston"),
request.getParameter("curdate"),
diff --git a/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java b/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java
index b6f1a192cb..0774a79990 100644
--- a/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java
+++ b/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java
@@ -82,7 +82,7 @@ public class WrappedHttpServletRequest extends HttpServletRequestWrapper
public WebBrowser getWebBrowser() {
WebApplicationContext context = (WebApplicationContext) Application
- .getCurrentApplication().getContext();
+ .getCurrent().getContext();
return context.getBrowser();
}
};
diff --git a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java
index 3838695aa3..85d8d5c69c 100644
--- a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java
+++ b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java
@@ -130,7 +130,7 @@ public class WrappedPortletRequest implements WrappedRequest {
public WebBrowser getWebBrowser() {
PortletApplicationContext2 context = (PortletApplicationContext2) Application
- .getCurrentApplication().getContext();
+ .getCurrent().getContext();
return context.getBrowser();
}
};
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java
index d8d3c23e0c..6d90a51761 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java
@@ -4,7 +4,6 @@
package com.vaadin.terminal.gwt.widgetsetutils;
import java.io.PrintWriter;
-import java.util.Collection;
import java.util.Date;
import com.google.gwt.core.ext.Generator;
@@ -16,14 +15,13 @@ import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
-import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
-import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
+import com.vaadin.terminal.gwt.client.ui.dd.AcceptCriterion;
import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterion;
import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory;
/**
* GWT generator to build {@link VAcceptCriterionFactory} implementation
- * dynamically based on {@link ClientCriterion} annotations available in
+ * dynamically based on {@link AcceptCriterion} annotations available in
* classpath.
*
*/
@@ -102,26 +100,28 @@ public class AcceptCriteriaFactoryGenerator extends Generator {
sourceWriter.println("name = name.intern();");
- Collection<Class<? extends AcceptCriterion>> clientSideVerifiableCriterion = ClassPathExplorer
- .getCriterion();
-
- for (Class<? extends AcceptCriterion> class1 : clientSideVerifiableCriterion) {
- logger.log(Type.INFO,
- "creating mapping for " + class1.getCanonicalName());
- String canonicalName = class1.getCanonicalName();
- Class<? extends VAcceptCriterion> clientClass = class1
- .getAnnotation(ClientCriterion.class).value();
- sourceWriter.print("if (\"");
- sourceWriter.print(canonicalName);
- sourceWriter.print("\" == name) return GWT.create(");
- sourceWriter.print(clientClass.getCanonicalName());
- sourceWriter.println(".class );");
- sourceWriter.print("else ");
+ JClassType criteriaType = context.getTypeOracle().findType(
+ VAcceptCriterion.class.getName());
+ for (JClassType clientClass : criteriaType.getSubtypes()) {
+ AcceptCriterion annotation = clientClass
+ .getAnnotation(AcceptCriterion.class);
+ if (annotation != null) {
+ String clientClassName = clientClass.getQualifiedSourceName();
+ Class<?> serverClass = clientClass.getAnnotation(
+ AcceptCriterion.class).value();
+ String serverClassName = serverClass.getCanonicalName();
+ logger.log(Type.INFO, "creating mapping for " + serverClassName);
+ sourceWriter.print("if (\"");
+ sourceWriter.print(serverClassName);
+ sourceWriter.print("\" == name) return GWT.create(");
+ sourceWriter.print(clientClassName);
+ sourceWriter.println(".class );");
+ sourceWriter.print("else ");
+ }
}
sourceWriter.println("return null;");
sourceWriter.outdent();
sourceWriter.println("}");
}
-
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java
index 6a0aa0f4c2..1c5b736492 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java
@@ -6,33 +6,23 @@ package com.vaadin.terminal.gwt.widgetsetutils;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
-import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
-import com.vaadin.terminal.gwt.server.ClientConnector;
-
/**
* Utility class to collect widgetset related information from classpath.
* Utility will seek all directories from classpaths, and jar files having
@@ -53,9 +43,6 @@ import com.vaadin.terminal.gwt.server.ClientConnector;
*/
public class ClassPathExplorer {
- private static Logger logger = Logger.getLogger(ClassPathExplorer.class
- .getName());
-
private static final String VAADIN_ADDON_VERSION_ATTRIBUTE = "Vaadin-Package-Version";
/**
@@ -92,46 +79,6 @@ public class ClassPathExplorer {
}
/**
- * Finds server side widgets with ClientWidget annotation on the class path
- * (entries that can contain widgets/widgetsets - see
- * getRawClasspathEntries()).
- *
- * As a side effect, also accept criteria are searched under the same class
- * path entries and added into the acceptCriterion collection.
- *
- * @return a collection of {@link ClientConnector} classes
- */
- public static void findAcceptCriteria() {
- logger.info("Searching for accept criteria..");
- long start = System.currentTimeMillis();
- Set<String> keySet = classpathLocations.keySet();
- for (String url : keySet) {
- logger.fine("Searching for accept criteria in "
- + classpathLocations.get(url));
- searchForPaintables(classpathLocations.get(url), url);
- }
- long end = System.currentTimeMillis();
-
- logger.info("Search took " + (end - start) + "ms");
-
- }
-
- /**
- * Finds all accept criteria having client side counterparts (classes with
- * the {@link ClientCriterion} annotation).
- *
- * @return Collection of AcceptCriterion classes
- */
- public static Collection<Class<? extends AcceptCriterion>> getCriterion() {
- if (acceptCriterion.isEmpty()) {
- // accept criterion are searched as a side effect, normally after
- // paintable detection
- findAcceptCriteria();
- }
- return acceptCriterion;
- }
-
- /**
* Finds the names and locations of widgetsets available on the class path.
*
* @return map from widgetset classname to widgetset location URL
@@ -154,6 +101,7 @@ public class ClassPathExplorer {
sb.append(widgetsets.get(ws));
sb.append("\n");
}
+ final Logger logger = getLogger();
logger.info(sb.toString());
logger.info("Search took " + (end - start) + "ms");
return widgetsets;
@@ -214,7 +162,7 @@ public class ClassPathExplorer {
} catch (MalformedURLException e) {
// should never happen as based on an existing URL,
// only changing end of file name/path part
- logger.log(Level.SEVERE,
+ getLogger().log(Level.SEVERE,
"Error locating the widgetset " + classname, e);
}
}
@@ -250,7 +198,7 @@ public class ClassPathExplorer {
}
}
} catch (IOException e) {
- logger.log(Level.WARNING, "Error parsing jar file", e);
+ getLogger().log(Level.WARNING, "Error parsing jar file", e);
}
}
@@ -278,7 +226,7 @@ public class ClassPathExplorer {
classpath = classpath.substring(0, classpath.length() - 1);
}
- logger.fine("Classpath: " + classpath);
+ getLogger().fine("Classpath: " + classpath);
String[] split = classpath.split(pathSep);
for (int i = 0; i < split.length; i++) {
@@ -312,6 +260,7 @@ public class ClassPathExplorer {
include(null, file, locations);
}
long end = System.currentTimeMillis();
+ Logger logger = getLogger();
if (logger.isLoggable(Level.FINE)) {
logger.fine("getClassPathLocations took " + (end - start) + "ms");
}
@@ -352,7 +301,7 @@ public class ClassPathExplorer {
url = new URL("jar:" + url.toExternalForm() + "!/");
JarURLConnection conn = (JarURLConnection) url
.openConnection();
- logger.fine(url.toString());
+ getLogger().fine(url.toString());
JarFile jarFile = conn.getJarFile();
Manifest manifest = jarFile.getManifest();
if (manifest != null) {
@@ -363,9 +312,11 @@ public class ClassPathExplorer {
}
}
} catch (MalformedURLException e) {
- logger.log(Level.FINEST, "Failed to inspect JAR file", e);
+ getLogger().log(Level.FINEST, "Failed to inspect JAR file",
+ e);
} catch (IOException e) {
- logger.log(Level.FINEST, "Failed to inspect JAR file", e);
+ getLogger().log(Level.FINEST, "Failed to inspect JAR file",
+ e);
}
return false;
@@ -445,151 +396,6 @@ public class ClassPathExplorer {
}
/**
- * Searches for all paintable classes and accept criteria under a location
- * based on {@link ClientCriterion} annotations.
- *
- * Note that client criteria are updated directly to the
- * {@link #acceptCriterion} field, whereas paintables are added to the
- * paintables map given as a parameter.
- *
- * @param location
- * @param locationString
- */
- private final static void searchForPaintables(URL location,
- String locationString) {
-
- // Get a File object for the package
- File directory = new File(location.getFile());
-
- if (directory.exists() && !directory.isHidden()) {
- // Get the list of the files contained in the directory
- String[] files = directory.list();
- for (int i = 0; i < files.length; i++) {
- // we are only interested in .class files
- if (files[i].endsWith(".class")) {
- // remove the .class extension
- String classname = files[i].substring(0,
- files[i].length() - 6);
- String packageName = locationString
- .substring(locationString.lastIndexOf("/") + 1);
- classname = packageName + "." + classname;
- tryToAdd(classname);
- }
- }
- } else {
- try {
- // check files in jar file, entries will list all directories
- // and files in jar
-
- URLConnection openConnection = location.openConnection();
-
- if (openConnection instanceof JarURLConnection) {
- JarURLConnection conn = (JarURLConnection) openConnection;
-
- JarFile jarFile = conn.getJarFile();
-
- // Only scan for paintables in Vaadin add-ons
- if (!isVaadinAddon(jarFile)) {
- return;
- }
-
- Enumeration<JarEntry> e = jarFile.entries();
- while (e.hasMoreElements()) {
- JarEntry entry = e.nextElement();
- String entryname = entry.getName();
- if (!entry.isDirectory()
- && entryname.endsWith(".class")) {
- String classname = entryname.substring(0,
- entryname.length() - 6);
- if (classname.startsWith("/")) {
- classname = classname.substring(1);
- }
- classname = classname.replace('/', '.');
- tryToAdd(classname);
- }
- }
- }
- } catch (IOException e) {
- logger.warning(e.toString());
- }
- }
-
- }
-
- /**
- * A print stream that ignores all output.
- *
- * This is used to hide error messages from static initializers of classes
- * being inspected.
- */
- private static PrintStream devnull = new PrintStream(new OutputStream() {
- @Override
- public void write(int b) throws IOException {
- // NOP
- }
- });
-
- /**
- * Collection of all {@link AcceptCriterion} classes, updated as a side
- * effect of {@link #searchForPaintables(URL, String, Collection)} based on
- * {@link ClientCriterion} annotations.
- */
- private static Set<Class<? extends AcceptCriterion>> acceptCriterion = new HashSet<Class<? extends AcceptCriterion>>();
-
- /**
- * Checks a class for the {@link ClientCriterion} annotations, and adds it
- * to the appropriate collection.
- *
- * @param fullclassName
- */
- @SuppressWarnings("unchecked")
- private static void tryToAdd(final String fullclassName) {
- PrintStream out = System.out;
- PrintStream err = System.err;
- Throwable errorToShow = null;
- Level logLevel = null;
- try {
- System.setErr(devnull);
- System.setOut(devnull);
-
- Class<?> c = Class.forName(fullclassName);
-
- if (c.getAnnotation(ClientCriterion.class) != null) {
- acceptCriterion.add((Class<? extends AcceptCriterion>) c);
- }
- } catch (UnsupportedClassVersionError e) {
- // Inform the user about this as the class might contain a Paintable
- // Typically happens when using an add-on that is compiled using a
- // newer Java version.
- logLevel = Level.INFO;
- errorToShow = e;
- } catch (ClassNotFoundException e) {
- // Don't show to avoid flooding the user with irrelevant messages
- logLevel = Level.FINE;
- errorToShow = e;
- } catch (LinkageError e) {
- // Don't show to avoid flooding the user with irrelevant messages
- logLevel = Level.FINE;
- errorToShow = e;
- } catch (Exception e) {
- // Don't show to avoid flooding the user with irrelevant messages
- logLevel = Level.FINE;
- errorToShow = e;
- } finally {
- System.setErr(err);
- System.setOut(out);
- }
-
- // Must be done here after stderr and stdout have been reset.
- if (errorToShow != null && logLevel != null) {
- logger.log(logLevel,
- "Failed to load class " + fullclassName + ". "
- + errorToShow.getClass().getName() + ": "
- + errorToShow.getMessage());
- }
- }
-
- /**
* Find and return the default source directory where to create new
* widgetsets.
*
@@ -601,6 +407,9 @@ public class ClassPathExplorer {
* @return URL
*/
public static URL getDefaultSourceDirectory() {
+
+ final Logger logger = getLogger();
+
if (logger.isLoggable(Level.FINE)) {
logger.fine("classpathLocations values:");
ArrayList<String> locations = new ArrayList<String>(
@@ -632,44 +441,21 @@ public class ClassPathExplorer {
}
/**
- * Checks if the given jarFile is a Vaadin add-on.
- *
- * @param jarFile
- * @return true if the file is an add-on, false otherwise
- * @throws IOException
- */
- private static boolean isVaadinAddon(JarFile jarFile) throws IOException {
- Manifest manifest = jarFile.getManifest();
- if (manifest == null) {
- return false;
- }
- Attributes mainAttributes = manifest.getMainAttributes();
- if (mainAttributes == null) {
- return false;
- }
-
- return (mainAttributes.getValue(VAADIN_ADDON_VERSION_ATTRIBUTE) != null);
- }
-
- /**
* Test method for helper tool
*/
public static void main(String[] args) {
- ClassPathExplorer.findAcceptCriteria();
- logger.info("Found client criteria:");
- for (Class<? extends AcceptCriterion> cls : acceptCriterion) {
- logger.info(cls.getCanonicalName());
- }
-
- logger.info("");
- logger.info("Searching available widgetsets...");
+ getLogger().info("Searching available widgetsets...");
Map<String, URL> availableWidgetSets = ClassPathExplorer
.getAvailableWidgetSets();
for (String string : availableWidgetSets.keySet()) {
- logger.info(string + " in " + availableWidgetSets.get(string));
+ getLogger().info(string + " in " + availableWidgetSets.get(string));
}
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(ClassPathExplorer.class.getName());
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java
index f0d6f0453b..f0f3df20b0 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java
@@ -7,6 +7,7 @@ import java.util.Collection;
import java.util.HashSet;
import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.ui.Connect;
import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
@@ -25,8 +26,7 @@ public abstract class CustomWidgetMapGenerator extends WidgetMapGenerator {
private Collection<Class<? extends ComponentConnector>> deferredPaintables = new HashSet<Class<? extends ComponentConnector>>();
@Override
- protected LoadStyle getLoadStyle(
- Class<? extends ComponentConnector> connector) {
+ protected LoadStyle getLoadStyle(Class<? extends ServerConnector> connector) {
if (eagerPaintables == null) {
init();
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java
index 8a1dfee3b5..084e1c3857 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java
@@ -3,7 +3,7 @@
*/
package com.vaadin.terminal.gwt.widgetsetutils;
-import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
/**
@@ -23,8 +23,7 @@ import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
public class EagerWidgetMapGenerator extends WidgetMapGenerator {
@Override
- protected LoadStyle getLoadStyle(
- Class<? extends ComponentConnector> connector) {
+ protected LoadStyle getLoadStyle(Class<? extends ServerConnector> connector) {
return LoadStyle.EAGER;
}
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java
index 2899061204..b1d69b178b 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java
@@ -16,15 +16,15 @@ import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
-import com.vaadin.terminal.gwt.client.ServerConnector;
-import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.communication.ClientRpc;
-import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.GeneratedRpcMethodProvider;
import com.vaadin.terminal.gwt.client.communication.RpcManager;
+import com.vaadin.terminal.gwt.client.communication.RpcMethod;
/**
* GWT generator that creates an implementation for {@link RpcManager} on the
@@ -32,7 +32,7 @@ import com.vaadin.terminal.gwt.client.communication.RpcManager;
*
* @since 7.0
*/
-public class RpcManagerGenerator extends Generator {
+public class GeneratedRpcMethodProviderGenerator extends Generator {
@Override
public String generate(TreeLogger logger, GeneratorContext context,
@@ -90,12 +90,24 @@ public class RpcManagerGenerator extends Generator {
ClassSourceFileComposerFactory composer = null;
composer = new ClassSourceFileComposerFactory(packageName, className);
composer.addImport("com.google.gwt.core.client.GWT");
- composer.addImplementedInterface(RpcManager.class.getName());
+ composer.addImport(RpcMethod.class.getName());
+ composer.addImport(ClientRpc.class.getName());
+ composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class
+ .getName());
+ composer.addImplementedInterface(GeneratedRpcMethodProvider.class
+ .getName());
SourceWriter sourceWriter = composer.createSourceWriter(context,
printWriter);
sourceWriter.indent();
- List<JClassType> rpcInterfaces = new ArrayList<JClassType>();
+ List<JMethod> rpcMethods = new ArrayList<JMethod>();
+
+ sourceWriter
+ .println("public java.util.Collection<RpcMethod> getGeneratedRpcMethods() {");
+ sourceWriter.indent();
+
+ sourceWriter
+ .println("java.util.ArrayList<RpcMethod> list = new java.util.ArrayList<RpcMethod>();");
// iterate over RPC interfaces and create helper methods for each
// interface
@@ -104,81 +116,56 @@ public class RpcManagerGenerator extends Generator {
// only interested in interfaces here, not implementations
continue;
}
- rpcInterfaces.add(type);
- // generate method to call methods of an RPC interface
- sourceWriter.println("private void " + getInvokeMethodName(type)
- + "(" + MethodInvocation.class.getName() + " invocation, "
- + ConnectorMap.class.getName() + " connectorMap) {");
- sourceWriter.indent();
// loop over the methods of the interface and its superinterfaces
// methods
for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) {
for (JMethod method : currentType.getMethods()) {
- sourceWriter.println("if (\"" + method.getName()
- + "\".equals(invocation.getMethodName())) {");
- sourceWriter.indent();
- // construct parameter string with appropriate casts
- String paramString = "";
+
+ // RpcMethod(String interfaceName, String methodName,
+ // Type... parameterTypes)
+ sourceWriter.print("list.add(new RpcMethod(\""
+ + type.getQualifiedSourceName() + "\", \""
+ + method.getName() + "\"");
JType[] parameterTypes = method.getParameterTypes();
- for (int i = 0; i < parameterTypes.length; ++i) {
- paramString = paramString + "("
- + parameterTypes[i].getQualifiedSourceName()
- + ") invocation.getParameters()[" + i + "]";
- if (i < parameterTypes.length - 1) {
- paramString = paramString + ", ";
- }
+ for (JType parameter : parameterTypes) {
+ sourceWriter.print(", ");
+ writeTypeCreator(sourceWriter, parameter);
}
+ sourceWriter.println(") {");
+ sourceWriter.indent();
+
sourceWriter
- .println(ServerConnector.class.getName()
- + " connector = connectorMap.getConnector(invocation.getConnectorId());");
- sourceWriter
- .println("for ("
- + ClientRpc.class.getName()
- + " rpcImplementation : connector.getRpcImplementations(\""
- + type.getQualifiedSourceName() + "\")) {");
+ .println("public void applyInvocation(ClientRpc target, Object... parameters) {");
sourceWriter.indent();
- sourceWriter.println("((" + type.getQualifiedSourceName()
- + ") rpcImplementation)." + method.getName() + "("
- + paramString + ");");
+
+ sourceWriter.print("((" + type.getQualifiedSourceName()
+ + ")target)." + method.getName() + "(");
+ for (int i = 0; i < parameterTypes.length; i++) {
+ JType parameterType = parameterTypes[i];
+ if (i != 0) {
+ sourceWriter.print(", ");
+ }
+ String parameterTypeName = getBoxedTypeName(parameterType);
+ sourceWriter.print("(" + parameterTypeName
+ + ") parameters[" + i + "]");
+ }
+ sourceWriter.println(");");
+
sourceWriter.outdent();
sourceWriter.println("}");
- sourceWriter.println("return;");
+
sourceWriter.outdent();
- sourceWriter.println("}");
+ sourceWriter.println("});");
}
}
-
- sourceWriter.outdent();
- sourceWriter.println("}");
-
- logger.log(Type.DEBUG,
- "Constructed helper method for server to client RPC for "
- + type.getName());
}
- // generate top-level "switch-case" method to select the correct
- // previously generated method based on the RPC interface
- sourceWriter.println("public void applyInvocation("
- + MethodInvocation.class.getName() + " invocation, "
- + ConnectorMap.class.getName() + " connectorMap) {");
- sourceWriter.indent();
+ sourceWriter.println("return list;");
- for (JClassType type : rpcInterfaces) {
- sourceWriter.println("if (\"" + type.getQualifiedSourceName()
- + "\".equals(invocation.getInterfaceName())) {");
- sourceWriter.indent();
- sourceWriter.println(getInvokeMethodName(type)
- + "(invocation, connectorMap);");
- sourceWriter.println("return;");
- sourceWriter.outdent();
- sourceWriter.println("}");
-
- logger.log(Type.INFO,
- "Configured server to client RPC for " + type.getName());
- }
sourceWriter.outdent();
sourceWriter.println("}");
+ sourceWriter.println();
// close generated class
sourceWriter.outdent();
@@ -191,6 +178,33 @@ public class RpcManagerGenerator extends Generator {
}
+ public static void writeTypeCreator(SourceWriter sourceWriter, JType type) {
+ String typeName = getBoxedTypeName(type);
+ sourceWriter.print("new Type(\"" + typeName + "\", ");
+ JParameterizedType parameterized = type.isParameterized();
+ if (parameterized != null) {
+ sourceWriter.print("new Type[] {");
+ JClassType[] typeArgs = parameterized.getTypeArgs();
+ for (JClassType jClassType : typeArgs) {
+ writeTypeCreator(sourceWriter, jClassType);
+ sourceWriter.print(", ");
+ }
+ sourceWriter.print("}");
+ } else {
+ sourceWriter.print("null");
+ }
+ sourceWriter.print(")");
+ }
+
+ public static String getBoxedTypeName(JType type) {
+ if (type.isPrimitive() != null) {
+ // Used boxed types for primitives
+ return type.isPrimitive().getQualifiedBoxedSourceName();
+ } else {
+ return type.getErasedType().getQualifiedSourceName();
+ }
+ }
+
private String getInvokeMethodName(JClassType type) {
return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_");
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java
index 729a999a21..f8366beb46 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java
@@ -3,7 +3,7 @@
*/
package com.vaadin.terminal.gwt.widgetsetutils;
-import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
/**
@@ -16,8 +16,7 @@ import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
*/
public class LazyWidgetMapGenerator extends WidgetMapGenerator {
@Override
- protected LoadStyle getLoadStyle(
- Class<? extends ComponentConnector> connector) {
+ protected LoadStyle getLoadStyle(Class<? extends ServerConnector> connector) {
return LoadStyle.LAZY;
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java
index ad4e513049..d9bc8bc832 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java
@@ -117,7 +117,7 @@ public class RpcProxyGenerator extends Generator {
writer.println(" {");
writer.indent();
- writer.print("connector.getConnection().addMethodInvocationToQueue(new MethodInvocation(connector.getConnectorId(), \""
+ writer.print("this.connector.getConnection().addMethodInvocationToQueue(new MethodInvocation(this.connector.getConnectorId(), \""
+ requestedType.getQualifiedBinaryName() + "\", \"");
writer.print(m.getName());
writer.print("\", new Object[] {");
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
index bc031f4bdb..1951f8ba40 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
@@ -6,7 +6,7 @@ package com.vaadin.terminal.gwt.widgetsetutils;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import com.google.gwt.core.client.GWT;
@@ -15,13 +15,14 @@ import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JEnumConstant;
import com.google.gwt.core.ext.typeinfo.JEnumType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.TypeOracleException;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
@@ -29,7 +30,7 @@ import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.communication.DiffJSONSerializer;
import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
@@ -46,26 +47,32 @@ import com.vaadin.terminal.gwt.client.communication.SerializerMap;
public class SerializerGenerator extends Generator {
private static final String SUBTYPE_SEPARATOR = "___";
- private static String beanSerializerPackageName = SerializerMap.class
+ private static String serializerPackageName = SerializerMap.class
.getPackage().getName();
@Override
public String generate(TreeLogger logger, GeneratorContext context,
- String beanTypeName) throws UnableToCompleteException {
- JClassType beanType = context.getTypeOracle().findType(beanTypeName);
- String beanSerializerClassName = getSerializerSimpleClassName(beanType);
+ String typeName) throws UnableToCompleteException {
+ JClassType type;
+ try {
+ type = (JClassType) context.getTypeOracle().parse(typeName);
+ } catch (TypeOracleException e1) {
+ logger.log(Type.ERROR, "Could not find type " + typeName, e1);
+ throw new UnableToCompleteException();
+ }
+ String serializerClassName = getSerializerSimpleClassName(type);
try {
// Generate class source code
- generateClass(logger, context, beanType, beanSerializerPackageName,
- beanSerializerClassName);
+ generateClass(logger, context, type, serializerPackageName,
+ serializerClassName);
} catch (Exception e) {
logger.log(TreeLogger.ERROR, "SerializerGenerator failed for "
- + beanType.getQualifiedSourceName(), e);
+ + type.getQualifiedSourceName(), e);
throw new UnableToCompleteException();
}
// return the fully qualifed name of the class generated
- return getFullyQualifiedSerializerClassName(beanType);
+ return getFullyQualifiedSerializerClassName(type);
}
/**
@@ -75,15 +82,16 @@ public class SerializerGenerator extends Generator {
* Logger object
* @param context
* Generator context
- * @param beanType
+ * @param type
* @param beanTypeName
* bean type for which the serializer is to be generated
* @param beanSerializerTypeName
* name of the serializer class to generate
+ * @throws UnableToCompleteException
*/
private void generateClass(TreeLogger logger, GeneratorContext context,
- JClassType beanType, String serializerPackageName,
- String serializerClassName) {
+ JClassType type, String serializerPackageName,
+ String serializerClassName) throws UnableToCompleteException {
// get print writer that receives the source code
PrintWriter printWriter = null;
printWriter = context.tryCreate(logger, serializerPackageName,
@@ -93,27 +101,33 @@ public class SerializerGenerator extends Generator {
if (printWriter == null) {
return;
}
- boolean isEnum = (beanType.isEnum() != null);
+ boolean isEnum = (type.isEnum() != null);
+ boolean isArray = (type.isArray() != null);
- Date date = new Date();
- TypeOracle typeOracle = context.getTypeOracle();
- String beanQualifiedSourceName = beanType.getQualifiedSourceName();
+ String qualifiedSourceName = type.getQualifiedSourceName();
logger.log(Type.DEBUG, "Processing serializable type "
- + beanQualifiedSourceName + "...");
+ + qualifiedSourceName + "...");
// init composer, set class properties, create source writer
ClassSourceFileComposerFactory composer = null;
composer = new ClassSourceFileComposerFactory(serializerPackageName,
serializerClassName);
composer.addImport(GWT.class.getName());
- composer.addImport(JSONArray.class.getName());
+ composer.addImport(JSONValue.class.getName());
+ composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class
+ .getName());
// composer.addImport(JSONObject.class.getName());
// composer.addImport(VPaintableMap.class.getName());
composer.addImport(JsonDecoder.class.getName());
// composer.addImport(VaadinSerializer.class.getName());
- composer.addImplementedInterface(JSONSerializer.class.getName() + "<"
- + beanQualifiedSourceName + ">");
+ if (isEnum || isArray) {
+ composer.addImplementedInterface(JSONSerializer.class.getName()
+ + "<" + qualifiedSourceName + ">");
+ } else {
+ composer.addImplementedInterface(DiffJSONSerializer.class.getName()
+ + "<" + qualifiedSourceName + ">");
+ }
SourceWriter sourceWriter = composer.createSourceWriter(context,
printWriter);
@@ -121,49 +135,76 @@ public class SerializerGenerator extends Generator {
// Serializer
- // public JSONValue serialize(Object value, ConnectorMap idMapper,
+ // public JSONValue serialize(Object value,
// ApplicationConnection connection) {
sourceWriter.println("public " + JSONValue.class.getName()
- + " serialize(" + beanQualifiedSourceName + " value, "
- + ConnectorMap.class.getName() + " idMapper, "
+ + " serialize(" + qualifiedSourceName + " value, "
+ ApplicationConnection.class.getName() + " connection) {");
sourceWriter.indent();
// MouseEventDetails castedValue = (MouseEventDetails) value;
- sourceWriter.println(beanQualifiedSourceName + " castedValue = ("
- + beanQualifiedSourceName + ") value;");
+ sourceWriter.println(qualifiedSourceName + " castedValue = ("
+ + qualifiedSourceName + ") value;");
if (isEnum) {
- writeEnumSerializer(logger, sourceWriter, beanType);
+ writeEnumSerializer(logger, sourceWriter, type);
+ } else if (isArray) {
+ writeArraySerializer(logger, sourceWriter, type.isArray());
} else {
- writeBeanSerializer(logger, sourceWriter, beanType);
+ writeBeanSerializer(logger, sourceWriter, type);
}
// }
+ sourceWriter.outdent();
sourceWriter.println("}");
+ sourceWriter.println();
+
+ // Updater
+ // public void update(T target, Type type, JSONValue jsonValue,
+ // ApplicationConnection connection);
+ if (!isEnum && !isArray) {
+ sourceWriter.println("public void update(" + qualifiedSourceName
+ + " target, Type type, " + JSONValue.class.getName()
+ + " jsonValue, " + ApplicationConnection.class.getName()
+ + " connection) {");
+ sourceWriter.indent();
+
+ writeBeanDeserializer(logger, sourceWriter, type);
+
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ }
// Deserializer
- sourceWriter.println("public " + beanQualifiedSourceName
- + " deserialize(" + JSONValue.class.getName() + " jsonValue, "
- + beanQualifiedSourceName + " target, "
- + ConnectorMap.class.getName() + " idMapper, "
- + ApplicationConnection.class.getName() + " connection) {");
+ // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection
+ // connection);
+ sourceWriter.println("public " + qualifiedSourceName
+ + " deserialize(Type type, " + JSONValue.class.getName()
+ + " jsonValue, " + ApplicationConnection.class.getName()
+ + " connection) {");
sourceWriter.indent();
if (isEnum) {
- writeEnumDeserializer(logger, sourceWriter, beanType.isEnum());
+ writeEnumDeserializer(logger, sourceWriter, type.isEnum());
+ } else if (isArray) {
+ writeArrayDeserializer(logger, sourceWriter, type.isArray());
} else {
- writeBeanDeserializer(logger, sourceWriter, beanType);
+ sourceWriter.println(qualifiedSourceName + " target = GWT.create("
+ + qualifiedSourceName + ".class);");
+ sourceWriter
+ .println("update(target, type, jsonValue, connection);");
+ // return target;
+ sourceWriter.println("return target;");
}
- sourceWriter.println("}");
sourceWriter.outdent();
+ sourceWriter.println("}");
// End of class
- sourceWriter.println("}");
sourceWriter.outdent();
+ sourceWriter.println("}");
// commit generated class
context.commit(logger, printWriter);
logger.log(TreeLogger.INFO, "Generated Serializer class "
- + getFullyQualifiedSerializerClassName(beanType));
+ + getFullyQualifiedSerializerClassName(type));
}
private void writeEnumDeserializer(TreeLogger logger,
@@ -182,27 +223,56 @@ public class SerializerGenerator extends Generator {
sourceWriter.println("return null;");
}
- private void writeBeanDeserializer(TreeLogger logger,
- SourceWriter sourceWriter, JClassType beanType) {
- String beanQualifiedSourceName = beanType.getQualifiedSourceName();
+ private void writeArrayDeserializer(TreeLogger logger,
+ SourceWriter sourceWriter, JArrayType type) {
+ JType leafType = type.getLeafType();
+ int rank = type.getRank();
+
+ sourceWriter.println(JSONArray.class.getName()
+ + " jsonArray = jsonValue.isArray();");
- // if (target == null) {
- sourceWriter.println("if (target == null) {");
+ // Type value = new Type[jsonArray.size()][][];
+ sourceWriter.print(type.getQualifiedSourceName() + " value = new "
+ + leafType.getQualifiedSourceName() + "[jsonArray.size()]");
+ for (int i = 1; i < rank; i++) {
+ sourceWriter.print("[]");
+ }
+ sourceWriter.println(";");
+
+ sourceWriter.println("for(int i = 0 ; i < value.length; i++) {");
sourceWriter.indent();
- // target = GWT.create(VButtonState.class);
- sourceWriter.println("target = GWT.create(" + beanQualifiedSourceName
- + ".class);");
+ JType componentType = type.getComponentType();
+
+ sourceWriter.print("value[i] = ("
+ + GeneratedRpcMethodProviderGenerator
+ .getBoxedTypeName(componentType) + ") "
+ + JsonDecoder.class.getName() + ".decodeValue(");
+ GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter,
+ componentType);
+ sourceWriter.print(", jsonArray.get(i), null, connection)");
+
+ sourceWriter.println(";");
+
sourceWriter.outdent();
sourceWriter.println("}");
+ sourceWriter.println("return value;");
+ }
+
+ private void writeBeanDeserializer(TreeLogger logger,
+ SourceWriter sourceWriter, JClassType beanType) {
+ String beanQualifiedSourceName = beanType.getQualifiedSourceName();
+
// JSONOBject json = (JSONObject)jsonValue;
sourceWriter.println(JSONObject.class.getName() + " json = ("
+ JSONObject.class.getName() + ")jsonValue;");
for (JMethod method : getSetters(beanType)) {
String setterName = method.getName();
- String fieldName = setterName.substring(3); // setZIndex() -> ZIndex
+ String baseName = setterName.substring(3);
+ String fieldName = getTransportFieldName(baseName); // setZIndex()
+ // -> zIndex
JType setterParameterType = method.getParameterTypes()[0];
logger.log(Type.DEBUG, "* Processing field " + fieldName + " in "
@@ -213,55 +283,41 @@ public class SerializerGenerator extends Generator {
+ "\")) {");
sourceWriter.indent();
String jsonFieldName = "json_" + fieldName;
- // JSONArray json_Height = (JSONArray) json.get("height");
- sourceWriter.println("JSONArray " + jsonFieldName
- + " = (JSONArray) json.get(\"" + fieldName + "\");");
+ // JSONValue json_Height = json.get("height");
+ sourceWriter.println("JSONValue " + jsonFieldName
+ + " = json.get(\"" + fieldName + "\");");
String fieldType;
- String getterName = "get" + fieldName;
+ String getterName = "get" + baseName;
JPrimitiveType primitiveType = setterParameterType.isPrimitive();
if (primitiveType != null) {
// This is a primitive type -> must used the boxed type
fieldType = primitiveType.getQualifiedBoxedSourceName();
if (primitiveType == JPrimitiveType.BOOLEAN) {
- getterName = "is" + fieldName;
+ getterName = "is" + baseName;
}
} else {
fieldType = setterParameterType.getQualifiedSourceName();
}
- // String referenceValue;
- sourceWriter.println(fieldType + " referenceValue;");
- // if (target == null) {
- sourceWriter.println("if (target == null) {");
- sourceWriter.indent();
- // referenceValue = null;
- sourceWriter.println("referenceValue = null;");
- // } else {
- sourceWriter.println("} else {");
- // referenceValue = target.getHeight();
- sourceWriter.println("referenceValue = target." + getterName
- + "();");
- // }
- sourceWriter.outdent();
- sourceWriter.println("}");
+ // String referenceValue = target.getHeight();
+ sourceWriter.println(fieldType + " referenceValue = target."
+ + getterName + "();");
// target.setHeight((String)
// JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper,
// connection));
- sourceWriter.println("target." + setterName + "((" + fieldType
- + ") " + JsonDecoder.class.getName() + ".decodeValue("
- + jsonFieldName
- + ", referenceValue, idMapper, connection));");
+ sourceWriter.print("target." + setterName + "((" + fieldType + ") "
+ + JsonDecoder.class.getName() + ".decodeValue(");
+ GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter,
+ setterParameterType);
+ sourceWriter.println(", " + jsonFieldName
+ + ", referenceValue, connection));");
// } ... end of if contains
- sourceWriter.println("}");
sourceWriter.outdent();
+ sourceWriter.println("}");
}
-
- // return target;
- sourceWriter.println("return target;");
-
}
private void writeEnumSerializer(TreeLogger logger,
@@ -271,16 +327,47 @@ public class SerializerGenerator extends Generator {
+ "(castedValue.name());");
}
+ private void writeArraySerializer(TreeLogger logger,
+ SourceWriter sourceWriter, JArrayType array) {
+ sourceWriter.println(JSONArray.class.getName() + " values = new "
+ + JSONArray.class.getName() + "();");
+ JType componentType = array.getComponentType();
+ // JPrimitiveType primitive = componentType.isPrimitive();
+ sourceWriter.println("for (int i = 0; i < castedValue.length; i++) {");
+ sourceWriter.indent();
+ sourceWriter.print("values.set(i, ");
+ sourceWriter.print(JsonEncoder.class.getName()
+ + ".encode(castedValue[i], false, connection)");
+ sourceWriter.println(");");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ sourceWriter.println("return values;");
+ }
+
private void writeBeanSerializer(TreeLogger logger,
- SourceWriter sourceWriter, JClassType beanType) {
+ SourceWriter sourceWriter, JClassType beanType)
+ throws UnableToCompleteException {
// JSONObject json = new JSONObject();
sourceWriter.println(JSONObject.class.getName() + " json = new "
+ JSONObject.class.getName() + "();");
+ HashSet<String> usedFieldNames = new HashSet<String>();
+
for (JMethod setterMethod : getSetters(beanType)) {
String setterName = setterMethod.getName();
- String fieldName = setterName.substring(3); // setZIndex() -> ZIndex
+ String fieldName = getTransportFieldName(setterName.substring(3)); // setZIndex()
+ // -> zIndex
+ if (!usedFieldNames.add(fieldName)) {
+ logger.log(
+ TreeLogger.ERROR,
+ "Can't encode "
+ + beanType.getQualifiedSourceName()
+ + " as it has multiple fields with the name "
+ + fieldName.toLowerCase()
+ + ". This can happen if only casing distinguishes one property name from another.");
+ throw new UnableToCompleteException();
+ }
String getterName = findGetter(beanType, setterMethod);
if (getterName == null) {
@@ -292,13 +379,18 @@ public class SerializerGenerator extends Generator {
// connection));
sourceWriter.println("json.put(\"" + fieldName + "\", "
+ JsonEncoder.class.getName() + ".encode(castedValue."
- + getterName + "(), false, idMapper, connection));");
+ + getterName + "(), false, connection));");
}
// return json;
sourceWriter.println("return json;");
}
+ private static String getTransportFieldName(String baseName) {
+ return Character.toLowerCase(baseName.charAt(0))
+ + baseName.substring(1);
+ }
+
private String findGetter(JClassType beanType, JMethod setterMethod) {
JType setterParameterType = setterMethod.getParameterTypes()[0];
String fieldName = setterMethod.getName().substring(3);
@@ -344,10 +436,15 @@ public class SerializerGenerator extends Generator {
return getSimpleClassName(beanType) + "_Serializer";
}
- private static String getSimpleClassName(JClassType type) {
- if (type.isMemberType()) {
+ private static String getSimpleClassName(JType type) {
+ JArrayType arrayType = type.isArray();
+ if (arrayType != null) {
+ return "Array" + getSimpleClassName(arrayType.getComponentType());
+ }
+ JClassType classType = type.isClass();
+ if (classType != null && classType.isMemberType()) {
// Assumed to be static sub class
- String baseName = getSimpleClassName(type.getEnclosingType());
+ String baseName = getSimpleClassName(classType.getEnclosingType());
String name = baseName + SUBTYPE_SEPARATOR
+ type.getSimpleSourceName();
return name;
@@ -356,7 +453,6 @@ public class SerializerGenerator extends Generator {
}
public static String getFullyQualifiedSerializerClassName(JClassType type) {
- return beanSerializerPackageName + "."
- + getSerializerSimpleClassName(type);
+ return serializerPackageName + "." + getSerializerSimpleClassName(type);
}
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
index 07efcda91b..5e151323a0 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
@@ -17,6 +17,7 @@ import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
@@ -96,6 +97,10 @@ public class SerializerMapGenerator extends Generator {
JClassType javaSerializable = typeOracle.findType(Serializable.class
.getName());
for (JClassType type : typesNeedingSerializers) {
+ if (type.isArray() != null) {
+ // Don't check for arrays
+ continue;
+ }
boolean serializable = type.isAssignableTo(javaSerializable);
if (!serializable) {
logger.log(
@@ -166,8 +171,15 @@ public class SerializerMapGenerator extends Generator {
// TODO cache serializer instances in a map
for (JClassType type : typesNeedingSerializers) {
- sourceWriter.println("if (type.equals(\""
- + type.getQualifiedBinaryName() + "\")) {");
+ sourceWriter.print("if (type.equals(\""
+ + type.getQualifiedSourceName() + "\")");
+ if (type instanceof JArrayType) {
+ // Also add binary name to support encoding based on
+ // object.getClass().getName()
+ sourceWriter.print("||type.equals(\"" + type.getJNISignature()
+ + "\")");
+ }
+ sourceWriter.println(") {");
sourceWriter.indent();
String serializerName = SerializerGenerator
.getFullyQualifiedSerializerClassName(type);
@@ -203,6 +215,7 @@ public class SerializerMapGenerator extends Generator {
// Generate serializer classes for each subclass of SharedState
JClassType serializerType = typeOracle.findType(SharedState.class
.getName());
+ types.add(serializerType);
JClassType[] serializerSubtypes = serializerType.getSubtypes();
for (JClassType type : serializerSubtypes) {
types.add(type);
@@ -276,6 +289,14 @@ public class SerializerMapGenerator extends Generator {
serializableTypes.add(typeClass);
findSubTypesNeedingSerializers(typeClass, serializableTypes);
}
+
+ // Generate (n-1)-dimensional array serializer for n-dimensional array
+ JArrayType arrayType = type.isArray();
+ if (arrayType != null) {
+ serializableTypes.add(arrayType);
+ addTypeIfNeeded(serializableTypes, arrayType.getComponentType());
+ }
+
}
Set<Class<?>> frameworkHandledTypes = new HashSet<Class<?>>();
@@ -292,15 +313,14 @@ public class SerializerMapGenerator extends Generator {
frameworkHandledTypes.add(Map.class);
frameworkHandledTypes.add(List.class);
frameworkHandledTypes.add(Set.class);
+ frameworkHandledTypes.add(Byte.class);
+ frameworkHandledTypes.add(Character.class);
}
private boolean serializationHandledByFramework(JType setterType) {
// Some types are handled by the framework at the moment. See #8449
// This method should be removed at some point.
- if (setterType.isArray() != null) {
- return true;
- }
if (setterType.isPrimitive() != null) {
return true;
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java
index 6d4289b173..b264a9c7fe 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java
@@ -22,8 +22,8 @@ import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
-import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
import com.vaadin.terminal.gwt.client.ui.Connect;
import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
@@ -71,7 +71,7 @@ import com.vaadin.terminal.gwt.server.ClientConnector;
*/
public class WidgetMapGenerator extends Generator {
- private static String componentConnectorClassName = ComponentConnector.class
+ private static String serverConnectorClassName = ServerConnector.class
.getName();
private String packageName;
@@ -115,7 +115,7 @@ public class WidgetMapGenerator extends Generator {
return;
}
logger.log(Type.INFO,
- "Detecting Vaadin components in classpath to generate WidgetMapImpl.java ...");
+ "Detecting Vaadin connectors in classpath to generate WidgetMapImpl.java ...");
Date date = new Date();
// init composer, set class properties, create source writer
@@ -128,7 +128,7 @@ public class WidgetMapGenerator extends Generator {
SourceWriter sourceWriter = composer.createSourceWriter(context,
printWriter);
- Collection<Class<? extends ComponentConnector>> connectors = getUsedConnectors(context
+ Collection<Class<? extends ServerConnector>> connectors = getUsedConnectors(context
.getTypeOracle());
validateConnectors(logger, connectors);
@@ -149,12 +149,11 @@ public class WidgetMapGenerator extends Generator {
}
private void validateConnectors(TreeLogger logger,
- Collection<Class<? extends ComponentConnector>> connectors) {
+ Collection<Class<? extends ServerConnector>> connectors) {
- Iterator<Class<? extends ComponentConnector>> iter = connectors
- .iterator();
+ Iterator<Class<? extends ServerConnector>> iter = connectors.iterator();
while (iter.hasNext()) {
- Class<? extends ComponentConnector> connectorClass = iter.next();
+ Class<? extends ServerConnector> connectorClass = iter.next();
Connect annotation = connectorClass.getAnnotation(Connect.class);
if (!ClientConnector.class.isAssignableFrom(annotation.value())) {
logger.log(
@@ -173,13 +172,13 @@ public class WidgetMapGenerator extends Generator {
}
private void logConnectors(TreeLogger logger, GeneratorContext context,
- Collection<Class<? extends ComponentConnector>> connectors) {
+ Collection<Class<? extends ServerConnector>> connectors) {
logger.log(Type.INFO,
"Widget set will contain implementations for following component connectors: ");
TreeSet<String> classNames = new TreeSet<String>();
HashMap<String, String> loadStyle = new HashMap<String, String>();
- for (Class<? extends ComponentConnector> connectorClass : connectors) {
+ for (Class<? extends ServerConnector> connectorClass : connectors) {
String className = connectorClass.getCanonicalName();
classNames.add(className);
if (getLoadStyle(connectorClass) == LoadStyle.DEFERRED) {
@@ -208,16 +207,16 @@ public class WidgetMapGenerator extends Generator {
* widgetset
*/
@SuppressWarnings("unchecked")
- private Collection<Class<? extends ComponentConnector>> getUsedConnectors(
+ private Collection<Class<? extends ServerConnector>> getUsedConnectors(
TypeOracle typeOracle) {
JClassType connectorType = typeOracle.findType(Connector.class
.getName());
- Collection<Class<? extends ComponentConnector>> connectors = new HashSet<Class<? extends ComponentConnector>>();
+ Collection<Class<? extends ServerConnector>> connectors = new HashSet<Class<? extends ServerConnector>>();
for (JClassType jClassType : connectorType.getSubtypes()) {
Connect annotation = jClassType.getAnnotation(Connect.class);
if (annotation != null) {
try {
- Class<? extends ComponentConnector> clazz = (Class<? extends ComponentConnector>) Class
+ Class<? extends ServerConnector> clazz = (Class<? extends ServerConnector>) Class
.forName(jClassType.getQualifiedSourceName());
connectors.add(clazz);
} catch (ClassNotFoundException e) {
@@ -242,15 +241,14 @@ public class WidgetMapGenerator extends Generator {
* @return true iff the widget for given component should be lazy loaded by
* the client side engine
*/
- protected LoadStyle getLoadStyle(
- Class<? extends ComponentConnector> connector) {
+ protected LoadStyle getLoadStyle(Class<? extends ServerConnector> connector) {
Connect annotation = connector.getAnnotation(Connect.class);
return annotation.loadStyle();
}
private void generateInstantiatorMethod(
SourceWriter sourceWriter,
- Collection<Class<? extends ComponentConnector>> connectorsHavingComponentAnnotation) {
+ Collection<Class<? extends ServerConnector>> connectorsHavingComponentAnnotation) {
Collection<Class<?>> deferredWidgets = new LinkedList<Class<?>>();
@@ -258,16 +256,16 @@ public class WidgetMapGenerator extends Generator {
// lookup with index than with the hashmap
sourceWriter.println("public void ensureInstantiator(Class<? extends "
- + componentConnectorClassName + "> classType) {");
+ + serverConnectorClassName + "> classType) {");
sourceWriter.println("if(!instmap.containsKey(classType)){");
boolean first = true;
- ArrayList<Class<? extends ComponentConnector>> lazyLoadedWidgets = new ArrayList<Class<? extends ComponentConnector>>();
+ ArrayList<Class<? extends ServerConnector>> lazyLoadedConnectors = new ArrayList<Class<? extends ServerConnector>>();
- HashSet<Class<? extends com.vaadin.terminal.gwt.client.ComponentConnector>> connectorsWithInstantiator = new HashSet<Class<? extends com.vaadin.terminal.gwt.client.ComponentConnector>>();
+ HashSet<Class<? extends ServerConnector>> connectorsWithInstantiator = new HashSet<Class<? extends ServerConnector>>();
- for (Class<? extends ComponentConnector> class1 : connectorsHavingComponentAnnotation) {
- Class<? extends ComponentConnector> clientClass = class1;
+ for (Class<? extends ServerConnector> class1 : connectorsHavingComponentAnnotation) {
+ Class<? extends ServerConnector> clientClass = class1;
if (connectorsWithInstantiator.contains(clientClass)) {
continue;
}
@@ -284,7 +282,7 @@ public class WidgetMapGenerator extends Generator {
+ ".class) {");
String instantiator = "new WidgetInstantiator() {\n public "
- + componentConnectorClassName
+ + serverConnectorClassName
+ " get() {\n return GWT.create(" + clientClass.getName()
+ ".class );\n}\n}\n";
@@ -298,7 +296,7 @@ public class WidgetMapGenerator extends Generator {
+ clientClass.getName()
+ ".class,"
+ instantiator + ");}});\n");
- lazyLoadedWidgets.add(class1);
+ lazyLoadedConnectors.add(class1);
if (loadStyle == LoadStyle.DEFERRED) {
deferredWidgets.add(class1);
@@ -321,8 +319,8 @@ public class WidgetMapGenerator extends Generator {
sourceWriter.println("}");
sourceWriter.println("public Class<? extends "
- + componentConnectorClassName
- + ">[] getDeferredLoadedWidgets() {");
+ + serverConnectorClassName
+ + ">[] getDeferredLoadedConnectors() {");
sourceWriter.println("return new Class[] {");
first = true;
@@ -344,11 +342,11 @@ public class WidgetMapGenerator extends Generator {
// TODO an index of last ensured widget in array
- sourceWriter.println("public " + componentConnectorClassName
- + " instantiate(Class<? extends " + componentConnectorClassName
+ sourceWriter.println("public " + serverConnectorClassName
+ + " instantiate(Class<? extends " + serverConnectorClassName
+ "> classType) {");
sourceWriter.indent();
- sourceWriter.println(componentConnectorClassName
+ sourceWriter.println(serverConnectorClassName
+ " p = super.instantiate(classType); if(p!= null) return p;");
sourceWriter.println("return instmap.get(classType).get();");
@@ -365,17 +363,17 @@ public class WidgetMapGenerator extends Generator {
*/
private void generateImplementationDetector(
SourceWriter sourceWriter,
- Collection<Class<? extends ComponentConnector>> paintablesHavingWidgetAnnotation) {
+ Collection<Class<? extends ServerConnector>> paintablesHavingWidgetAnnotation) {
sourceWriter
.println("public Class<? extends "
- + componentConnectorClassName
+ + serverConnectorClassName
+ "> "
+ "getConnectorClassForServerSideClassName(String fullyQualifiedName) {");
sourceWriter.indent();
sourceWriter
.println("fullyQualifiedName = fullyQualifiedName.intern();");
- for (Class<? extends ComponentConnector> connectorClass : paintablesHavingWidgetAnnotation) {
+ for (Class<? extends ServerConnector> connectorClass : paintablesHavingWidgetAnnotation) {
Class<? extends ClientConnector> clientConnectorClass = getClientConnectorClass(connectorClass);
sourceWriter.print("if ( fullyQualifiedName == \"");
sourceWriter.print(clientConnectorClass.getName());
@@ -393,7 +391,7 @@ public class WidgetMapGenerator extends Generator {
}
private static Class<? extends ClientConnector> getClientConnectorClass(
- Class<? extends ComponentConnector> connectorClass) {
+ Class<? extends ServerConnector> connectorClass) {
Connect annotation = connectorClass.getAnnotation(Connect.class);
return (Class<? extends ClientConnector>) annotation.value();
}