aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/vaadin/Application.java53
-rw-r--r--src/com/vaadin/data/util/converter/ConverterUtil.java165
-rw-r--r--src/com/vaadin/navigator/Navigator.java34
-rw-r--r--src/com/vaadin/terminal/AbstractClientConnector.java7
-rw-r--r--src/com/vaadin/terminal/AbstractErrorMessage.java7
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManager.java36
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java43
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/label/ContentMode.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java15
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java88
-rw-r--r--src/com/vaadin/terminal/gwt/server/CommunicationManager.java18
-rw-r--r--src/com/vaadin/terminal/gwt/server/Constants.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonCodec.java148
-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/ui/AbstractField.java84
-rw-r--r--src/com/vaadin/ui/Component.java29
-rw-r--r--src/com/vaadin/ui/ConnectorTracker.java229
-rw-r--r--src/com/vaadin/ui/CustomField.java24
-rw-r--r--src/com/vaadin/ui/DirtyConnectorTracker.java113
-rw-r--r--src/com/vaadin/ui/Form.java28
-rw-r--r--src/com/vaadin/ui/Label.java159
-rw-r--r--src/com/vaadin/ui/Root.java31
-rw-r--r--src/com/vaadin/ui/Table.java10
-rw-r--r--tests/server-side/com/vaadin/tests/data/bean/Person.java10
-rw-r--r--tests/server-side/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java42
-rw-r--r--tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java13
-rw-r--r--tests/server-side/com/vaadin/tests/server/component/label/LabelConverters.java59
-rw-r--r--[-rwxr-xr-x]tests/testbench/com/vaadin/tests/components/formlayout/FormLayouts.java0
-rw-r--r--tests/testbench/com/vaadin/tests/components/orderedlayout/LayoutResizeTest.java140
-rw-r--r--tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.html57
-rw-r--r--tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.java56
34 files changed, 1198 insertions, 554 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java
index dbf71408a9..3da314a11e 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;
@@ -2359,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;
/**
@@ -2372,53 +2367,7 @@ 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);
- }
-
- /**
- * 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();
- }
- }
- }
-
+ return String.valueOf(connectorIdSequence++);
}
private static final Logger getLogger() {
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..239baf6b6d
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/ConverterUtil.java
@@ -0,0 +1,165 @@
+/*
+@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#getCurrentApplication()}.
+ *
+ * @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.getCurrentApplication();
+ }
+
+ 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;
+ }
+
+ // check that the value class is compatible with the model type
+ if (modelType.isAssignableFrom(presentationValue.getClass())) {
+ 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/navigator/Navigator.java b/src/com/vaadin/navigator/Navigator.java
index 387f1d4eae..3ff727b504 100644
--- a/src/com/vaadin/navigator/Navigator.java
+++ b/src/com/vaadin/navigator/Navigator.java
@@ -1,9 +1,9 @@
+package com.vaadin.navigator;
+
/*
-@VaadinApache2LicenseForJavaFiles@
+ @VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.navigator;
-
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
@@ -16,7 +16,6 @@ import com.vaadin.terminal.Page.FragmentChangedListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.CustomComponent;
-import com.vaadin.ui.Root;
/**
* Navigator utility that allows switching of views in a part of an application.
@@ -60,7 +59,7 @@ public class Navigator implements Serializable {
}
/**
- * Fragment manager using URI fragments of a Root to track views and enable
+ * 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
@@ -73,10 +72,10 @@ public class Navigator implements Serializable {
/**
* Create a new URIFragmentManager and attach it to listen to URI
- * fragment changes of a {@link Root}.
+ * fragment changes of a {@link Page}.
*
- * @param root
- * root whose URI fragment to get and modify
+ * @param page
+ * page whose URI fragment to get and modify
* @param navigator
* {@link Navigator} to notify of fragment changes (using
* {@link Navigator#navigateTo(String)}
@@ -93,8 +92,7 @@ public class Navigator implements Serializable {
}
public void setFragment(String fragment) {
- // TODO ", false" ???
- page.setFragment(fragment);
+ page.setFragment(fragment, false);
}
public void fragmentChanged(FragmentChangedEvent event) {
@@ -272,7 +270,7 @@ public class Navigator implements Serializable {
/**
* Create a navigator that is tracking the active view using URI fragments.
*
- * @param root
+ * @param page
* whose URI fragments are used
* @param display
* where to display the views
@@ -280,6 +278,7 @@ public class Navigator implements Serializable {
public Navigator(Page page, ViewDisplay display) {
this.display = display;
fragmentManager = new UriFragmentManager(page, this);
+ navigateTo(page.getFragment());
}
/**
@@ -287,21 +286,25 @@ public class Navigator implements Serializable {
* By default, a {@link SimpleViewDisplay} is used and can be obtained using
* {@link #getDisplay()}.
*
- * @param root
+ * @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(Root, ViewDisplay)} which uses a URI fragment based
+ * {@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
@@ -335,12 +338,13 @@ public class Navigator implements Serializable {
* view name and parameters
*/
public void navigateTo(String viewAndParameters) {
- String longestViewName = "";
+ String longestViewName = null;
View viewWithLongestName = null;
for (ViewProvider provider : providers) {
String viewName = provider.getViewName(viewAndParameters);
if (null != viewName
- && viewName.length() > longestViewName.length()) {
+ && (longestViewName == null || viewName.length() > longestViewName
+ .length())) {
View view = provider.getView(viewName);
if (null != view) {
longestViewName = viewName;
diff --git a/src/com/vaadin/terminal/AbstractClientConnector.java b/src/com/vaadin/terminal/AbstractClientConnector.java
index 6a87f58c71..9de444d70e 100644
--- a/src/com/vaadin/terminal/AbstractClientConnector.java
+++ b/src/com/vaadin/terminal/AbstractClientConnector.java
@@ -72,7 +72,7 @@ public abstract class AbstractClientConnector implements ClientConnector {
public void requestRepaint() {
Root root = getRoot();
if (root != null) {
- root.getDirtyConnectorTracker().markDirty(this);
+ root.getConnectorTracker().markDirty(this);
}
}
@@ -455,9 +455,12 @@ public abstract class AbstractClientConnector implements ClientConnector {
public void attach() {
requestRepaint();
+ getRoot().getConnectorTracker().registerConnector(this);
+
for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.attach();
}
+
}
/**
@@ -472,6 +475,8 @@ public abstract class AbstractClientConnector implements ClientConnector {
for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.detach();
}
+
+ getRoot().getConnectorTracker().unregisterConnector(this);
}
public boolean 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/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
index 2281b4ab9c..74586a6def 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
@@ -138,7 +138,17 @@ public class LayoutManager {
}
}-*/;
- private static native final MeasuredSize getMeasuredSize(Element element,
+ /**
+ * Gets the measured size for an element. Method defined as protected to
+ * allow separate implementation for IE8.
+ *
+ * @param element
+ * The element to get measured size for
+ * @param defaultSize
+ * 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)
/*-{
return element.vMeasuredSize || defaultSize;
@@ -389,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");
@@ -480,7 +495,7 @@ public class LayoutManager {
ComponentConnector[] connectors = ConnectorMap.get(connection)
.getComponentConnectors();
for (ComponentConnector connector : connectors) {
- measueConnector(connector);
+ measureConnector(connector);
}
for (ComponentConnector connector : connectors) {
layoutDependencyTree.setNeedsMeasure(connector, false);
@@ -492,7 +507,7 @@ public class LayoutManager {
Collection<ComponentConnector> measureTargets = layoutDependencyTree
.getMeasureTargets();
for (ComponentConnector connector : measureTargets) {
- measueConnector(connector);
+ measureConnector(connector);
measureCount++;
}
for (ComponentConnector connector : measureTargets) {
@@ -502,7 +517,7 @@ public class LayoutManager {
return measureCount;
}
- private void measueConnector(ComponentConnector connector) {
+ private void measureConnector(ComponentConnector connector) {
Element element = connector.getWidget().getElement();
MeasuredSize measuredSize = getMeasuredSize(connector);
MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
@@ -1190,4 +1205,11 @@ public class LayoutManager {
public void setEverythingNeedsMeasure() {
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 2b677985b5..742594671f 100644
--- a/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
+++ b/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
@@ -3,21 +3,46 @@
*/
package com.vaadin.terminal.gwt.client;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.ui.RootPanel;
public class LayoutManagerIE8 extends LayoutManager {
+ private Map<Element, MeasuredSize> measuredSizes = new HashMap<Element, MeasuredSize>();
+
+ @Override
+ protected void setMeasuredSize(Element element, MeasuredSize measuredSize) {
+ if (measuredSize != null) {
+ measuredSizes.put(element, measuredSize);
+ } else {
+ 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;
+ protected MeasuredSize getMeasuredSize(Element element,
+ MeasuredSize defaultSize) {
+ MeasuredSize measured = measuredSizes.get(element);
+ if (measured != null) {
+ return measured;
} else {
- element.vMeasuredSize = undefined;
+ return defaultSize;
}
- }-*/;
+ }
+ @Override
+ protected void cleanMeasuredSizes() {
+ Iterator<Element> i = measuredSizes.keySet().iterator();
+ while (i.hasNext()) {
+ Element e = i.next();
+ if (e.getOwnerDocument() != RootPanel.get().getElement()
+ .getOwnerDocument()) {
+ i.remove();
+ }
+ }
+ }
}
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/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
index bf29144cc1..b7be6fd394 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
@@ -638,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) {
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
index 2179761d31..0e548f61c8 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
@@ -421,25 +421,28 @@ 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);
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index ecd4fdd1ad..1ea713b4f6 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -71,10 +71,11 @@ 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.Root;
import com.vaadin.ui.Window;
@@ -134,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;
@@ -517,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);
}
}
@@ -617,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) {
@@ -763,8 +768,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>();
Application application = root.getApplication();
// Paints components
- DirtyConnectorTracker rootConnectorTracker = root
- .getDirtyConnectorTracker();
+ ConnectorTracker rootConnectorTracker = root.getConnectorTracker();
getLogger().log(Level.FINE, "* Creating response to client");
if (repaintAll) {
getClientCache(root).clear();
@@ -848,7 +852,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
}
Object stateJson = JsonCodec.encode(state, referenceState,
- stateType, application);
+ stateType, root.getConnectorTracker());
sharedStates.put(connector.getConnectorId(), stateJson);
} catch (JSONException e) {
@@ -950,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);
@@ -1411,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
@@ -1453,22 +1457,23 @@ 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()) {
@@ -1479,7 +1484,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
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) {
@@ -1526,8 +1531,18 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
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
@@ -1557,8 +1572,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
errorComponent = (Component) dropHandlerOwner;
}
}
- handleChangeVariablesError(app, errorComponent, e,
- changes);
+ handleChangeVariablesError(root.getApplication(),
+ errorComponent, e, changes);
}
}
}
@@ -1577,12 +1592,15 @@ public abstract class AbstractCommunicationManager implements Serializable {
* Parse a message burst from the client into a list of MethodInvocation
* instances.
*
+ * @param root
+ * The root for this request
* @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);
@@ -1595,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
@@ -1607,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);
@@ -1623,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);
}
}
@@ -1634,7 +1653,8 @@ 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 "
@@ -1642,7 +1662,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
String variableName = parametersJson.getString(0);
UidlValue uidlValue = (UidlValue) JsonCodec.decodeInternalType(
- UidlValue.class, true, parametersJson.get(1), application);
+ UidlValue.class, true, parametersJson.get(1), connectorTracker);
Object value = uidlValue.getValue();
@@ -1658,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());
@@ -1670,7 +1691,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
Object parameterValue = parametersJson.get(j);
Type parameterType = declaredRpcMethodParameterTypes[j];
parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType,
- parameterValue, application);
+ parameterValue, connectorTracker);
}
invocation.setParameters(parameters);
return invocation;
@@ -1681,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();
@@ -1764,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 {
@@ -2037,9 +2059,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
* @return
*/
private ArrayList<ClientConnector> getDirtyVisibleConnectors(
- DirtyConnectorTracker dirtyConnectorTracker) {
+ ConnectorTracker connectorTracker) {
ArrayList<ClientConnector> dirtyConnectors = new ArrayList<ClientConnector>();
- for (ClientConnector c : dirtyConnectorTracker.getDirtyConnectors()) {
+ for (ClientConnector c : connectorTracker.getDirtyConnectors()) {
if (isVisible(c)) {
dirtyConnectors.add(c);
}
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/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/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
index 4fb7681e4a..d3a2ef56f8 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
@@ -24,7 +24,6 @@ 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;
@@ -32,6 +31,7 @@ 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
@@ -110,16 +110,18 @@ public class JsonCodec implements Serializable {
}
public static Object decodeInternalOrCustomType(Type targetType,
- Object value, Application application) throws JSONException {
+ Object value, ConnectorTracker connectorTracker)
+ throws JSONException {
if (isInternalType(targetType)) {
- return decodeInternalType(targetType, false, value, application);
+ return decodeInternalType(targetType, false, value,
+ connectorTracker);
} else {
- return decodeCustomType(targetType, value, application);
+ return decodeCustomType(targetType, value, connectorTracker);
}
}
public static Object decodeCustomType(Type targetType, Object value,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
if (isInternalType(targetType)) {
throw new JSONException("decodeCustomType cannot be used for "
+ targetType + ", which is an internal type");
@@ -137,22 +139,23 @@ public class JsonCodec implements Serializable {
// Legacy Object[] and String[] handled elsewhere, this takes care
// of generic arrays
return decodeArray((Class<?>) targetType, (JSONArray) value,
- application);
+ 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,
- Application application) throws JSONException {
+ 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), application);
+ value.get(i), connectorTracker);
Array.set(array, i, decodedValue);
}
return array;
@@ -184,7 +187,7 @@ public class JsonCodec implements Serializable {
*/
public static Object decodeInternalType(Type targetType,
boolean restrictToInternalTypes, Object encodedJsonValue,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
if (!isInternalType(targetType)) {
throw new JSONException("Type " + targetType
+ " is not a supported internal type.");
@@ -197,26 +200,27 @@ public class JsonCodec implements Serializable {
// UidlValue
if (targetType == UidlValue.class) {
- return decodeUidlValue((JSONArray) encodedJsonValue, application);
+ 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,
- 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);
@@ -227,7 +231,7 @@ public class JsonCodec implements Serializable {
String stringValue = String.valueOf(encodedJsonValue);
if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) {
- return application.getConnector(stringValue);
+ return connectorTracker.getConnector(stringValue);
}
// Legacy types
@@ -250,11 +254,11 @@ public class JsonCodec implements Serializable {
}
private static UidlValue decodeUidlValue(JSONArray encodedJsonValue,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
String type = encodedJsonValue.getString(0);
Object decodedValue = decodeInternalType(getType(type), true,
- encodedJsonValue.get(1), application);
+ encodedJsonValue.get(1), connectorTracker);
return new UidlValue(decodedValue);
}
@@ -275,7 +279,7 @@ public class JsonCodec implements Serializable {
private static Map<Object, Object> decodeMap(Type targetType,
boolean restrictToInternalTypes, Object jsonMap,
- Application application) throws JSONException {
+ 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.
@@ -293,22 +297,22 @@ public class JsonCodec implements Serializable {
.getActualTypeArguments()[1];
if (keyType == String.class) {
return decodeStringMap(valueType, (JSONObject) jsonMap,
- application);
+ connectorTracker);
} else if (keyType == Connector.class) {
return decodeConnectorMap(valueType, (JSONObject) jsonMap,
- application);
+ connectorTracker);
} else {
return decodeObjectMap(keyType, valueType, (JSONArray) jsonMap,
- application);
+ connectorTracker);
}
} else {
return decodeStringMap(UidlValue.class, (JSONObject) jsonMap,
- application);
+ connectorTracker);
}
}
private static Map<Object, Object> decodeObjectMap(Type keyType,
- Type valueType, JSONArray jsonMap, Application application)
+ Type valueType, JSONArray jsonMap, ConnectorTracker connectorTracker)
throws JSONException {
Map<Object, Object> map = new HashMap<Object, Object>();
@@ -319,9 +323,9 @@ public class JsonCodec implements Serializable {
for (int i = 0; i < keys.length(); i++) {
Object key = decodeInternalOrCustomType(keyType, keys.get(i),
- application);
+ connectorTracker);
Object value = decodeInternalOrCustomType(valueType, values.get(i),
- application);
+ connectorTracker);
map.put(key, value);
}
@@ -330,30 +334,32 @@ public class JsonCodec implements Serializable {
}
private static Map<Object, Object> decodeConnectorMap(Type valueType,
- JSONObject jsonMap, Application application) throws JSONException {
+ 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), application);
+ jsonMap.get(key), connectorTracker);
if (valueType == UidlValue.class) {
value = ((UidlValue) value).getValue();
}
- map.put(application.getConnector(key), value);
+ map.put(connectorTracker.getConnector(key), value);
}
return map;
}
private static Map<Object, Object> decodeStringMap(Type valueType,
- JSONObject jsonMap, Application application) throws JSONException {
+ 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), application);
+ jsonMap.get(key), connectorTracker);
if (valueType == UidlValue.class) {
value = ((UidlValue) value).getValue();
}
@@ -376,17 +382,18 @@ public class JsonCodec implements Serializable {
*/
private static Object decodeParametrizedType(Type targetType,
boolean restrictToInternalTypes, int typeIndex, Object value,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
Type childType = ((ParameterizedType) targetType)
.getActualTypeArguments()[typeIndex];
// Only decode the given type
- return decodeInternalOrCustomType(childType, value, application);
+ return decodeInternalOrCustomType(childType, value,
+ connectorTracker);
} else {
// Only UidlValue when not enforcing a given type to avoid security
// issues
UidlValue decodeInternalType = (UidlValue) decodeInternalType(
- UidlValue.class, true, value, application);
+ UidlValue.class, true, value, connectorTracker);
return decodeInternalType.getValue();
}
}
@@ -407,20 +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
Object encodedValue = jsonArray.get(i);
Object decodedChild = decodeParametrizedType(targetType,
- restrictToInternalTypes, 0, encodedValue, application);
+ restrictToInternalTypes, 0, encodedValue, connectorTracker);
list.add(decodedChild);
}
return list;
@@ -428,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(targetType, restrictToInternalTypes, jsonArray,
- application));
+ connectorTracker));
return set;
}
@@ -457,7 +465,7 @@ public class JsonCodec implements Serializable {
}
private static Object decodeObject(Type targetType,
- JSONObject serializedObject, Application application)
+ JSONObject serializedObject, ConnectorTracker connectorTracker)
throws JSONException {
Class<?> targetClass = getClassForType(targetType);
@@ -478,7 +486,7 @@ public class JsonCodec implements Serializable {
Object encodedFieldValue = serializedObject.get(fieldName);
Type fieldType = pd.getReadMethod().getGenericReturnType();
Object decodedFieldValue = decodeInternalOrCustomType(
- fieldType, encodedFieldValue, application);
+ fieldType, encodedFieldValue, connectorTracker);
pd.getWriteMethod().invoke(decodedObject, decodedFieldValue);
}
@@ -498,7 +506,8 @@ public class JsonCodec implements Serializable {
}
public static Object encode(Object value, Object referenceValue,
- Type valueType, Application application) throws JSONException {
+ Type valueType, ConnectorTracker connectorTracker)
+ throws JSONException {
if (valueType == null) {
throw new IllegalArgumentException("type must be defined");
@@ -527,15 +536,15 @@ public class JsonCodec implements Serializable {
} else if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
JSONArray jsonArray = encodeCollection(valueType, collection,
- application);
+ connectorTracker);
return jsonArray;
} else if (valueType instanceof Class<?>
&& ((Class<?>) valueType).isArray()) {
- JSONArray jsonArray = encodeArrayContents(value, application);
+ JSONArray jsonArray = encodeArrayContents(value, connectorTracker);
return jsonArray;
} else if (value instanceof Map) {
Object jsonMap = encodeMap(valueType, (Map<?, ?>) value,
- application);
+ connectorTracker);
return jsonMap;
} else if (value instanceof Connector) {
Connector connector = (Connector) value;
@@ -546,13 +555,13 @@ public class JsonCodec implements Serializable {
}
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 encodeObject(value, referenceValue, application);
+ return encodeObject(value, referenceValue, connectorTracker);
}
}
@@ -561,7 +570,7 @@ public class JsonCodec implements Serializable {
}
private static Object encodeObject(Object value, Object referenceValue,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
JSONObject jsonMap = new JSONObject();
try {
@@ -595,7 +604,7 @@ public class JsonCodec implements Serializable {
jsonMap.put(
fieldName,
encode(fieldValue, referenceFieldValue, fieldType,
- application));
+ connectorTracker));
// } else {
// System.out.println("Skipping field " + fieldName
// + " of type " + fieldType.getName()
@@ -629,46 +638,46 @@ public class JsonCodec implements Serializable {
return false;
}
- private static String encodeEnum(Enum<?> e, Application application)
- throws JSONException {
+ private static String encodeEnum(Enum<?> e,
+ ConnectorTracker connectorTracker) throws JSONException {
return e.name();
}
private static JSONArray encodeArrayContents(Object array,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
JSONArray jsonArray = new JSONArray();
Class<?> componentType = array.getClass().getComponentType();
for (int i = 0; i < Array.getLength(array); i++) {
jsonArray.put(encode(Array.get(array, i), null, componentType,
- application));
+ 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 Object encodeChild(Type targetType, int typeIndex, Object o,
- Application application) throws JSONException {
+ 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 {
throw new JSONException("Collection is missing generics");
}
}
private static Object encodeMap(Type mapType, Map<?, ?> map,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
Type keyType, valueType;
if (mapType instanceof ParameterizedType) {
@@ -685,24 +694,25 @@ public class JsonCodec implements Serializable {
}
if (keyType == String.class) {
- return encodeStringMap(valueType, map, application);
+ return encodeStringMap(valueType, map, connectorTracker);
} else if (keyType == Connector.class) {
- return encodeConnectorMap(valueType, map, application);
+ return encodeConnectorMap(valueType, map, connectorTracker);
} else {
- return encodeObjectMap(keyType, valueType, map, application);
+ return encodeObjectMap(keyType, valueType, map, connectorTracker);
}
}
private static JSONArray encodeObjectMap(Type keyType, Type valueType,
- Map<?, ?> map, Application application) throws JSONException {
+ 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,
- application);
+ connectorTracker);
Object encodedValue = encode(entry.getValue(), null, valueType,
- application);
+ connectorTracker);
keys.put(encodedKey);
values.put(encodedValue);
@@ -712,13 +722,13 @@ public class JsonCodec implements Serializable {
}
private static JSONObject encodeConnectorMap(Type valueType, Map<?, ?> map,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
JSONObject jsonMap = new JSONObject();
for (Entry<?, ?> entry : map.entrySet()) {
Connector key = (Connector) entry.getKey();
Object encodedValue = encode(entry.getValue(), null, valueType,
- application);
+ connectorTracker);
jsonMap.put(key.getConnectorId(), encodedValue);
}
@@ -726,13 +736,13 @@ public class JsonCodec implements Serializable {
}
private static JSONObject encodeStringMap(Type valueType, Map<?, ?> map,
- Application application) throws JSONException {
+ 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,
- application);
+ connectorTracker);
jsonMap.put(key, encodedValue);
}
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/ui/AbstractField.java b/src/com/vaadin/ui/AbstractField.java
index 4efed11e2c..21ca01f592 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;
@@ -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());
}
/**
@@ -817,38 +794,17 @@ public abstract class AbstractField<T> extends AbstractComponent implements
*/
private Object convertToDataSource(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();
}
- }
-
- 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);
}
}
diff --git a/src/com/vaadin/ui/Component.java b/src/com/vaadin/ui/Component.java
index ce6df9854f..2394d0f307 100644
--- a/src/com/vaadin/ui/Component.java
+++ b/src/com/vaadin/ui/Component.java
@@ -497,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();
@@ -520,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#getCurrentApplication()}
+ * 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>.
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/CustomField.java b/src/com/vaadin/ui/CustomField.java
index 269f24fb2c..0998c11612 100644
--- a/src/com/vaadin/ui/CustomField.java
+++ b/src/com/vaadin/ui/CustomField.java
@@ -62,21 +62,18 @@ public abstract class CustomField<T> extends AbstractField<T> implements
*/
@Override
public void attach() {
- root = getContent();
- getContent().setParent(this);
- fireComponentAttachEvent(getContent());
+ // First call super attach to notify all children (none if content has
+ // not yet been created)
super.attach();
- }
- /**
- * Notifies the content that the {@link CustomField} is detached from a
- * window.
- *
- * @see com.vaadin.ui.Component#detach()
- */
- @Override
- public void detach() {
- super.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());
+ }
}
/**
@@ -87,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;
}
diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java
deleted file mode 100644
index ae47d87eae..0000000000
--- a/src/com/vaadin/ui/DirtyConnectorTracker.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import java.io.Serializable;
-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.AbstractClientConnector;
-import com.vaadin.terminal.gwt.server.ClientConnector;
-
-/**
- * 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 Serializable {
- 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(DirtyConnectorTracker.class.getName());
- }
-
- public DirtyConnectorTracker(Root root) {
- this.root = root;
- }
-
- 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/Form.java b/src/com/vaadin/ui/Form.java
index 39b12e67e2..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()
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/Root.java b/src/com/vaadin/ui/Root.java
index 50ad99571e..64bfaf57a5 100644
--- a/src/com/vaadin/ui/Root.java
+++ b/src/com/vaadin/ui/Root.java
@@ -560,8 +560,7 @@ public abstract class Root extends AbstractComponentContainer implements
/** 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);
@@ -1178,38 +1177,14 @@ public abstract class Root extends AbstractComponentContainer implements
removeListener(CLICK_EVENT_ID, ClickEvent.class, listener);
}
- /**
- * Notifies the child components and windows that the root is attached to
- * the application.
- */
- @Override
- public void attach() {
- super.attach();
- for (Window w : windows) {
- w.attach();
- }
- }
-
- /**
- * Notifies the child components and windows that the root is detached from
- * the application.
- */
- @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 ConnectorTracker getConnectorTracker() {
+ return connectorTracker;
}
public Page getPage() {
diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java
index 5d4f919704..b74de7e180 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;
@@ -3664,12 +3664,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) {
diff --git a/tests/server-side/com/vaadin/tests/data/bean/Person.java b/tests/server-side/com/vaadin/tests/data/bean/Person.java
index 2cb3a29368..f7bad31d0e 100644
--- a/tests/server-side/com/vaadin/tests/data/bean/Person.java
+++ b/tests/server-side/com/vaadin/tests/data/bean/Person.java
@@ -130,4 +130,14 @@ public class Person {
this.birthDate = birthDate;
}
+ public static Person createTestPerson1() {
+ return new Person("Foo", "Bar", "yeah@cool.com", 46, Sex.MALE,
+ new Address("Street", 1123, "Turku", Country.FINLAND));
+ }
+
+ public static Person createTestPerson2() {
+ return new Person("Maya", "Dinkelstein", "maya@foo.bar", 18,
+ Sex.FEMALE, new Address("Red street", 12, "Amsterdam",
+ Country.NETHERLANDS));
+ }
}
diff --git a/tests/server-side/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java b/tests/server-side/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java
index 050ab282a6..7305e022ee 100644
--- a/tests/server-side/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java
+++ b/tests/server-side/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java
@@ -4,6 +4,7 @@ import java.util.Locale;
import junit.framework.TestCase;
+import com.vaadin.Application;
import com.vaadin.data.util.MethodProperty;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.data.util.converter.StringToIntegerConverter;
@@ -159,4 +160,45 @@ public class AbstractFieldValueConversions extends TestCase {
}
+ public static class NumberBean {
+ private Number number;
+
+ public Number getNumber() {
+ return number;
+ }
+
+ public void setNumber(Number number) {
+ this.number = number;
+ }
+
+ }
+
+ public void testNumberDoubleConverterChange() {
+ final Application a = new Application();
+ Application.setCurrentApplication(a);
+ TextField tf = new TextField() {
+ @Override
+ public Application getApplication() {
+ return a;
+ }
+ };
+ NumberBean nb = new NumberBean();
+ nb.setNumber(490);
+
+ tf.setPropertyDataSource(new MethodProperty<Number>(nb, "number"));
+ assertEquals(490, tf.getPropertyDataSource().getValue());
+ assertEquals("490", tf.getValue());
+
+ Converter c1 = tf.getConverter();
+
+ tf.setPropertyDataSource(new MethodProperty<Number>(nb, "number"));
+ Converter c2 = tf.getConverter();
+ assertTrue(
+ "StringToNumber converter is ok for integer types and should stay even though property is changed",
+ c1 == c2);
+ assertEquals(490, tf.getPropertyDataSource().getValue());
+ assertEquals("490", tf.getValue());
+
+ }
+
}
diff --git a/tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java b/tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java
index 851fece4d1..94385700d8 100644
--- a/tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java
+++ b/tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java
@@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
+import com.vaadin.Application;
import com.vaadin.data.Property;
import com.vaadin.data.util.AbstractProperty;
import com.vaadin.data.util.converter.Converter.ConversionException;
@@ -25,10 +26,13 @@ public class RemoveListenersOnDetach {
}
};
+ private Application application = new Application() {
+
+ };
@Override
public Class<?> getType() {
- return null;
+ return String.class;
}
@Override
@@ -48,6 +52,11 @@ public class RemoveListenersOnDetach {
public com.vaadin.ui.Root getRoot() {
return root;
};
+
+ @Override
+ public Application getApplication() {
+ return application;
+ };
};
Property property = new AbstractProperty() {
@@ -61,7 +70,7 @@ public class RemoveListenersOnDetach {
}
public Class<?> getType() {
- return null;
+ return String.class;
}
};
diff --git a/tests/server-side/com/vaadin/tests/server/component/label/LabelConverters.java b/tests/server-side/com/vaadin/tests/server/component/label/LabelConverters.java
new file mode 100644
index 0000000000..e79bd84741
--- /dev/null
+++ b/tests/server-side/com/vaadin/tests/server/component/label/LabelConverters.java
@@ -0,0 +1,59 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.tests.server.component.label;
+
+import junit.framework.TestCase;
+
+import com.vaadin.Application;
+import com.vaadin.data.Property;
+import com.vaadin.data.util.MethodProperty;
+import com.vaadin.tests.data.bean.Person;
+import com.vaadin.ui.Label;
+
+public class LabelConverters extends TestCase {
+
+ public void testLabelSetDataSourceLaterOn() {
+ Person p = Person.createTestPerson1();
+ Label l = new Label("My label");
+ assertEquals("My label", l.getValue());
+ assertNull(l.getConverter());
+ l.setPropertyDataSource(new MethodProperty<String>(p, "firstName"));
+ assertEquals(p.getFirstName(), l.getValue());
+ p.setFirstName("123");
+ assertEquals("123", l.getValue());
+ }
+
+ public void testIntegerDataSource() {
+ Application.setCurrentApplication(new Application());
+ Label l = new Label("Foo");
+ Property ds = new MethodProperty<Integer>(Person.createTestPerson1(),
+ "age");
+ l.setPropertyDataSource(ds);
+ assertEquals(String.valueOf(Person.createTestPerson1().getAge()),
+ l.getValue());
+ }
+
+ public void testSetValueWithDataSource() {
+ try {
+ MethodProperty<String> property = new MethodProperty<String>(
+ Person.createTestPerson1(), "firstName");
+ Label l = new Label(property);
+ l.setValue("Foo");
+ fail("setValue should throw an exception when a data source is set");
+ } catch (Exception e) {
+ }
+
+ }
+
+ public void testLabelWithoutDataSource() {
+ Label l = new Label("My label");
+ assertEquals("My label", l.getValue());
+ assertNull(l.getConverter());
+ assertNull(l.getPropertyDataSource());
+ l.setValue("New value");
+ assertEquals("New value", l.getValue());
+ assertNull(l.getConverter());
+ assertNull(l.getPropertyDataSource());
+ }
+}
diff --git a/tests/testbench/com/vaadin/tests/components/formlayout/FormLayouts.java b/tests/testbench/com/vaadin/tests/components/formlayout/FormLayouts.java
index e247ce95f7..e247ce95f7 100755..100644
--- a/tests/testbench/com/vaadin/tests/components/formlayout/FormLayouts.java
+++ b/tests/testbench/com/vaadin/tests/components/formlayout/FormLayouts.java
diff --git a/tests/testbench/com/vaadin/tests/components/orderedlayout/LayoutResizeTest.java b/tests/testbench/com/vaadin/tests/components/orderedlayout/LayoutResizeTest.java
new file mode 100644
index 0000000000..455c16c425
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/orderedlayout/LayoutResizeTest.java
@@ -0,0 +1,140 @@
+package com.vaadin.tests.components.orderedlayout;
+
+import com.vaadin.terminal.ThemeResource;
+import com.vaadin.terminal.gwt.client.ui.label.ContentMode;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Embedded;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.HorizontalSplitPanel;
+import com.vaadin.ui.JavaScript;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.VerticalSplitPanel;
+import com.vaadin.ui.themes.Reindeer;
+
+public class LayoutResizeTest extends TestBase {
+
+ @Override
+ protected void setup() {
+ getLayout().setSizeFull();
+
+ HorizontalSplitPanel split1 = new HorizontalSplitPanel();
+ split1.setSizeFull();
+ addComponent(split1);
+
+ VerticalLayout left = new VerticalLayout();
+ left.setSizeFull();
+ split1.setFirstComponent(left);
+
+ left.setSpacing(true);
+ left.setMargin(true);
+
+ left.addComponent(new Label("<h2>Layout resize test</h2>",
+ ContentMode.XHTML));
+
+ Button resize = new Button("Resize to 700x400",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ JavaScript
+ .getCurrent()
+ .execute(
+ "setTimeout(function() {window.resizeTo(700,400)}, 500)");
+ }
+ });
+ left.addComponent(resize);
+
+ resize = new Button("Resize to 900x600", new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ JavaScript
+ .getCurrent()
+ .execute(
+ "setTimeout(function() {window.resizeTo(900,600)}, 500)");
+ }
+ });
+ left.addComponent(resize);
+
+ left.addComponent(new Label(
+ "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin vel ante a orci tempus eleifend ut et magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus urna sed urna ultricies."));
+
+ Table table1 = new Table();
+ table1.setSizeFull();
+ table1.addContainerProperty("Column", String.class, "");
+ for (int i = 1; i <= 100; i++) {
+ table1.addItem(new Object[] { "Value " + i }, i);
+ }
+ left.addComponent(table1);
+ left.setExpandRatio(table1, 1);
+
+ VerticalSplitPanel split2 = new VerticalSplitPanel();
+ split2.setSizeFull();
+ split1.setSecondComponent(split2);
+
+ Table table2 = new Table();
+ table2.setSizeFull();
+ table2.addContainerProperty("Column 1", String.class, "");
+ table2.addContainerProperty("Column 2", String.class, "");
+ table2.addContainerProperty("Column 3", String.class, "");
+ table2.addContainerProperty("Column 4", String.class, "");
+ for (int i = 1; i <= 100; i++) {
+ table2.addItem(new Object[] { "Value " + i, "Value " + i,
+ "Value " + i, "Value " + i }, i);
+ }
+ split2.setFirstComponent(table2);
+
+ VerticalLayout rows = new VerticalLayout();
+ rows.setWidth("100%");
+ rows.setSpacing(true);
+ rows.setMargin(true);
+ for (int i = 1; i <= 100; i++) {
+ rows.addComponent(getRow(i));
+ }
+ split2.setSecondComponent(rows);
+ }
+
+ private HorizontalLayout getRow(int i) {
+ HorizontalLayout row = new HorizontalLayout();
+ row.setWidth("100%");
+ row.setSpacing(true);
+
+ Embedded icon = new Embedded(null, new ThemeResource(
+ "../runo/icons/32/document.png"));
+ row.addComponent(icon);
+ row.setComponentAlignment(icon, Alignment.MIDDLE_LEFT);
+
+ Label text = new Label(
+ "Row content #"
+ + i
+ + ". In pellentesque faucibus vestibulum. Nulla at nulla justo, eget luctus tortor. Nulla facilisi. Duis aliquet.");
+ row.addComponent(text);
+ row.setExpandRatio(text, 1);
+
+ Button button = new Button("Edit");
+ button.addStyleName(Reindeer.BUTTON_SMALL);
+ row.addComponent(button);
+ row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+
+ button = new Button("Delete");
+ button.addStyleName(Reindeer.BUTTON_SMALL);
+ row.addComponent(button);
+ row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+
+ return row;
+ }
+
+ @Override
+ protected String getDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.html b/tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.html
new file mode 100644
index 0000000000..9cbcf1d5ea
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.html
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://arturwin.office.itmill.com:8888/" />
+<title>New Test</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">New Test</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.window.RepaintWindowContents?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td>Button 1</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td>Button 2</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td>Button 1</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentswindowRepaintWindowContents::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td>Button 2</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.java b/tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.java
new file mode 100644
index 0000000000..353eb535ca
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/window/RepaintWindowContents.java
@@ -0,0 +1,56 @@
+package com.vaadin.tests.components.window;
+
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Layout;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+
+public class RepaintWindowContents extends AbstractTestRoot {
+ private static final long serialVersionUID = 1L;
+
+ @SuppressWarnings("serial")
+ @Override
+ protected void setup(WrappedRequest request) {
+ final Window window = new Window("Test window");
+ addWindow(window);
+
+ final Layout layout1 = new VerticalLayout();
+ Button button1 = new Button("Button 1");
+ layout1.addComponent(button1);
+
+ final Layout layout2 = new VerticalLayout();
+ Button button2 = new Button("Button 2");
+ layout2.addComponent(button2);
+
+ window.setContent(layout1);
+
+ button1.addListener(new ClickListener() {
+
+ public void buttonClick(ClickEvent event) {
+ window.setContent(layout2);
+ }
+ });
+
+ button2.addListener(new ClickListener() {
+
+ public void buttonClick(ClickEvent event) {
+ window.setContent(layout1);
+ }
+ });
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Clicking the button switches the content between content1 and content2";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 8832;
+ }
+
+} \ No newline at end of file