aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/vaadin/Application.java75
-rw-r--r--src/com/vaadin/annotations/LoadScripts.java24
-rw-r--r--src/com/vaadin/data/util/AbstractBeanContainer.java5
-rw-r--r--src/com/vaadin/data/util/AbstractInMemoryContainer.java4
-rw-r--r--src/com/vaadin/data/util/MethodProperty.java9
-rw-r--r--src/com/vaadin/data/util/MethodPropertyDescriptor.java12
-rw-r--r--src/com/vaadin/data/util/converter/ConverterUtil.java168
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/SQLContainer.java129
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java5
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java29
-rw-r--r--src/com/vaadin/event/ListenerMethod.java16
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java2
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/And.java1
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java29
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java2
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/Not.java2
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/Or.java2
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java2
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/SourceIs.java14
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java2
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java2
-rw-r--r--src/com/vaadin/navigator/FragmentManager.java38
-rw-r--r--src/com/vaadin/navigator/Navigator.java586
-rw-r--r--src/com/vaadin/navigator/View.java36
-rw-r--r--src/com/vaadin/navigator/ViewChangeListener.java118
-rw-r--r--src/com/vaadin/navigator/ViewDisplay.java29
-rw-r--r--src/com/vaadin/navigator/ViewProvider.java44
-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
-rw-r--r--src/com/vaadin/tools/WidgetsetCompiler.java14
-rw-r--r--src/com/vaadin/ui/AbsoluteLayout.java3
-rw-r--r--src/com/vaadin/ui/AbstractComponent.java396
-rw-r--r--src/com/vaadin/ui/AbstractComponentContainer.java70
-rw-r--r--src/com/vaadin/ui/AbstractField.java92
-rw-r--r--src/com/vaadin/ui/AbstractJavaScriptComponent.java161
-rw-r--r--src/com/vaadin/ui/AbstractSelect.java5
-rw-r--r--src/com/vaadin/ui/AbstractSplitPanel.java128
-rw-r--r--src/com/vaadin/ui/Button.java20
-rw-r--r--src/com/vaadin/ui/Component.java167
-rw-r--r--src/com/vaadin/ui/ConnectorTracker.java229
-rw-r--r--src/com/vaadin/ui/CssLayout.java5
-rw-r--r--src/com/vaadin/ui/CustomField.java31
-rw-r--r--src/com/vaadin/ui/DirtyConnectorTracker.java132
-rw-r--r--src/com/vaadin/ui/Form.java32
-rw-r--r--src/com/vaadin/ui/HasComponents.java11
-rw-r--r--src/com/vaadin/ui/HelloWorldExtension.java38
-rw-r--r--src/com/vaadin/ui/JavaScript.java146
-rw-r--r--src/com/vaadin/ui/JavaScriptCallback.java41
-rw-r--r--src/com/vaadin/ui/Label.java159
-rw-r--r--src/com/vaadin/ui/Link.java7
-rw-r--r--src/com/vaadin/ui/Notification.java50
-rw-r--r--src/com/vaadin/ui/Panel.java42
-rw-r--r--src/com/vaadin/ui/Root.java1057
-rw-r--r--src/com/vaadin/ui/TabSheet.java88
-rw-r--r--src/com/vaadin/ui/Table.java257
-rw-r--r--src/com/vaadin/ui/Tree.java5
-rw-r--r--src/com/vaadin/ui/TreeTable.java12
-rw-r--r--src/com/vaadin/ui/UniqueSerializable.java30
-rw-r--r--src/com/vaadin/ui/Window.java82
-rw-r--r--src/com/vaadin/ui/themes/ChameleonTheme.java2
204 files changed, 8644 insertions, 3952 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java
index 4da1d52c00..84a8df5053 100644
--- a/src/com/vaadin/Application.java
+++ b/src/com/vaadin/Application.java
@@ -18,7 +18,6 @@ import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
@@ -48,14 +47,12 @@ import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.WrappedRequest.BrowserDetails;
import com.vaadin.terminal.WrappedResponse;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
import com.vaadin.terminal.gwt.server.ClientConnector;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField;
-import com.vaadin.ui.Component;
import com.vaadin.ui.Root;
import com.vaadin.ui.Table;
import com.vaadin.ui.Window;
@@ -194,6 +191,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
* @see #getWindow(String)
* @see Application#getRoot(WrappedRequest)
*/
+
@Override
public Root.LegacyWindow getRoot(WrappedRequest request) {
String pathInfo = request.getRequestPathInfo();
@@ -246,6 +244,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
* <p>
* {@inheritDoc}
*/
+
@Override
public String getThemeForRoot(Root root) {
return theme;
@@ -1066,12 +1065,14 @@ public class Application implements Terminal.ErrorListener, Serializable {
* the change event.
* @see com.vaadin.terminal.Terminal.ErrorListener#terminalError(com.vaadin.terminal.Terminal.ErrorEvent)
*/
+
public void terminalError(Terminal.ErrorEvent event) {
final Throwable t = event.getThrowable();
if (t instanceof SocketException) {
// Most likely client browser closed socket
- logger.info("SocketException in CommunicationManager."
- + " Most likely client (browser) closed socket.");
+ getLogger().info(
+ "SocketException in CommunicationManager."
+ + " Most likely client (browser) closed socket.");
return;
}
@@ -1090,7 +1091,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
}
// also print the error on console
- logger.log(Level.SEVERE, "Terminal error:", t);
+ getLogger().log(Level.SEVERE, "Terminal error:", t);
}
/**
@@ -2118,11 +2119,11 @@ public class Application implements Terminal.ErrorListener, Serializable {
* @return the current application instance if available, otherwise
* <code>null</code>
*
- * @see #setCurrentApplication(Application)
+ * @see #setCurrent(Application)
*
* @since 7.0
*/
- public static Application getCurrentApplication() {
+ public static Application getCurrent() {
return currentApplication.get();
}
@@ -2138,12 +2139,12 @@ public class Application implements Terminal.ErrorListener, Serializable {
*
* @param application
*
- * @see #getCurrentApplication()
+ * @see #getCurrent()
* @see ThreadLocal
*
* @since 7.0
*/
- public static void setCurrentApplication(Application application) {
+ public static void setCurrent(Application application) {
currentApplication.set(application);
}
@@ -2187,7 +2188,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
*/
public Root getRootForRequest(WrappedRequest request)
throws RootRequiresMoreInformationException {
- Root root = Root.getCurrentRoot();
+ Root root = Root.getCurrent();
if (root != null) {
return root;
}
@@ -2239,7 +2240,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
}
// Set thread local here so it is available in init
- Root.setCurrentRoot(root);
+ Root.setCurrent(root);
if (!initedRoots.contains(rootId)) {
boolean initRequiresBrowserDetails = isRootPreserved()
@@ -2355,8 +2356,6 @@ public class Application implements Terminal.ErrorListener, Serializable {
return Collections.unmodifiableCollection(roots.values());
}
- private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>();
-
private int connectorIdSequence = 0;
/**
@@ -2368,52 +2367,10 @@ public class Application implements Terminal.ErrorListener, Serializable {
* @return A new id for the connector
*/
public String createConnectorId(ClientConnector connector) {
- String connectorId = String.valueOf(connectorIdSequence++);
- Connector oldReference = connectorIdToConnector.put(connectorId,
- connector);
- if (oldReference != null) {
- throw new RuntimeException(
- "An error occured while generating connector ids. A connector with id "
- + connectorId + " was already found!");
- }
- return connectorId;
- }
-
- /**
- * Gets a connector by its id.
- *
- * @param connectorId
- * The connector id to look for
- * @return The connector with the given id or null if no connector has the
- * given id
- */
- public ClientConnector getConnector(String connectorId) {
- return connectorIdToConnector.get(connectorId);
+ return String.valueOf(connectorIdSequence++);
}
- /**
- * Cleans the connector map from all connectors that are no longer attached
- * to the application. This should only be called by the framework.
- */
- public void cleanConnectorMap() {
- // remove detached components from paintableIdMap so they
- // can be GC'ed
- Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
-
- while (iterator.hasNext()) {
- String connectorId = iterator.next();
- Connector connector = connectorIdToConnector.get(connectorId);
- if (connector instanceof Component) {
- Component component = (Component) connector;
- if (component.getApplication() != this) {
- // If component is no longer part of this application,
- // remove it from the map. If it is re-attached to the
- // application at some point it will be re-added to this
- // collection when sent to the client.
- iterator.remove();
- }
- }
- }
-
+ private static final Logger getLogger() {
+ return Logger.getLogger(Application.class.getName());
}
}
diff --git a/src/com/vaadin/annotations/LoadScripts.java b/src/com/vaadin/annotations/LoadScripts.java
new file mode 100644
index 0000000000..84ac2d2fb7
--- /dev/null
+++ b/src/com/vaadin/annotations/LoadScripts.java
@@ -0,0 +1,24 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Temporary hack used for ensuring external javascript libraries are included.
+ * To add a javascript, add this annotation to your Root class.
+ *
+ * @deprecated Will be removed in favor of a more robust solution before version
+ * 7.0.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Deprecated
+public @interface LoadScripts {
+ public String[] value();
+
+}
diff --git a/src/com/vaadin/data/util/AbstractBeanContainer.java b/src/com/vaadin/data/util/AbstractBeanContainer.java
index bed3ca0450..300f92ea3a 100644
--- a/src/com/vaadin/data/util/AbstractBeanContainer.java
+++ b/src/com/vaadin/data/util/AbstractBeanContainer.java
@@ -8,6 +8,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import com.vaadin.data.Container;
@@ -247,8 +248,8 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
*/
@Override
@SuppressWarnings("unchecked")
- public Collection<IDTYPE> getItemIds() {
- return (Collection<IDTYPE>) super.getItemIds();
+ public List<IDTYPE> getItemIds() {
+ return (List<IDTYPE>) super.getItemIds();
}
/*
diff --git a/src/com/vaadin/data/util/AbstractInMemoryContainer.java b/src/com/vaadin/data/util/AbstractInMemoryContainer.java
index cc7f469c0c..795aabb419 100644
--- a/src/com/vaadin/data/util/AbstractInMemoryContainer.java
+++ b/src/com/vaadin/data/util/AbstractInMemoryContainer.java
@@ -165,8 +165,8 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
}
}
- public Collection<?> getItemIds() {
- return Collections.unmodifiableCollection(getVisibleItemIds());
+ public List<?> getItemIds() {
+ return Collections.unmodifiableList(getVisibleItemIds());
}
// Container.Ordered
diff --git a/src/com/vaadin/data/util/MethodProperty.java b/src/com/vaadin/data/util/MethodProperty.java
index 4fc5531320..7df45e04ab 100644
--- a/src/com/vaadin/data/util/MethodProperty.java
+++ b/src/com/vaadin/data/util/MethodProperty.java
@@ -48,8 +48,6 @@ import com.vaadin.util.SerializerHelper;
@SuppressWarnings("serial")
public class MethodProperty<T> extends AbstractProperty<T> {
- private static final Logger logger = Logger.getLogger(MethodProperty.class
- .getName());
/**
* The object that includes the property the MethodProperty is bound to.
*/
@@ -130,9 +128,9 @@ public class MethodProperty<T> extends AbstractProperty<T> {
getMethod = null;
}
} catch (SecurityException e) {
- logger.log(Level.SEVERE, "Internal deserialization error", e);
+ getLogger().log(Level.SEVERE, "Internal deserialization error", e);
} catch (NoSuchMethodException e) {
- logger.log(Level.SEVERE, "Internal deserialization error", e);
+ getLogger().log(Level.SEVERE, "Internal deserialization error", e);
}
};
@@ -777,4 +775,7 @@ public class MethodProperty<T> extends AbstractProperty<T> {
super.fireValueChange();
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(MethodProperty.class.getName());
+ }
}
diff --git a/src/com/vaadin/data/util/MethodPropertyDescriptor.java b/src/com/vaadin/data/util/MethodPropertyDescriptor.java
index 10faa7a0f3..5fdb982b9e 100644
--- a/src/com/vaadin/data/util/MethodPropertyDescriptor.java
+++ b/src/com/vaadin/data/util/MethodPropertyDescriptor.java
@@ -23,9 +23,6 @@ import com.vaadin.util.SerializerHelper;
public class MethodPropertyDescriptor<BT> implements
VaadinPropertyDescriptor<BT> {
- private static final Logger logger = Logger
- .getLogger(MethodPropertyDescriptor.class.getName());
-
private final String name;
private Class<?> propertyType;
private transient Method readMethod;
@@ -109,9 +106,9 @@ public class MethodPropertyDescriptor<BT> implements
readMethod = null;
}
} catch (SecurityException e) {
- logger.log(Level.SEVERE, "Internal deserialization error", e);
+ getLogger().log(Level.SEVERE, "Internal deserialization error", e);
} catch (NoSuchMethodException e) {
- logger.log(Level.SEVERE, "Internal deserialization error", e);
+ getLogger().log(Level.SEVERE, "Internal deserialization error", e);
}
};
@@ -128,4 +125,7 @@ public class MethodPropertyDescriptor<BT> implements
writeMethod);
}
-}
+ private static final Logger getLogger() {
+ return Logger.getLogger(MethodPropertyDescriptor.class.getName());
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/data/util/converter/ConverterUtil.java b/src/com/vaadin/data/util/converter/ConverterUtil.java
new file mode 100644
index 0000000000..7011496ed7
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/ConverterUtil.java
@@ -0,0 +1,168 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.util.converter;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+import com.vaadin.Application;
+
+public class ConverterUtil implements Serializable {
+
+ /**
+ * Finds a converter that can convert from the given presentation type to
+ * the given model type and back. Uses the given application to find a
+ * {@link ConverterFactory} or, if application is null, uses the
+ * {@link Application#getCurrent()}.
+ *
+ * @param <PRESENTATIONTYPE>
+ * The presentation type
+ * @param <MODELTYPE>
+ * The model type
+ * @param presentationType
+ * The presentation type
+ * @param modelType
+ * The model type
+ * @param application
+ * The application to use to find a ConverterFactory or null to
+ * use the current application
+ * @return A Converter capable of converting between the given types or null
+ * if no converter was found
+ */
+ public static <PRESENTATIONTYPE, MODELTYPE> Converter<PRESENTATIONTYPE, MODELTYPE> getConverter(
+ Class<PRESENTATIONTYPE> presentationType,
+ Class<MODELTYPE> modelType, Application application) {
+ Converter<PRESENTATIONTYPE, MODELTYPE> converter = null;
+ if (application == null) {
+ application = Application.getCurrent();
+ }
+
+ if (application != null) {
+ ConverterFactory factory = application.getConverterFactory();
+ converter = factory.createConverter(presentationType, modelType);
+ }
+ return converter;
+
+ }
+
+ /**
+ * Convert the given value from the data source type to the UI type.
+ *
+ * @param modelValue
+ * The model value to convert
+ * @param presentationType
+ * The type of the presentation value
+ * @param converter
+ * The converter to (try to) use
+ * @param locale
+ * The locale to use for conversion
+ * @param <PRESENTATIONTYPE>
+ * Presentation type
+ *
+ * @return The converted value, compatible with the presentation type, or
+ * the original value if its type is compatible and no converter is
+ * set.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the model type.
+ */
+ @SuppressWarnings("unchecked")
+ public static <PRESENTATIONTYPE, MODELTYPE> PRESENTATIONTYPE convertFromModel(
+ MODELTYPE modelValue,
+ Class<? extends PRESENTATIONTYPE> presentationType,
+ Converter<PRESENTATIONTYPE, MODELTYPE> converter, Locale locale)
+ throws Converter.ConversionException {
+ if (converter != null) {
+ return converter.convertToPresentation(modelValue, locale);
+ }
+ if (modelValue == null) {
+ return null;
+ }
+
+ if (presentationType.isAssignableFrom(modelValue.getClass())) {
+ return (PRESENTATIONTYPE) modelValue;
+ } else {
+ throw new Converter.ConversionException(
+ "Unable to convert value of type "
+ + modelValue.getClass().getName()
+ + " to presentation type "
+ + presentationType
+ + ". No converter is set and the types are not compatible.");
+ }
+ }
+
+ /**
+ * @param <MODELTYPE>
+ * @param <PRESENTATIONTYPE>
+ * @param presentationValue
+ * @param modelType
+ * @param converter
+ * @param locale
+ * @return
+ * @throws Converter.ConversionException
+ */
+ public static <MODELTYPE, PRESENTATIONTYPE> MODELTYPE convertToModel(
+ PRESENTATIONTYPE presentationValue, Class<MODELTYPE> modelType,
+ Converter<PRESENTATIONTYPE, MODELTYPE> converter, Locale locale)
+ throws Converter.ConversionException {
+ if (converter != null) {
+ /*
+ * If there is a converter, always use it. It must convert or throw
+ * an exception.
+ */
+ return converter.convertToModel(presentationValue, locale);
+ }
+
+ if (presentationValue == null) {
+ // Null should always be passed through the converter but if there
+ // is no converter we can safely return null
+ return null;
+ }
+
+ if (modelType == null) {
+ // No model type, return original value
+ return (MODELTYPE) presentationValue;
+ } else if (modelType.isAssignableFrom(presentationValue.getClass())) {
+ // presentation type directly compatible with model type
+ return modelType.cast(presentationValue);
+ } else {
+ throw new Converter.ConversionException(
+ "Unable to convert value of type "
+ + presentationValue.getClass().getName()
+ + " to model type "
+ + modelType
+ + ". No converter is set and the types are not compatible.");
+ }
+
+ }
+
+ /**
+ * Checks if the given converter can handle conversion between the given
+ * presentation and model type
+ *
+ * @param converter
+ * The converter to check
+ * @param presentationType
+ * The presentation type
+ * @param modelType
+ * The model type
+ * @return true if the converter supports conversion between the given
+ * presentation and model type, false otherwise
+ */
+ public static boolean canConverterHandle(Converter<?, ?> converter,
+ Class<?> presentationType, Class<?> modelType) {
+ if (converter == null) {
+ return false;
+ }
+
+ if (!modelType.isAssignableFrom(converter.getModelType())) {
+ return false;
+ }
+ if (!presentationType.isAssignableFrom(converter.getPresentationType())) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
index 3bf33defd5..e9b5b4fea3 100644
--- a/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
+++ b/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
@@ -36,9 +36,6 @@ import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator;
public class SQLContainer implements Container, Container.Filterable,
Container.Indexed, Container.Sortable, Container.ItemSetChangeNotifier {
- private static final Logger logger = Logger.getLogger(SQLContainer.class
- .getName());
-
/** Query delegate */
private QueryDelegate delegate;
/** Auto commit mode, default = false */
@@ -132,6 +129,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* {@inheritDoc}
*/
+
public Object addItem() throws UnsupportedOperationException {
Object emptyKey[] = new Object[delegate.getPrimaryKeyColumns().size()];
RowId itemId = new TemporaryRowId(emptyKey);
@@ -162,15 +160,15 @@ public class SQLContainer implements Container, Container.Filterable,
if (notificationsEnabled) {
CacheFlushNotifier.notifyOfCacheFlush(this);
}
- logger.log(Level.FINER, "Row added to DB...");
+ getLogger().log(Level.FINER, "Row added to DB...");
return itemId;
} catch (SQLException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"Failed to add row to DB. Rolling back.", e);
try {
delegate.rollback();
} catch (SQLException ee) {
- logger.log(Level.SEVERE,
+ getLogger().log(Level.SEVERE,
"Failed to roll back row addition", e);
}
return null;
@@ -187,6 +185,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#containsId(java.lang.Object)
*/
+
public boolean containsId(Object itemId) {
if (itemId == null) {
return false;
@@ -215,7 +214,7 @@ public class SQLContainer implements Container, Container.Filterable,
return delegate.containsRowWithKey(((RowId) itemId).getId());
} catch (Exception e) {
/* Query failed, just return false. */
- logger.log(Level.WARNING, "containsId query failed", e);
+ getLogger().log(Level.WARNING, "containsId query failed", e);
}
}
return false;
@@ -227,6 +226,7 @@ public class SQLContainer implements Container, Container.Filterable,
* @see com.vaadin.data.Container#getContainerProperty(java.lang.Object,
* java.lang.Object)
*/
+
public Property<?> getContainerProperty(Object itemId, Object propertyId) {
Item item = getItem(itemId);
if (item == null) {
@@ -240,6 +240,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#getContainerPropertyIds()
*/
+
public Collection<?> getContainerPropertyIds() {
return Collections.unmodifiableCollection(propertyIds);
}
@@ -249,6 +250,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#getItem(java.lang.Object)
*/
+
public Item getItem(Object itemId) {
if (!cachedItems.containsKey(itemId)) {
int index = indexOfId(itemId);
@@ -295,6 +297,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* {@inheritDoc}
*/
+
public Collection<?> getItemIds() {
updateCount();
ArrayList<RowId> ids = new ArrayList<RowId>();
@@ -325,17 +328,18 @@ public class SQLContainer implements Container, Container.Filterable,
rs.close();
delegate.commit();
} catch (SQLException e) {
- logger.log(Level.WARNING, "getItemIds() failed, rolling back.", e);
+ getLogger().log(Level.WARNING,
+ "getItemIds() failed, rolling back.", e);
try {
delegate.rollback();
} catch (SQLException e1) {
- logger.log(Level.SEVERE, "Failed to roll back state", e1);
+ getLogger().log(Level.SEVERE, "Failed to roll back state", e1);
}
try {
rs.getStatement().close();
rs.close();
} catch (SQLException e1) {
- logger.log(Level.WARNING, "Closing session failed", e1);
+ getLogger().log(Level.WARNING, "Closing session failed", e1);
}
throw new RuntimeException("Failed to fetch item indexes.", e);
}
@@ -350,6 +354,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#getType(java.lang.Object)
*/
+
public Class<?> getType(Object propertyId) {
if (!propertyIds.contains(propertyId)) {
return null;
@@ -362,6 +367,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#size()
*/
+
public int size() {
updateCount();
return size + sizeOfAddedItems() - removedItems.size();
@@ -372,6 +378,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#removeItem(java.lang.Object)
*/
+
public boolean removeItem(Object itemId)
throws UnsupportedOperationException {
if (!containsId(itemId)) {
@@ -400,29 +407,29 @@ public class SQLContainer implements Container, Container.Filterable,
CacheFlushNotifier.notifyOfCacheFlush(this);
}
if (success) {
- logger.log(Level.FINER, "Row removed from DB...");
+ getLogger().log(Level.FINER, "Row removed from DB...");
}
return success;
} catch (SQLException e) {
- logger.log(Level.WARNING, "Failed to remove row, rolling back",
- e);
+ getLogger().log(Level.WARNING,
+ "Failed to remove row, rolling back", e);
try {
delegate.rollback();
} catch (SQLException ee) {
/* Nothing can be done here */
- logger.log(Level.SEVERE, "Failed to rollback row removal",
- ee);
+ getLogger().log(Level.SEVERE,
+ "Failed to rollback row removal", ee);
}
return false;
} catch (OptimisticLockException e) {
- logger.log(Level.WARNING, "Failed to remove row, rolling back",
- e);
+ getLogger().log(Level.WARNING,
+ "Failed to remove row, rolling back", e);
try {
delegate.rollback();
} catch (SQLException ee) {
/* Nothing can be done here */
- logger.log(Level.SEVERE, "Failed to rollback row removal",
- ee);
+ getLogger().log(Level.SEVERE,
+ "Failed to rollback row removal", ee);
}
throw e;
}
@@ -439,6 +446,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#removeAllItems()
*/
+
public boolean removeAllItems() throws UnsupportedOperationException {
if (autoCommit) {
/* Remove and commit instantly. */
@@ -452,7 +460,7 @@ public class SQLContainer implements Container, Container.Filterable,
}
if (success) {
delegate.commit();
- logger.log(Level.FINER, "All rows removed from DB...");
+ getLogger().log(Level.FINER, "All rows removed from DB...");
refresh();
if (notificationsEnabled) {
CacheFlushNotifier.notifyOfCacheFlush(this);
@@ -462,23 +470,23 @@ public class SQLContainer implements Container, Container.Filterable,
}
return success;
} catch (SQLException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"removeAllItems() failed, rolling back", e);
try {
delegate.rollback();
} catch (SQLException ee) {
/* Nothing can be done here */
- logger.log(Level.SEVERE, "Failed to roll back", ee);
+ getLogger().log(Level.SEVERE, "Failed to roll back", ee);
}
return false;
} catch (OptimisticLockException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"removeAllItems() failed, rolling back", e);
try {
delegate.rollback();
} catch (SQLException ee) {
/* Nothing can be done here */
- logger.log(Level.SEVERE, "Failed to roll back", ee);
+ getLogger().log(Level.SEVERE, "Failed to roll back", ee);
}
throw e;
}
@@ -499,6 +507,7 @@ public class SQLContainer implements Container, Container.Filterable,
/**
* {@inheritDoc}
*/
+
public void addContainerFilter(Filter filter)
throws UnsupportedFilterException {
// filter.setCaseSensitive(!ignoreCase);
@@ -510,6 +519,7 @@ public class SQLContainer implements Container, Container.Filterable,
/**
* {@inheritDoc}
*/
+
public void removeContainerFilter(Filter filter) {
filters.remove(filter);
}
@@ -549,6 +559,7 @@ public class SQLContainer implements Container, Container.Filterable,
/**
* {@inheritDoc}
*/
+
public void removeAllContainerFilters() {
filters.clear();
refresh();
@@ -563,6 +574,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Indexed#indexOfId(java.lang.Object)
*/
+
public int indexOfId(Object itemId) {
// First check if the id is in the added items
for (int ix = 0; ix < addedItems.size(); ix++) {
@@ -609,6 +621,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Indexed#getIdByIndex(int)
*/
+
public Object getIdByIndex(int index) {
if (index < 0 || index > size() - 1) {
return null;
@@ -635,6 +648,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
*/
+
public Object nextItemId(Object itemId) {
return getIdByIndex(indexOfId(itemId) + 1);
}
@@ -644,6 +658,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
*/
+
public Object prevItemId(Object itemId) {
return getIdByIndex(indexOfId(itemId) - 1);
}
@@ -653,6 +668,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#firstItemId()
*/
+
public Object firstItemId() {
updateCount();
if (size == 0) {
@@ -680,6 +696,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#lastItemId()
*/
+
public Object lastItemId() {
if (addedItems.isEmpty()) {
int lastIx = size() - 1;
@@ -705,6 +722,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
*/
+
public boolean isFirstId(Object itemId) {
return firstItemId().equals(itemId);
}
@@ -714,6 +732,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
*/
+
public boolean isLastId(Object itemId) {
return lastItemId().equals(itemId);
}
@@ -728,6 +747,7 @@ public class SQLContainer implements Container, Container.Filterable,
* @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
* boolean[])
*/
+
public void sort(Object[] propertyId, boolean[] ascending) {
sorters.clear();
if (propertyId == null || propertyId.length == 0) {
@@ -743,7 +763,7 @@ public class SQLContainer implements Container, Container.Filterable,
try {
asc = ascending[i];
} catch (Exception e) {
- logger.log(Level.WARNING, "", e);
+ getLogger().log(Level.WARNING, "", e);
}
sorters.add(new OrderBy((String) propertyId[i], asc));
}
@@ -756,6 +776,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
*/
+
public Collection<?> getSortableContainerPropertyIds() {
return getContainerPropertyIds();
}
@@ -872,7 +893,8 @@ public class SQLContainer implements Container, Container.Filterable,
*/
public void commit() throws UnsupportedOperationException, SQLException {
try {
- logger.log(Level.FINER, "Commiting changes through delegate...");
+ getLogger().log(Level.FINER,
+ "Commiting changes through delegate...");
delegate.beginTransaction();
/* Perform buffered deletions */
for (RowItem item : removedItems.values()) {
@@ -926,7 +948,7 @@ public class SQLContainer implements Container, Container.Filterable,
* @throws SQLException
*/
public void rollback() throws UnsupportedOperationException, SQLException {
- logger.log(Level.FINE, "Rolling back changes...");
+ getLogger().log(Level.FINE, "Rolling back changes...");
removedItems.clear();
addedItems.clear();
modifiedItems.clear();
@@ -956,15 +978,15 @@ public class SQLContainer implements Container, Container.Filterable,
if (notificationsEnabled) {
CacheFlushNotifier.notifyOfCacheFlush(this);
}
- logger.log(Level.FINER, "Row updated to DB...");
+ getLogger().log(Level.FINER, "Row updated to DB...");
} catch (SQLException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"itemChangeNotification failed, rolling back...", e);
try {
delegate.rollback();
} catch (SQLException ee) {
/* Nothing can be done here */
- logger.log(Level.SEVERE, "Rollback failed", e);
+ getLogger().log(Level.SEVERE, "Rollback failed", e);
}
throw new RuntimeException(e);
}
@@ -1009,13 +1031,13 @@ public class SQLContainer implements Container, Container.Filterable,
try {
delegate.setFilters(filters);
} catch (UnsupportedOperationException e) {
- logger.log(Level.FINE,
+ getLogger().log(Level.FINE,
"The query delegate doesn't support filtering", e);
}
try {
delegate.setOrderBy(sorters);
} catch (UnsupportedOperationException e) {
- logger.log(Level.FINE,
+ getLogger().log(Level.FINE,
"The query delegate doesn't support filtering", e);
}
int newSize = delegate.getCount();
@@ -1025,7 +1047,8 @@ public class SQLContainer implements Container, Container.Filterable,
}
sizeUpdated = new Date();
sizeDirty = false;
- logger.log(Level.FINER, "Updated row count. New count is: " + size);
+ getLogger().log(Level.FINER,
+ "Updated row count. New count is: " + size);
} catch (SQLException e) {
throw new RuntimeException("Failed to update item set size.", e);
}
@@ -1069,7 +1092,7 @@ public class SQLContainer implements Container, Container.Filterable,
try {
type = Class.forName(rsmd.getColumnClassName(i));
} catch (Exception e) {
- logger.log(Level.WARNING, "Class not found", e);
+ getLogger().log(Level.WARNING, "Class not found", e);
/* On failure revert to Object and hope for the best. */
type = Object.class;
}
@@ -1095,14 +1118,14 @@ public class SQLContainer implements Container, Container.Filterable,
rs.getStatement().close();
rs.close();
delegate.commit();
- logger.log(Level.FINER, "Property IDs fetched.");
+ getLogger().log(Level.FINER, "Property IDs fetched.");
} catch (SQLException e) {
- logger.log(Level.WARNING,
+ getLogger().log(Level.WARNING,
"Failed to fetch property ids, rolling back", e);
try {
delegate.rollback();
} catch (SQLException e1) {
- logger.log(Level.SEVERE, "Failed to roll back", e1);
+ getLogger().log(Level.SEVERE, "Failed to roll back", e1);
}
try {
if (rs != null) {
@@ -1112,7 +1135,7 @@ public class SQLContainer implements Container, Container.Filterable,
rs.close();
}
} catch (SQLException e1) {
- logger.log(Level.WARNING, "Failed to close session", e1);
+ getLogger().log(Level.WARNING, "Failed to close session", e1);
}
throw e;
}
@@ -1135,7 +1158,7 @@ public class SQLContainer implements Container, Container.Filterable,
} catch (UnsupportedOperationException e) {
/* The query delegate doesn't support sorting. */
/* No need to do anything. */
- logger.log(Level.FINE,
+ getLogger().log(Level.FINE,
"The query delegate doesn't support sorting", e);
}
delegate.beginTransaction();
@@ -1217,14 +1240,17 @@ public class SQLContainer implements Container, Container.Filterable,
rs.getStatement().close();
rs.close();
delegate.commit();
- logger.log(Level.FINER, "Fetched " + pageLength * CACHE_RATIO
- + " rows starting from " + currentOffset);
+ getLogger().log(
+ Level.FINER,
+ "Fetched " + pageLength * CACHE_RATIO
+ + " rows starting from " + currentOffset);
} catch (SQLException e) {
- logger.log(Level.WARNING, "Failed to fetch rows, rolling back", e);
+ getLogger().log(Level.WARNING,
+ "Failed to fetch rows, rolling back", e);
try {
delegate.rollback();
} catch (SQLException e1) {
- logger.log(Level.SEVERE, "Failed to roll back", e1);
+ getLogger().log(Level.SEVERE, "Failed to roll back", e1);
}
try {
if (rs != null) {
@@ -1234,7 +1260,7 @@ public class SQLContainer implements Container, Container.Filterable,
}
}
} catch (SQLException e1) {
- logger.log(Level.WARNING, "Failed to close session", e1);
+ getLogger().log(Level.WARNING, "Failed to close session", e1);
}
throw new RuntimeException("Failed to fetch page.", e);
}
@@ -1324,6 +1350,7 @@ public class SQLContainer implements Container, Container.Filterable,
* @see com.vaadin.data.Container#addContainerProperty(java.lang.Object,
* java.lang.Class, java.lang.Object)
*/
+
public boolean addContainerProperty(Object propertyId, Class<?> type,
Object defaultValue) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
@@ -1334,6 +1361,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object)
*/
+
public boolean removeContainerProperty(Object propertyId)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
@@ -1344,6 +1372,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container#addItem(java.lang.Object)
*/
+
public Item addItem(Object itemId) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
@@ -1354,6 +1383,7 @@ public class SQLContainer implements Container, Container.Filterable,
* @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
* java.lang.Object)
*/
+
public Item addItemAfter(Object previousItemId, Object newItemId)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
@@ -1364,6 +1394,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object)
*/
+
public Item addItemAt(int index, Object newItemId)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
@@ -1374,6 +1405,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Indexed#addItemAt(int)
*/
+
public Object addItemAt(int index) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
@@ -1383,6 +1415,7 @@ public class SQLContainer implements Container, Container.Filterable,
*
* @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
*/
+
public Object addItemAfter(Object previousItemId)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
@@ -1399,6 +1432,7 @@ public class SQLContainer implements Container, Container.Filterable,
* com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin
* .data.Container.ItemSetChangeListener)
*/
+
public void addListener(Container.ItemSetChangeListener listener) {
if (itemSetChangeListeners == null) {
itemSetChangeListeners = new LinkedList<Container.ItemSetChangeListener>();
@@ -1413,6 +1447,7 @@ public class SQLContainer implements Container, Container.Filterable,
* com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin
* .data.Container.ItemSetChangeListener)
*/
+
public void removeListener(Container.ItemSetChangeListener listener) {
if (itemSetChangeListeners != null) {
itemSetChangeListeners.remove(listener);
@@ -1577,7 +1612,8 @@ public class SQLContainer implements Container, Container.Filterable,
r.getReferencedColumn()));
return true;
} catch (Exception e) {
- logger.log(Level.WARNING, "Setting referenced item failed.", e);
+ getLogger()
+ .log(Level.WARNING, "Setting referenced item failed.", e);
return false;
}
}
@@ -1640,4 +1676,7 @@ public class SQLContainer implements Container, Container.Filterable,
}
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(SQLContainer.class.getName());
+ }
}
diff --git a/src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java b/src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java
index 9e4bb772f5..40d0d0426f 100644
--- a/src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java
+++ b/src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java
@@ -13,8 +13,6 @@ import javax.naming.NamingException;
import javax.sql.DataSource;
public class J2EEConnectionPool implements JDBCConnectionPool {
- private static final Logger logger = Logger
- .getLogger(J2EEConnectionPool.class.getName());
private String dataSourceJndiName;
@@ -58,7 +56,8 @@ public class J2EEConnectionPool implements JDBCConnectionPool {
try {
conn.close();
} catch (SQLException e) {
- logger.log(Level.FINE, "Could not release SQL connection", e);
+ Logger.getLogger(J2EEConnectionPool.class.getName()).log(
+ Level.FINE, "Could not release SQL connection", e);
}
}
}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
index 7e546309f6..22ca30cc32 100644
--- a/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
+++ b/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
@@ -38,9 +38,6 @@ import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
public class TableQuery implements QueryDelegate,
QueryDelegate.RowIdChangeNotifier {
- private static final Logger logger = Logger.getLogger(TableQuery.class
- .getName());
-
/** Table name, primary key column name(s) and version column name */
private String tableName;
private List<String> primaryKeyColumns;
@@ -115,7 +112,7 @@ public class TableQuery implements QueryDelegate,
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getCount()
*/
public int getCount() throws SQLException {
- logger.log(Level.FINE, "Fetching count...");
+ getLogger().log(Level.FINE, "Fetching count...");
StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
filters, null, 0, 0, "COUNT(*)");
boolean shouldCloseTransaction = false;
@@ -228,7 +225,7 @@ public class TableQuery implements QueryDelegate,
PreparedStatement pstmt = activeConnection.prepareStatement(
sh.getQueryString(), primaryKeyColumns.toArray(new String[0]));
sh.setParameterValuesToStatement(pstmt);
- logger.log(Level.FINE, "DB -> " + sh.getQueryString());
+ getLogger().log(Level.FINE, "DB -> " + sh.getQueryString());
int result = pstmt.executeUpdate();
if (result > 0) {
/*
@@ -293,7 +290,7 @@ public class TableQuery implements QueryDelegate,
throw new IllegalStateException();
}
- logger.log(Level.FINE, "DB -> begin transaction");
+ getLogger().log(Level.FINE, "DB -> begin transaction");
activeConnection = connectionPool.reserveConnection();
activeConnection.setAutoCommit(false);
transactionOpen = true;
@@ -306,7 +303,7 @@ public class TableQuery implements QueryDelegate,
*/
public void commit() throws UnsupportedOperationException, SQLException {
if (transactionOpen && activeConnection != null) {
- logger.log(Level.FINE, "DB -> commit");
+ getLogger().log(Level.FINE, "DB -> commit");
activeConnection.commit();
connectionPool.releaseConnection(activeConnection);
} else {
@@ -334,7 +331,7 @@ public class TableQuery implements QueryDelegate,
*/
public void rollback() throws UnsupportedOperationException, SQLException {
if (transactionOpen && activeConnection != null) {
- logger.log(Level.FINE, "DB -> rollback");
+ getLogger().log(Level.FINE, "DB -> rollback");
activeConnection.rollback();
connectionPool.releaseConnection(activeConnection);
} else {
@@ -389,7 +386,7 @@ public class TableQuery implements QueryDelegate,
}
PreparedStatement pstmt = c.prepareStatement(sh.getQueryString());
sh.setParameterValuesToStatement(pstmt);
- logger.log(Level.FINE, "DB -> " + sh.getQueryString());
+ getLogger().log(Level.FINE, "DB -> " + sh.getQueryString());
return pstmt.executeQuery();
}
@@ -415,7 +412,7 @@ public class TableQuery implements QueryDelegate,
}
pstmt = c.prepareStatement(sh.getQueryString());
sh.setParameterValuesToStatement(pstmt);
- logger.log(Level.FINE, "DB -> " + sh.getQueryString());
+ getLogger().log(Level.FINE, "DB -> " + sh.getQueryString());
int retval = pstmt.executeUpdate();
return retval;
} finally {
@@ -458,7 +455,7 @@ public class TableQuery implements QueryDelegate,
pstmt = c.prepareStatement(sh.getQueryString(),
primaryKeyColumns.toArray(new String[0]));
sh.setParameterValuesToStatement(pstmt);
- logger.log(Level.FINE, "DB -> " + sh.getQueryString());
+ getLogger().log(Level.FINE, "DB -> " + sh.getQueryString());
int result = pstmt.executeUpdate();
genKeys = pstmt.getGeneratedKeys();
RowId newId = getNewRowId(row, genKeys);
@@ -571,7 +568,7 @@ public class TableQuery implements QueryDelegate,
}
return new RowId(newRowId.toArray());
} catch (Exception e) {
- logger.log(Level.FINE,
+ getLogger().log(Level.FINE,
"Failed to fetch key values on insert: " + e.getMessage());
return null;
}
@@ -586,8 +583,8 @@ public class TableQuery implements QueryDelegate,
*/
public boolean removeRow(RowItem row) throws UnsupportedOperationException,
SQLException {
- logger.log(Level.FINE, "Removing row with id: "
- + row.getId().getId()[0].toString());
+ getLogger().log(Level.FINE,
+ "Removing row with id: " + row.getId().getId()[0].toString());
if (executeUpdate(sqlGenerator.generateDeleteQuery(getTableName(),
primaryKeyColumns, versionColumn, row)) == 1) {
return true;
@@ -695,4 +692,8 @@ public class TableQuery implements QueryDelegate,
rowIdChangeListeners.remove(listener);
}
}
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(TableQuery.class.getName());
+ }
}
diff --git a/src/com/vaadin/event/ListenerMethod.java b/src/com/vaadin/event/ListenerMethod.java
index 1f1305fa69..f7dc8a7f13 100644
--- a/src/com/vaadin/event/ListenerMethod.java
+++ b/src/com/vaadin/event/ListenerMethod.java
@@ -43,9 +43,6 @@ import java.util.logging.Logger;
@SuppressWarnings("serial")
public class ListenerMethod implements EventListener, Serializable {
- private static final Logger logger = Logger.getLogger(ListenerMethod.class
- .getName());
-
/**
* Type of the event that should trigger this listener. Also the subclasses
* of this class are accepted to trigger the listener.
@@ -84,9 +81,10 @@ public class ListenerMethod implements EventListener, Serializable {
out.writeObject(name);
out.writeObject(paramTypes);
} catch (NotSerializableException e) {
- logger.warning("Error in serialization of the application: Class "
- + target.getClass().getName()
- + " must implement serialization.");
+ getLogger().warning(
+ "Error in serialization of the application: Class "
+ + target.getClass().getName()
+ + " must implement serialization.");
throw e;
}
@@ -103,7 +101,7 @@ public class ListenerMethod implements EventListener, Serializable {
// inner classes
method = findHighestMethod(target.getClass(), name, paramTypes);
} catch (SecurityException e) {
- logger.log(Level.SEVERE, "Internal deserialization error", e);
+ getLogger().log(Level.SEVERE, "Internal deserialization error", e);
}
};
@@ -658,4 +656,8 @@ public class ListenerMethod implements EventListener, Serializable {
return target;
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(ListenerMethod.class.getName());
+ }
+
}
diff --git a/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java b/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java
index 87193e8b3e..52ffab9263 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java
@@ -7,7 +7,6 @@
package com.vaadin.event.dd.acceptcriteria;
import com.vaadin.event.dd.DragAndDropEvent;
-import com.vaadin.terminal.gwt.client.ui.dd.VAcceptAll;
/**
* Criterion that accepts all drops anywhere on the component.
@@ -18,7 +17,6 @@ import com.vaadin.terminal.gwt.client.ui.dd.VAcceptAll;
* @since 6.3
*
*/
-@ClientCriterion(VAcceptAll.class)
public final class AcceptAll extends ClientSideCriterion {
private static final long serialVersionUID = 7406683402153141461L;
diff --git a/src/com/vaadin/event/dd/acceptcriteria/And.java b/src/com/vaadin/event/dd/acceptcriteria/And.java
index 89c8cb377b..a0340a9cdb 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/And.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/And.java
@@ -19,7 +19,6 @@ import com.vaadin.terminal.PaintTarget;
* @since 6.3
*
*/
-@ClientCriterion(com.vaadin.terminal.gwt.client.ui.dd.VAnd.class)
public class And extends ClientSideCriterion {
private static final long serialVersionUID = -5242574480825471748L;
diff --git a/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java b/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java
deleted file mode 100644
index 8b64106be7..0000000000
--- a/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.event.dd.acceptcriteria;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterion;
-
-/**
- * An annotation type used to point the client side counterpart for server side
- * a {@link AcceptCriterion} class.
- * <p>
- * Annotations are used at GWT compilation phase, so remember to rebuild your
- * widgetset if you do changes for {@link ClientCriterion} mappings.
- *
- * @since 6.3
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface ClientCriterion {
- /**
- * @return the client side counterpart for the annotated criterion
- */
- Class<? extends VAcceptCriterion> value();
-}
diff --git a/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java b/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java
index 298a50a1d5..7eb04d9647 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java
@@ -10,7 +10,6 @@ import com.vaadin.event.Transferable;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VContainsDataFlavor;
/**
* A Criterion that checks whether {@link Transferable} contains given data
@@ -19,7 +18,6 @@ import com.vaadin.terminal.gwt.client.ui.dd.VContainsDataFlavor;
*
* @since 6.3
*/
-@ClientCriterion(VContainsDataFlavor.class)
public class ContainsDataFlavor extends ClientSideCriterion {
private String dataFlavorId;
diff --git a/src/com/vaadin/event/dd/acceptcriteria/Not.java b/src/com/vaadin/event/dd/acceptcriteria/Not.java
index 774dcc642e..85ff7e9bc9 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/Not.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/Not.java
@@ -9,7 +9,6 @@ package com.vaadin.event.dd.acceptcriteria;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VNot;
/**
* Criterion that wraps another criterion and inverts its return value.
@@ -17,7 +16,6 @@ import com.vaadin.terminal.gwt.client.ui.dd.VNot;
* @since 6.3
*
*/
-@ClientCriterion(VNot.class)
public class Not extends ClientSideCriterion {
private static final long serialVersionUID = 1131422338558613244L;
diff --git a/src/com/vaadin/event/dd/acceptcriteria/Or.java b/src/com/vaadin/event/dd/acceptcriteria/Or.java
index 2e7cdf45a3..077e9114d8 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/Or.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/Or.java
@@ -9,7 +9,6 @@ package com.vaadin.event.dd.acceptcriteria;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VOr;
/**
* A compound criterion that accepts the drag if any of its criterion accepts
@@ -20,7 +19,6 @@ import com.vaadin.terminal.gwt.client.ui.dd.VOr;
* @since 6.3
*
*/
-@ClientCriterion(VOr.class)
public class Or extends ClientSideCriterion {
private static final long serialVersionUID = 1L;
private AcceptCriterion criteria[];
diff --git a/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java b/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java
index b3f4702c51..77840bc94f 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java
@@ -8,7 +8,6 @@ import java.io.Serializable;
import com.vaadin.event.Transferable;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VServerAccept;
/**
* Parent class for criteria which are verified on the server side during a drag
@@ -27,7 +26,6 @@ import com.vaadin.terminal.gwt.client.ui.dd.VServerAccept;
*
* @since 6.3
*/
-@ClientCriterion(VServerAccept.class)
public abstract class ServerSideCriterion implements Serializable,
AcceptCriterion {
diff --git a/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java b/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
index 0d29e9a327..d7c47dc915 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
@@ -13,7 +13,6 @@ import com.vaadin.event.TransferableImpl;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VDragSourceIs;
import com.vaadin.ui.Component;
/**
@@ -23,10 +22,7 @@ import com.vaadin.ui.Component;
* @since 6.3
*/
@SuppressWarnings("serial")
-@ClientCriterion(VDragSourceIs.class)
public class SourceIs extends ClientSideCriterion {
- private static final Logger logger = Logger.getLogger(SourceIs.class
- .getName());
private Component[] components;
@@ -43,11 +39,11 @@ public class SourceIs extends ClientSideCriterion {
if (c.getApplication() != null) {
target.addAttribute("component" + paintedComponents++, c);
} else {
- logger.log(
- Level.WARNING,
- "SourceIs component {0} at index {1} is not attached to the component hierachy and will thus be ignored",
- new Object[] { c.getClass().getName(),
- Integer.valueOf(i) });
+ Logger.getLogger(SourceIs.class.getName())
+ .log(Level.WARNING,
+ "SourceIs component {0} at index {1} is not attached to the component hierachy and will thus be ignored",
+ new Object[] { c.getClass().getName(),
+ Integer.valueOf(i) });
}
}
target.addAttribute("c", paintedComponents);
diff --git a/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java b/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java
index 8303809140..454159b3da 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java
@@ -10,7 +10,6 @@ import com.vaadin.event.Transferable;
import com.vaadin.event.TransferableImpl;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DropTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VSourceIsTarget;
import com.vaadin.ui.Component;
import com.vaadin.ui.Table;
import com.vaadin.ui.Tree;
@@ -26,7 +25,6 @@ import com.vaadin.ui.Tree;
* @since 6.3
*
*/
-@ClientCriterion(VSourceIsTarget.class)
public class SourceIsTarget extends ClientSideCriterion {
private static final long serialVersionUID = -451399314705532584L;
diff --git a/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java b/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java
index 25371938d2..641fddc3ea 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java
@@ -10,7 +10,6 @@ import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.TargetDetails;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.dd.VTargetDetailIs;
/**
* Criterion for checking if drop target details contains the specific property
@@ -21,7 +20,6 @@ import com.vaadin.terminal.gwt.client.ui.dd.VTargetDetailIs;
* TODO add support for other basic data types that we support in UIDL.
*
*/
-@ClientCriterion(VTargetDetailIs.class)
public class TargetDetailIs extends ClientSideCriterion {
private static final long serialVersionUID = 763165450054331246L;
diff --git a/src/com/vaadin/navigator/FragmentManager.java b/src/com/vaadin/navigator/FragmentManager.java
new file mode 100644
index 0000000000..f1fd90e569
--- /dev/null
+++ b/src/com/vaadin/navigator/FragmentManager.java
@@ -0,0 +1,38 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.navigator;
+
+import java.io.Serializable;
+
+/**
+ * Fragment manager that handles interaction between Navigator and URI fragments
+ * or other similar view identification and bookmarking system.
+ *
+ * Alternative implementations can be created for HTML5 pushState, for portlet
+ * URL navigation and other similar systems.
+ *
+ * This interface is mostly for internal use by {@link Navigator}.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0
+ */
+public interface FragmentManager extends Serializable {
+ /**
+ * Return the current fragment (location string) including view name and any
+ * optional parameters.
+ *
+ * @return current view and parameter string, not null
+ */
+ public String getFragment();
+
+ /**
+ * Set the current fragment (location string) in the application URL or
+ * similar location, including view name and any optional parameters.
+ *
+ * @param fragment
+ * new view and parameter string, not null
+ */
+ public void setFragment(String fragment);
+} \ No newline at end of file
diff --git a/src/com/vaadin/navigator/Navigator.java b/src/com/vaadin/navigator/Navigator.java
new file mode 100644
index 0000000000..3ff727b504
--- /dev/null
+++ b/src/com/vaadin/navigator/Navigator.java
@@ -0,0 +1,586 @@
+package com.vaadin.navigator;
+
+/*
+ @VaadinApache2LicenseForJavaFiles@
+ */
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
+import com.vaadin.terminal.Page;
+import com.vaadin.terminal.Page.FragmentChangedEvent;
+import com.vaadin.terminal.Page.FragmentChangedListener;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.CssLayout;
+import com.vaadin.ui.CustomComponent;
+
+/**
+ * Navigator utility that allows switching of views in a part of an application.
+ *
+ * The view switching can be based e.g. on URI fragments containing the view
+ * name and parameters to the view. There are two types of parameters for views:
+ * an optional parameter string that is included in the fragment (may be
+ * bookmarkable).
+ *
+ * Views can be explicitly registered or dynamically generated and listening to
+ * view changes is possible.
+ *
+ * Note that {@link Navigator} is not a component itself but comes with
+ * {@link SimpleViewDisplay} which is a component that displays the selected
+ * view as its contents.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0
+ */
+public class Navigator implements Serializable {
+
+ // TODO divert navigation e.g. if no permissions? Or just show another view
+ // but keep URL? how best to intercept
+ // TODO investigate relationship with TouchKit navigation support
+
+ /**
+ * Empty view component.
+ */
+ public static class EmptyView extends CssLayout implements View {
+ /**
+ * Create minimally sized empty view.
+ */
+ public EmptyView() {
+ setWidth("0px");
+ setHeight("0px");
+ }
+
+ public void navigateTo(String fragmentParameters) {
+ // nothing to do
+ }
+ }
+
+ /**
+ * Fragment manager using URI fragments of a Page to track views and enable
+ * listening to view changes.
+ *
+ * This class is mostly for internal use by Navigator, and is only public
+ * and static to enable testing.
+ */
+ public static class UriFragmentManager implements FragmentManager,
+ FragmentChangedListener {
+ private final Page page;
+ private final Navigator navigator;
+
+ /**
+ * Create a new URIFragmentManager and attach it to listen to URI
+ * fragment changes of a {@link Page}.
+ *
+ * @param page
+ * page whose URI fragment to get and modify
+ * @param navigator
+ * {@link Navigator} to notify of fragment changes (using
+ * {@link Navigator#navigateTo(String)}
+ */
+ public UriFragmentManager(Page page, Navigator navigator) {
+ this.page = page;
+ this.navigator = navigator;
+
+ page.addListener(this);
+ }
+
+ public String getFragment() {
+ return page.getFragment();
+ }
+
+ public void setFragment(String fragment) {
+ page.setFragment(fragment, false);
+ }
+
+ public void fragmentChanged(FragmentChangedEvent event) {
+ UriFragmentManager.this.navigator.navigateTo(getFragment());
+ }
+ }
+
+ /**
+ * View display that is a component itself and replaces its contents with
+ * the view.
+ *
+ * This display only supports views that are {@link Component}s themselves.
+ * Attempting to display a view that is not a component causes an exception
+ * to be thrown.
+ *
+ * By default, the view display has full size.
+ */
+ public static class SimpleViewDisplay extends CustomComponent implements
+ ViewDisplay {
+
+ /**
+ * Create new {@link ViewDisplay} that is itself a component displaying
+ * the view.
+ */
+ public SimpleViewDisplay() {
+ setSizeFull();
+ }
+
+ public void showView(View view) {
+ if (view instanceof Component) {
+ setCompositionRoot((Component) view);
+ } else {
+ throw new IllegalArgumentException("View is not a component: "
+ + view);
+ }
+ }
+ }
+
+ /**
+ * View provider which supports mapping a single view name to a single
+ * pre-initialized view instance.
+ *
+ * For most cases, ClassBasedViewProvider should be used instead of this.
+ */
+ public static class StaticViewProvider implements ViewProvider {
+ private final String viewName;
+ private final View view;
+
+ /**
+ * Create a new view provider which returns a pre-created view instance.
+ *
+ * @param viewName
+ * name of the view (not null)
+ * @param view
+ * view instance to return (not null), reused on every
+ * request
+ */
+ public StaticViewProvider(String viewName, View view) {
+ this.viewName = viewName;
+ this.view = view;
+ }
+
+ public String getViewName(String viewAndParameters) {
+ if (null == viewAndParameters) {
+ return null;
+ }
+ if (viewAndParameters.startsWith(viewName)) {
+ return viewName;
+ }
+ return null;
+ }
+
+ public View getView(String viewName) {
+ if (this.viewName.equals(viewName)) {
+ return view;
+ }
+ return null;
+ }
+
+ /**
+ * Get the view name for this provider.
+ *
+ * @return view name for this provider
+ */
+ public String getViewName() {
+ return viewName;
+ }
+ }
+
+ /**
+ * View provider which maps a single view name to a class to instantiate for
+ * the view.
+ *
+ * Note that the view class must be accessible by the class loader used by
+ * the provider. This may require its visibility to be public.
+ *
+ * This class is primarily for internal use by {@link Navigator}.
+ */
+ public static class ClassBasedViewProvider implements ViewProvider {
+
+ private final String viewName;
+ private final Class<? extends View> viewClass;
+
+ /**
+ * Create a new view provider which creates new view instances based on
+ * a view class.
+ *
+ * @param viewName
+ * name of the views to create (not null)
+ * @param viewClass
+ * class to instantiate when a view is requested (not null)
+ */
+ public ClassBasedViewProvider(String viewName,
+ Class<? extends View> viewClass) {
+ if (null == viewName || null == viewClass) {
+ throw new IllegalArgumentException(
+ "View name and class should not be null");
+ }
+ this.viewName = viewName;
+ this.viewClass = viewClass;
+ }
+
+ public String getViewName(String viewAndParameters) {
+ if (null == viewAndParameters) {
+ return null;
+ }
+ if (viewAndParameters.equals(viewName)
+ || viewAndParameters.startsWith(viewName + "/")) {
+ return viewName;
+ }
+ return null;
+ }
+
+ public View getView(String viewName) {
+ if (this.viewName.equals(viewName)) {
+ try {
+ View view = viewClass.newInstance();
+ return view;
+ } catch (InstantiationException e) {
+ // TODO error handling
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ // TODO error handling
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the view name for this provider.
+ *
+ * @return view name for this provider
+ */
+ public String getViewName() {
+ return viewName;
+ }
+
+ /**
+ * Get the view class for this provider.
+ *
+ * @return {@link View} class
+ */
+ public Class<? extends View> getViewClass() {
+ return viewClass;
+ }
+ }
+
+ private final FragmentManager fragmentManager;
+ private final ViewDisplay display;
+ private View currentView = null;
+ private List<ViewChangeListener> listeners = new LinkedList<ViewChangeListener>();
+ private List<ViewProvider> providers = new LinkedList<ViewProvider>();
+
+ /**
+ * Create a navigator that is tracking the active view using URI fragments.
+ *
+ * @param page
+ * whose URI fragments are used
+ * @param display
+ * where to display the views
+ */
+ public Navigator(Page page, ViewDisplay display) {
+ this.display = display;
+ fragmentManager = new UriFragmentManager(page, this);
+ navigateTo(page.getFragment());
+ }
+
+ /**
+ * Create a navigator that is tracking the active view using URI fragments.
+ * By default, a {@link SimpleViewDisplay} is used and can be obtained using
+ * {@link #getDisplay()}.
+ *
+ * @param page
+ * whose URI fragments are used
+ */
+ public Navigator(Page page) {
+ display = new SimpleViewDisplay();
+ fragmentManager = new UriFragmentManager(page, this);
+ navigateTo(page.getFragment());
+ }
+
+ /**
+ * Create a navigator.
+ *
+ * When a custom fragment manager is not needed, use the constructor
+ * {@link #Navigator(Page, ViewDisplay)} which uses a URI fragment based
+ * fragment manager.
+ *
+ * Note that navigation to the initial view must be performed explicitly by
+ * the application after creating a Navigator using this constructor.
+ *
+ * @param fragmentManager
+ * fragment manager keeping track of the active view and enabling
+ * bookmarking and direct navigation
+ * @param display
+ * where to display the views
+ */
+ public Navigator(FragmentManager fragmentManager, ViewDisplay display) {
+ this.display = display;
+ this.fragmentManager = fragmentManager;
+ }
+
+ /**
+ * Navigate to a view and initialize the view with given parameters.
+ *
+ * The view string consists of a view name optionally followed by a slash
+ * and (fragment) parameters. ViewProviders are used to find and create the
+ * correct type of view.
+ *
+ * If multiple providers return a matching view, the view with the longest
+ * name is selected. This way, e.g. hierarchies of subviews can be
+ * registered like "admin/", "admin/users", "admin/settings" and the longest
+ * match is used.
+ *
+ * If the view being deactivated indicates it wants a confirmation for the
+ * navigation operation, the user is asked for the confirmation.
+ *
+ * Registered {@link ViewChangeListener}s are called upon successful view
+ * change.
+ *
+ * @param viewAndParameters
+ * view name and parameters
+ */
+ public void navigateTo(String viewAndParameters) {
+ String longestViewName = null;
+ View viewWithLongestName = null;
+ for (ViewProvider provider : providers) {
+ String viewName = provider.getViewName(viewAndParameters);
+ if (null != viewName
+ && (longestViewName == null || viewName.length() > longestViewName
+ .length())) {
+ View view = provider.getView(viewName);
+ if (null != view) {
+ longestViewName = viewName;
+ viewWithLongestName = view;
+ }
+ }
+ }
+ if (viewWithLongestName != null) {
+ String parameters = null;
+ if (viewAndParameters.length() > longestViewName.length() + 1) {
+ parameters = viewAndParameters.substring(longestViewName
+ .length() + 1);
+ }
+ navigateTo(viewWithLongestName, longestViewName, parameters);
+ }
+ // TODO if no view is found, what to do?
+ }
+
+ /**
+ * Internal method activating a view, setting its parameters and calling
+ * listeners.
+ *
+ * This method also verifies that the user is allowed to perform the
+ * navigation operation.
+ *
+ * @param view
+ * view to activate
+ * @param viewName
+ * (optional) name of the view or null not to set the fragment
+ * @param fragmentParameters
+ * parameters passed in the fragment for the view
+ */
+ protected void navigateTo(View view, String viewName,
+ String fragmentParameters) {
+ ViewChangeEvent event = new ViewChangeEvent(this, currentView, view,
+ viewName, fragmentParameters);
+ if (!isViewChangeAllowed(event)) {
+ return;
+ }
+
+ if (null != viewName && getFragmentManager() != null) {
+ String currentFragment = viewName;
+ if (fragmentParameters != null) {
+ currentFragment += "/" + fragmentParameters;
+ }
+ if (!currentFragment.equals(getFragmentManager().getFragment())) {
+ getFragmentManager().setFragment(currentFragment);
+ }
+ }
+
+ view.navigateTo(fragmentParameters);
+ currentView = view;
+
+ if (display != null) {
+ display.showView(view);
+ }
+
+ fireViewChange(event);
+ }
+
+ /**
+ * Check whether view change is allowed.
+ *
+ * All related listeners are called. The view change is blocked if any of
+ * them wants to block the navigation operation.
+ *
+ * The view change listeners may also e.g. open a warning or question dialog
+ * and save the parameters to re-initiate the navigation operation upon user
+ * action.
+ *
+ * @param event
+ * view change event (not null, view change not yet performed)
+ * @return true if the view change should be allowed, false to silently
+ * block the navigation operation
+ */
+ protected boolean isViewChangeAllowed(ViewChangeEvent event) {
+ for (ViewChangeListener l : listeners) {
+ if (!l.isViewChangeAllowed(event)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the fragment manager that is used to get, listen to and manipulate
+ * the URI fragment or other source of navigation information.
+ *
+ * @return fragment manager in use
+ */
+ protected FragmentManager getFragmentManager() {
+ return fragmentManager;
+ }
+
+ /**
+ * Returns the ViewDisplay used by the navigator. Unless another display is
+ * specified, a {@link SimpleViewDisplay} (which is a {@link Component}) is
+ * used by default.
+ *
+ * @return current ViewDisplay
+ */
+ public ViewDisplay getDisplay() {
+ return display;
+ }
+
+ /**
+ * Fire an event when the current view has changed.
+ *
+ * @param event
+ * view change event (not null)
+ */
+ protected void fireViewChange(ViewChangeEvent event) {
+ for (ViewChangeListener l : listeners) {
+ l.navigatorViewChanged(event);
+ }
+ }
+
+ /**
+ * Register a static, pre-initialized view instance for a view name.
+ *
+ * Registering another view with a name that is already registered
+ * overwrites the old registration of the same type.
+ *
+ * @param viewName
+ * String that identifies a view (not null nor empty string)
+ * @param view
+ * {@link View} instance (not null)
+ */
+ public void addView(String viewName, View view) {
+
+ // Check parameters
+ if (viewName == null || view == null) {
+ throw new IllegalArgumentException(
+ "view and viewName must be non-null");
+ }
+
+ removeView(viewName);
+ registerProvider(new StaticViewProvider(viewName, view));
+ }
+
+ /**
+ * Register for a view name a view class.
+ *
+ * Registering another view with a name that is already registered
+ * overwrites the old registration of the same type.
+ *
+ * A new view instance is created every time a view is requested.
+ *
+ * @param viewName
+ * String that identifies a view (not null nor empty string)
+ * @param viewClass
+ * {@link View} class to instantiate when a view is requested
+ * (not null)
+ */
+ public void addView(String viewName, Class<? extends View> viewClass) {
+
+ // Check parameters
+ if (viewName == null || viewClass == null) {
+ throw new IllegalArgumentException(
+ "view and viewClass must be non-null");
+ }
+
+ removeView(viewName);
+ registerProvider(new ClassBasedViewProvider(viewName, viewClass));
+ }
+
+ /**
+ * Remove view from navigator.
+ *
+ * This method only applies to views registered using
+ * {@link #addView(String, View)} or {@link #addView(String, Class)}.
+ *
+ * @param viewName
+ * name of the view to remove
+ */
+ public void removeView(String viewName) {
+ Iterator<ViewProvider> it = providers.iterator();
+ while (it.hasNext()) {
+ ViewProvider provider = it.next();
+ if (provider instanceof StaticViewProvider) {
+ StaticViewProvider staticProvider = (StaticViewProvider) provider;
+ if (staticProvider.getViewName().equals(viewName)) {
+ it.remove();
+ }
+ } else if (provider instanceof ClassBasedViewProvider) {
+ ClassBasedViewProvider classBasedProvider = (ClassBasedViewProvider) provider;
+ if (classBasedProvider.getViewName().equals(viewName)) {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Register a view provider (factory).
+ *
+ * Providers are called in order of registration until one that can handle
+ * the requested view name is found.
+ *
+ * @param provider
+ * provider to register
+ */
+ public void registerProvider(ViewProvider provider) {
+ providers.add(provider);
+ }
+
+ /**
+ * Unregister a view provider (factory).
+ *
+ * @param provider
+ * provider to unregister
+ */
+ public void unregisterProvider(ViewProvider provider) {
+ providers.remove(provider);
+ }
+
+ /**
+ * Listen to changes of the active view.
+ *
+ * The listener will get notified after the view has changed.
+ *
+ * @param listener
+ * Listener to invoke after view changes.
+ */
+ public void addListener(ViewChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Remove a view change listener.
+ *
+ * @param listener
+ * Listener to remove.
+ */
+ public void removeListener(ViewChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+}
diff --git a/src/com/vaadin/navigator/View.java b/src/com/vaadin/navigator/View.java
new file mode 100644
index 0000000000..4d135b4c0b
--- /dev/null
+++ b/src/com/vaadin/navigator/View.java
@@ -0,0 +1,36 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.navigator;
+
+import java.io.Serializable;
+
+import com.vaadin.ui.Component;
+
+/**
+ * Interface for all views controlled by the navigator.
+ *
+ * Each view added to the navigator must implement this interface. Typically, a
+ * view is a {@link Component}.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0
+ */
+public interface View extends Serializable {
+
+ /**
+ * This view is navigated to.
+ *
+ * This method is always called before the view is shown on screen. If there
+ * is any additional id to data what should be shown in the view, it is also
+ * optionally passed as parameter.
+ *
+ * TODO fragmentParameters null if no parameters or empty string?
+ *
+ * @param fragmentParameters
+ * parameters to the view or null if none given. This is the
+ * string that appears e.g. in URI after "viewname/"
+ */
+ public void navigateTo(String fragmentParameters);
+} \ No newline at end of file
diff --git a/src/com/vaadin/navigator/ViewChangeListener.java b/src/com/vaadin/navigator/ViewChangeListener.java
new file mode 100644
index 0000000000..2eb34e6fcf
--- /dev/null
+++ b/src/com/vaadin/navigator/ViewChangeListener.java
@@ -0,0 +1,118 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.navigator;
+
+import java.io.Serializable;
+import java.util.EventObject;
+
+/**
+ * Interface for listening to View changes before and after they occur.
+ *
+ * Implementations of this interface can also block navigation between views
+ * before it is performed.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0
+ */
+public interface ViewChangeListener extends Serializable {
+
+ /**
+ * Event received by the listener for attempted and executed view changes.
+ */
+ public static class ViewChangeEvent extends EventObject {
+ private final View oldView;
+ private final View newView;
+ private final String viewName;
+ private final String fragmentParameters;
+
+ /**
+ * Create a new view change event.
+ *
+ * @param navigator
+ * Navigator that triggered the event, not null
+ */
+ public ViewChangeEvent(Navigator navigator, View oldView, View newView,
+ String viewName, String fragmentParameters) {
+ super(navigator);
+ this.oldView = oldView;
+ this.newView = newView;
+ this.viewName = viewName;
+ this.fragmentParameters = fragmentParameters;
+ }
+
+ /**
+ * Returns the navigator that triggered this event.
+ *
+ * @return Navigator (not null)
+ */
+ public Navigator getNavigator() {
+ return (Navigator) getSource();
+ }
+
+ /**
+ * Returns the view being deactivated.
+ *
+ * @return old View
+ */
+ public View getOldView() {
+ return oldView;
+ }
+
+ /**
+ * Returns the view being activated.
+ *
+ * @return new View
+ */
+ public View getNewView() {
+ return newView;
+ }
+
+ /**
+ * Returns the view name of the view being activated.
+ *
+ * @return view name of the new View
+ */
+ public String getViewName() {
+ return viewName;
+ }
+
+ /**
+ * Returns the parameters for the view being activated.
+ *
+ * @return fragment parameters (potentially bookmarkable) for the new
+ * view
+ */
+ public String getFragmentParameters() {
+ return fragmentParameters;
+ }
+ }
+
+ /**
+ * Check whether changing the view is permissible.
+ *
+ * This method may also e.g. open a "save" dialog or question about the
+ * change, which may re-initiate the navigation operation after user action.
+ *
+ * If this listener does not want to block the view change (e.g. does not
+ * know the view in question), it should return true. If any listener
+ * returns false, the view change is not allowed.
+ *
+ * @param event
+ * view change event
+ * @return true if the view change should be allowed or this listener does
+ * not care about the view change, false to block the change
+ */
+ public boolean isViewChangeAllowed(ViewChangeEvent event);
+
+ /**
+ * Invoked after the view has changed. Be careful for deadlocks if you
+ * decide to change the view again in the listener.
+ *
+ * @param event
+ * view change event
+ */
+ public void navigatorViewChanged(ViewChangeEvent event);
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/navigator/ViewDisplay.java b/src/com/vaadin/navigator/ViewDisplay.java
new file mode 100644
index 0000000000..6016951394
--- /dev/null
+++ b/src/com/vaadin/navigator/ViewDisplay.java
@@ -0,0 +1,29 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.navigator;
+
+import java.io.Serializable;
+
+/**
+ * Interface for displaying a view in an appropriate location.
+ *
+ * The view display can be a component/layout itself or can modify a separate
+ * layout.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0
+ */
+public interface ViewDisplay extends Serializable {
+ /**
+ * Remove previously shown view and show the newly selected view in its
+ * place.
+ *
+ * The parameters for the view have been set before this method is called.
+ *
+ * @param view
+ * new view to show
+ */
+ public void showView(View view);
+} \ No newline at end of file
diff --git a/src/com/vaadin/navigator/ViewProvider.java b/src/com/vaadin/navigator/ViewProvider.java
new file mode 100644
index 0000000000..4d9d22acab
--- /dev/null
+++ b/src/com/vaadin/navigator/ViewProvider.java
@@ -0,0 +1,44 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.navigator;
+
+import java.io.Serializable;
+
+/**
+ * A provider for view instances that can return pre-registered views or
+ * dynamically create new views.
+ *
+ * If multiple providers are used, {@link #getViewName(String)} of each is
+ * called (in registration order) until one of them returns a non-null value.
+ * The {@link #getView(String)} method of that provider is then used.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0
+ */
+public interface ViewProvider extends Serializable {
+ /**
+ * Extract the view name from a combined view name and parameter string.
+ * This method should return a view name if and only if this provider
+ * handles creation of such views.
+ *
+ * @param viewAndParameters
+ * string with view name and its fragment parameters (if given),
+ * not null
+ * @return view name if the view is handled by this provider, null otherwise
+ */
+ public String getViewName(String viewAndParameters);
+
+ /**
+ * Create or return a pre-created instance of a view.
+ *
+ * The parameters for the view are set separately by the navigator when the
+ * view is activated.
+ *
+ * @param viewName
+ * name of the view, not null
+ * @return newly created view (null if none available for the view name)
+ */
+ public View getView(String viewName);
+} \ No newline at end of file
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();
}
diff --git a/src/com/vaadin/tools/WidgetsetCompiler.java b/src/com/vaadin/tools/WidgetsetCompiler.java
index 323fb17e32..407f0e2387 100644
--- a/src/com/vaadin/tools/WidgetsetCompiler.java
+++ b/src/com/vaadin/tools/WidgetsetCompiler.java
@@ -30,12 +30,12 @@ import com.vaadin.terminal.gwt.widgetsetutils.WidgetSetBuilder;
* The source directory containing widgetset and related classes must be
* included in the classpath, as well as the gwt-dev-[platform].jar and other
* relevant JARs.
+ *
+ * @deprecated with Java 6, can use com.google.gwt.dev.Compiler directly (also in Eclipse plug-in etc.)
*/
+@Deprecated
public class WidgetsetCompiler {
- private static final Logger logger = Logger
- .getLogger(WidgetsetCompiler.class.getName());
-
/**
* @param args
* same arguments as for com.google.gwt.dev.Compiler
@@ -72,7 +72,7 @@ public class WidgetsetCompiler {
String[].class);
method.invoke(null, new Object[] { args });
} catch (Throwable thr) {
- logger.log(Level.SEVERE,
+ getLogger().log(Level.SEVERE,
"Widgetset compilation failed", thr);
}
}
@@ -82,7 +82,11 @@ public class WidgetsetCompiler {
runThread.join();
System.out.println("Widgetset compilation finished");
} catch (Throwable thr) {
- logger.log(Level.SEVERE, "Widgetset compilation failed", thr);
+ getLogger().log(Level.SEVERE, "Widgetset compilation failed", thr);
}
}
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(WidgetsetCompiler.class.getName());
+ }
}
diff --git a/src/com/vaadin/ui/AbsoluteLayout.java b/src/com/vaadin/ui/AbsoluteLayout.java
index 9ba005f75a..7d8c402fc9 100644
--- a/src/com/vaadin/ui/AbsoluteLayout.java
+++ b/src/com/vaadin/ui/AbsoluteLayout.java
@@ -161,7 +161,8 @@ public class AbsoluteLayout extends AbstractLayout implements
// connectorId unless the component is attached to the application so
// the String->String map cannot be populated in internal* either.
Map<String, String> connectorToPosition = new HashMap<String, String>();
- for (Component c : this) {
+ for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) {
+ Component c = ci.next();
connectorToPosition.put(c.getConnectorId(), getPosition(c)
.getCSSString());
}
diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java
index 554d7806f9..ba0e5db89c 100644
--- a/src/com/vaadin/ui/AbstractComponent.java
+++ b/src/com/vaadin/ui/AbstractComponent.java
@@ -5,20 +5,13 @@
package com.vaadin.ui;
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.LinkedList;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
-import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -27,18 +20,14 @@ import com.vaadin.event.ActionManager;
import com.vaadin.event.EventRouter;
import com.vaadin.event.MethodEventSource;
import com.vaadin.event.ShortcutListener;
+import com.vaadin.terminal.AbstractClientConnector;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Terminal;
import com.vaadin.terminal.gwt.client.ComponentState;
-import com.vaadin.terminal.gwt.client.communication.ClientRpc;
-import com.vaadin.terminal.gwt.client.communication.ServerRpc;
-import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
+import com.vaadin.terminal.gwt.server.ClientConnector;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
import com.vaadin.terminal.gwt.server.ResourceReference;
-import com.vaadin.terminal.gwt.server.RpcManager;
-import com.vaadin.terminal.gwt.server.RpcTarget;
-import com.vaadin.terminal.gwt.server.ServerRpcManager;
import com.vaadin.tools.ReflectTools;
/**
@@ -53,7 +42,8 @@ import com.vaadin.tools.ReflectTools;
* @since 3.0
*/
@SuppressWarnings("serial")
-public abstract class AbstractComponent implements Component, MethodEventSource {
+public abstract class AbstractComponent extends AbstractClientConnector
+ implements Component, MethodEventSource {
/* Private members */
@@ -64,11 +54,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource
private Object applicationData;
/**
- * The container this component resides in.
- */
- private HasComponents parent = null;
-
- /**
* The EventRouter used for the event model.
*/
private EventRouter eventRouter = null;
@@ -88,11 +73,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
private boolean delayedFocus;
- /**
- * List of repaint request listeners or null if not listened at all.
- */
- private LinkedList<RepaintRequestListener> repaintRequestListeners = null;
-
/* Sizeable fields */
private float width = SIZE_UNDEFINED;
@@ -110,31 +90,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
private ActionManager actionManager;
- /**
- * 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 ComponentState sharedState;
-
- /**
- * Pending RPC method invocations to be sent.
- */
- private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
-
- private String connectorId;
-
/* Constructor */
/**
@@ -287,6 +242,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
if (locale != null) {
return locale;
}
+ HasComponents parent = getParent();
if (parent != null) {
return parent.getLocale();
}
@@ -378,21 +334,18 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*
* @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled()
*/
+ @Override
public boolean isConnectorEnabled() {
- if (getParent() == null) {
- // No parent -> the component cannot receive updates from the client
+ if (!isVisible()) {
+ return false;
+ } else if (!isEnabled()) {
+ return false;
+ } else if (!super.isConnectorEnabled()) {
+ return false;
+ } else if (!getParent().isComponentVisible(this)) {
return false;
} else {
- boolean thisEnabledAndVisible = isEnabled() && isVisible();
- if (!thisEnabledAndVisible) {
- return false;
- }
-
- if (!getParent().isConnectorEnabled()) {
- return false;
- }
-
- return getParent().isComponentVisible(this);
+ return true;
}
}
@@ -529,8 +482,20 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* Gets the component's parent component. Don't add a JavaDoc comment here,
* we use the default documentation from implemented interface.
*/
+ @Override
public HasComponents getParent() {
- return parent;
+ return (HasComponents) super.getParent();
+ }
+
+ @Override
+ public void setParent(ClientConnector parent) {
+ if (parent == null || parent instanceof HasComponents) {
+ super.setParent(parent);
+ } else {
+ throw new IllegalArgumentException(
+ "The parent of a Component must implement HasComponents, which "
+ + parent.getClass() + " doesn't do.");
+ }
}
/**
@@ -558,36 +523,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource
return null;
}
- /*
- * Sets the parent component. Don't add a JavaDoc comment here, we use the
- * default documentation from implemented interface.
- */
- public void setParent(HasComponents 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 a window
- if (getApplication() != null) {
- attach();
- }
- }
-
/**
* Gets the error message for this component.
*
@@ -648,12 +583,10 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* Gets the parent window of the component. Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
*/
+ @Override
public Root getRoot() {
- if (parent == null) {
- return null;
- } else {
- return parent.getRoot();
- }
+ // Just make method from implemented Component interface public
+ return super.getRoot();
}
/*
@@ -661,9 +594,9 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* comment here, we use the default documentation from implemented
* interface.
*/
+ @Override
public void attach() {
- getRoot().componentAttached(this);
- requestRepaint();
+ super.attach();
if (delayedFocus) {
focus();
}
@@ -674,13 +607,14 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* Detach the component from application. Don't add a JavaDoc comment here,
* we use the default documentation from implemented interface.
*/
+ @Override
public void detach() {
+ super.detach();
if (actionManager != null) {
// Remove any existing viewer. Root cast is just to make the
// compiler happy
actionManager.setViewer((Root) null);
}
- getRoot().componentDetached(this);
}
/**
@@ -717,12 +651,10 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @return the parent application of the component or <code>null</code>.
* @see #attach()
*/
+ @Override
public Application getApplication() {
- if (parent == null) {
- return null;
- } else {
- return parent.getApplication();
- }
+ // Just make method inherited from Component interface public
+ return super.getApplication();
}
/**
@@ -762,11 +694,9 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*
* @return updated component shared state
*/
+ @Override
public ComponentState getState() {
- if (null == sharedState) {
- sharedState = createState();
- }
- return sharedState;
+ return (ComponentState) super.getState();
}
/*
@@ -801,95 +731,15 @@ public abstract class AbstractComponent implements Component, MethodEventSource
}
}
- /**
- * 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 ComponentState 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 ComponentState> getStateType() {
- try {
- Method m = getClass().getMethod("getState", (Class[]) null);
- Class<? extends ComponentState> type = (Class<? extends ComponentState>) m
- .getReturnType();
- return type;
- } catch (Exception e) {
- throw new RuntimeException("Error finding state type for "
- + getClass().getName(), e);
- }
- }
-
/* Documentation copied from interface */
+ @Override
public void requestRepaint() {
// Invisible components (by flag in this particular component) do not
// need repaints
if (!getState().isVisible()) {
return;
}
-
- fireRequestRepaintEvent();
- }
-
- /**
- * Fires the repaint request event.
- *
- * @param alreadyNotified
- */
- // Notify listeners only once
- private void fireRequestRepaintEvent() {
- // Notify the listeners
- if (repaintRequestListeners != null
- && !repaintRequestListeners.isEmpty()) {
- final Object[] listeners = repaintRequestListeners.toArray();
- final RepaintRequestEvent event = new RepaintRequestEvent(this);
- for (int i = 0; i < listeners.length; i++) {
- ((RepaintRequestListener) listeners[i]).repaintRequested(event);
- }
- }
- }
-
- /* Documentation copied from interface */
- public void addListener(RepaintRequestListener listener) {
- if (repaintRequestListeners == null) {
- repaintRequestListeners = new LinkedList<RepaintRequestListener>();
- }
- if (!repaintRequestListeners.contains(listener)) {
- repaintRequestListeners.add(listener);
- }
- }
-
- /* Documentation copied from interface */
- public void removeListener(RepaintRequestListener listener) {
- if (repaintRequestListeners != null) {
- repaintRequestListeners.remove(listener);
- if (repaintRequestListeners.isEmpty()) {
- repaintRequestListeners = null;
- }
- }
+ super.requestRepaint();
}
/* General event framework */
@@ -1155,15 +1005,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* are found.
*/
public Collection<?> getListeners(Class<?> eventType) {
- if (eventType.isAssignableFrom(RepaintRequestEvent.class)) {
- // RepaintRequestListeners are not stored in eventRouter
- if (repaintRequestListeners == null) {
- return Collections.EMPTY_LIST;
- } else {
- return Collections
- .unmodifiableCollection(repaintRequestListeners);
- }
- }
if (eventRouter == null) {
return Collections.EMPTY_LIST;
}
@@ -1512,159 +1353,4 @@ public abstract class AbstractComponent implements Component, MethodEventSource
actionManager.removeAction(shortcut);
}
}
-
- /**
- * 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");
- }
- Class<T> type = (Class<T>) interfaces[0];
- registerRpc(implementation, type);
- }
-
- /**
- * 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<T> proxyClass = (Class) Proxy.getProxyClass(
- rpcInterface.getClassLoader(), rpcInterface);
- Constructor<T> constructor = proxyClass
- .getConstructor(InvocationHandler.class);
- T rpcProxy = 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 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 methodName
- * RPC method name
- * @param parameters
- * RPC vall 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;
- }
-
- private Logger getLogger() {
- return Logger.getLogger(AbstractComponent.class.getName());
- }
}
diff --git a/src/com/vaadin/ui/AbstractComponentContainer.java b/src/com/vaadin/ui/AbstractComponentContainer.java
index 1c857a03cd..8ef458b704 100644
--- a/src/com/vaadin/ui/AbstractComponentContainer.java
+++ b/src/com/vaadin/ui/AbstractComponentContainer.java
@@ -71,36 +71,6 @@ public abstract class AbstractComponentContainer extends AbstractComponent
}
}
- /**
- * Notifies all contained components that the container is attached to a
- * window.
- *
- * @see com.vaadin.ui.Component#attach()
- */
- @Override
- public void attach() {
- super.attach();
-
- for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) {
- (i.next()).attach();
- }
- }
-
- /**
- * Notifies all contained components that the container is detached from a
- * window.
- *
- * @see com.vaadin.ui.Component#detach()
- */
- @Override
- public void detach() {
- super.detach();
-
- for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) {
- (i.next()).detach();
- }
- }
-
/* Events */
private static final Method COMPONENT_ATTACHED_METHOD;
@@ -355,46 +325,6 @@ public abstract class AbstractComponentContainer extends AbstractComponent
true);
}
- public void requestRepaintAll() {
- requestRepaintAll(this);
- }
-
- /**
- * Helper that implements the logic needed by requestRepaintAll. Calls
- * requestRepaintAll/requestRepaint for the component container and all its
- * children recursively.
- *
- * @param container
- */
- public static void requestRepaintAll(HasComponents container) {
- container.requestRepaint();
- if (container instanceof Panel) {
- Panel p = (Panel) container;
- // #2924 Panel is invalid, really invalid.
- // Panel.getComponentIterator returns the children of content, not
- // of Panel...
- if (p.getContent() != null) {
- p.getContent().requestRepaint();
- }
- }
- for (Iterator<Component> childIterator = container
- .getComponentIterator(); childIterator.hasNext();) {
- Component c = childIterator.next();
- if (c instanceof HasComponents) {
- requestRepaintAll((HasComponents) c);
- } else {
- c.requestRepaint();
- }
- }
- }
-
- /**
- * Returns an iterator for the child components.
- *
- * @return An iterator for the child components.
- * @see #getComponentIterator()
- * @since 7.0.0
- */
public Iterator<Component> iterator() {
return getComponentIterator();
}
diff --git a/src/com/vaadin/ui/AbstractField.java b/src/com/vaadin/ui/AbstractField.java
index 4efed11e2c..ce62fb43ed 100644
--- a/src/com/vaadin/ui/AbstractField.java
+++ b/src/com/vaadin/ui/AbstractField.java
@@ -14,14 +14,14 @@ import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
-import com.vaadin.Application;
import com.vaadin.data.Buffered;
import com.vaadin.data.Property;
import com.vaadin.data.Validatable;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.data.util.converter.Converter;
-import com.vaadin.data.util.converter.ConverterFactory;
+import com.vaadin.data.util.converter.Converter.ConversionException;
+import com.vaadin.data.util.converter.ConverterUtil;
import com.vaadin.event.Action;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutListener;
@@ -591,7 +591,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Commits the value to datasource
committingValueToDataSource = true;
getPropertyDataSource().setValue(
- convertToDataSource(newFieldValue));
+ convertToModel(newFieldValue));
// The buffer is now unmodified
setModified(false);
@@ -701,8 +701,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Check if the current converter is compatible.
if (newDataSource != null
- && (getConverter() == null || !newDataSource.getType()
- .isAssignableFrom(getConverter().getModelType()))) {
+ && !ConverterUtil.canConverterHandle(getConverter(), getType(),
+ newDataSource.getType())) {
// Changing from e.g. Number -> Double should set a new converter,
// changing from Double -> Number can keep the old one (Property
// accepts Number)
@@ -760,15 +760,9 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* from
*/
public void setConverter(Class<?> datamodelType) {
- Converter<T, ?> converter = null;
-
- Application app = Application.getCurrentApplication();
- if (app != null) {
- ConverterFactory factory = app.getConverterFactory();
- converter = (Converter<T, ?>) factory.createConverter(getType(),
- datamodelType);
- }
- setConverter(converter);
+ Converter<T, ?> c = (Converter<T, ?>) ConverterUtil.getConverter(
+ getType(), datamodelType, getApplication());
+ setConverter(c);
}
/**
@@ -782,26 +776,9 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* if there is no converter and the type is not compatible with
* the data source type.
*/
- @SuppressWarnings("unchecked")
- private T convertFromDataSource(Object newValue)
- throws Converter.ConversionException {
- if (converter != null) {
- return converter.convertToPresentation(newValue, getLocale());
- }
- if (newValue == null) {
- return null;
- }
-
- if (getType().isAssignableFrom(newValue.getClass())) {
- return (T) newValue;
- } else {
- throw new Converter.ConversionException(
- "Unable to convert value of type "
- + newValue.getClass().getName()
- + " to "
- + getType()
- + ". No converter is set and the types are not compatible.");
- }
+ private T convertFromDataSource(Object newValue) {
+ return ConverterUtil.convertFromModel(newValue, getType(),
+ getConverter(), getLocale());
}
/**
@@ -815,40 +792,21 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* if there is no converter and the type is not compatible with
* the data source type.
*/
- private Object convertToDataSource(T fieldValue)
+ private Object convertToModel(T fieldValue)
throws Converter.ConversionException {
- if (converter != null) {
- /*
- * If there is a converter, always use it. It must convert or throw
- * an exception.
- */
- try {
- return converter.convertToModel(fieldValue, getLocale());
- } catch (com.vaadin.data.util.converter.Converter.ConversionException e) {
- throw new Converter.ConversionException(
- getConversionError(converter.getModelType()), e);
+ try {
+ Class<?> modelType = null;
+ Property pd = getPropertyDataSource();
+ if (pd != null) {
+ modelType = pd.getType();
+ } else if (getConverter() != null) {
+ modelType = getConverter().getModelType();
}
- }
-
- if (fieldValue == null) {
- // Null should always be passed through the converter but if there
- // is no converter we can safely return null
- return null;
- }
-
- // check that the value class is compatible with the data source type
- // (if data source set) or field type
- Class<?> type;
- if (getPropertyDataSource() != null) {
- type = getPropertyDataSource().getType();
- } else {
- type = getType();
- }
-
- if (type.isAssignableFrom(fieldValue.getClass())) {
- return fieldValue;
- } else {
- throw new Converter.ConversionException(getConversionError(type));
+ return ConverterUtil.convertToModel(fieldValue,
+ (Class<Object>) modelType, getConverter(), getLocale());
+ } catch (ConversionException e) {
+ throw new ConversionException(
+ getConversionError(converter.getModelType()), e);
}
}
@@ -880,7 +838,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* @return The converted value that is compatible with the data source type
*/
public Object getConvertedValue() {
- return convertToDataSource(getFieldValue());
+ return convertToModel(getFieldValue());
}
/**
diff --git a/src/com/vaadin/ui/AbstractJavaScriptComponent.java b/src/com/vaadin/ui/AbstractJavaScriptComponent.java
new file mode 100644
index 0000000000..95c45f55f9
--- /dev/null
+++ b/src/com/vaadin/ui/AbstractJavaScriptComponent.java
@@ -0,0 +1,161 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import com.vaadin.terminal.JavaScriptCallbackHelper;
+import com.vaadin.terminal.gwt.client.ui.JavaScriptComponentState;
+import com.vaadin.terminal.gwt.client.ui.JavaScriptWidget;
+
+/**
+ * Base class for Components with all client-side logic implemented using
+ * JavaScript.
+ * <p>
+ * When a new JavaScript component is initialized in the browser, the framework
+ * will look for a globally defined JavaScript function that will initialize the
+ * component. The name of the initialization function is formed by replacing .
+ * with _ in the name of the server-side class. If no such function is defined,
+ * each super class is used in turn until a match is found. The framework will
+ * thus first attempt with <code>com_example_MyComponent</code> for the
+ * server-side
+ * <code>com.example.MyComponent extends AbstractJavaScriptComponent</code>
+ * class. If MyComponent instead extends <code>com.example.SuperComponent</code>
+ * , then <code>com_example_SuperComponent</code> will also be attempted if
+ * <code>com_example_MyComponent</code> has not been defined.
+ * <p>
+ * JavaScript components have a very simple GWT widget ({@link JavaScriptWidget}
+ * ) just consisting of a <code>div</code> element to which the JavaScript code
+ * should initialize its own user interface.
+ * <p>
+ * The initialization function will be called with <code>this</code> pointing to
+ * a connector wrapper object providing integration to Vaadin with the following
+ * functions:
+ * <ul>
+ * <li><code>getConnectorId()</code> - returns a string with the id of the
+ * connector.</li>
+ * <li><code>getParentId([connectorId])</code> - returns a string with the id of
+ * the connector's parent. If <code>connectorId</code> is provided, the id of
+ * the parent of the corresponding connector with the passed id is returned
+ * instead.</li>
+ * <li><code>getWidgetElement([connectorId])</code> - returns the DOM Element
+ * that is the root of a connector's widget. <code>null</code> is returned if
+ * the connector can not be found or if the connector doesn't have a widget. If
+ * <code>connectorId</code> is not provided, the connector id of the current
+ * connector will be used.</li>
+ * <li><code>getState()</code> - returns an object corresponding to the shared
+ * state defined on the server. The scheme for conversion between Java and
+ * JavaScript types is described bellow.</li>
+ * <li><code>registerRpc([name, ] rpcObject)</code> - registers the
+ * <code>rpcObject</code> as a RPC handler. <code>rpcObject</code> should be an
+ * object with field containing functions for all eligible RPC functions. If
+ * <code>name</code> is provided, the RPC handler will only used for RPC calls
+ * for the RPC interface with the same fully qualified Java name. If no
+ * <code>name</code> is provided, the RPC handler will be used for all incoming
+ * RPC invocations where the RPC method name is defined as a function field in
+ * the handler. The scheme for conversion between Java types in the RPC
+ * interface definition and the JavaScript values passed as arguments to the
+ * handler functions is described bellow.</li>
+ * <li><code>getRpcProxy([name])</code> - returns an RPC proxy object. If
+ * <code>name</code> is provided, the proxy object will contain functions for
+ * all methods in the RPC interface with the same fully qualified name, provided
+ * a RPC handler has been registered by the server-side code. If no
+ * <code>name</code> is provided, the returned RPC proxy object will contain
+ * functions for all methods in all RPC interfaces registered for the connector
+ * on the server. If the same method name is present in multiple registered RPC
+ * interfaces, the corresponding function in the RPC proxy object will throw an
+ * exception when called. The scheme for conversion between Java types in the
+ * RPC interface and the JavaScript values that should be passed to the
+ * functions is described bellow.</li>
+ * </ul>
+ * The connector wrapper also supports these special functions:
+ * <ul>
+ * <li><code>onStateChange</code> - If the JavaScript code assigns a function to
+ * the field, that function is called whenever the contents of the shared state
+ * is changed.</li>
+ * <li>Any field name corresponding to a call to
+ * {@link #registerCallback(String, JavaScriptCallback)} on the server will
+ * automatically be present as a function that triggers the registered callback
+ * on the server.</li>
+ * <li>Any field name referred to using
+ * {@link #invokeCallback(String, Object...)} on the server will be called if a
+ * function has been assigned to the field.</li>
+ * </ul>
+ * <p>
+ *
+ * Values in the Shared State and in RPC calls are converted between Java and
+ * JavaScript using the following conventions:
+ * <ul>
+ * <li>Primitive Java numbers (byte, char, int, long, float, double) and their
+ * boxed types (Byte, Character, Integer, Long, Float, Double) are represented
+ * by JavaScript numbers.</li>
+ * <li>The primitive Java boolean and the boxed Boolean are represented by
+ * JavaScript booleans.</li>
+ * <li>Java Strings are represented by JavaScript strings.</li>
+ * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
+ * <li>Map<String, ?> in Java is represented by JavaScript object with fields
+ * corresponding to the map keys.</li>
+ * <li>Any other Java Map is represented by a JavaScript array containing two
+ * arrays, the first contains the keys and the second contains the values in the
+ * same order.</li>
+ * <li>A Java Bean is represented by a JavaScript object with fields
+ * corresponding to the bean's properties.</li>
+ * <li>A Java Connector is represented by a JavaScript string containing the
+ * connector's id.</li>
+ * <li>A pluggable serialization mechanism is provided for types not described
+ * here. Please refer to the documentation for specific types for serialization
+ * information.</li>
+ * </ul>
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public class AbstractJavaScriptComponent extends AbstractComponent {
+ private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper(
+ this);
+
+ @Override
+ protected <T> void registerRpc(T implementation, 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 JavaScriptComponentState getState() {
+ return (JavaScriptComponentState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/ui/AbstractSelect.java b/src/com/vaadin/ui/AbstractSelect.java
index e586810b2d..6a927251af 100644
--- a/src/com/vaadin/ui/AbstractSelect.java
+++ b/src/com/vaadin/ui/AbstractSelect.java
@@ -24,7 +24,6 @@ import com.vaadin.event.Transferable;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DropTarget;
import com.vaadin.event.dd.TargetDetailsImpl;
-import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor;
import com.vaadin.event.dd.acceptcriteria.TargetDetailIs;
@@ -33,8 +32,6 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Vaadin6Component;
-import com.vaadin.terminal.gwt.client.ui.dd.VIsOverId;
-import com.vaadin.terminal.gwt.client.ui.dd.VItemIdIs;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
import com.vaadin.ui.AbstractSelect.ItemCaptionMode;
@@ -1800,7 +1797,6 @@ public abstract class AbstractSelect extends AbstractField<Object> implements
*
* @since 6.3
*/
- @ClientCriterion(VIsOverId.class)
public static class TargetItemIs extends AbstractItemSetCriterion {
/**
@@ -1867,7 +1863,6 @@ public abstract class AbstractSelect extends AbstractField<Object> implements
*
* @since 6.3
*/
- @ClientCriterion(VItemIdIs.class)
public static class AcceptItem extends AbstractItemSetCriterion {
/**
diff --git a/src/com/vaadin/ui/AbstractSplitPanel.java b/src/com/vaadin/ui/AbstractSplitPanel.java
index 5205952621..876d39f2ae 100644
--- a/src/com/vaadin/ui/AbstractSplitPanel.java
+++ b/src/com/vaadin/ui/AbstractSplitPanel.java
@@ -22,7 +22,7 @@ import com.vaadin.tools.ReflectTools;
* AbstractSplitPanel.
*
* <code>AbstractSplitPanel</code> is base class for a component container that
- * can contain two components. The comopnents are split by a divider element.
+ * can contain two components. The components are split by a divider element.
*
* @author Vaadin Ltd.
* @version
@@ -31,7 +31,10 @@ import com.vaadin.tools.ReflectTools;
*/
public abstract class AbstractSplitPanel extends AbstractComponentContainer {
+ // TODO use Unit in AbstractSplitPanelState and remove these
private Unit posUnit;
+ private Unit posMinUnit;
+ private Unit posMaxUnit;
private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() {
@@ -41,13 +44,14 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
}
public void setSplitterPosition(float position) {
- getState().getSplitterState().setPosition(position);
+ getSplitterState().setPosition(position);
}
};
public AbstractSplitPanel() {
registerRpc(rpc);
setSplitPosition(50, Unit.PERCENTAGE, false);
+ setSplitPositionLimits(0, Unit.PERCENTAGE, 100, Unit.PERCENTAGE);
}
/**
@@ -101,6 +105,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
* @param c
* the component to be added.
*/
+
@Override
public void addComponent(Component c) {
if (getFirstComponent() == null) {
@@ -188,6 +193,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
* @param c
* the component to be removed.
*/
+
@Override
public void removeComponent(Component c) {
super.removeComponent(c);
@@ -204,6 +210,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
*
* @see com.vaadin.ui.ComponentContainer#getComponentIterator()
*/
+
public Iterator<Component> getComponentIterator() {
return new ComponentIterator();
}
@@ -214,6 +221,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
*
* @return the number of contained components (zero, one or two)
*/
+
public int getComponentCount() {
int count = 0;
if (getFirstComponent() != null) {
@@ -226,6 +234,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
}
/* Documented in superclass */
+
public void replaceComponent(Component oldComponent, Component newComponent) {
if (oldComponent == getFirstComponent()) {
setFirstComponent(newComponent);
@@ -297,7 +306,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
if (unit != Unit.PERCENTAGE) {
pos = Math.round(pos);
}
- SplitterState splitterState = getState().getSplitterState();
+ SplitterState splitterState = getSplitterState();
splitterState.setPosition(pos);
splitterState.setPositionUnit(unit.getSymbol());
splitterState.setPositionReversed(reverse);
@@ -313,7 +322,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
* @return position of the splitter
*/
public float getSplitPosition() {
- return getState().getSplitterState().getPosition();
+ return getSplitterState().getPosition();
}
/**
@@ -326,6 +335,110 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
}
/**
+ * Sets the minimum split position to the given position and unit. If the
+ * split position is reversed, maximum and minimum are also reversed.
+ *
+ * @param pos
+ * the minimum position of the split
+ * @param unit
+ * the unit (from {@link Sizeable}) in which the size is given.
+ * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
+ */
+ public void setMinSplitPosition(int pos, Unit unit) {
+ setSplitPositionLimits(pos, unit, getSplitterState().getMaxPosition(),
+ posMaxUnit);
+ }
+
+ /**
+ * Returns the current minimum position of the splitter, in
+ * {@link #getMinSplitPositionUnit()} units.
+ *
+ * @return the minimum position of the splitter
+ */
+ public float getMinSplitPosition() {
+ return getSplitterState().getMinPosition();
+ }
+
+ /**
+ * Returns the unit of the minimum position of the splitter.
+ *
+ * @return the unit of the minimum position of the splitter
+ */
+ public Unit getMinSplitPositionUnit() {
+ return posMinUnit;
+ }
+
+ /**
+ * Sets the maximum split position to the given position and unit. If the
+ * split position is reversed, maximum and minimum are also reversed.
+ *
+ * @param pos
+ * the maximum position of the split
+ * @param unit
+ * the unit (from {@link Sizeable}) in which the size is given.
+ * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
+ */
+ public void setMaxSplitPosition(float pos, Unit unit) {
+ setSplitPositionLimits(getSplitterState().getMinPosition(), posMinUnit,
+ pos, unit);
+ }
+
+ /**
+ * Returns the current maximum position of the splitter, in
+ * {@link #getMaxSplitPositionUnit()} units.
+ *
+ * @return the maximum position of the splitter
+ */
+ public float getMaxSplitPosition() {
+ return getSplitterState().getMaxPosition();
+ }
+
+ /**
+ * Returns the unit of the maximum position of the splitter
+ *
+ * @return the unit of the maximum position of the splitter
+ */
+ public Unit getMaxSplitPositionUnit() {
+ return posMaxUnit;
+ }
+
+ /**
+ * Sets the maximum and minimum position of the splitter. If the split
+ * position is reversed, maximum and minimum are also reversed.
+ *
+ * @param minPos
+ * the new minimum position
+ * @param minPosUnit
+ * the unit (from {@link Sizeable}) in which the minimum position
+ * is given.
+ * @param maxPos
+ * the new maximum position
+ * @param maxPosUnit
+ * the unit (from {@link Sizeable}) in which the maximum position
+ * is given.
+ */
+ private void setSplitPositionLimits(float minPos, Unit minPosUnit,
+ float maxPos, Unit maxPosUnit) {
+ if ((minPosUnit != Unit.PERCENTAGE && minPosUnit != Unit.PIXELS)
+ || (maxPosUnit != Unit.PERCENTAGE && maxPosUnit != Unit.PIXELS)) {
+ throw new IllegalArgumentException(
+ "Only percentage and pixel units are allowed");
+ }
+
+ SplitterState state = getSplitterState();
+
+ state.setMinPosition(minPos);
+ state.setMinPositionUnit(minPosUnit.getSymbol());
+ posMinUnit = minPosUnit;
+
+ state.setMaxPosition(maxPos);
+ state.setMaxPositionUnit(maxPosUnit.getSymbol());
+ posMaxUnit = maxPosUnit;
+
+ requestRepaint();
+ }
+
+ /**
* Lock the SplitPanels position, disabling the user from dragging the split
* handle.
*
@@ -333,7 +446,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
* Set <code>true</code> if locked, <code>false</code> otherwise.
*/
public void setLocked(boolean locked) {
- getState().getSplitterState().setLocked(locked);
+ getSplitterState().setLocked(locked);
requestRepaint();
}
@@ -344,7 +457,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
* @return <code>true</code> if locked, <code>false</code> otherwise.
*/
public boolean isLocked() {
- return getState().getSplitterState().isLocked();
+ return getSplitterState().isLocked();
}
/**
@@ -394,4 +507,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer {
return (AbstractSplitPanelState) super.getState();
}
+ private SplitterState getSplitterState() {
+ return getState().getSplitterState();
+ }
}
diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java
index 876fe593e2..0abc50f26f 100644
--- a/src/com/vaadin/ui/Button.java
+++ b/src/com/vaadin/ui/Button.java
@@ -38,6 +38,7 @@ public class Button extends AbstractComponent implements
Action.ShortcutNotifier {
private ButtonServerRpc rpc = new ButtonServerRpc() {
+
public void click(MouseEventDetails mouseEventDetails) {
fireClick(mouseEventDetails);
}
@@ -50,6 +51,7 @@ public class Button extends AbstractComponent implements
};
FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(this) {
+
@Override
protected void fireEvent(Event event) {
Button.this.fireEvent(event);
@@ -355,8 +357,6 @@ public class Button extends AbstractComponent implements
protected ClickShortcut clickShortcut;
- private int tabIndex = 0;
-
/**
* Makes it possible to invoke a click on this button by pressing the given
* {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
@@ -469,13 +469,23 @@ public class Button extends AbstractComponent implements
requestRepaint();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component.Focusable#getTabIndex()
+ */
public int getTabIndex() {
- return tabIndex;
+ return getState().getTabIndex();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
+ */
public void setTabIndex(int tabIndex) {
- this.tabIndex = tabIndex;
-
+ getState().setTabIndex(tabIndex);
+ requestRepaint();
}
@Override
diff --git a/src/com/vaadin/ui/Component.java b/src/com/vaadin/ui/Component.java
index 3632c4ca5e..81e0319880 100644
--- a/src/com/vaadin/ui/Component.java
+++ b/src/com/vaadin/ui/Component.java
@@ -304,41 +304,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* </p>
*
* @return the parent component
- * @see #setParent(Component)
*/
public HasComponents getParent();
/**
- * Sets the parent component of the component.
- *
- * <p>
- * This method automatically calls {@link #attach()} if the parent becomes
- * attached to the application, regardless of whether it was attached
- * previously. Conversely, if the parent is {@code null} and the component
- * is attached to the application, {@link #detach()} is called for the
- * component.
- * </p>
- * <p>
- * This method is rarely called directly. The
- * {@link ComponentContainer#addComponent(Component)} method is normally
- * used for adding components to a container and it 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 component
- * @throws IllegalStateException
- * if a parent is given even though the component already has a
- * parent
- */
- public void setParent(HasComponents parent);
-
- /**
* Tests whether the component is in the read-only mode. The user can not
* change the value of a read-only component. As only {@link Field}
* components normally have a value that can be input or changed by the
@@ -528,21 +497,15 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
public void setIcon(Resource icon);
/**
- * Gets the parent window of the component.
+ * Gets the Root the component is attached to.
*
* <p>
- * If the component is not attached to a window through a component
+ * If the component is not attached to a Root through a component
* containment hierarchy, <code>null</code> is returned.
* </p>
*
- * <p>
- * The window can be either an application-level window or a sub-window. If
- * the component is itself a window, it returns a reference to itself, not
- * to its containing window (of a sub-window).
- * </p>
- *
- * @return the parent window of the component or <code>null</code> if it is
- * not attached to a window or is itself a window
+ * @return the Root of the component or <code>null</code> if it is not
+ * attached to a Root
*/
public Root getRoot();
@@ -551,11 +514,16 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
*
* <p>
* The method will return {@code null} if the component is not currently
- * attached to an application. This is often a problem in constructors of
- * regular components and in the initializers of custom composite
- * components. A standard workaround is to move the problematic
- * initialization to {@link #attach()}, as described in the documentation of
- * the method.
+ * attached to an application.
+ * </p>
+ *
+ * <p>
+ * Getting a null value is often a problem in constructors of regular
+ * components and in the initializers of custom composite components. A
+ * standard workaround is to use {@link Application#getCurrent()}
+ * to retrieve the application instance that the current request relates to.
+ * Another way is to move the problematic initialization to
+ * {@link #attach()}, as described in the documentation of the method.
* </p>
*
* @return the parent application of the component or <code>null</code>.
@@ -564,15 +532,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
public Application getApplication();
/**
- * Notifies the component that it is connected to an application.
- *
- * <p>
- * The caller of this method is {@link #setParent(Component)} 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 component is
- * painted for the first time.
- * </p>
+ * {@inheritDoc}
*
* <p>
* Reimplementing the {@code attach()} method is useful for tasks that need
@@ -624,38 +584,10 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
* }
* }
* </pre>
- *
- * <p>
- * The attachment logic is implemented in {@link AbstractComponent}.
- * </p>
- *
- * @see #getApplication()
*/
public void attach();
/**
- * Notifies the component that it is detached from the application.
- *
- * <p>
- * The {@link #getApplication()} and {@link #getRoot()} methods might return
- * <code>null</code> after this method is called.
- * </p>
- *
- * <p>
- * This method must call {@link Root#componentDetached(Component)} to let
- * the Root know that a new Component has been attached.
- * </p>
- * *
- * <p>
- * The caller of this method is {@link #setParent(Component)} if the parent
- * is in the application. When the parent is detached from the application
- * it is its response to call {@link #detach()} for all the children and to
- * detach itself from the terminal.
- * </p>
- */
- public void detach();
-
- /**
* Gets the locale of the component.
*
* <p>
@@ -719,75 +651,6 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
*/
public String getDebugId();
- /**
- * Requests that the component should be repainted as soon as possible.
- */
- public void requestRepaint();
-
- /**
- * Repaint request event is thrown when the connector needs to be repainted.
- * This is typically done when the <code>paint</code> method would return
- * dissimilar UIDL from the previous call of the method.
- */
- @SuppressWarnings("serial")
- public static class RepaintRequestEvent extends EventObject {
-
- /**
- * Constructs a new event.
- *
- * @param source
- * the paintable needing repaint.
- */
- public RepaintRequestEvent(ClientConnector source) {
- super(source);
- }
-
- /**
- * Gets the connector needing repainting.
- *
- * @return Paintable for which the <code>paint</code> method will return
- * dissimilar UIDL from the previous call of the method.
- */
- public ClientConnector getConnector() {
- return (ClientConnector) getSource();
- }
- }
-
- /**
- * Listens repaint requests. The <code>repaintRequested</code> method is
- * called when the paintable needs to be repainted. This is typically done
- * when the <code>paint</code> method would return dissimilar UIDL from the
- * previous call of the method.
- */
- public interface RepaintRequestListener extends Serializable {
-
- /**
- * Receives repaint request events.
- *
- * @param event
- * the repaint request event specifying the paintable source.
- */
- public void repaintRequested(RepaintRequestEvent event);
- }
-
- /**
- * Adds repaint request listener. In order to assure that no repaint
- * requests are missed, the new repaint listener should paint the paintable
- * right after adding itself as listener.
- *
- * @param listener
- * the listener to be added.
- */
- public void addListener(RepaintRequestListener listener);
-
- /**
- * Removes repaint request listener.
- *
- * @param listener
- * the listener to be removed.
- */
- public void removeListener(RepaintRequestListener listener);
-
/* Component event framework */
/**
diff --git a/src/com/vaadin/ui/ConnectorTracker.java b/src/com/vaadin/ui/ConnectorTracker.java
new file mode 100644
index 0000000000..75a75ad22a
--- /dev/null
+++ b/src/com/vaadin/ui/ConnectorTracker.java
@@ -0,0 +1,229 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.terminal.AbstractClientConnector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.server.ClientConnector;
+
+/**
+ * A class which takes care of book keeping of {@link ClientConnector}s for one
+ * Root.
+ * <p>
+ * Provides {@link #getConnector(String)} which can be used to lookup a
+ * connector from its id. This is for framework use only and should not be
+ * needed in applications.
+ * </p>
+ * <p>
+ * Tracks which {@link ClientConnector}s are dirty so they can be updated to the
+ * client when the following response is sent. A connector is dirty when an
+ * operation has been performed on it on the server and as a result of this
+ * operation new information needs to be sent to its {@link ServerConnector}.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public class ConnectorTracker implements Serializable {
+
+ private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>();
+ private Set<ClientConnector> dirtyConnectors = new HashSet<ClientConnector>();
+
+ private Root root;
+
+ /**
+ * Gets a logger for this class
+ *
+ * @return A logger instance for logging within this class
+ *
+ */
+ public static Logger getLogger() {
+ return Logger.getLogger(ConnectorTracker.class.getName());
+ }
+
+ public ConnectorTracker(Root root) {
+ this.root = root;
+ }
+
+ /**
+ * Register the given connector.
+ * <p>
+ * The lookup method {@link #getConnector(String)} only returns registered
+ * connectors.
+ * </p>
+ *
+ * @param connector
+ * The connector to register.
+ */
+ public void registerConnector(ClientConnector connector) {
+ String connectorId = connector.getConnectorId();
+ ClientConnector previouslyRegistered = connectorIdToConnector
+ .get(connectorId);
+ if (previouslyRegistered == null) {
+ connectorIdToConnector.put(connectorId, connector);
+ getLogger().fine(
+ "Registered " + connector.getClass().getSimpleName() + " ("
+ + connectorId + ")");
+ } else if (previouslyRegistered != connector) {
+ throw new RuntimeException("A connector with id " + connectorId
+ + " is already registered!");
+ } else {
+ getLogger().warning(
+ "An already registered connector was registered again: "
+ + connector.getClass().getSimpleName() + " ("
+ + connectorId + ")");
+ }
+
+ }
+
+ /**
+ * Unregister the given connector.
+ *
+ * <p>
+ * The lookup method {@link #getConnector(String)} only returns registered
+ * connectors.
+ * </p>
+ *
+ * @param connector
+ * The connector to unregister
+ */
+ public void unregisterConnector(ClientConnector connector) {
+ String connectorId = connector.getConnectorId();
+ if (!connectorIdToConnector.containsKey(connectorId)) {
+ getLogger().warning(
+ "Tried to unregister "
+ + connector.getClass().getSimpleName() + " ("
+ + connectorId + ") which is not registered");
+ return;
+ }
+ if (connectorIdToConnector.get(connectorId) != connector) {
+ throw new RuntimeException("The given connector with id "
+ + connectorId
+ + " is not the one that was registered for that id");
+ }
+
+ getLogger().fine(
+ "Unregistered " + connector.getClass().getSimpleName() + " ("
+ + connectorId + ")");
+ connectorIdToConnector.remove(connectorId);
+ }
+
+ /**
+ * Gets a connector by its id.
+ *
+ * @param connectorId
+ * The connector id to look for
+ * @return The connector with the given id or null if no connector has the
+ * given id
+ */
+ public ClientConnector getConnector(String connectorId) {
+ return connectorIdToConnector.get(connectorId);
+ }
+
+ /**
+ * Cleans the connector map from all connectors that are no longer attached
+ * to the application. This should only be called by the framework.
+ */
+ public void cleanConnectorMap() {
+ // remove detached components from paintableIdMap so they
+ // can be GC'ed
+ Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
+
+ while (iterator.hasNext()) {
+ String connectorId = iterator.next();
+ ClientConnector connector = connectorIdToConnector.get(connectorId);
+ if (connector instanceof Component) {
+ Component component = (Component) connector;
+ if (component.getRoot() != root) {
+ // If component is no longer part of this application,
+ // remove it from the map. If it is re-attached to the
+ // application at some point it will be re-added through
+ // registerConnector(connector)
+ iterator.remove();
+ }
+ }
+ }
+
+ }
+
+ public void markDirty(ClientConnector connector) {
+ if (getLogger().isLoggable(Level.FINE)) {
+ if (!dirtyConnectors.contains(connector)) {
+ getLogger()
+ .fine(getDebugInfo(connector) + " " + "is now dirty");
+ }
+ }
+
+ dirtyConnectors.add(connector);
+ }
+
+ public void markClean(ClientConnector connector) {
+ if (getLogger().isLoggable(Level.FINE)) {
+ if (dirtyConnectors.contains(connector)) {
+ getLogger().fine(
+ getDebugInfo(connector) + " " + "is no longer dirty");
+ }
+ }
+
+ dirtyConnectors.remove(connector);
+ }
+
+ private String getDebugInfo(ClientConnector connector) {
+ String message = getObjectString(connector);
+ if (connector.getParent() != null) {
+ message += " (parent: " + getObjectString(connector.getParent())
+ + ")";
+ }
+ return message;
+ }
+
+ private String getObjectString(Object connector) {
+ return connector.getClass().getName() + "@"
+ + Integer.toHexString(connector.hashCode());
+ }
+
+ public void markAllConnectorsDirty() {
+ markConnectorsDirtyRecursively(root);
+ getLogger().fine("All connectors are now dirty");
+ }
+
+ public void markAllConnectorsClean() {
+ dirtyConnectors.clear();
+ getLogger().fine("All connectors are now clean");
+ }
+
+ /**
+ * Marks all visible connectors dirty, starting from the given connector and
+ * going downwards in the hierarchy.
+ *
+ * @param c
+ * The component to start iterating downwards from
+ */
+ private void markConnectorsDirtyRecursively(ClientConnector c) {
+ if (c instanceof Component && !((Component) c).isVisible()) {
+ return;
+ }
+ markDirty(c);
+ for (ClientConnector child : AbstractClientConnector
+ .getAllChildrenIterable(c)) {
+ markConnectorsDirtyRecursively(child);
+ }
+ }
+
+ public Collection<ClientConnector> getDirtyConnectors() {
+ return dirtyConnectors;
+ }
+
+}
diff --git a/src/com/vaadin/ui/CssLayout.java b/src/com/vaadin/ui/CssLayout.java
index 0a2656af31..e8ec6bd041 100644
--- a/src/com/vaadin/ui/CssLayout.java
+++ b/src/com/vaadin/ui/CssLayout.java
@@ -11,9 +11,9 @@ import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutServerRpc;
import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutState;
-import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
/**
* CssLayout is a layout component that can be used in browser environment only.
@@ -185,7 +185,8 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier {
public void updateState() {
super.updateState();
getState().getChildCss().clear();
- for (Component child : this) {
+ for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) {
+ Component child = ci.next();
String componentCssString = getCss(child);
if (componentCssString != null) {
getState().getChildCss().put(child, componentCssString);
diff --git a/src/com/vaadin/ui/CustomField.java b/src/com/vaadin/ui/CustomField.java
index 806ee91335..0998c11612 100644
--- a/src/com/vaadin/ui/CustomField.java
+++ b/src/com/vaadin/ui/CustomField.java
@@ -62,24 +62,18 @@ public abstract class CustomField<T> extends AbstractField<T> implements
*/
@Override
public void attach() {
- root = getContent();
+ // First call super attach to notify all children (none if content has
+ // not yet been created)
super.attach();
- getContent().setParent(this);
- getContent().attach();
- fireComponentAttachEvent(getContent());
- }
-
- /**
- * Notifies the content that the {@link CustomField} is detached from a
- * window.
- *
- * @see com.vaadin.ui.Component#detach()
- */
- @Override
- public void detach() {
- super.detach();
- getContent().detach();
+ // If the content has not yet been created, we create and attach it at
+ // this point.
+ if (root == null) {
+ // Ensure content is created and its parent is set.
+ // The getContent() call creates the content and attaches the
+ // content
+ fireComponentAttachEvent(getContent());
+ }
}
/**
@@ -90,6 +84,7 @@ public abstract class CustomField<T> extends AbstractField<T> implements
protected Component getContent() {
if (null == root) {
root = initContent();
+ root.setParent(this);
}
return root;
}
@@ -154,10 +149,6 @@ public abstract class CustomField<T> extends AbstractField<T> implements
return (null != getContent()) ? 1 : 0;
}
- public void requestRepaintAll() {
- AbstractComponentContainer.requestRepaintAll(this);
- }
-
/**
* Fires the component attached event. This should be called by the
* addComponent methods after the component have been added to this
diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java
deleted file mode 100644
index 84df7e7c7c..0000000000
--- a/src/com/vaadin/ui/DirtyConnectorTracker.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.vaadin.terminal.gwt.server.AbstractCommunicationManager;
-import com.vaadin.terminal.gwt.server.ClientConnector;
-import com.vaadin.ui.Component.RepaintRequestEvent;
-import com.vaadin.ui.Component.RepaintRequestListener;
-
-/**
- * A class that tracks dirty {@link ClientConnector}s. A {@link ClientConnector}
- * is dirty when an operation has been performed on it on the server and as a
- * result of this operation new information needs to be sent to its client side
- * counterpart.
- *
- * @author Vaadin Ltd
- * @version @VERSION@
- * @since 7.0.0
- *
- */
-public class DirtyConnectorTracker implements RepaintRequestListener {
- private Set<Component> dirtyComponents = new HashSet<Component>();
- private Root root;
-
- /**
- * Gets a logger for this class
- *
- * @return A logger instance for logging within this class
- *
- */
- public static Logger getLogger() {
- return Logger.getLogger(DirtyConnectorTracker.class.getName());
- }
-
- public DirtyConnectorTracker(Root root) {
- this.root = root;
- }
-
- public void repaintRequested(RepaintRequestEvent event) {
- markDirty((Component) event.getConnector());
- }
-
- public void componentAttached(Component component) {
- component.addListener(this);
- markDirty(component);
- }
-
- private void markDirty(Component component) {
- if (getLogger().isLoggable(Level.FINE)) {
- if (!dirtyComponents.contains(component)) {
- getLogger()
- .fine(getDebugInfo(component) + " " + "is now dirty");
- }
- }
-
- dirtyComponents.add(component);
- }
-
- private void markClean(Component component) {
- if (getLogger().isLoggable(Level.FINE)) {
- if (dirtyComponents.contains(component)) {
- getLogger().fine(
- getDebugInfo(component) + " " + "is no longer dirty");
- }
- }
-
- dirtyComponents.remove(component);
- }
-
- private String getDebugInfo(Component component) {
- String message = getObjectString(component);
- if (component.getParent() != null) {
- message += " (parent: " + getObjectString(component.getParent())
- + ")";
- }
- return message;
- }
-
- private String getObjectString(Object component) {
- return component.getClass().getName() + "@"
- + Integer.toHexString(component.hashCode());
- }
-
- public void componentDetached(Component component) {
- component.removeListener(this);
- markClean(component);
- }
-
- public void markAllComponentsDirty() {
- markComponentsDirtyRecursively(root);
- getLogger().fine("All components are now dirty");
- }
-
- public void markAllComponentsClean() {
- dirtyComponents.clear();
- getLogger().fine("All components are now clean");
- }
-
- /**
- * Marks all visible components dirty, starting from the given component and
- * going downwards in the hierarchy.
- *
- * @param c
- * The component to start iterating downwards from
- */
- private void markComponentsDirtyRecursively(Component c) {
- if (!c.isVisible()) {
- return;
- }
- markDirty(c);
- if (c instanceof HasComponents) {
- HasComponents container = (HasComponents) c;
- for (Component child : AbstractCommunicationManager
- .getChildComponents(container)) {
- markComponentsDirtyRecursively(child);
- }
- }
-
- }
-
- public Collection<Component> getDirtyComponents() {
- return dirtyComponents;
- }
-
-}
diff --git a/src/com/vaadin/ui/Form.java b/src/com/vaadin/ui/Form.java
index 5f5516b21f..796ad17448 100644
--- a/src/com/vaadin/ui/Form.java
+++ b/src/com/vaadin/ui/Form.java
@@ -968,34 +968,6 @@ public class Form extends AbstractField<Object> implements Item.Editor,
}
/**
- * Notifies the component that it is connected to an application
- *
- * @see com.vaadin.ui.Component#attach()
- */
- @Override
- public void attach() {
- super.attach();
- getLayout().attach();
- if (getFooter() != null) {
- getFooter().attach();
- }
- }
-
- /**
- * Notifies the component that it is detached from the application.
- *
- * @see com.vaadin.ui.Component#detach()
- */
- @Override
- public void detach() {
- super.detach();
- getLayout().detach();
- if (getFooter() != null) {
- getFooter().detach();
- }
- }
-
- /**
* Checks the validity of the Form and all of its fields.
*
* @see com.vaadin.data.Validatable#validate()
@@ -1411,10 +1383,6 @@ public class Form extends AbstractField<Object> implements Item.Editor,
return new ComponentIterator();
}
- public void requestRepaintAll() {
- AbstractComponentContainer.requestRepaintAll(this);
- }
-
public int getComponentCount() {
int count = 0;
if (getLayout() != null) {
diff --git a/src/com/vaadin/ui/HasComponents.java b/src/com/vaadin/ui/HasComponents.java
index eca89ddcd2..3ebd63bff2 100644
--- a/src/com/vaadin/ui/HasComponents.java
+++ b/src/com/vaadin/ui/HasComponents.java
@@ -21,7 +21,10 @@ public interface HasComponents extends Component, Iterable<Component> {
* container.
*
* @return the component iterator.
+ *
+ * @deprecated Use {@link #iterator()} instead.
*/
+ @Deprecated
public Iterator<Component> getComponentIterator();
/**
@@ -43,12 +46,4 @@ public interface HasComponents extends Component, Iterable<Component> {
*/
public boolean isComponentVisible(Component childComponent);
- /**
- * Causes a repaint of this component, and all components below it.
- *
- * This should only be used in special cases, e.g when the state of a
- * descendant depends on the state of a ancestor.
- */
- public void requestRepaintAll();
-
}
diff --git a/src/com/vaadin/ui/HelloWorldExtension.java b/src/com/vaadin/ui/HelloWorldExtension.java
new file mode 100644
index 0000000000..e705954f2e
--- /dev/null
+++ b/src/com/vaadin/ui/HelloWorldExtension.java
@@ -0,0 +1,38 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import com.vaadin.terminal.AbstractExtension;
+import com.vaadin.terminal.gwt.client.ui.helloworldfeature.GreetAgainRpc;
+import com.vaadin.terminal.gwt.client.ui.helloworldfeature.HelloWorldRpc;
+import com.vaadin.terminal.gwt.client.ui.helloworldfeature.HelloWorldState;
+
+public class HelloWorldExtension extends AbstractExtension {
+
+ public HelloWorldExtension() {
+ registerRpc(new HelloWorldRpc() {
+ public void onMessageSent(String message) {
+ Notification.show(message);
+ }
+ });
+ }
+
+ @Override
+ public HelloWorldState getState() {
+ return (HelloWorldState) super.getState();
+ }
+
+ public void setGreeting(String greeting) {
+ getState().setGreeting(greeting);
+ requestRepaint();
+ }
+
+ public String getGreeting() {
+ return getState().getGreeting();
+ }
+
+ public void greetAgain() {
+ getRpcProxy(GreetAgainRpc.class).greetAgain();
+ }
+}
diff --git a/src/com/vaadin/ui/JavaScript.java b/src/com/vaadin/ui/JavaScript.java
new file mode 100644
index 0000000000..d256717711
--- /dev/null
+++ b/src/com/vaadin/ui/JavaScript.java
@@ -0,0 +1,146 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.terminal.AbstractExtension;
+import com.vaadin.terminal.Page;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.ExecuteJavaScriptRpc;
+import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavaScriptManagerState;
+
+/**
+ * Provides access to JavaScript functionality in the web browser. To get an
+ * instance of JavaScript, either use Page.getJavaScript() or
+ * JavaScript.getCurrent() as a shorthand for getting the JavaScript object
+ * corresponding to the current Page.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public class JavaScript extends AbstractExtension {
+ private Map<String, JavaScriptCallback> callbacks = new HashMap<String, JavaScriptCallback>();
+
+ // Can not be defined in client package as this JSONArray is not available
+ // in GWT
+ public interface JavaScriptCallbackRpc extends ServerRpc {
+ public void call(String name, JSONArray arguments);
+ }
+
+ /**
+ * Creates a new JavaScript object. You should typically not this, but
+ * instead use the JavaScript object already associated with your Page
+ * object.
+ */
+ public JavaScript() {
+ registerRpc(new JavaScriptCallbackRpc() {
+ public void call(String name, JSONArray arguments) {
+ JavaScriptCallback callback = callbacks.get(name);
+ // TODO handle situation if name is not registered
+ try {
+ callback.call(arguments);
+ } catch (JSONException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public JavaScriptManagerState getState() {
+ return (JavaScriptManagerState) super.getState();
+ }
+
+ /**
+ * Add a new function to the global JavaScript namespace (i.e. the window
+ * object). The <code>call</code> method in the passed
+ * {@link JavaScriptCallback} object will be invoked with the same
+ * parameters whenever the JavaScript function is called in the browser.
+ *
+ * A callback added with the name <code>"myCallback"</code> can thus be
+ * invoked with the following JavaScript code:
+ * <code>window.myCallback(argument1, argument2)</code>.
+ *
+ * If the name parameter contains dots, simple objects are created on demand
+ * to allow calling the function using the same name (e.g.
+ * <code>window.myObject.myFunction</code>).
+ *
+ * @param name
+ * the name that the callback function should get in the global
+ * JavaScript namespace.
+ * @param callback
+ * the JavaScriptCallback that will be invoked if the JavaScript
+ * function is called.
+ */
+ public void addCallback(String name, JavaScriptCallback callback) {
+ callbacks.put(name, callback);
+ if (getState().getNames().add(name)) {
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Removes a JavaScripCallback from the browser's global JavaScript
+ * namespace.
+ *
+ * If the name contains dots and intermediate were created by
+ * {@link #addCallback(String, JavaScriptCallback)}addCallback, these
+ * objects will not be removed when the callback is removed.
+ *
+ * @param name
+ * the name of the callback to remove
+ */
+ public void removeCallback(String name) {
+ callbacks.remove(name);
+ if (getState().getNames().remove(name)) {
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Executes the given JavaScript code in the browser.
+ *
+ * @param script
+ * The JavaScript code to run.
+ */
+ public void execute(String script) {
+ getRpcProxy(ExecuteJavaScriptRpc.class).executeJavaScript(script);
+ }
+
+ /**
+ * Get the JavaScript object for the current Page, or null if there is no
+ * current page.
+ *
+ * @see Page#getCurrent()
+ *
+ * @return the JavaScript object corresponding to the current Page, or
+ * <code>null</code> if there is no current page.
+ */
+ public static JavaScript getCurrent() {
+ Page page = Page.getCurrent();
+ if (page == null) {
+ return null;
+ }
+ return page.getJavaScript();
+ }
+
+ /**
+ * JavaScript is not designed to be removed.
+ *
+ * @throws UnsupportedOperationException
+ * when invoked
+ */
+ @Override
+ public void removeFromTarget() {
+ throw new UnsupportedOperationException(
+ "JavaScript is not designed to be removed.");
+ }
+
+}
diff --git a/src/com/vaadin/ui/JavaScriptCallback.java b/src/com/vaadin/ui/JavaScriptCallback.java
new file mode 100644
index 0000000000..49f7695e89
--- /dev/null
+++ b/src/com/vaadin/ui/JavaScriptCallback.java
@@ -0,0 +1,41 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.terminal.AbstractJavaScriptExtension;
+
+/**
+ * Defines a method that is called by a client-side JavaScript function. When
+ * the corresponding JavaScript function is called, the {@link #call(JSONArray)}
+ * method is invoked.
+ *
+ * @see JavaScript#addCallback(String, JavaScriptCallback)
+ * @see AbstractJavaScriptComponent#registerCallback(String, JavaScriptCallback)
+ * @see AbstractJavaScriptExtension#registerCallback(String, JavaScriptCallback)
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public interface JavaScriptCallback extends Serializable {
+ /**
+ * Invoked whenever the corresponding JavaScript function is called in the
+ * browser.
+ * <p>
+ * Because of the asynchronous nature of the communication between client
+ * and server, no return value can be sent back to the browser.
+ *
+ * @param arguments
+ * an array with JSON representations of the arguments with which
+ * the JavaScript function was called.
+ * @throws JSONException
+ * if the arguments can not be interpreted
+ */
+ public void call(JSONArray arguments) throws JSONException;
+}
diff --git a/src/com/vaadin/ui/Label.java b/src/com/vaadin/ui/Label.java
index 99a0f89e5c..e1c64605d7 100644
--- a/src/com/vaadin/ui/Label.java
+++ b/src/com/vaadin/ui/Label.java
@@ -7,7 +7,8 @@ package com.vaadin.ui;
import java.lang.reflect.Method;
import com.vaadin.data.Property;
-import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterUtil;
import com.vaadin.terminal.gwt.client.ui.label.ContentMode;
import com.vaadin.terminal.gwt.client.ui.label.LabelState;
@@ -36,10 +37,9 @@ import com.vaadin.terminal.gwt.client.ui.label.LabelState;
* @since 3.0
*/
@SuppressWarnings("serial")
-// TODO generics for interface Property
-public class Label extends AbstractComponent implements Property,
+public class Label extends AbstractComponent implements Property<String>,
Property.Viewer, Property.ValueChangeListener,
- Property.ValueChangeNotifier, Comparable<Object> {
+ Property.ValueChangeNotifier, Comparable<Label> {
/**
* @deprecated From 7.0, use {@link ContentMode#TEXT} instead
@@ -77,9 +77,13 @@ public class Label extends AbstractComponent implements Property,
@Deprecated
public static final ContentMode CONTENT_DEFAULT = ContentMode.TEXT;
- private static final String DATASOURCE_MUST_BE_SET = "Datasource must be set";
+ /**
+ * A converter used to convert from the data model type to the field type
+ * and vice versa. Label type is always String.
+ */
+ private Converter<String, Object> converter = null;
- private Property dataSource;
+ private Property<String> dataSource = null;
/**
* Creates an empty Label.
@@ -114,7 +118,9 @@ public class Label extends AbstractComponent implements Property,
* @param contentMode
*/
public Label(String content, ContentMode contentMode) {
- this(new ObjectProperty<String>(content, String.class), contentMode);
+ setValue(content);
+ setContentMode(contentMode);
+ setWidth(100, Unit.PERCENTAGE);
}
/**
@@ -127,15 +133,7 @@ public class Label extends AbstractComponent implements Property,
public Label(Property contentSource, ContentMode contentMode) {
setPropertyDataSource(contentSource);
setContentMode(contentMode);
- setWidth(100, UNITS_PERCENTAGE);
- }
-
- @Override
- public void updateState() {
- super.updateState();
- // We don't know when the text is updated so update it here before
- // sending the state to the client
- getState().setText(getStringValue());
+ setWidth(100, Unit.PERCENTAGE);
}
@Override
@@ -149,25 +147,35 @@ public class Label extends AbstractComponent implements Property,
*
* @return the Value of the label.
*/
- public Object getValue() {
- if (dataSource == null) {
- throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
+ public String getValue() {
+ if (getPropertyDataSource() == null) {
+ // Use internal value if we are running without a data source
+ return getState().getText();
}
- return dataSource.getValue();
+ return ConverterUtil.convertFromModel(getPropertyDataSource()
+ .getValue(), String.class, getConverter(), getLocale());
}
/**
* Set the value of the label. Value of the label is the XML contents of the
* label.
*
- * @param newValue
+ * @param newStringValue
* the New value of the label.
*/
- public void setValue(Object newValue) {
- if (dataSource == null) {
- throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
+ public void setValue(Object newStringValue) {
+ if (newStringValue != null && newStringValue.getClass() != String.class) {
+ throw new Converter.ConversionException("Value of type "
+ + newStringValue.getClass() + " cannot be assigned to "
+ + String.class.getName());
+ }
+ if (getPropertyDataSource() == null) {
+ getState().setText((String) newStringValue);
+ requestRepaint();
+ } else {
+ throw new IllegalStateException(
+ "Label is only a Property.Viewer and cannot update its data source");
}
- dataSource.setValue(newValue);
}
/**
@@ -179,26 +187,7 @@ public class Label extends AbstractComponent implements Property,
@Override
public String toString() {
throw new UnsupportedOperationException(
- "Use Property.getValue() instead of Label.toString()");
- }
-
- /**
- * Returns the value of the <code>Property</code> in human readable textual
- * format.
- *
- * This method exists to help migration from previous Vaadin versions by
- * providing a simple replacement for {@link #toString()}. However, it is
- * normally better to use the value of the label directly.
- *
- * @return String representation of the value stored in the Property
- * @since 7.0
- */
- public String getStringValue() {
- if (dataSource == null) {
- throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
- }
- Object value = dataSource.getValue();
- return (null != value) ? value.toString() : null;
+ "Use getValue() instead of Label.toString()");
}
/**
@@ -206,11 +195,8 @@ public class Label extends AbstractComponent implements Property,
*
* @see com.vaadin.data.Property#getType()
*/
- public Class getType() {
- if (dataSource == null) {
- throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
- }
- return dataSource.getType();
+ public Class<String> getType() {
+ return String.class;
}
/**
@@ -238,7 +224,13 @@ public class Label extends AbstractComponent implements Property,
((Property.ValueChangeNotifier) dataSource).removeListener(this);
}
- // Sets the new data source
+ if (!ConverterUtil.canConverterHandle(getConverter(), String.class,
+ newDataSource.getType())) {
+ // Try to find a converter
+ Converter<String, ?> c = ConverterUtil.getConverter(String.class,
+ newDataSource.getType(), getApplication());
+ setConverter(c);
+ }
dataSource = newDataSource;
// Listens the new data source if possible
@@ -354,7 +346,6 @@ public class Label extends AbstractComponent implements Property,
protected void fireValueChange() {
// Set the error message
fireEvent(new Label.ValueChangeEvent(this));
- requestRepaint();
}
/**
@@ -363,9 +354,28 @@ public class Label extends AbstractComponent implements Property,
* @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
*/
public void valueChange(Property.ValueChangeEvent event) {
+ // Update the internal value from the data source
+ getState().setText(getValue());
+ requestRepaint();
+
fireValueChange();
}
+ private String getComparableValue() {
+ String stringValue = getValue();
+ if (stringValue == null) {
+ stringValue = "";
+ }
+
+ if (getContentMode() == ContentMode.XHTML
+ || getContentMode() == ContentMode.XML) {
+ return stripTags(stringValue);
+ } else {
+ return stringValue;
+ }
+
+ }
+
/**
* Compares the Label to other objects.
*
@@ -387,27 +397,10 @@ public class Label extends AbstractComponent implements Property,
* less than, equal to, or greater than the specified object.
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
- public int compareTo(Object other) {
-
- String thisValue;
- String otherValue;
+ public int compareTo(Label other) {
- if (getContentMode() == ContentMode.XML
- || getContentMode() == ContentMode.XHTML) {
- thisValue = stripTags(getStringValue());
- } else {
- thisValue = getStringValue();
- }
-
- if (other instanceof Label
- && (((Label) other).getContentMode() == ContentMode.XML || ((Label) other)
- .getContentMode() == ContentMode.XHTML)) {
- otherValue = stripTags(((Label) other).getStringValue());
- } else {
- // TODO not a good idea - and might assume that Field.toString()
- // returns a string representation of the value
- otherValue = other.toString();
- }
+ String thisValue = getComparableValue();
+ String otherValue = other.getComparableValue();
return thisValue.compareTo(otherValue);
}
@@ -443,4 +436,26 @@ public class Label extends AbstractComponent implements Property,
return res.toString();
}
+ /**
+ * Gets the converter used to convert the property data source value to the
+ * label value.
+ *
+ * @return The converter or null if none is set.
+ */
+ public Converter<String, Object> getConverter() {
+ return converter;
+ }
+
+ /**
+ * Sets the converter used to convert the label value to the property data
+ * source type. The converter must have a presentation type of String.
+ *
+ * @param converter
+ * The new converter to use.
+ */
+ public void setConverter(Converter<String, ?> converter) {
+ this.converter = (Converter<String, Object>) converter;
+ requestRepaint();
+ }
+
}
diff --git a/src/com/vaadin/ui/Link.java b/src/com/vaadin/ui/Link.java
index ed5ffbba3a..db0dc58e6b 100644
--- a/src/com/vaadin/ui/Link.java
+++ b/src/com/vaadin/ui/Link.java
@@ -6,6 +6,7 @@ package com.vaadin.ui;
import java.util.Map;
+import com.vaadin.terminal.Page;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
@@ -23,13 +24,13 @@ import com.vaadin.terminal.Vaadin6Component;
public class Link extends AbstractComponent implements Vaadin6Component {
/* Target window border type constant: No window border */
- public static final int TARGET_BORDER_NONE = Root.BORDER_NONE;
+ public static final int TARGET_BORDER_NONE = Page.BORDER_NONE;
/* Target window border type constant: Minimal window border */
- public static final int TARGET_BORDER_MINIMAL = Root.BORDER_MINIMAL;
+ public static final int TARGET_BORDER_MINIMAL = Page.BORDER_MINIMAL;
/* Target window border type constant: Default window border */
- public static final int TARGET_BORDER_DEFAULT = Root.BORDER_DEFAULT;
+ public static final int TARGET_BORDER_DEFAULT = Page.BORDER_DEFAULT;
private Resource resource = null;
diff --git a/src/com/vaadin/ui/Notification.java b/src/com/vaadin/ui/Notification.java
index bb1f874635..0358283cb4 100644
--- a/src/com/vaadin/ui/Notification.java
+++ b/src/com/vaadin/ui/Notification.java
@@ -6,6 +6,7 @@ package com.vaadin.ui;
import java.io.Serializable;
+import com.vaadin.terminal.Page;
import com.vaadin.terminal.Resource;
/**
@@ -318,4 +319,53 @@ public class Notification implements Serializable {
public boolean isHtmlContentAllowed() {
return htmlContentAllowed;
}
+
+ /**
+ * Shows this notification on a Page.
+ *
+ * @param page
+ * The page on which the notification should be shown
+ */
+ public void show(Page page) {
+ // TODO Can avoid deprecated API when Notification extends Extension
+ page.showNotification(this);
+ }
+
+ /**
+ * Shows a notification message on the middle of the current page. The
+ * message automatically disappears ("humanized message").
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is
+ * rendered as html.
+ *
+ * @see #Notification(String)
+ * @see #show(Page)
+ *
+ * @param caption
+ * The message
+ */
+ public static void show(String caption) {
+ new Notification(caption).show(Page.getCurrent());
+ }
+
+ /**
+ * Shows a notification message the current page. The position and behavior
+ * of the message depends on the type, which is one of the basic types
+ * defined in {@link Notification}, for instance
+ * Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is
+ * rendered as html.
+ *
+ * @see #Notification(String, int)
+ * @see #show(Page)
+ *
+ * @param caption
+ * The message
+ * @param type
+ * The message type
+ */
+ public static void show(String caption, int type) {
+ new Notification(caption, type).show(Page.getCurrent());
+ }
} \ No newline at end of file
diff --git a/src/com/vaadin/ui/Panel.java b/src/com/vaadin/ui/Panel.java
index b2916f78c7..c339100cda 100644
--- a/src/com/vaadin/ui/Panel.java
+++ b/src/com/vaadin/ui/Panel.java
@@ -4,6 +4,7 @@
package com.vaadin.ui;
+import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
@@ -164,6 +165,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
.addListener((ComponentContainer.ComponentDetachListener) this);
content = newContent;
+ requestRepaint();
}
/**
@@ -192,15 +194,6 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
}
}
- @Override
- public void requestRepaintAll() {
- // Panel has odd structure, delegate to layout
- requestRepaint();
- if (getContent() != null) {
- getContent().requestRepaintAll();
- }
- }
-
/**
* Adds the component into this container.
*
@@ -237,7 +230,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* @see com.vaadin.ui.ComponentContainer#getComponentIterator()
*/
public Iterator<Component> getComponentIterator() {
- return content.getComponentIterator();
+ return Collections.singleton((Component) content).iterator();
}
/**
@@ -354,35 +347,6 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
}
/**
- * Notifies the component that it is connected to an application.
- *
- * @see com.vaadin.ui.Component#attach()
- */
- @Override
- public void attach() {
- getRoot().componentAttached(this);
- // can't call parent here as this is Panels hierarchy is a hack
- requestRepaint();
- if (content != null) {
- content.attach();
- }
- }
-
- /**
- * Notifies the component that it is detached from the application.
- *
- * @see com.vaadin.ui.Component#detach()
- */
- @Override
- public void detach() {
- // can't call parent here as this is Panels hierarchy is a hack
- if (content != null) {
- content.detach();
- }
- getRoot().componentDetached(this);
- }
-
- /**
* Removes all components from this container.
*
* @see com.vaadin.ui.ComponentContainer#removeAllComponents()
diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java
index 93b98693c2..7ae687be79 100644
--- a/src/com/vaadin/ui/Root.java
+++ b/src/com/vaadin/ui/Root.java
@@ -4,8 +4,6 @@
package com.vaadin.ui;
-import java.io.Serializable;
-import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -13,8 +11,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import com.vaadin.Application;
@@ -24,6 +20,9 @@ import com.vaadin.event.Action.Handler;
import com.vaadin.event.ActionManager;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.MouseEvents.ClickListener;
+import com.vaadin.terminal.Page;
+import com.vaadin.terminal.Page.BrowserWindowResizeEvent;
+import com.vaadin.terminal.Page.BrowserWindowResizeListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
@@ -31,11 +30,9 @@ import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.WrappedRequest.BrowserDetails;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
import com.vaadin.terminal.gwt.client.ui.root.RootServerRpc;
import com.vaadin.terminal.gwt.client.ui.root.RootState;
import com.vaadin.terminal.gwt.client.ui.root.VRoot;
-import com.vaadin.tools.ReflectTools;
import com.vaadin.ui.Window.CloseListener;
/**
@@ -79,121 +76,6 @@ public abstract class Root extends AbstractComponentContainer implements
Action.Container, Action.Notifier, Vaadin6Component {
/**
- * 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 Component.Event {
-
- 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(Root source, int width, int height) {
- super(source);
- this.width = width;
- this.height = height;
- }
-
- @Override
- public Root getSource() {
- return (Root) 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 static final Method BROWSWER_RESIZE_METHOD = ReflectTools
- .findMethod(BrowserWindowResizeListener.class,
- "browserWindowResized", BrowserWindowResizeEvent.class);
-
- /**
- * Listener that listens changes in URI fragment.
- */
- public interface FragmentChangedListener extends Serializable {
- public void fragmentChanged(FragmentChangedEvent event);
- }
-
- /**
- * Event fired when uri fragment changes.
- */
- public class FragmentChangedEvent extends Component.Event {
-
- /**
- * 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(Root 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 Root getRoot() {
- return (Root) getComponent();
- }
-
- /**
- * Get the new fragment
- *
- * @return the new fragment
- */
- public String getFragment() {
- return fragment;
- }
- }
-
- /**
* Helper class to emulate the main window from Vaadin 6 using roots. This
* class should be used in the same way as Window used as a browser level
* window in Vaadin 6 with {@link com.vaadin.Application.LegacyApplication}
@@ -312,55 +194,191 @@ public abstract class Root extends AbstractComponentContainer implements
"Internal problem getting window URL, please report");
}
}
- }
- private static final Method FRAGMENT_CHANGED_METHOD;
-
- static {
- try {
- FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
- .getDeclaredMethod("fragmentChanged",
- new Class[] { FragmentChangedEvent.class });
- } catch (final java.lang.NoSuchMethodException e) {
- // This should never happen
- throw new java.lang.RuntimeException(
- "Internal error finding methods in FragmentChangedListener");
+ /**
+ * 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
+ *
+ * @deprecated As of 7.0, use getPage().open instead
+ */
+ @Deprecated
+ public void open(Resource resource) {
+ getPage().open(resource);
+ }
+
+ /* ********************************************************************* */
+
+ /**
+ * 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.
+ * @deprecated As of 7.0, use getPage().open instead
+ */
+ @Deprecated
+ public void open(Resource resource, String windowName) {
+ getPage().open(resource, windowName);
}
- }
- /**
- * A border style used for opening resources in a window without a border.
- */
- public static final int BORDER_NONE = 0;
+ /**
+ * 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}
+ * @deprecated As of 7.0, use getPage().open instead
+ */
+ @Deprecated
+ public void open(Resource resource, String windowName, int width,
+ int height, int border) {
+ getPage().open(resource, windowName, width, height, border);
+ }
- /**
- * A border style used for opening resources in a window with a minimal
- * border.
- */
- public static final int BORDER_MINIMAL = 1;
+ /**
+ * 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)
+ *
+ * @deprecated As of 7.0, use the similarly named api in Page instead
+ */
+ @Deprecated
+ public void addListener(BrowserWindowResizeListener resizeListener) {
+ getPage().addListener(resizeListener);
+ }
- /**
- * A border style that indicates that the default border style should be
- * used when opening resources.
- */
- public static final int BORDER_DEFAULT = 2;
+ /**
+ * 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
+ * @deprecated As of 7.0, use the similarly named api in Page instead
+ */
+ @Deprecated
+ public void removeListener(BrowserWindowResizeListener resizeListener) {
+ getPage().removeListener(resizeListener);
+ }
- /**
- * The application to which this root belongs
- */
- private Application application;
+ /**
+ * Gets the last known height of the browser window in which this root
+ * resides.
+ *
+ * @return the browser window height in pixels
+ * @deprecated As of 7.0, use the similarly named api in Page instead
+ */
+ @Deprecated
+ public int getBrowserWindowHeight() {
+ return getPage().getBrowserWindowHeight();
+ }
- /**
- * 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;
+ /**
+ * Gets the last known width of the browser window in which this root
+ * resides.
+ *
+ * @return the browser window width in pixels
+ *
+ * @deprecated As of 7.0, use the similarly named api in Page instead
+ */
+ @Deprecated
+ public int getBrowserWindowWidth() {
+ return getPage().getBrowserWindowWidth();
+ }
+
+ /**
+ * Executes JavaScript in this window.
+ *
+ * <p>
+ * This method allows one to inject javascript from the server to
+ * client. A client implementation is not required to implement this
+ * functionality, but currently all web-based clients do implement this.
+ * </p>
+ *
+ * <p>
+ * Executing javascript this way often leads to cross-browser
+ * compatibility issues and regressions that are hard to resolve. Use of
+ * this method should be avoided and instead it is recommended to create
+ * new widgets with GWT. For more info on creating own, reusable
+ * client-side widgets in Java, read the corresponding chapter in Book
+ * of Vaadin.
+ * </p>
+ *
+ * @param script
+ * JavaScript snippet that will be executed.
+ *
+ * @deprecated as of 7.0, use JavaScript.getCurrent().execute(String)
+ * instead
+ */
+ @Deprecated
+ public void executeJavaScript(String script) {
+ getPage().getJavaScript().execute(script);
+ }
+
+ @Override
+ public void setCaption(String caption) {
+ // Override to provide backwards compatibility
+ getState().setCaption(caption);
+ getPage().setTitle(caption);
+ }
+
+ }
/**
- * A list of javascript commands that are waiting to be sent to the client.
- * Cleared (set to null) when the commands have been sent.
+ * The application to which this root belongs
*/
- private List<String> jsExecQueue = null;
+ private Application application;
/**
* List of windows in this root.
@@ -368,12 +386,6 @@ public abstract class Root extends AbstractComponentContainer implements
private final LinkedHashSet<Window> windows = new LinkedHashSet<Window>();
/**
- * 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>();
-
- /**
* The component that should be scrolled into view after the next repaint.
* Null if nothing should be scrolled into view.
*/
@@ -399,14 +411,12 @@ public abstract class Root extends AbstractComponentContainer implements
*/
private static final ThreadLocal<Root> currentRoot = new ThreadLocal<Root>();
- private int browserWindowWidth = -1;
- private int browserWindowHeight = -1;
-
/** Identifies the click event */
private static final String CLICK_EVENT_ID = VRoot.CLICK_EVENT_ID;
- private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
- this);
+ private ConnectorTracker connectorTracker = new ConnectorTracker(this);
+
+ private Page page = new Page(this);
private RootServerRpc rpc = new RootServerRpc() {
public void click(MouseEventDetails mouseDetails) {
@@ -502,68 +512,7 @@ public abstract class Root extends AbstractComponentContainer implements
}
public void paintContent(PaintTarget target) throws PaintException {
- // Open requested resource
- synchronized (openList) {
- 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;
- }
-
- // Add executable javascripts if needed
- if (jsExecQueue != null) {
- for (String script : jsExecQueue) {
- target.startTag("execJS");
- target.addAttribute("script", script);
- target.endTag("execJS");
- }
- jsExecQueue = null;
- }
+ page.paintContent(target);
if (scrollIntoView != null) {
target.addAttribute("scrollTo", scrollIntoView);
@@ -584,10 +533,6 @@ public abstract class Root extends AbstractComponentContainer implements
actionManager.paintActions(null, target);
}
- if (fragment != null) {
- target.addAttribute(VRoot.FRAGMENT_VARIABLE, fragment);
- }
-
if (isResizeLazy()) {
target.addAttribute(VRoot.RESIZE_LAZY, true);
}
@@ -618,23 +563,14 @@ public abstract class Root extends AbstractComponentContainer implements
if (variables.containsKey(VRoot.FRAGMENT_VARIABLE)) {
String fragment = (String) variables.get(VRoot.FRAGMENT_VARIABLE);
- setFragment(fragment, true);
+ getPage().setFragment(fragment, true);
}
- boolean sendResizeEvent = false;
- if (variables.containsKey("height")) {
- browserWindowHeight = ((Integer) variables.get("height"))
- .intValue();
- sendResizeEvent = true;
- }
- if (variables.containsKey("width")) {
- browserWindowWidth = ((Integer) variables.get("width")).intValue();
- sendResizeEvent = true;
- }
- if (sendResizeEvent) {
- fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
- browserWindowHeight));
+ if (variables.containsKey("height") || variables.containsKey("width")) {
+ getPage().setBrowserWindowSize((Integer) variables.get("width"),
+ (Integer) variables.get("height"));
}
+
}
/*
@@ -643,11 +579,17 @@ public abstract class Root extends AbstractComponentContainer implements
* @see com.vaadin.ui.ComponentContainer#getComponentIterator()
*/
public Iterator<Component> getComponentIterator() {
- if (getContent() == null) {
- return Collections.EMPTY_LIST.iterator();
+ // TODO could directly create some kind of combined iterator instead of
+ // creating a new ArrayList
+ ArrayList<Component> components = new ArrayList<Component>();
+
+ if (getContent() != null) {
+ components.add(getContent());
}
- return Collections.singleton((Component) getContent()).iterator();
+ components.addAll(windows);
+
+ return components.iterator();
}
/*
@@ -656,7 +598,7 @@ public abstract class Root extends AbstractComponentContainer implements
* @see com.vaadin.ui.ComponentContainer#getComponentCount()
*/
public int getComponentCount() {
- return getContent() == null ? 0 : 1;
+ return windows.size() + (getContent() == null ? 0 : 1);
}
/**
@@ -809,11 +751,6 @@ public abstract class Root extends AbstractComponentContainer implements
*/
private Focusable pendingFocus;
- /**
- * The current URI fragment.
- */
- private String fragment;
-
private boolean resizeLazy = false;
/**
@@ -835,175 +772,6 @@ public abstract class Root extends AbstractComponentContainer implements
}
/**
- * Shows a notification message on the middle of the root. The message
- * automatically disappears ("humanized message").
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption is
- * rendered as html.
- *
- * @see #showNotification(Notification)
- * @see Notification
- *
- * @param caption
- * The message
- */
- public void showNotification(String caption) {
- addNotification(new Notification(caption));
- }
-
- /**
- * Shows a notification message the root. The position and behavior of the
- * message depends on the type, which is one of the basic types defined in
- * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption is
- * rendered as html.
- *
- * @see #showNotification(Notification)
- * @see Notification
- *
- * @param caption
- * The message
- * @param type
- * The message type
- */
- public void showNotification(String caption, int type) {
- addNotification(new Notification(caption, type));
- }
-
- /**
- * Shows a notification consisting of a bigger caption and a smaller
- * description on the middle of the root. The message automatically
- * disappears ("humanized message").
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption and
- * description are rendered as html.
- *
- * @see #showNotification(Notification)
- * @see Notification
- *
- * @param caption
- * The caption of the message
- * @param description
- * The message description
- *
- */
- public void showNotification(String caption, String description) {
- addNotification(new Notification(caption, description));
- }
-
- /**
- * Shows a notification consisting of a bigger caption and a smaller
- * description. The position and behavior of the message depends on the
- * type, which is one of the basic types defined in {@link Notification},
- * for instance Notification.TYPE_WARNING_MESSAGE.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption and
- * description are rendered as html.
- *
- * @see #showNotification(Notification)
- * @see Notification
- *
- * @param caption
- * The caption of the message
- * @param description
- * The message description
- * @param type
- * The message type
- */
- public void showNotification(String caption, String description, int type) {
- addNotification(new Notification(caption, description, type));
- }
-
- /**
- * Shows a notification consisting of a bigger caption and a smaller
- * description. The position and behavior of the message depends on the
- * type, which is one of the basic types defined in {@link Notification},
- * for instance Notification.TYPE_WARNING_MESSAGE.
- *
- * Care should be taken to avoid XSS vulnerabilities if html content is
- * allowed.
- *
- * @see #showNotification(Notification)
- * @see Notification
- *
- * @param caption
- * The message caption
- * @param description
- * The message description
- * @param type
- * The type of message
- * @param htmlContentAllowed
- * Whether html in the caption and description should be
- * displayed as html or as plain text
- */
- public void showNotification(String caption, String description, int type,
- boolean htmlContentAllowed) {
- addNotification(new Notification(caption, description, type,
- htmlContentAllowed));
- }
-
- /**
- * Shows a notification message.
- *
- * @see Notification
- * @see #showNotification(String)
- * @see #showNotification(String, int)
- * @see #showNotification(String, String)
- * @see #showNotification(String, String, int)
- *
- * @param notification
- * The notification message to show
- */
- public void showNotification(Notification notification) {
- addNotification(notification);
- }
-
- /**
- * 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);
- requestRepaint();
- }
-
- /**
- * Executes JavaScript in this root.
- *
- * <p>
- * This method allows one to inject javascript from the server to client. A
- * client implementation is not required to implement this functionality,
- * but currently all web-based clients do implement this.
- * </p>
- *
- * <p>
- * Executing javascript this way often leads to cross-browser compatibility
- * issues and regressions that are hard to resolve. Use of this method
- * should be avoided and instead it is recommended to create new widgets
- * with GWT. For more info on creating own, reusable client-side widgets in
- * Java, read the corresponding chapter in Book of Vaadin.
- * </p>
- *
- * @param script
- * JavaScript snippet that will be executed.
- */
- public void executeJavaScript(String script) {
- if (jsExecQueue == null) {
- jsExecQueue = new ArrayList<String>();
- }
-
- jsExecQueue.add(script);
-
- requestRepaint();
- }
-
- /**
* Scrolls any component between the component and root to a suitable
* position so the component is visible to the user. The given component
* must belong to this root.
@@ -1072,6 +840,8 @@ public abstract class Root extends AbstractComponentContainer implements
if (content != null) {
super.addComponent(content);
}
+
+ requestRepaint();
}
/**
@@ -1115,10 +885,7 @@ public abstract class Root extends AbstractComponentContainer implements
* the initialization request
*/
public void doInit(WrappedRequest request) {
- BrowserDetails browserDetails = request.getBrowserDetails();
- if (browserDetails != null) {
- fragment = browserDetails.getUriFragment();
- }
+ getPage().init(request);
// Call the init overridden by the application developer
init(request);
@@ -1156,10 +923,10 @@ public abstract class Root extends AbstractComponentContainer implements
* @param root
* the root to register as the current root
*
- * @see #getCurrentRoot()
+ * @see #getCurrent()
* @see ThreadLocal
*/
- public static void setCurrentRoot(Root root) {
+ public static void setCurrent(Root root) {
currentRoot.set(root);
}
@@ -1171,192 +938,12 @@ public abstract class Root extends AbstractComponentContainer implements
* @return the current root instance if available, otherwise
* <code>null</code>
*
- * @see #setCurrentRoot(Root)
+ * @see #setCurrent(Root)
*/
- public static Root getCurrentRoot() {
+ public static Root getCurrent() {
return currentRoot.get();
}
- /**
- * 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) {
- synchronized (openList) {
- if (!openList.contains(resource)) {
- openList.add(new OpenResource(resource, null, -1, -1,
- BORDER_DEFAULT));
- }
- }
- 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) {
- synchronized (openList) {
- if (!openList.contains(resource)) {
- openList.add(new OpenResource(resource, windowName, -1, -1,
- BORDER_DEFAULT));
- }
- }
- 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) {
- synchronized (openList) {
- if (!openList.contains(resource)) {
- openList.add(new OpenResource(resource, windowName, width,
- height, border));
- }
- }
- requestRepaint();
- }
-
- /**
- * 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");
- }
- }
-
public void setScrollTop(int scrollTop) {
throw new RuntimeException("Not yet implemented");
}
@@ -1444,150 +1031,176 @@ public abstract class Root extends AbstractComponentContainer implements
removeListener(CLICK_EVENT_ID, ClickEvent.class, listener);
}
- public void addListener(FragmentChangedListener listener) {
- addListener(FragmentChangedEvent.class, listener,
- FRAGMENT_CHANGED_METHOD);
+ @Override
+ public boolean isConnectorEnabled() {
+ // TODO How can a Root be invisible? What does it mean?
+ return isVisible() && isEnabled();
}
- public void removeListener(FragmentChangedListener listener) {
- removeListener(FragmentChangedEvent.class, listener,
- FRAGMENT_CHANGED_METHOD);
+ public ConnectorTracker getConnectorTracker() {
+ return connectorTracker;
}
- /**
- * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
- *
- * @param newFragment
- * id of the new fragment
- * @param fireEvent
- * true to fire event
- * @see FragmentChangedEvent
- * @see 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));
- }
- requestRepaint();
- }
+ public Page getPage() {
+ return page;
}
/**
- * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
+ * Setting the caption of a Root is not supported. To set the title of the
+ * HTML page, use Page.setTitle
*
- * @param newFragment
- * id of the new fragment
- * @see FragmentChangedEvent
- * @see FragmentChangedListener
+ * @deprecated as of 7.0.0, use {@link Page#setTitle(String)}
*/
- public void setFragment(String newFragment) {
- setFragment(newFragment, true);
+ @Override
+ @Deprecated
+ public void setCaption(String caption) {
+ throw new IllegalStateException(
+ "You can not set the title of a Root. To set the title of the HTML page, use Page.setTitle");
}
/**
- * Gets currently set URI fragment.
- * <p>
- * To listen changes in fragment, hook a {@link FragmentChangedListener}.
+ * Shows a notification message on the middle of the root. The message
+ * automatically disappears ("humanized message").
*
- * @return the current fragment in browser uri or null if not known
- */
- public String getFragment() {
- return fragment;
- }
-
- /**
- * Adds a new {@link BrowserWindowResizeListener} to this root. The listener
- * will be notified whenever the browser window within which this root
- * resides is resized.
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is
+ * rendered as html.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
*
- * @param resizeListener
- * the listener to add
+ * @param caption
+ * The message
*
- * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent)
- * @see #setResizeLazy(boolean)
+ * @deprecated As of 7.0, use Notification.show instead
*/
- public void addListener(BrowserWindowResizeListener resizeListener) {
- addListener(BrowserWindowResizeEvent.class, resizeListener,
- BROWSWER_RESIZE_METHOD);
+ @Deprecated
+ public void showNotification(String caption) {
+ getPage().showNotification(new Notification(caption));
}
/**
- * Removes a {@link BrowserWindowResizeListener} from this root. The
- * listener will no longer be notified when the browser window is resized.
+ * Shows a notification message the root. The position and behavior of the
+ * message depends on the type, which is one of the basic types defined in
+ * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is
+ * rendered as html.
*
- * @param resizeListener
- * the listener to remove
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The message
+ * @param type
+ * The message type
+ *
+ * @deprecated As of 7.0, use Notification.show instead
*/
- public void removeListener(BrowserWindowResizeListener resizeListener) {
- removeListener(BrowserWindowResizeEvent.class, resizeListener,
- BROWSWER_RESIZE_METHOD);
+ @Deprecated
+ public void showNotification(String caption, int type) {
+ getPage().showNotification(new Notification(caption, type));
}
/**
- * Gets the last known height of the browser window in which this root
- * resides.
+ * Shows a notification consisting of a bigger caption and a smaller
+ * description on the middle of the root. The message automatically
+ * disappears ("humanized message").
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption and
+ * description are rendered as html.
*
- * @return the browser window height in pixels
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The caption of the message
+ * @param description
+ * The message description
+ *
+ * @deprecated As of 7.0, use Notification.show instead
*/
- public int getBrowserWindowHeight() {
- return browserWindowHeight;
+ @Deprecated
+ public void showNotification(String caption, String description) {
+ getPage().showNotification(new Notification(caption, description));
}
/**
- * Gets the last known width of the browser window in which this root
- * resides.
+ * Shows a notification consisting of a bigger caption and a smaller
+ * description. The position and behavior of the message depends on the
+ * type, which is one of the basic types defined in {@link Notification} ,
+ * for instance Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption and
+ * description are rendered as html.
*
- * @return the browser window width in pixels
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The caption of the message
+ * @param description
+ * The message description
+ * @param type
+ * The message type
+ *
+ * @deprecated As of 7.0, use Notification.show instead
*/
- public int getBrowserWindowWidth() {
- return browserWindowWidth;
+ @Deprecated
+ public void showNotification(String caption, String description, int type) {
+ getPage()
+ .showNotification(new Notification(caption, description, type));
}
/**
- * Notifies the child components and windows that the root is attached to
- * the application.
+ * Shows a notification consisting of a bigger caption and a smaller
+ * description. The position and behavior of the message depends on the
+ * type, which is one of the basic types defined in {@link Notification} ,
+ * for instance Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to avoid XSS vulnerabilities if html content is
+ * allowed.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The message caption
+ * @param description
+ * The message description
+ * @param type
+ * The type of message
+ * @param htmlContentAllowed
+ * Whether html in the caption and description should be
+ * displayed as html or as plain text
+ *
+ * @deprecated As of 7.0, use Notification.show instead
*/
- @Override
- public void attach() {
- super.attach();
- for (Window w : windows) {
- w.attach();
- }
+ @Deprecated
+ public void showNotification(String caption, String description, int type,
+ boolean htmlContentAllowed) {
+ getPage()
+ .showNotification(
+ new Notification(caption, description, type,
+ htmlContentAllowed));
}
/**
- * Notifies the child components and windows that the root is detached from
- * the application.
+ * Shows a notification message.
+ *
+ * @see Notification
+ * @see #showNotification(String)
+ * @see #showNotification(String, int)
+ * @see #showNotification(String, String)
+ * @see #showNotification(String, String, int)
+ *
+ * @param notification
+ * The notification message to show
+ *
+ * @deprecated As of 7.0, use Notification.show instead
*/
- @Override
- public void detach() {
- super.detach();
- for (Window w : windows) {
- w.detach();
- }
- }
-
- @Override
- public boolean isConnectorEnabled() {
- // TODO How can a Root be invisible? What does it mean?
- return isVisible() && isEnabled();
- }
-
- public DirtyConnectorTracker getDirtyConnectorTracker() {
- return dirtyConnectorTracker;
- }
-
- public void componentAttached(Component component) {
- getDirtyConnectorTracker().componentAttached(component);
- }
-
- public void componentDetached(Component component) {
- getDirtyConnectorTracker().componentDetached(component);
+ @Deprecated
+ public void showNotification(Notification notification) {
+ getPage().showNotification(notification);
}
}
diff --git a/src/com/vaadin/ui/TabSheet.java b/src/com/vaadin/ui/TabSheet.java
index 23dee15359..061809de67 100644
--- a/src/com/vaadin/ui/TabSheet.java
+++ b/src/com/vaadin/ui/TabSheet.java
@@ -108,6 +108,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
setWidth(100, UNITS_PERCENTAGE);
setImmediate(true);
setCloseHandler(new CloseHandler() {
+
public void onTabClose(TabSheet tabsheet, Component c) {
tabsheet.removeComponent(c);
}
@@ -120,6 +121,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
*
* @return the unmodifiable Iterator of the tab content components
*/
+
public Iterator<Component> getComponentIterator() {
return Collections.unmodifiableList(components).iterator();
}
@@ -130,6 +132,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
*
* @return the number of contained components
*/
+
public int getComponentCount() {
return components.size();
}
@@ -143,6 +146,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @param c
* the component to be removed.
*/
+
@Override
public void removeComponent(Component c) {
if (c != null && components.contains(c)) {
@@ -193,6 +197,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @param c
* the component to be added.
*/
+
@Override
public void addComponent(Component c) {
addTab(c);
@@ -334,6 +339,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @param source
* the container components are removed from.
*/
+
@Override
public void moveComponentsFrom(ComponentContainer source) {
for (final Iterator<Component> i = source.getComponentIterator(); i
@@ -359,6 +365,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @throws PaintException
* if the paint operation failed.
*/
+
public void paintContent(PaintTarget target) throws PaintException {
if (areTabsHidden()) {
@@ -683,6 +690,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
}
// inherits javadoc
+
public void changeVariables(Object source, Map<String, Object> variables) {
if (variables.containsKey("selected")) {
setSelectedTab(keyMapper.get((String) variables.get("selected")));
@@ -719,6 +727,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
*
* {@inheritDoc}
*/
+
public void replaceComponent(Component oldComponent, Component newComponent) {
if (selected == oldComponent) {
@@ -729,25 +738,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
Tab newTab = tabs.get(newComponent);
Tab oldTab = tabs.get(oldComponent);
- // Gets the captions
- String oldCaption = null;
- Resource oldIcon = null;
- String newCaption = null;
- Resource newIcon = null;
-
- if (oldTab != null) {
- oldCaption = oldTab.getCaption();
- oldIcon = oldTab.getIcon();
- }
-
- if (newTab != null) {
- newCaption = newTab.getCaption();
- newIcon = newTab.getIcon();
- } else {
- newCaption = newComponent.getCaption();
- newIcon = newComponent.getIcon();
- }
-
// Gets the locations
int oldLocation = -1;
int newLocation = -1;
@@ -769,35 +759,21 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
addComponent(newComponent);
} else if (newLocation == -1) {
removeComponent(oldComponent);
- keyMapper.remove(oldComponent);
- newTab = addTab(newComponent);
- components.remove(newComponent);
- components.add(oldLocation, newComponent);
- newTab.setCaption(oldCaption);
- newTab.setIcon(oldIcon);
+ newTab = addTab(newComponent, oldLocation);
+ // Copy all relevant metadata to the new tab (#8793)
+ // TODO Should reuse the old tab instance instead?
+ copyTabMetadata(oldTab, newTab);
} else {
- if (oldLocation > newLocation) {
- components.remove(oldComponent);
- components.add(newLocation, oldComponent);
- components.remove(newComponent);
- components.add(oldLocation, newComponent);
- } else {
- components.remove(newComponent);
- components.add(oldLocation, newComponent);
- components.remove(oldComponent);
- components.add(newLocation, oldComponent);
- }
+ components.set(oldLocation, newComponent);
+ components.set(newLocation, oldComponent);
- if (newTab != null) {
- // This should always be true
- newTab.setCaption(oldCaption);
- newTab.setIcon(oldIcon);
- }
- if (oldTab != null) {
- // This should always be true
- oldTab.setCaption(newCaption);
- oldTab.setIcon(newIcon);
- }
+ // Tab associations are not changed, but metadata is swapped between
+ // the instances
+ // TODO Should reassociate the instances instead?
+ Tab tmp = new TabSheetTabImpl(null, null);
+ copyTabMetadata(newTab, tmp);
+ copyTabMetadata(oldTab, newTab);
+ copyTabMetadata(tmp, oldTab);
requestRepaint();
}
@@ -1106,6 +1082,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
/**
* Returns the tab caption. Can never be null.
*/
+
public String getCaption() {
return caption;
}
@@ -1300,4 +1277,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
public boolean isComponentVisible(Component childComponent) {
return childComponent == getSelectedTab();
}
+
+ /**
+ * Copies properties from one Tab to another.
+ *
+ * @param from
+ * The tab whose data to copy.
+ * @param to
+ * The tab to which copy the data.
+ */
+ private static void copyTabMetadata(Tab from, Tab to) {
+ to.setCaption(from.getCaption());
+ to.setIcon(from.getIcon());
+ to.setDescription(from.getDescription());
+ to.setVisible(from.isVisible());
+ to.setEnabled(from.isEnabled());
+ to.setClosable(from.isClosable());
+ to.setStyleName(from.getStyleName());
+ to.setComponentError(from.getComponentError());
+ }
}
diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java
index ba4c6529c5..a1cc4f95fe 100644
--- a/src/com/vaadin/ui/Table.java
+++ b/src/com/vaadin/ui/Table.java
@@ -21,13 +21,13 @@ import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.vaadin.Application;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.ContainerOrderedWrapper;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterUtil;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.DataBoundTransferable;
@@ -39,7 +39,6 @@ import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DragSource;
import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.DropTarget;
-import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
import com.vaadin.terminal.KeyMapper;
import com.vaadin.terminal.LegacyPaint;
@@ -47,7 +46,6 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
import com.vaadin.terminal.gwt.client.ui.table.VScrollTable;
/**
@@ -78,8 +76,7 @@ public class Table extends AbstractSelect implements Action.Container,
Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource,
DropTarget, HasComponents {
- private static final Logger logger = Logger
- .getLogger(Table.class.getName());
+ private transient Logger logger = null;
/**
* Modes that Table support as drag sourse.
@@ -331,7 +328,8 @@ public class Table extends AbstractSelect implements Action.Container,
private static final double CACHE_RATE_DEFAULT = 2;
private static final String ROW_HEADER_COLUMN_KEY = "0";
- private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new Object();
+ private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() {
+ };
/* Private table extensions to Select */
@@ -356,6 +354,11 @@ public class Table extends AbstractSelect implements Action.Container,
private LinkedList<Object> visibleColumns = new LinkedList<Object>();
/**
+ * Holds noncollapsible columns.
+ */
+ private HashSet<Object> noncollapsibleColumns = new HashSet<Object>();
+
+ /**
* Holds propertyIds of currently collapsed columns.
*/
private final HashSet<Object> collapsedColumns = new HashSet<Object>();
@@ -473,10 +476,9 @@ public class Table extends AbstractSelect implements Action.Container,
private Object sortContainerPropertyId = null;
/**
- * Is table sorting disabled alltogether; even if some of the properties
- * would be sortable.
+ * Is table sorting by the user enabled.
*/
- private boolean sortDisabled = false;
+ private boolean sortEnabled = true;
/**
* Number of rows explicitly requested by the client to be painted on next
@@ -1248,6 +1250,9 @@ public class Table extends AbstractSelect implements Action.Container,
if (!isColumnCollapsingAllowed()) {
throw new IllegalStateException("Column collapsing not allowed!");
}
+ if (collapsed && noncollapsibleColumns.contains(propertyId)) {
+ throw new IllegalStateException("The column is noncollapsible!");
+ }
if (collapsed) {
collapsedColumns.add(propertyId);
@@ -1287,6 +1292,41 @@ public class Table extends AbstractSelect implements Action.Container,
}
/**
+ * Sets whether the given column is collapsible. Note that collapsible
+ * columns can only be actually collapsed (via UI or with
+ * {@link #setColumnCollapsed(Object, boolean) setColumnCollapsed()}) if
+ * {@link #isColumnCollapsingAllowed()} is true. By default all columns are
+ * collapsible.
+ *
+ * @param propertyId
+ * the propertyID identifying the column.
+ * @param collapsible
+ * true if the column should be collapsible, false otherwise.
+ */
+ public void setColumnCollapsible(Object propertyId, boolean collapsible) {
+ if (collapsible) {
+ noncollapsibleColumns.remove(propertyId);
+ } else {
+ noncollapsibleColumns.add(propertyId);
+ collapsedColumns.remove(propertyId);
+ }
+ refreshRowCache();
+ }
+
+ /**
+ * Checks if the given column is collapsible. Note that even if this method
+ * returns <code>true</code>, the column can only be actually collapsed (via
+ * UI or with {@link #setColumnCollapsed(Object, boolean)
+ * setColumnCollapsed()}) if {@link #isColumnCollapsingAllowed()} is also
+ * true.
+ *
+ * @return true if the column can be collapsed; false otherwise.
+ */
+ public boolean isColumnCollapsible(Object propertyId) {
+ return !noncollapsibleColumns.contains(propertyId);
+ }
+
+ /**
* Checks if column reordering is allowed.
*
* @return true if columns can be reordered; false otherwise.
@@ -1569,6 +1609,13 @@ public class Table extends AbstractSelect implements Action.Container,
}
} else {
// initial load
+
+ // #8805 send one extra row in the beginning in case a partial
+ // row is shown on the UI
+ if (firstIndex > 0) {
+ firstIndex = firstIndex - 1;
+ rows = rows + 1;
+ }
firstToBeRenderedInClient = firstIndex;
}
if (totalRows > 0) {
@@ -1597,12 +1644,21 @@ public class Table extends AbstractSelect implements Action.Container,
* this method has been called. See {@link #refreshRowCache()} for forcing
* an update of the contents.
*/
+
@Override
public void requestRepaint() {
// Overridden only for javadoc
super.requestRepaint();
}
+ @Override
+ public void requestRepaintAll() {
+ super.requestRepaintAll();
+
+ // Avoid sending a partial repaint (#8714)
+ refreshRowCache();
+ }
+
private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) {
int totalCachedRows = pageBuffer[CELL_ITEMID].length;
int totalRows = size();
@@ -1706,8 +1762,9 @@ public class Table extends AbstractSelect implements Action.Container,
* @return
*/
private Object[][] getVisibleCellsInsertIntoCache(int firstIndex, int rows) {
- logger.finest("Insert " + rows + " rows at index " + firstIndex
- + " to existing page buffer requested");
+ getLogger().finest(
+ "Insert " + rows + " rows at index " + firstIndex
+ + " to existing page buffer requested");
// Page buffer must not become larger than pageLength*cacheRate before
// or after the current page
@@ -1810,11 +1867,14 @@ public class Table extends AbstractSelect implements Action.Container,
}
}
pageBuffer = newPageBuffer;
- logger.finest("Page Buffer now contains "
- + pageBuffer[CELL_ITEMID].length + " rows ("
- + pageBufferFirstIndex + "-"
- + (pageBufferFirstIndex + pageBuffer[CELL_ITEMID].length - 1)
- + ")");
+ getLogger().finest(
+ "Page Buffer now contains "
+ + pageBuffer[CELL_ITEMID].length
+ + " rows ("
+ + pageBufferFirstIndex
+ + "-"
+ + (pageBufferFirstIndex
+ + pageBuffer[CELL_ITEMID].length - 1) + ")");
return cells;
}
@@ -1831,8 +1891,9 @@ public class Table extends AbstractSelect implements Action.Container,
*/
private Object[][] getVisibleCellsNoCache(int firstIndex, int rows,
boolean replaceListeners) {
- logger.finest("Render visible cells for rows " + firstIndex + "-"
- + (firstIndex + rows - 1));
+ getLogger().finest(
+ "Render visible cells for rows " + firstIndex + "-"
+ + (firstIndex + rows - 1));
final Object[] colids = getVisibleColumns();
final int cols = colids.length;
@@ -2014,8 +2075,9 @@ public class Table extends AbstractSelect implements Action.Container,
}
protected void registerComponent(Component component) {
- logger.finest("Registered " + component.getClass().getSimpleName()
- + ": " + component.getCaption());
+ getLogger().finest(
+ "Registered " + component.getClass().getSimpleName() + ": "
+ + component.getCaption());
if (component.getParent() != this) {
component.setParent(this);
}
@@ -2046,8 +2108,9 @@ public class Table extends AbstractSelect implements Action.Container,
* @param count
*/
private void unregisterComponentsAndPropertiesInRows(int firstIx, int count) {
- logger.finest("Unregistering components in rows " + firstIx + "-"
- + (firstIx + count - 1));
+ getLogger().finest(
+ "Unregistering components in rows " + firstIx + "-"
+ + (firstIx + count - 1));
Object[] colids = getVisibleColumns();
if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
int bufSize = pageBuffer[CELL_ITEMID].length;
@@ -2127,8 +2190,9 @@ public class Table extends AbstractSelect implements Action.Container,
* a set of components that should be unregistered.
*/
protected void unregisterComponent(Component component) {
- logger.finest("Unregistered " + component.getClass().getSimpleName()
- + ": " + component.getCaption());
+ getLogger().finest(
+ "Unregistered " + component.getClass().getSimpleName() + ": "
+ + component.getCaption());
component.setParent(null);
/*
* Also remove property data sources to unregister listeners keeping the
@@ -2438,6 +2502,7 @@ public class Table extends AbstractSelect implements Action.Container,
* @see com.vaadin.ui.Select#changeVariables(java.lang.Object,
* java.util.Map)
*/
+
@Override
public void changeVariables(Object source, Map<String, Object> variables) {
@@ -2497,7 +2562,7 @@ public class Table extends AbstractSelect implements Action.Container,
.get("lastToBeRendered")).intValue();
} catch (Exception e) {
// FIXME: Handle exception
- logger.log(Level.FINER,
+ getLogger().log(Level.FINER,
"Could not parse the first and/or last rows.", e);
}
@@ -2517,12 +2582,13 @@ public class Table extends AbstractSelect implements Action.Container,
}
}
}
- logger.finest("Client wants rows " + reqFirstRowToPaint + "-"
- + (reqFirstRowToPaint + reqRowsToPaint - 1));
+ getLogger().finest(
+ "Client wants rows " + reqFirstRowToPaint + "-"
+ + (reqFirstRowToPaint + reqRowsToPaint - 1));
clientNeedsContentRefresh = true;
}
- if (!sortDisabled) {
+ if (isSortEnabled()) {
// Sorting
boolean doSort = false;
if (variables.containsKey("sortcolumn")) {
@@ -2564,7 +2630,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
} catch (final Exception e) {
// FIXME: Handle exception
- logger.log(Level.FINER,
+ getLogger().log(Level.FINER,
"Could not determine column collapsing state", e);
}
clientNeedsContentRefresh = true;
@@ -2586,7 +2652,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
} catch (final Exception e) {
// FIXME: Handle exception
- logger.log(Level.FINER,
+ getLogger().log(Level.FINER,
"Could not determine column reordering state", e);
}
clientNeedsContentRefresh = true;
@@ -2759,6 +2825,7 @@ public class Table extends AbstractSelect implements Action.Container,
* @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin.
* terminal.PaintTarget)
*/
+
@Override
public void paintContent(PaintTarget target) throws PaintException {
/*
@@ -2876,8 +2943,9 @@ public class Table extends AbstractSelect implements Action.Container,
target.startTag("prows");
if (!shouldHideAddedRows()) {
- logger.finest("Paint rows for add. Index: " + firstIx + ", count: "
- + count + ".");
+ getLogger().finest(
+ "Paint rows for add. Index: " + firstIx + ", count: "
+ + count + ".");
// Partial row additions bypass the normal caching mechanism.
Object[][] cells = getVisibleCellsInsertIntoCache(firstIx, count);
@@ -2900,8 +2968,9 @@ public class Table extends AbstractSelect implements Action.Container,
indexInRowbuffer, itemId);
}
} else {
- logger.finest("Paint rows for remove. Index: " + firstIx
- + ", count: " + count + ".");
+ getLogger().finest(
+ "Paint rows for remove. Index: " + firstIx + ", count: "
+ + count + ".");
removeRowsFromCacheAndFillBottom(firstIx, count);
target.addAttribute("hide", true);
}
@@ -3100,7 +3169,17 @@ public class Table extends AbstractSelect implements Action.Container,
}
}
target.addVariable(this, "collapsedcolumns", collapsedKeys);
+
+ final String[] noncollapsibleKeys = new String[noncollapsibleColumns
+ .size()];
+ nextColumn = 0;
+ for (Object colId : noncollapsibleColumns) {
+ noncollapsibleKeys[nextColumn++] = columnIdMap.key(colId);
+ }
+ target.addVariable(this, "noncollapsiblecolumns",
+ noncollapsibleKeys);
}
+
}
private void paintActions(PaintTarget target, final Set<Action> actionSet)
@@ -3584,12 +3663,8 @@ public class Table extends AbstractSelect implements Action.Container,
if (hasConverter(colId)) {
converter = getConverter(colId);
} else {
- Application app = Application.getCurrentApplication();
- if (app != null) {
- converter = (Converter<String, Object>) app
- .getConverterFactory().createConverter(String.class,
- property.getType());
- }
+ ConverterUtil.getConverter(String.class, property.getType(),
+ getApplication());
}
Object value = property.getValue();
if (converter != null) {
@@ -3605,6 +3680,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
*/
+
public void addActionHandler(Action.Handler actionHandler) {
if (actionHandler != null) {
@@ -3631,6 +3707,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
*/
+
public void removeActionHandler(Action.Handler actionHandler) {
if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
@@ -3670,6 +3747,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
*/
+
@Override
public void valueChange(Property.ValueChangeEvent event) {
if (event.getProperty() == this
@@ -3700,18 +3778,12 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.ui.Component#attach()
*/
+
@Override
public void attach() {
super.attach();
refreshRenderedCells();
-
- if (visibleComponents != null) {
- for (final Iterator<Component> i = visibleComponents.iterator(); i
- .hasNext();) {
- i.next().attach();
- }
- }
}
/**
@@ -3719,16 +3791,10 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.ui.Component#detach()
*/
+
@Override
public void detach() {
super.detach();
-
- if (visibleComponents != null) {
- for (final Iterator<Component> i = visibleComponents.iterator(); i
- .hasNext();) {
- i.next().detach();
- }
- }
}
/**
@@ -3736,6 +3802,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container#removeAllItems()
*/
+
@Override
public boolean removeAllItems() {
currentPageFirstItemId = null;
@@ -3748,6 +3815,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container#removeItem(Object)
*/
+
@Override
public boolean removeItem(Object itemId) {
final Object nextItemId = nextItemId(itemId);
@@ -3766,6 +3834,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container#removeContainerProperty(Object)
*/
+
@Override
public boolean removeContainerProperty(Object propertyId)
throws UnsupportedOperationException {
@@ -3792,6 +3861,7 @@ public class Table extends AbstractSelect implements Action.Container,
* @see com.vaadin.data.Container#addContainerProperty(Object, Class,
* Object)
*/
+
@Override
public boolean addContainerProperty(Object propertyId, Class<?> type,
Object defaultValue) throws UnsupportedOperationException {
@@ -3947,6 +4017,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.ui.Select#getVisibleItemIds()
*/
+
@Override
public Collection<?> getVisibleItemIds() {
@@ -3970,6 +4041,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
*/
+
@Override
public void containerItemSetChange(Container.ItemSetChangeEvent event) {
super.containerItemSetChange(event);
@@ -3986,6 +4058,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
*/
+
@Override
public void containerPropertySetChange(
Container.PropertySetChangeEvent event) {
@@ -4029,6 +4102,7 @@ public class Table extends AbstractSelect implements Action.Container,
* if set to true.
* @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
*/
+
@Override
public void setNewItemsAllowed(boolean allowNewOptions)
throws UnsupportedOperationException {
@@ -4042,6 +4116,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
*/
+
public Object nextItemId(Object itemId) {
return ((Container.Ordered) items).nextItemId(itemId);
}
@@ -4052,6 +4127,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
*/
+
public Object prevItemId(Object itemId) {
return ((Container.Ordered) items).prevItemId(itemId);
}
@@ -4061,6 +4137,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#firstItemId()
*/
+
public Object firstItemId() {
return ((Container.Ordered) items).firstItemId();
}
@@ -4070,6 +4147,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#lastItemId()
*/
+
public Object lastItemId() {
return ((Container.Ordered) items).lastItemId();
}
@@ -4080,6 +4158,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
*/
+
public boolean isFirstId(Object itemId) {
return ((Container.Ordered) items).isFirstId(itemId);
}
@@ -4090,6 +4169,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
*/
+
public boolean isLastId(Object itemId) {
return ((Container.Ordered) items).isLastId(itemId);
}
@@ -4099,6 +4179,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
*/
+
public Object addItemAfter(Object previousItemId)
throws UnsupportedOperationException {
Object itemId = ((Container.Ordered) items)
@@ -4115,6 +4196,7 @@ public class Table extends AbstractSelect implements Action.Container,
* @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
* java.lang.Object)
*/
+
public Item addItemAfter(Object previousItemId, Object newItemId)
throws UnsupportedOperationException {
Item item = ((Container.Ordered) items).addItemAfter(previousItemId,
@@ -4207,6 +4289,7 @@ public class Table extends AbstractSelect implements Action.Container,
* boolean[])
*
*/
+
public void sort(Object[] propertyId, boolean[] ascending)
throws UnsupportedOperationException {
final Container c = getContainerDataSource();
@@ -4239,15 +4322,21 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Gets the container property IDs, which can be used to sort the item.
+ * <p>
+ * Note that the {@link #isSortEnabled()} state affects what this method
+ * returns. Disabling sorting causes this method to always return an empty
+ * collection.
+ * </p>
*
* @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
*/
+
public Collection<?> getSortableContainerPropertyIds() {
final Container c = getContainerDataSource();
- if (c instanceof Container.Sortable && !isSortDisabled()) {
+ if (c instanceof Container.Sortable && isSortEnabled()) {
return ((Container.Sortable) c).getSortableContainerPropertyIds();
} else {
- return new LinkedList<Object>();
+ return Collections.EMPTY_LIST;
}
}
@@ -4338,37 +4427,48 @@ public class Table extends AbstractSelect implements Action.Container,
* would support this.
*
* @return True iff sorting is disabled.
+ * @deprecated Use {@link #isSortEnabled()} instead
*/
+ @Deprecated
public boolean isSortDisabled() {
- return sortDisabled;
+ return !isSortEnabled();
}
/**
- * Disables the sorting altogether.
+ * Checks if sorting is enabled.
*
- * To disable sorting altogether, set to true. In this case no sortable
- * columns are given even in the case where datasource would support this.
+ * @return true if sorting by the user is allowed, false otherwise
+ */
+ public boolean isSortEnabled() {
+ return sortEnabled;
+ }
+
+ /**
+ * Disables the sorting by the user altogether.
*
* @param sortDisabled
* True iff sorting is disabled.
+ * @deprecated Use {@link #setSortEnabled(boolean)} instead
*/
+ @Deprecated
public void setSortDisabled(boolean sortDisabled) {
- if (this.sortDisabled != sortDisabled) {
- this.sortDisabled = sortDisabled;
- requestRepaint();
- }
+ setSortEnabled(!sortDisabled);
}
/**
- * Table does not support lazy options loading mode. Setting this true will
- * throw UnsupportedOperationException.
+ * Enables or disables sorting.
+ * <p>
+ * Setting this to false disallows sorting by the user. It is still possible
+ * to call {@link #sort()}.
+ * </p>
*
- * @see com.vaadin.ui.Select#setLazyLoading(boolean)
+ * @param sortEnabled
+ * true to allow the user to sort the table, false to disallow it
*/
- public void setLazyLoading(boolean useLazyLoading) {
- if (useLazyLoading) {
- throw new UnsupportedOperationException(
- "Lazy options loading is not supported by Table.");
+ public void setSortEnabled(boolean sortEnabled) {
+ if (this.sortEnabled != sortEnabled) {
+ this.sortEnabled = sortEnabled;
+ requestRepaint();
}
}
@@ -4455,6 +4555,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
// Identical to AbstractCompoenentContainer.setEnabled();
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -4466,10 +4567,6 @@ public class Table extends AbstractSelect implements Action.Container,
}
}
- public void requestRepaintAll() {
- AbstractComponentContainer.requestRepaintAll(this);
- }
-
/**
* Sets the drag start mode of the Table. Drag start mode controls how Table
* behaves as a drag source.
@@ -4583,7 +4680,6 @@ public class Table extends AbstractSelect implements Action.Container,
* initialized from server and no subsequent requests requests are needed
* during that drag and drop operation.
*/
- @ClientCriterion(VLazyInitItemIdentifiers.class)
public static abstract class TableDropCriterion extends ServerSideCriterion {
private Table table;
@@ -4597,6 +4693,7 @@ public class Table extends AbstractSelect implements Action.Container,
* com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier
* ()
*/
+
@Override
protected String getIdentifier() {
return TableDropCriterion.class.getCanonicalName();
@@ -4628,6 +4725,7 @@ public class Table extends AbstractSelect implements Action.Container,
* com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse(
* com.vaadin.terminal.PaintTarget)
*/
+
@Override
public void paintResponse(PaintTarget target) throws PaintException {
/*
@@ -5299,4 +5397,11 @@ public class Table extends AbstractSelect implements Action.Container,
public boolean isComponentVisible(Component childComponent) {
return true;
}
+
+ private final Logger getLogger() {
+ if (logger == null) {
+ logger = Logger.getLogger(Table.class.getName());
+ }
+ return logger;
+ }
}
diff --git a/src/com/vaadin/ui/Tree.java b/src/com/vaadin/ui/Tree.java
index db738fee58..dacb3a2027 100644
--- a/src/com/vaadin/ui/Tree.java
+++ b/src/com/vaadin/ui/Tree.java
@@ -34,7 +34,6 @@ import com.vaadin.event.dd.DragSource;
import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.DropTarget;
import com.vaadin.event.dd.TargetDetails;
-import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
import com.vaadin.event.dd.acceptcriteria.TargetDetailIs;
@@ -43,8 +42,6 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
-import com.vaadin.terminal.gwt.client.ui.dd.VTargetInSubtree;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
import com.vaadin.terminal.gwt.client.ui.tree.TreeConnector;
import com.vaadin.terminal.gwt.client.ui.tree.VTree;
@@ -1396,7 +1393,6 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
* initialized from server and no subsequent requests requests are needed
* during that drag and drop operation.
*/
- @ClientCriterion(VLazyInitItemIdentifiers.class)
public static abstract class TreeDropCriterion extends ServerSideCriterion {
private Tree tree;
@@ -1513,7 +1509,6 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
* <p>
* The root items is also consider to be valid target.
*/
- @ClientCriterion(VTargetInSubtree.class)
public class TargetInSubtree extends ClientSideCriterion {
private Object rootId;
diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java
index 9607add2c9..3294f6fab0 100644
--- a/src/com/vaadin/ui/TreeTable.java
+++ b/src/com/vaadin/ui/TreeTable.java
@@ -49,9 +49,6 @@ import com.vaadin.ui.Tree.ExpandListener;
@SuppressWarnings({ "serial" })
public class TreeTable extends Table implements Hierarchical {
- private static final Logger logger = Logger.getLogger(TreeTable.class
- .getName());
-
private interface ContainerStrategy extends Serializable {
public int size();
@@ -84,6 +81,7 @@ public class TreeTable extends Table implements Hierarchical {
* Consider adding getDepth to {@link Collapsible}, might help
* scalability with some container implementations.
*/
+
public int getDepth(Object itemId) {
int depth = 0;
Hierarchical hierarchicalContainer = getContainerDataSource();
@@ -222,9 +220,9 @@ public class TreeTable extends Table implements Hierarchical {
boolean removed = openItems.remove(itemId);
if (!removed) {
openItems.add(itemId);
- logger.finest("Item " + itemId + " is now expanded");
+ getLogger().finest("Item " + itemId + " is now expanded");
} else {
- logger.finest("Item " + itemId + " is now collapsed");
+ getLogger().finest("Item " + itemId + " is now collapsed");
}
clearPreorderCache();
}
@@ -789,4 +787,8 @@ public class TreeTable extends Table implements Hierarchical {
requestRepaint();
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(TreeTable.class.getName());
+ }
+
}
diff --git a/src/com/vaadin/ui/UniqueSerializable.java b/src/com/vaadin/ui/UniqueSerializable.java
new file mode 100644
index 0000000000..828b285538
--- /dev/null
+++ b/src/com/vaadin/ui/UniqueSerializable.java
@@ -0,0 +1,30 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import java.io.Serializable;
+
+/**
+ * A base class for generating an unique object that is serializable.
+ * <p>
+ * This class is abstract but has no abstract methods to force users to create
+ * an anonymous inner class. Otherwise each instance will not be unique.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0
+ *
+ */
+public abstract class UniqueSerializable implements Serializable {
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return getClass() == obj.getClass();
+ }
+}
diff --git a/src/com/vaadin/ui/Window.java b/src/com/vaadin/ui/Window.java
index 3c17baf414..8866362587 100644
--- a/src/com/vaadin/ui/Window.java
+++ b/src/com/vaadin/ui/Window.java
@@ -8,7 +8,6 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
-import com.vaadin.Application;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
@@ -24,47 +23,26 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.root.VRoot;
import com.vaadin.terminal.gwt.client.ui.window.WindowServerRpc;
import com.vaadin.terminal.gwt.client.ui.window.WindowState;
/**
- * A component that represents an application (browser native) window or a sub
- * window.
+ * A component that represents a floating popup window that can be added to a
+ * {@link Root}. A window is added to a {@code Root} using
+ * {@link Root#addWindow(Window)}. </p>
* <p>
- * If the window is a application window or a sub window depends on how it is
- * added to the application. Adding a {@code Window} to a {@code Window} using
- * {@link Window#addWindow(Window)} makes it a sub window and adding a
- * {@code Window} to the {@code Application} using
- * {@link Application#addWindow(Window)} makes it an application window.
+ * The contents of a window is set using {@link #setContent(ComponentContainer)}
+ * or by using the {@link #Window(String, ComponentContainer)} constructor. The
+ * contents can in turn contain other components. By default, a
+ * {@link VerticalLayout} is used as content.
* </p>
* <p>
- * An application window is the base of any view in a Vaadin application. All
- * applications contain a main application window (set using
- * {@link Application#setMainWindow(Window)} which is what is initially shown to
- * the user. The contents of a window is set using
- * {@link #setContent(ComponentContainer)}. The contents can in turn contain
- * other components. For multi-tab applications there is one window instance per
- * opened tab.
+ * A window can be positioned on the screen using absolute coordinates (pixels)
+ * or set to be centered using {@link #center()}
* </p>
* <p>
- * A sub window is floating popup style window that can be added to an
- * application window. Like the application window its content is set using
- * {@link #setContent(ComponentContainer)}. A sub window can be positioned on
- * the screen using absolute coordinates (pixels). The default content of the
- * Window is set to be suitable for application windows. For sub windows it
- * might be necessary to set the size of the content to work as expected.
- * </p>
- * <p>
- * Window caption is displayed in the browser title bar for application level
- * windows and in the window header for sub windows.
- * </p>
- * <p>
- * Certain methods in this class are only meaningful for sub windows and other
- * parts only for application windows. These are marked using <b>Sub window
- * only</b> and <b>Application window only</b> respectively in the javadoc.
- * </p>
- * <p>
- * Sub window is to be split into a separate component in Vaadin 7.
+ * The caption is displayed in the window header.
* </p>
*
* @author Vaadin Ltd.
@@ -83,6 +61,10 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
}
};
+ private int browserWindowWidth = -1;
+
+ private int browserWindowHeight = -1;
+
/**
* Creates a new unnamed window with a default layout.
*/
@@ -119,6 +101,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
*
* @see com.vaadin.ui.Panel#addComponent(com.vaadin.ui.Component)
*/
+
@Override
public void addComponent(Component c) {
if (c instanceof Window) {
@@ -136,6 +119,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
*
* @see com.vaadin.ui.Panel#paintContent(com.vaadin.terminal.PaintTarget)
*/
+
@Override
public synchronized void paintContent(PaintTarget target)
throws PaintException {
@@ -153,6 +137,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
*
* @see com.vaadin.ui.Panel#changeVariables(java.lang.Object, java.util.Map)
*/
+
@Override
public void changeVariables(Object source, Map<String, Object> variables) {
@@ -161,15 +146,29 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
// size is handled in super class, but resize events only in windows ->
// so detect if size change occurs before super.changeVariables()
if (variables.containsKey("height")
- && (getHeightUnits() != UNITS_PIXELS || (Integer) variables
+ && (getHeightUnits() != Unit.PIXELS || (Integer) variables
.get("height") != getHeight())) {
sizeHasChanged = true;
}
if (variables.containsKey("width")
- && (getWidthUnits() != UNITS_PIXELS || (Integer) variables
+ && (getWidthUnits() != Unit.PIXELS || (Integer) variables
.get("width") != getWidth())) {
sizeHasChanged = true;
}
+ Integer browserHeightVar = (Integer) variables
+ .get(VRoot.BROWSER_HEIGHT_VAR);
+ if (browserHeightVar != null
+ && browserHeightVar.intValue() != browserWindowHeight) {
+ browserWindowHeight = browserHeightVar.intValue();
+ sizeHasChanged = true;
+ }
+ Integer browserWidthVar = (Integer) variables
+ .get(VRoot.BROWSER_WIDTH_VAR);
+ if (browserWidthVar != null
+ && browserWidthVar.intValue() != browserWindowWidth) {
+ browserWindowWidth = browserWidthVar.intValue();
+ sizeHasChanged = true;
+ }
super.changeVariables(source, variables);
@@ -604,8 +603,13 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
}
/**
- * Request to center this window on the screen. <b>Note:</b> affects
- * sub-windows only.
+ * Sets this window to be centered relative to its parent window. Affects
+ * sub-windows only. If the window is resized as a result of the size of its
+ * content changing, it will keep itself centered as long as its position is
+ * not explicitly changed programmatically or by the user.
+ * <p>
+ * <b>NOTE:</b> This method has several issues as currently implemented.
+ * Please refer to http://dev.vaadin.com/ticket/8971 for details.
*/
public void center() {
getState().setCentered(true);
@@ -788,6 +792,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
*
* @see com.vaadin.event.FieldEvents.FocusNotifier#addListener(com.vaadin.event.FieldEvents.FocusListener)
*/
+
public void addListener(FocusListener listener) {
addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
FocusListener.focusMethod);
@@ -804,6 +809,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
*
* @see com.vaadin.event.FieldEvents.BlurNotifier#addListener(com.vaadin.event.FieldEvents.BlurListener)
*/
+
public void addListener(BlurListener listener) {
addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
BlurListener.blurMethod);
@@ -819,6 +825,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
* If the window is a sub-window focusing will cause the sub-window to be
* brought on top of other sub-windows on gain keyboard focus.
*/
+
@Override
public void focus() {
/*
@@ -834,5 +841,4 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
public WindowState getState() {
return (WindowState) super.getState();
}
-
}
diff --git a/src/com/vaadin/ui/themes/ChameleonTheme.java b/src/com/vaadin/ui/themes/ChameleonTheme.java
index bfb9686018..5ae8cd4e57 100644
--- a/src/com/vaadin/ui/themes/ChameleonTheme.java
+++ b/src/com/vaadin/ui/themes/ChameleonTheme.java
@@ -5,7 +5,7 @@ package com.vaadin.ui.themes;
public class ChameleonTheme extends BaseTheme {
- public static final String THEME_NAME = "Chameleon";
+ public static final String THEME_NAME = "chameleon";
/***************************************************************************
* Label styles