summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Dahlström <johannesd@vaadin.com>2012-06-13 17:46:09 +0300
committerJohannes Dahlström <johannesd@vaadin.com>2012-06-13 17:46:09 +0300
commit52d06a13b9e20cb752a21413e15adc250daa5e1c (patch)
treeac0e5878490d802960d9463354721ce1820b4aa8
parentde5fd8e0aefa0c43e2b0fe724102bca92eeaaf96 (diff)
parentadcf03c1b5f05b38cda25819b732745c1a4859fc (diff)
downloadvaadin-framework-52d06a13b9e20cb752a21413e15adc250daa5e1c.tar.gz
vaadin-framework-52d06a13b9e20cb752a21413e15adc250daa5e1c.zip
Merge remote-tracking branch 'origin/master'
Conflicts: src/com/vaadin/terminal/gwt/server/DragAndDropService.java
-rw-r--r--WebContent/statictestfiles/jsconnector.js12
-rw-r--r--WebContent/statictestfiles/jsextension.js13
-rw-r--r--src/com/vaadin/annotations/LoadScripts.java16
-rw-r--r--src/com/vaadin/navigator/Navigator.java411
-rw-r--r--src/com/vaadin/navigator/View.java16
-rw-r--r--src/com/vaadin/navigator/ViewChangeListener.java100
-rw-r--r--src/com/vaadin/terminal/AbstractClientConnector.java8
-rw-r--r--src/com/vaadin/terminal/AbstractExtension.java4
-rw-r--r--src/com/vaadin/terminal/AbstractJavascriptExtension.java20
-rw-r--r--src/com/vaadin/terminal/JavascriptRpcHelper.java61
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConnection.java43
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java205
-rw-r--r--src/com/vaadin/terminal/gwt/client/JavascriptExtension.java33
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetSet.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcManager.java59
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java78
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java22
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java60
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java25
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/BootstrapHandler.java57
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientConnector.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/DragAndDropService.java2
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonCodec.java7
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java6
-rw-r--r--src/com/vaadin/ui/AbstractJavascriptComponent.java19
-rw-r--r--src/com/vaadin/ui/DirtyConnectorTracker.java2
-rw-r--r--src/com/vaadin/ui/JavascriptCallback.java14
-rw-r--r--src/com/vaadin/ui/JavascriptManager.java58
-rw-r--r--src/com/vaadin/ui/Root.java12
-rw-r--r--src/com/vaadin/ui/Table.java8
-rw-r--r--tests/server-side/com/vaadin/tests/server/navigator/ClassBasedViewProviderTest.java183
-rw-r--r--tests/server-side/com/vaadin/tests/server/navigator/NavigatorTest.java449
-rw-r--r--tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavascriptComponent.java73
-rw-r--r--tests/testbench/com/vaadin/tests/components/table/TableInTabsheet.java114
-rw-r--r--tests/testbench/com/vaadin/tests/extensions/HelloWorldExtensionTest.java (renamed from tests/testbench/com/vaadin/tests/features/HelloWorldFeatureTest.java)6
-rw-r--r--tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.html47
-rw-r--r--tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.java47
-rw-r--r--tests/testbench/com/vaadin/tests/extensions/SimpleJavascriptExtensionTest.java95
41 files changed, 1907 insertions, 503 deletions
diff --git a/WebContent/statictestfiles/jsconnector.js b/WebContent/statictestfiles/jsconnector.js
new file mode 100644
index 0000000000..db8a065e86
--- /dev/null
+++ b/WebContent/statictestfiles/jsconnector.js
@@ -0,0 +1,12 @@
+window.com_vaadin_tests_components_javascriptcomponent_BasicJavascriptComponent_ExampleWidget = function() {
+ var connector = this;
+
+ var rootElement = connector.getWidgetElement();
+ rootElement.innerHTML = 'Hello world!';
+ rootElement.onclick = function() {
+ connector.getRpcProxyFunction("com.vaadin.tests.components.javascriptcomponent.BasicJavascriptComponent$ExampleClickRpc", "onClick")("message");
+ }
+ connector.onStateChange = function() {
+ console.log('state change:', this.getState());
+ }
+} \ No newline at end of file
diff --git a/WebContent/statictestfiles/jsextension.js b/WebContent/statictestfiles/jsextension.js
new file mode 100644
index 0000000000..df67db8927
--- /dev/null
+++ b/WebContent/statictestfiles/jsextension.js
@@ -0,0 +1,13 @@
+window.com_vaadin_tests_features_SimpleJavascriptExtensionTest_SimpleJavascriptExtension = function() {
+ var state = this.getState();
+ var greetBack = this.getRpcProxyFunction('com.vaadin.tests.features.SimpleJavascriptExtensionTest$SimpleJavascriptExtensionServerRpc', 'greet');
+
+ this.registerRpc("com.vaadin.tests.features.SimpleJavascriptExtensionTest.SimpleJavascriptExtensionClientRpc", {
+ 'greet': function(greeting) {
+ var response = window.prompt(state.prefix + greeting);
+ if (response !== null) {
+ greetBack(response);
+ }
+ }
+ });
+} \ No newline at end of file
diff --git a/src/com/vaadin/annotations/LoadScripts.java b/src/com/vaadin/annotations/LoadScripts.java
new file mode 100644
index 0000000000..f2b72407f7
--- /dev/null
+++ b/src/com/vaadin/annotations/LoadScripts.java
@@ -0,0 +1,16 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface LoadScripts {
+ public String[] value();
+
+}
diff --git a/src/com/vaadin/navigator/Navigator.java b/src/com/vaadin/navigator/Navigator.java
index d0bba584f1..9d9acf9ed3 100644
--- a/src/com/vaadin/navigator/Navigator.java
+++ b/src/com/vaadin/navigator/Navigator.java
@@ -5,10 +5,11 @@
package com.vaadin.navigator;
import java.io.Serializable;
-import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.CustomComponent;
@@ -22,7 +23,7 @@ import com.vaadin.ui.Root.FragmentChangedListener;
* The view switching can be based e.g. on URI fragments containing the view
* name and parameters to the view. There are two types of parameters for views:
* an optional parameter string that is included in the fragment (may be
- * bookmarkable) and optional internal parameters that are not.
+ * bookmarkable).
*
* Views can be explicitly registered or dynamically generated and listening to
* view changes is possible.
@@ -38,19 +39,21 @@ public class Navigator implements Serializable {
// TODO divert navigation e.g. if no permissions? Or just show another view
// but keep URL? how best to intercept
- // TODO investigate relationship to TouchKit navigation support
+ // TODO investigate relationship with TouchKit navigation support
/**
* Empty view component.
*/
public static class EmptyView extends CssLayout implements View {
- public void init() {
+ /**
+ * Create minimally sized empty view.
+ */
+ public EmptyView() {
setWidth("0px");
setHeight("0px");
}
- public void navigateTo(String fragmentParameters,
- Object... internalParameters) {
+ public void navigateTo(String fragmentParameters) {
// nothing to do
}
}
@@ -75,7 +78,7 @@ public class Navigator implements Serializable {
* root whose URI fragment to get and modify
* @param navigator
* {@link Navigator} to notify of fragment changes (using
- * {@link Navigator#navigateTo(String, Object...)}
+ * {@link Navigator#navigateTo(String)}
*/
public UriFragmentManager(Root root, Navigator navigator) {
this.root = root;
@@ -130,106 +133,105 @@ public class Navigator implements Serializable {
}
/**
- * View provider which uses a map from view name to pre-created and
- * registered view instances.
+ * View provider which supports mapping a single view name to a single
+ * pre-initialized view instance.
+ *
+ * For most cases, ClassBasedViewProvider should be used instead of this.
*/
- public static class RegisteredViewProvider implements ViewProvider {
+ public static class StaticViewProvider implements ViewProvider {
+ private final String viewName;
+ private final View view;
- private HashMap<String, View> viewNameToView = new HashMap<String, View>();
+ /**
+ * Create a new view provider which returns a pre-created view instance.
+ *
+ * @param viewName
+ * name of the view (not null)
+ * @param view
+ * view instance to return (not null), reused on every
+ * request
+ */
+ public StaticViewProvider(String viewName, View view) {
+ this.viewName = viewName;
+ this.view = view;
+ }
public String getViewName(String viewAndParameters) {
if (null == viewAndParameters) {
return null;
}
- for (String viewName : viewNameToView.keySet()) {
- if (viewAndParameters.equals(viewName)
- || viewAndParameters.startsWith(viewName + "/")) {
- return viewName;
- }
+ if (viewAndParameters.startsWith(viewName)) {
+ return viewName;
}
return null;
}
public View getView(String viewName) {
- return viewNameToView.get(viewName);
- }
-
- /**
- * Register a view for a view name.
- *
- * Registering another view with a name that is already registered
- * overwrites the old registration.
- *
- * @param viewName
- * String that identifies a view (not null nor empty string)
- * @param view
- * {@link View} instance (not null)
- */
- public void addView(String viewName, View view) {
-
- // Check parameters
- if (viewName == null || view == null || viewName.length() == 0) {
- throw new IllegalArgumentException(
- "view and viewName must be non-null and not empty");
+ if (this.viewName.equals(viewName)) {
+ return view;
}
-
- viewNameToView.put(viewName, view);
+ return null;
}
/**
- * Remove view from navigator.
+ * Get the view name for this provider.
*
- * @param viewName
- * name of the view to remove
+ * @return view name for this provider
*/
- public void removeView(String viewName) {
- viewNameToView.remove(viewName);
+ public String getViewName() {
+ return viewName;
}
}
/**
- * View provider which uses a map from view name to the class to instantiate
- * for the view.
- *
- * Views that have been created are cached and reused when the same view
- * name is requested again.
+ * View provider which maps a single view name to a class to instantiate for
+ * the view.
*
* Note that the view class must be accessible by the class loader used by
* the provider. This may require its visibility to be public.
+ *
+ * This class is primarily for internal use by {@link Navigator}.
*/
public static class ClassBasedViewProvider implements ViewProvider {
- private HashMap<String, Class<? extends View>> viewNameToClass = new HashMap<String, Class<? extends View>>();
- private HashMap<Class<? extends View>, String> classToViewName = new HashMap<Class<? extends View>, String>();
+ private final String viewName;
+ private final Class<? extends View> viewClass;
+
/**
- * Already opened (cached) views that can be reopened or reused with new
- * parameters.
+ * Create a new view provider which creates new view instances based on
+ * a view class.
+ *
+ * @param viewName
+ * name of the views to create (not null)
+ * @param viewClass
+ * class to instantiate when a view is requested (not null)
*/
- private HashMap<Class<? extends View>, View> classToView = new HashMap<Class<? extends View>, View>();
+ public ClassBasedViewProvider(String viewName,
+ Class<? extends View> viewClass) {
+ if (null == viewName || null == viewClass) {
+ throw new IllegalArgumentException(
+ "View name and class should not be null");
+ }
+ this.viewName = viewName;
+ this.viewClass = viewClass;
+ }
public String getViewName(String viewAndParameters) {
if (null == viewAndParameters) {
return null;
}
- for (String viewName : viewNameToClass.keySet()) {
- if (viewAndParameters.equals(viewName)
- || viewAndParameters.startsWith(viewName + "/")) {
- return viewName;
- }
+ if (viewAndParameters.equals(viewName)
+ || viewAndParameters.startsWith(viewName + "/")) {
+ return viewName;
}
return null;
}
public View getView(String viewName) {
- Class<? extends View> newViewClass = viewNameToClass.get(viewName);
- if (null == newViewClass) {
- return null;
- }
- if (!classToView.containsKey(newViewClass)) {
+ if (this.viewName.equals(viewName)) {
try {
- View view = newViewClass.newInstance();
- view.init();
- classToView.put(newViewClass, view);
+ View view = viewClass.newInstance();
+ return view;
} catch (InstantiationException e) {
// TODO error handling
throw new RuntimeException(e);
@@ -238,95 +240,30 @@ public class Navigator implements Serializable {
throw new RuntimeException(e);
}
}
- // return already cached view
- final View v = classToView.get(newViewClass);
- return v;
- }
-
- /**
- * Register a view class for a view name.
- *
- * @param viewName
- * String that identifies a view (not null nor empty string)
- * @param viewClass
- * Component class that implements Navigator.View interface
- * (not null)
- */
- public void addView(String viewName, Class<? extends View> viewClass) {
-
- // Check parameters
- if (viewName == null || viewClass == null || viewName.length() == 0) {
- throw new IllegalArgumentException(
- "viewClass and viewName must be non-null and not empty");
- }
-
- if (!View.class.isAssignableFrom(viewClass)) {
- throw new IllegalArgumentException(
- "viewClass must implement Navigator.View");
- }
-
- if (viewNameToClass.containsKey(viewName)) {
- if (viewNameToClass.get(viewName) == viewClass) {
- return;
- }
-
- throw new IllegalArgumentException(viewNameToClass
- .get(viewName).getName()
- + " is already mapped to '"
- + viewName + "'");
- }
-
- if (classToViewName.containsKey(viewClass)) {
- throw new IllegalArgumentException(
- "Each view class can only be added to Navigator with one view name");
- }
-
- viewNameToClass.put(viewName, viewClass);
- classToViewName.put(viewClass, viewName);
- }
-
- /**
- * Remove view from navigator.
- *
- * @param viewName
- * name of the view to remove
- */
- public void removeView(String viewName) {
- Class<? extends View> c = viewNameToClass.get(viewName);
- if (c != null) {
- viewNameToClass.remove(viewName);
- classToViewName.remove(c);
- classToView.remove(c);
- }
+ return null;
}
/**
- * Get the view name for given view implementation class.
+ * Get the view name for this provider.
*
- * @param viewClass
- * Class that implements the view.
- * @return view name for which the view class is registered, null if
- * none
+ * @return view name for this provider
*/
- public String getViewName(Class<? extends View> viewClass) {
- return classToViewName.get(viewClass);
+ public String getViewName() {
+ return viewName;
}
/**
- * Get the view class for given view name.
+ * Get the view class for this provider.
*
- * @param viewName
- * view name to get view for
- * @return View that corresponds to the name
+ * @return {@link View} class
*/
- public Class<? extends View> getViewClass(String viewName) {
- return viewNameToClass.get(viewName);
+ public Class<? extends View> getViewClass() {
+ return viewClass;
}
}
private final FragmentManager fragmentManager;
private final ViewDisplay display;
- private String mainViewName = null;
private View currentView = null;
private List<ViewChangeListener> listeners = new LinkedList<ViewChangeListener>();
private List<ViewProvider> providers = new LinkedList<ViewProvider>();
@@ -345,6 +282,19 @@ public class Navigator implements Serializable {
}
/**
+ * Create a navigator that is tracking the active view using URI fragments.
+ * By default, a {@link SimpleViewDisplay} is used and can be obtained using
+ * {@link #getDisplay()}.
+ *
+ * @param root
+ * whose URI fragments are used
+ */
+ public Navigator(Root root) {
+ display = new SimpleViewDisplay();
+ fragmentManager = new UriFragmentManager(root, this);
+ }
+
+ /**
* Create a navigator.
*
* When a custom fragment manager is not needed, use the constructor
@@ -369,37 +319,43 @@ public class Navigator implements Serializable {
* and (fragment) parameters. ViewProviders are used to find and create the
* correct type of view.
*
- * If the view being left indicates it wants a confirmation for the
+ * If multiple providers return a matching view, the view with the longest
+ * name is selected. This way, e.g. hierarchies of subviews can be
+ * registered like "admin/", "admin/users", "admin/settings" and the longest
+ * match is used.
+ *
+ * If the view being deactivated indicates it wants a confirmation for the
* navigation operation, the user is asked for the confirmation.
*
+ * Registered {@link ViewChangeListener}s are called upon successful view
+ * change.
+ *
* @param viewAndParameters
* view name and parameters
- * @param internalParameters
- * parameters that are only passed when switching views
- * explicitly on the server side, not bookmarkable
*/
- public void navigateTo(String viewAndParameters,
- Object... internalParameters) {
- if ("".equals(viewAndParameters)) {
- viewAndParameters = mainViewName;
- }
+ public void navigateTo(String viewAndParameters) {
+ String longestViewName = "";
+ View viewWithLongestName = null;
for (ViewProvider provider : providers) {
String viewName = provider.getViewName(viewAndParameters);
- if (null != viewName) {
- String parameters = null;
- if (viewAndParameters.length() > viewName.length() + 1) {
- parameters = viewAndParameters
- .substring(viewName.length() + 1);
- }
+ if (null != viewName
+ && viewName.length() > longestViewName.length()) {
View view = provider.getView(viewName);
if (null != view) {
- navigateTo(view, viewName, parameters, internalParameters);
- // stop after a view is found
- return;
+ longestViewName = viewName;
+ viewWithLongestName = view;
}
}
}
- // TODO if no view is found, use main view or do nothing?
+ if (viewWithLongestName != null) {
+ String parameters = null;
+ if (viewAndParameters.length() > longestViewName.length() + 1) {
+ parameters = viewAndParameters.substring(longestViewName
+ .length() + 1);
+ }
+ navigateTo(viewWithLongestName, longestViewName, parameters);
+ }
+ // TODO if no view is found, what to do?
}
/**
@@ -415,14 +371,12 @@ public class Navigator implements Serializable {
* (optional) name of the view or null not to set the fragment
* @param fragmentParameters
* parameters passed in the fragment for the view
- * @param internalParameters
- * parameters that are only passed when switching views
- * explicitly on the server side, not bookmarkable
*/
protected void navigateTo(View view, String viewName,
- String fragmentParameters, Object... internalParameters) {
- if (!isViewChangeAllowed(currentView, view, viewName,
- fragmentParameters, internalParameters)) {
+ String fragmentParameters) {
+ ViewChangeEvent event = new ViewChangeEvent(this, currentView, view,
+ viewName, fragmentParameters);
+ if (!isViewChangeAllowed(event)) {
return;
}
@@ -436,15 +390,14 @@ public class Navigator implements Serializable {
}
}
- view.navigateTo(fragmentParameters, internalParameters);
- View previousView = currentView;
+ view.navigateTo(fragmentParameters);
currentView = view;
if (display != null) {
display.showView(view);
}
- fireViewChange(previousView);
+ fireViewChange(event);
}
/**
@@ -457,26 +410,14 @@ public class Navigator implements Serializable {
* and save the parameters to re-initiate the navigation operation upon user
* action.
*
- * @param previous
- * the view being deactivated
- * @param next
- * the view being activated
- * @param viewName
- * name of the view being activated
- * @param fragmentParameters
- * parameters passed in the fragment for the view
- * @param internalParameters
- * parameters that are only passed on the server side, not
- * bookmarkable
+ * @param event
+ * view change event (not null, view change not yet performed)
* @return true if the view change should be allowed, false to silently
* block the navigation operation
*/
- protected boolean isViewChangeAllowed(View previous, View next,
- String viewName, String fragmentParameters,
- Object[] internalParameters) {
+ protected boolean isViewChangeAllowed(ViewChangeEvent event) {
for (ViewChangeListener l : listeners) {
- if (!l.isViewChangeAllowed(previous, next, viewName,
- fragmentParameters, internalParameters)) {
+ if (!l.isViewChangeAllowed(event)) {
return false;
}
}
@@ -494,43 +435,101 @@ public class Navigator implements Serializable {
}
/**
- * Get the main view.
+ * Returns the ViewDisplay used by the navigator. Unless another display is
+ * specified, a {@link SimpleViewDisplay} (which is a {@link Component}) is
+ * used by default.
*
- * Main view is the default view shown to user when he opens application
- * without specifying view name.
+ * @return current ViewDisplay
+ */
+ public ViewDisplay getDisplay() {
+ return display;
+ }
+
+ /**
+ * Fire an event when the current view has changed.
*
- * @return name of the main view.
+ * @param event
+ * view change event (not null)
*/
- public String getMainView() {
- return mainViewName;
+ protected void fireViewChange(ViewChangeEvent event) {
+ for (ViewChangeListener l : listeners) {
+ l.navigatorViewChanged(event);
+ }
}
/**
- * Set the main view.
+ * Register a static, pre-initialized view instance for a view name.
*
- * Main view is the default view shown to user when he opens application
- * without specifying view name. If the {@link Navigator} does not have a
- * current view, the main view is automatically selected when set.
+ * Registering another view with a name that is already registered
+ * overwrites the old registration of the same type.
*
- * @param mainViewName
- * name of the main view.
+ * @param viewName
+ * String that identifies a view (not null nor empty string)
+ * @param view
+ * {@link View} instance (not null)
*/
- public void setMainView(String mainViewName) {
- this.mainViewName = mainViewName;
- // TODO should this be done?
- if (currentView == null) {
- navigateTo(mainViewName);
+ public void addView(String viewName, View view) {
+
+ // Check parameters
+ if (viewName == null || view == null) {
+ throw new IllegalArgumentException(
+ "view and viewName must be non-null");
}
+
+ removeView(viewName);
+ registerProvider(new StaticViewProvider(viewName, view));
}
/**
- * Fire an event when the current view has changed.
+ * Register for a view name a view class.
+ *
+ * Registering another view with a name that is already registered
+ * overwrites the old registration of the same type.
+ *
+ * A new view instance is created every time a view is requested.
+ *
+ * @param viewName
+ * String that identifies a view (not null nor empty string)
+ * @param viewClass
+ * {@link View} class to instantiate when a view is requested
+ * (not null)
+ */
+ public void addView(String viewName, Class<? extends View> viewClass) {
+
+ // Check parameters
+ if (viewName == null || viewClass == null) {
+ throw new IllegalArgumentException(
+ "view and viewClass must be non-null");
+ }
+
+ removeView(viewName);
+ registerProvider(new ClassBasedViewProvider(viewName, viewClass));
+ }
+
+ /**
+ * Remove view from navigator.
+ *
+ * This method only applies to views registered using
+ * {@link #addView(String, View)} or {@link #addView(String, Class)}.
*
- * @param previousView
+ * @param viewName
+ * name of the view to remove
*/
- protected void fireViewChange(View previousView) {
- for (ViewChangeListener l : listeners) {
- l.navigatorViewChanged(previousView, currentView);
+ public void removeView(String viewName) {
+ Iterator<ViewProvider> it = providers.iterator();
+ while (it.hasNext()) {
+ ViewProvider provider = it.next();
+ if (provider instanceof StaticViewProvider) {
+ StaticViewProvider staticProvider = (StaticViewProvider) provider;
+ if (staticProvider.getViewName().equals(viewName)) {
+ it.remove();
+ }
+ } else if (provider instanceof ClassBasedViewProvider) {
+ ClassBasedViewProvider classBasedProvider = (ClassBasedViewProvider) provider;
+ if (classBasedProvider.getViewName().equals(viewName)) {
+ it.remove();
+ }
+ }
}
}
diff --git a/src/com/vaadin/navigator/View.java b/src/com/vaadin/navigator/View.java
index efa13fbac2..4d135b4c0b 100644
--- a/src/com/vaadin/navigator/View.java
+++ b/src/com/vaadin/navigator/View.java
@@ -20,15 +20,6 @@ import com.vaadin.ui.Component;
public interface View extends Serializable {
/**
- * Init view.
- *
- * Convenience method which navigator calls just before the view is rendered
- * for the first time. This is called only once in the lifetime of each view
- * instance.
- */
- public void init();
-
- /**
* This view is navigated to.
*
* This method is always called before the view is shown on screen. If there
@@ -40,11 +31,6 @@ public interface View extends Serializable {
* @param fragmentParameters
* parameters to the view or null if none given. This is the
* string that appears e.g. in URI after "viewname/"
- * @param internalParameters
- * parameters given directly to
- * {@link Navigator#navigateTo(String, Object...)}, not passed
- * via the fragment and not preserved in bookmarks
*/
- public void navigateTo(String fragmentParameters,
- Object... internalParameters);
+ public void navigateTo(String fragmentParameters);
} \ No newline at end of file
diff --git a/src/com/vaadin/navigator/ViewChangeListener.java b/src/com/vaadin/navigator/ViewChangeListener.java
index 9ac48e27cc..2eb34e6fcf 100644
--- a/src/com/vaadin/navigator/ViewChangeListener.java
+++ b/src/com/vaadin/navigator/ViewChangeListener.java
@@ -5,6 +5,7 @@
package com.vaadin.navigator;
import java.io.Serializable;
+import java.util.EventObject;
/**
* Interface for listening to View changes before and after they occur.
@@ -18,6 +19,77 @@ import java.io.Serializable;
public interface ViewChangeListener extends Serializable {
/**
+ * Event received by the listener for attempted and executed view changes.
+ */
+ public static class ViewChangeEvent extends EventObject {
+ private final View oldView;
+ private final View newView;
+ private final String viewName;
+ private final String fragmentParameters;
+
+ /**
+ * Create a new view change event.
+ *
+ * @param navigator
+ * Navigator that triggered the event, not null
+ */
+ public ViewChangeEvent(Navigator navigator, View oldView, View newView,
+ String viewName, String fragmentParameters) {
+ super(navigator);
+ this.oldView = oldView;
+ this.newView = newView;
+ this.viewName = viewName;
+ this.fragmentParameters = fragmentParameters;
+ }
+
+ /**
+ * Returns the navigator that triggered this event.
+ *
+ * @return Navigator (not null)
+ */
+ public Navigator getNavigator() {
+ return (Navigator) getSource();
+ }
+
+ /**
+ * Returns the view being deactivated.
+ *
+ * @return old View
+ */
+ public View getOldView() {
+ return oldView;
+ }
+
+ /**
+ * Returns the view being activated.
+ *
+ * @return new View
+ */
+ public View getNewView() {
+ return newView;
+ }
+
+ /**
+ * Returns the view name of the view being activated.
+ *
+ * @return view name of the new View
+ */
+ public String getViewName() {
+ return viewName;
+ }
+
+ /**
+ * Returns the parameters for the view being activated.
+ *
+ * @return fragment parameters (potentially bookmarkable) for the new
+ * view
+ */
+ public String getFragmentParameters() {
+ return fragmentParameters;
+ }
+ }
+
+ /**
* Check whether changing the view is permissible.
*
* This method may also e.g. open a "save" dialog or question about the
@@ -27,36 +99,20 @@ public interface ViewChangeListener extends Serializable {
* know the view in question), it should return true. If any listener
* returns false, the view change is not allowed.
*
- * TODO move to separate interface?
- *
- * @param previous
- * view that is being deactivated
- * @param next
- * view that is being activated
- * @param viewName
- * name of the new view that is being activated
- * @param fragmentParameters
- * fragment parameters (potentially bookmarkable) for the new
- * view
- * @param internalParameters
- * internal parameters for the new view, not visible in the
- * browser
+ * @param event
+ * view change event
* @return true if the view change should be allowed or this listener does
* not care about the view change, false to block the change
*/
- public boolean isViewChangeAllowed(View previous, View next,
- String viewName, String fragmentParameters,
- Object... internalParameters);
+ public boolean isViewChangeAllowed(ViewChangeEvent event);
/**
* Invoked after the view has changed. Be careful for deadlocks if you
* decide to change the view again in the listener.
*
- * @param previous
- * Preview view before the change.
- * @param current
- * New view after the change.
+ * @param event
+ * view change event
*/
- public void navigatorViewChanged(View previous, View current);
+ public void navigatorViewChanged(ViewChangeEvent event);
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/AbstractClientConnector.java b/src/com/vaadin/terminal/AbstractClientConnector.java
index 136ed702fc..a2bf4a0be8 100644
--- a/src/com/vaadin/terminal/AbstractClientConnector.java
+++ b/src/com/vaadin/terminal/AbstractClientConnector.java
@@ -334,7 +334,7 @@ public abstract class AbstractClientConnector implements ClientConnector {
public void requestRepaintAll() {
requestRepaint();
- for (ClientConnector connector : getAllChildrenIteratable(this)) {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.requestRepaintAll();
}
}
@@ -371,7 +371,7 @@ public abstract class AbstractClientConnector implements ClientConnector {
}
}
- public static Iterable<ClientConnector> getAllChildrenIteratable(
+ public static Iterable<ClientConnector> getAllChildrenIterable(
final ClientConnector connector) {
return new AllChildrenIterable(connector);
}
@@ -442,13 +442,13 @@ public abstract class AbstractClientConnector implements ClientConnector {
public void attach() {
requestRepaint();
- for (ClientConnector connector : getAllChildrenIteratable(this)) {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.attach();
}
}
public void detach() {
- for (ClientConnector connector : getAllChildrenIteratable(this)) {
+ for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.detach();
}
}
diff --git a/src/com/vaadin/terminal/AbstractExtension.java b/src/com/vaadin/terminal/AbstractExtension.java
index 35123bc488..353db85a18 100644
--- a/src/com/vaadin/terminal/AbstractExtension.java
+++ b/src/com/vaadin/terminal/AbstractExtension.java
@@ -13,6 +13,10 @@ public abstract class AbstractExtension extends AbstractClientConnector
return ClientConnector.class;
}
+ protected void attachTo(AbstractClientConnector parent) {
+ parent.addExtension(this);
+ }
+
@Override
public void setParent(ClientConnector parent) {
Class<? extends ClientConnector> acceptedParentType = getAcceptedParentType();
diff --git a/src/com/vaadin/terminal/AbstractJavascriptExtension.java b/src/com/vaadin/terminal/AbstractJavascriptExtension.java
new file mode 100644
index 0000000000..e741e2af1e
--- /dev/null
+++ b/src/com/vaadin/terminal/AbstractJavascriptExtension.java
@@ -0,0 +1,20 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import com.vaadin.ui.JavascriptCallback;
+
+public class AbstractJavascriptExtension extends AbstractExtension {
+ private JavascriptRpcHelper rpcHelper = new JavascriptRpcHelper(this);
+
+ protected void registerCallback(String functionName,
+ JavascriptCallback javascriptCallback) {
+ rpcHelper.registerCallback(functionName, javascriptCallback);
+ }
+
+ protected void invokeCallback(String name, Object... arguments) {
+ rpcHelper.invokeCallback(name, arguments);
+ }
+}
diff --git a/src/com/vaadin/terminal/JavascriptRpcHelper.java b/src/com/vaadin/terminal/JavascriptRpcHelper.java
new file mode 100644
index 0000000000..b566462833
--- /dev/null
+++ b/src/com/vaadin/terminal/JavascriptRpcHelper.java
@@ -0,0 +1,61 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.JavascriptCallback;
+import com.vaadin.ui.JavascriptManager.JavascriptCallbackRpc;
+
+public class JavascriptRpcHelper {
+
+ private static final Method CALL_METHOD = ReflectTools.findMethod(
+ JavascriptCallbackRpc.class, "call", String.class, JSONArray.class);
+ private AbstractClientConnector connector;
+
+ private Map<String, JavascriptCallback> callbacks = new HashMap<String, JavascriptCallback>();
+ private JavascriptCallbackRpc javascriptCallbackRpc;
+
+ public JavascriptRpcHelper(AbstractClientConnector connector) {
+ this.connector = connector;
+ }
+
+ public void registerCallback(String functionName,
+ JavascriptCallback javascriptCallback) {
+ callbacks.put(functionName, javascriptCallback);
+ ensureRpc();
+ }
+
+ private void ensureRpc() {
+ if (javascriptCallbackRpc == null) {
+ javascriptCallbackRpc = new JavascriptCallbackRpc() {
+ public void call(String name, JSONArray arguments) {
+ JavascriptCallback callback = callbacks.get(name);
+ try {
+ callback.call(arguments);
+ } catch (JSONException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ };
+ connector.registerRpc(javascriptCallbackRpc);
+ }
+ }
+
+ public void invokeCallback(String name, Object... arguments) {
+ JSONArray args = new JSONArray(Arrays.asList(arguments));
+ connector.addMethodInvocationToQueue(
+ JavascriptCallbackRpc.class.getName(), CALL_METHOD,
+ new Object[] { name, args });
+ connector.requestRepaint();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index e8013ccc72..7d11c6245a 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -40,6 +40,7 @@ import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
@@ -1438,12 +1439,19 @@ public class ApplicationConnection {
.getConnector(connectorId);
if (null != connector) {
- JSONObject stateDataAndType = new JSONObject(
+ JSONObject stateJson = new JSONObject(
states.getJavaScriptObject(connectorId));
+ if (connector instanceof HasJavascriptConnectorHelper) {
+ ((HasJavascriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper()
+ .setNativeState(
+ stateJson.getJavaScriptObject());
+ }
+
SharedState state = connector.getState();
JsonDecoder.decodeValue(new Type(state.getClass()
- .getName(), null), stateDataAndType, state,
+ .getName(), null), stateJson, state,
ApplicationConnection.this);
StateChangeEvent event = GWT
@@ -1584,11 +1592,8 @@ public class ApplicationConnection {
for (int i = 0; i < rpcLength; i++) {
try {
JSONArray rpcCall = (JSONArray) rpcCalls.get(i);
- MethodInvocation invocation = parseMethodInvocation(rpcCall);
- VConsole.log("Server to client RPC call: "
- + invocation);
- rpcManager.applyInvocation(invocation,
- getConnectorMap());
+ rpcManager.parseAndApplyInvocation(rpcCall,
+ ApplicationConnection.this);
} catch (final Throwable e) {
VConsole.error(e);
}
@@ -1601,26 +1606,6 @@ public class ApplicationConnection {
ApplicationConfiguration.runWhenWidgetsLoaded(c);
}
- private MethodInvocation parseMethodInvocation(JSONArray rpcCall) {
- String connectorId = ((JSONString) rpcCall.get(0)).stringValue();
- String interfaceName = ((JSONString) rpcCall.get(1)).stringValue();
- String methodName = ((JSONString) rpcCall.get(2)).stringValue();
- JSONArray parametersJson = (JSONArray) rpcCall.get(3);
-
- MethodInvocation methodInvocation = new MethodInvocation(connectorId,
- interfaceName, methodName);
- Type[] parameterTypes = rpcManager.getParameterTypes(methodInvocation);
-
- Object[] parameters = new Object[parametersJson.size()];
- for (int j = 0; j < parametersJson.size(); ++j) {
- parameters[j] = JsonDecoder.decodeValue(parameterTypes[j],
- parametersJson.get(j), null, this);
- }
-
- methodInvocation.setParameters(parameters);
- return methodInvocation;
- }
-
// Redirect browser, null reloads current page
private static native void redirect(String url)
/*-{
@@ -2169,8 +2154,8 @@ public class ApplicationConnection {
private ServerConnector createAndRegisterConnector(String connectorId,
int connectorType) {
// Create and register a new connector with the given type
- ServerConnector p = widgetSet
- .createConnector(connectorType, configuration);
+ ServerConnector p = widgetSet.createConnector(connectorType,
+ configuration);
connectorMap.registerConnector(connectorId, p);
p.doInit(connectorId, this);
diff --git a/src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java b/src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java
new file mode 100644
index 0000000000..ab0e62222c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavascriptConnectorHelper.java
@@ -0,0 +1,205 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.ArrayList;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.json.client.JSONArray;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+
+public class JavascriptConnectorHelper {
+
+ private final ServerConnector connector;
+ private final JavaScriptObject nativeState = JavaScriptObject
+ .createObject();
+ private final JavaScriptObject rpcMap = JavaScriptObject.createObject();
+
+ private JavaScriptObject connectorWrapper;
+ private int tag;
+
+ public JavascriptConnectorHelper(ServerConnector connector) {
+ this.connector = connector;
+ }
+
+ public boolean init() {
+ ApplicationConfiguration conf = connector.getConnection()
+ .getConfiguration();
+ ArrayList<String> attemptedNames = new ArrayList<String>();
+ Integer tag = Integer.valueOf(this.tag);
+ while (tag != null) {
+ String serverSideClassName = conf.getServerSideClassNameForTag(tag);
+ String initFunctionName = serverSideClassName
+ .replaceAll("\\.", "_");
+ if (tryInitJs(initFunctionName, getConnectorWrapper())) {
+ VConsole.log("Javascript connector initialized using "
+ + initFunctionName);
+ return true;
+ } else {
+ VConsole.log("No javascript function " + initFunctionName
+ + " found");
+ attemptedNames.add(initFunctionName);
+ tag = conf.getParentTag(tag.intValue());
+ }
+ }
+ VConsole.log("No javascript init for connector not found");
+ showInitProblem(attemptedNames);
+ return false;
+ }
+
+ protected void showInitProblem(ArrayList<String> attemptedNames) {
+ // Default does nothing
+ }
+
+ private static native boolean tryInitJs(String initFunctionName,
+ JavaScriptObject connectorWrapper)
+ /*-{
+ if (typeof $wnd[initFunctionName] == 'function') {
+ $wnd[initFunctionName].apply(connectorWrapper);
+ return true;
+ } else {
+ return false;
+ }
+ }-*/;
+
+ private JavaScriptObject getConnectorWrapper() {
+ if (connectorWrapper == null) {
+ connectorWrapper = createConnectorWrapper();
+ }
+
+ return connectorWrapper;
+ }
+
+ protected JavaScriptObject createConnectorWrapper() {
+ return createConnectorWrapper(this, nativeState, rpcMap,
+ connector.getConnectorId());
+ }
+
+ public void fireNativeStateChange() {
+ fireNativeStateChange(getConnectorWrapper());
+ }
+
+ private static native void fireNativeStateChange(
+ JavaScriptObject connectorWrapper)
+ /*-{
+ if (typeof connectorWrapper.onStateChange == 'function') {
+ connectorWrapper.onStateChange();
+ }
+ }-*/;
+
+ private static native JavaScriptObject createConnectorWrapper(
+ JavascriptConnectorHelper h, JavaScriptObject nativeState,
+ JavaScriptObject registeredRpc, String connectorId)
+ /*-{
+ return {
+ 'getConnectorId': function() {
+ return connectorId;
+ },
+ 'getState': function() {
+ return nativeState;
+ },
+ 'getRpcProxyFunction': function(iface, method) {
+ return $entry(function() {
+ h.@com.vaadin.terminal.gwt.client.JavascriptConnectorHelper::fireRpc(Ljava/lang/String;Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(iface, method, arguments);
+ });
+ },
+ 'getCallback': function(name) {
+ return $entry(function() {
+ var args = [name, Array.prototype.slice.call(arguments, 0)];
+ var iface = "com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc";
+ var method = "call";
+ h.@com.vaadin.terminal.gwt.client.JavascriptConnectorHelper::fireRpc(Ljava/lang/String;Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(iface, method, args);
+ });
+ },
+ 'registerCallback': function(name, callback) {
+ //TODO maintain separate map
+ if (!registeredRpc[name]) {
+ registeredRpc[name] = [];
+ }
+ registeredRpc[name].push(callback);
+ },
+ 'registerRpc': function(iface, rpcHandler) {
+ if (!registeredRpc[iface]) {
+ registeredRpc[iface] = [];
+ }
+ registeredRpc[iface].push(rpcHandler);
+ },
+ };
+ }-*/;
+
+ private void fireRpc(String iface, String method,
+ JsArray<JavaScriptObject> arguments) {
+ JSONArray argumentsArray = new JSONArray(arguments);
+ Object[] parameters = new Object[arguments.length()];
+ for (int i = 0; i < parameters.length; i++) {
+ parameters[i] = argumentsArray.get(i);
+ }
+ connector.getConnection().addMethodInvocationToQueue(
+ new MethodInvocation(connector.getConnectorId(), iface, method,
+ parameters), true);
+ }
+
+ public void setNativeState(JavaScriptObject state) {
+ updateNativeState(nativeState, state);
+ }
+
+ private static native void updateNativeState(JavaScriptObject state,
+ JavaScriptObject input)
+ /*-{
+ // Copy all fields to existing state object
+ for(var key in state) {
+ if (state.hasOwnProperty(key)) {
+ delete state[key];
+ }
+ }
+
+ for(var key in input) {
+ if (input.hasOwnProperty(key)) {
+ state[key] = input[key];
+ }
+ }
+ }-*/;
+
+ public Object[] decodeRpcParameters(JSONArray parametersJson) {
+ return new Object[] { parametersJson.getJavaScriptObject() };
+ }
+
+ public void setTag(int tag) {
+ this.tag = tag;
+ }
+
+ public void invokeJsRpc(MethodInvocation invocation,
+ JSONArray parametersJson) {
+ if ("com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc"
+ .equals(invocation.getInterfaceName())
+ && "call".equals(invocation.getMethodName())) {
+ invokeJsRpc(rpcMap, parametersJson.get(0).isString().stringValue(),
+ null, parametersJson.get(1).isArray().getJavaScriptObject());
+ } else {
+ invokeJsRpc(rpcMap, invocation.getInterfaceName(),
+ invocation.getMethodName(),
+ parametersJson.getJavaScriptObject());
+ }
+ }
+
+ private static native void invokeJsRpc(JavaScriptObject rpcMap,
+ String interfaceName, String methodName, JavaScriptObject parameters)
+ /*-{
+ var targets = rpcMap[interfaceName];
+ if (!targets) {
+ return;
+ }
+ for(var i = 0; i < targets.length; i++) {
+ var target = targets[i];
+ if (methodName === null && typeof target === 'function') {
+ target.apply($wnd, parameters);
+ } else {
+ target[methodName].apply(target, parameters);
+ }
+ }
+ }-*/;
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/JavascriptExtension.java b/src/com/vaadin/terminal/gwt/client/JavascriptExtension.java
new file mode 100644
index 0000000000..6c098a52f6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/JavascriptExtension.java
@@ -0,0 +1,33 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import com.vaadin.terminal.AbstractJavascriptExtension;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+
+@Connect(AbstractJavascriptExtension.class)
+public class JavascriptExtension extends AbstractConnector implements
+ HasJavascriptConnectorHelper {
+ private final JavascriptConnectorHelper helper = new JavascriptConnectorHelper(
+ this);
+
+ @Override
+ protected void init() {
+ helper.init();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ helper.fireNativeStateChange();
+ }
+
+ public JavascriptConnectorHelper getJavascriptConnectorHelper() {
+ return helper;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetSet.java b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
index d7cc2df00d..ecbfb0ecc9 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetSet.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
@@ -5,6 +5,7 @@
package com.vaadin.terminal.gwt.client;
import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
public class WidgetSet {
@@ -52,7 +53,12 @@ public class WidgetSet {
/*
* let the auto generated code instantiate this type
*/
- return widgetMap.instantiate(classType);
+ ServerConnector connector = widgetMap.instantiate(classType);
+ if (connector instanceof HasJavascriptConnectorHelper) {
+ ((HasJavascriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper().setTag(tag);
+ }
+ return connector;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java b/src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java
new file mode 100644
index 0000000000..74bc75da66
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/HasJavascriptConnectorHelper.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.vaadin.terminal.gwt.client.JavascriptConnectorHelper;
+
+public interface HasJavascriptConnectorHelper {
+ public JavascriptConnectorHelper getJavascriptConnectorHelper();
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
index 657f44896d..cb7dbe5e72 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
@@ -59,6 +59,8 @@ public class JsonEncoder {
boolean restrictToInternalTypes, ApplicationConnection connection) {
if (null == value) {
return JSONNull.getInstance();
+ } else if (value instanceof JSONValue) {
+ return (JSONValue) value;
} else if (value instanceof String[]) {
String[] array = (String[]) value;
JSONArray jsonArray = new JSONArray();
diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
index 1d3447687d..e0ffb40125 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
@@ -9,8 +9,12 @@ import java.util.HashMap;
import java.util.Map;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONString;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.VConsole;
/**
* Client side RPC manager that can invoke methods based on RPC calls received
@@ -41,20 +45,10 @@ public class RpcManager {
*
* @param invocation
* method to invoke
- * @param connectorMap
- * mapper used to find Connector for the method call and any
- * connectors referenced in parameters
*/
public void applyInvocation(MethodInvocation invocation,
- ConnectorMap connectorMap) {
- ServerConnector connector = connectorMap.getConnector(invocation
- .getConnectorId());
+ ServerConnector connector) {
String signature = getSignature(invocation);
- if (connector == null) {
- throw new IllegalStateException("Target connector ("
- + invocation.getConnectorId() + ") not found for RCC to "
- + signature);
- }
RpcMethod rpcMethod = getRpcMethod(signature);
Collection<ClientRpc> implementations = connector
@@ -82,4 +76,47 @@ public class RpcManager {
return getRpcMethod(getSignature(invocation)).getParameterTypes();
}
+ public void parseAndApplyInvocation(JSONArray rpcCall,
+ ApplicationConnection connection) {
+ ConnectorMap connectorMap = ConnectorMap.get(connection);
+
+ String connectorId = ((JSONString) rpcCall.get(0)).stringValue();
+ String interfaceName = ((JSONString) rpcCall.get(1)).stringValue();
+ String methodName = ((JSONString) rpcCall.get(2)).stringValue();
+ JSONArray parametersJson = (JSONArray) rpcCall.get(3);
+
+ ServerConnector connector = connectorMap.getConnector(connectorId);
+
+ MethodInvocation invocation = new MethodInvocation(connectorId,
+ interfaceName, methodName);
+ if (connector instanceof HasJavascriptConnectorHelper) {
+ ((HasJavascriptConnectorHelper) connector)
+ .getJavascriptConnectorHelper().invokeJsRpc(invocation,
+ parametersJson);
+ } else {
+ if (connector == null) {
+ throw new IllegalStateException("Target connector ("
+ + connector + ") not found for RCC to "
+ + getSignature(invocation));
+ }
+
+ parseMethodParameters(invocation, parametersJson, connection);
+ VConsole.log("Server to client RPC call: " + invocation);
+ applyInvocation(invocation, connector);
+ }
+ }
+
+ private void parseMethodParameters(MethodInvocation methodInvocation,
+ JSONArray parametersJson, ApplicationConnection connection) {
+ Type[] parameterTypes = getParameterTypes(methodInvocation);
+
+ Object[] parameters = new Object[parametersJson.size()];
+ for (int j = 0; j < parametersJson.size(); ++j) {
+ parameters[j] = JsonDecoder.decodeValue(parameterTypes[j],
+ parametersJson.get(j), null, connection);
+ }
+
+ methodInvocation.setParameters(parameters);
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java
new file mode 100644
index 0000000000..979a6d22c4
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java
@@ -0,0 +1,78 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.json.client.JSONArray;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.JavascriptManager;
+
+@Connect(JavascriptManager.class)
+public class JavascriptManagerConnector extends AbstractConnector {
+ private Set<String> currentNames = new HashSet<String>();
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ Set<String> newNames = getState().getNames();
+
+ // Current names now only contains orphan callbacks
+ currentNames.removeAll(newNames);
+
+ for (String name : currentNames) {
+ removeCallback(name);
+ }
+
+ currentNames = new HashSet<String>(newNames);
+ for (String name : newNames) {
+ addCallback(name);
+ }
+ }
+
+ // TODO Ensure we don't overwrite anything (important) in $wnd
+ private native void addCallback(String name)
+ /*-{
+ var m = this;
+ $wnd[name] = $entry(function() {
+ //Must make a copy because arguments is an array-like object (not instanceof Array), causing suboptimal JSON encoding
+ var args = Array.prototype.slice.call(arguments, 0);
+ m.@com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavascriptManagerConnector::sendRpc(Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(name, args);
+ });
+ }-*/;
+
+ // TODO only remove what we actually added
+ private native void removeCallback(String name)
+ /*-{
+ delete $wnd[name];
+ }-*/;
+
+ public void sendRpc(String name, JsArray<JavaScriptObject> arguments) {
+ Object[] parameters = new Object[] { name, new JSONArray(arguments) };
+
+ /*
+ * Must invoke manually as the RPC interface can't be used in GWT
+ * because of the JSONArray parameter
+ */
+ getConnection()
+ .addMethodInvocationToQueue(
+ new MethodInvocation(
+ getConnectorId(),
+ "com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc",
+ "call", parameters), true);
+ }
+
+ @Override
+ public JavascriptManagerState getState() {
+ return (JavascriptManagerState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java
new file mode 100644
index 0000000000..77794ffdca
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java
@@ -0,0 +1,22 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+
+public class JavascriptManagerState extends SharedState {
+ private Set<String> names = new HashSet<String>();
+
+ public Set<String> getNames() {
+ return names;
+ }
+
+ public void setNames(Set<String> names) {
+ this.names = names;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java
new file mode 100644
index 0000000000..57e65e91c6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/JavascriptComponentConnector.java
@@ -0,0 +1,60 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.Element;
+import com.vaadin.terminal.gwt.client.JavascriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.HasJavascriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.ui.AbstractJavascriptComponent;
+
+@Connect(AbstractJavascriptComponent.class)
+public class JavascriptComponentConnector extends AbstractComponentConnector
+ implements HasJavascriptConnectorHelper {
+
+ private final JavascriptConnectorHelper helper = new JavascriptConnectorHelper(
+ this) {
+ @Override
+ protected void showInitProblem(
+ java.util.ArrayList<String> attemptedNames) {
+ getWidget().showNoInitFound(attemptedNames);
+ }
+
+ @Override
+ protected JavaScriptObject createConnectorWrapper() {
+ JavaScriptObject connectorWrapper = super.createConnectorWrapper();
+ addGetWidgetElement(connectorWrapper, getWidget().getElement());
+ return connectorWrapper;
+ }
+ };
+
+ @Override
+ protected void init() {
+ helper.init();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ helper.fireNativeStateChange();
+ }
+
+ private static native void addGetWidgetElement(
+ JavaScriptObject connectorWrapper, Element element)
+ /*-{
+ connectorWrapper.getWidgetElement = function() {
+ return element;
+ };
+ }-*/;
+
+ @Override
+ public JavascriptWidget getWidget() {
+ return (JavascriptWidget) super.getWidget();
+ }
+
+ public JavascriptConnectorHelper getJavascriptConnectorHelper() {
+ return helper;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java b/src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java
new file mode 100644
index 0000000000..93a4417b1c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/JavascriptWidget.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.ArrayList;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.ui.Widget;
+
+public class JavascriptWidget extends Widget {
+ public JavascriptWidget() {
+ setElement(Document.get().createDivElement());
+ }
+
+ public void showNoInitFound(ArrayList<String> attemptedNames) {
+ String message = "Could not initialize JavascriptConnector because no javascript init function was found. Make sure one of these functions are defined: <ul>";
+ for (String name : attemptedNames) {
+ message += "<li>" + name + "</li>";
+ }
+ message += "</ul>";
+
+ getElement().setInnerHTML(message);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index b77dc961d6..86bbb4e80b 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -899,7 +899,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
JSONArray children = new JSONArray();
for (ClientConnector child : AbstractClientConnector
- .getAllChildrenIteratable(connector)) {
+ .getAllChildrenIterable(connector)) {
if (isVisible(child)) {
children.put(child.getConnectorId());
}
diff --git a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
index 8a0c700121..ae1fadd91b 100644
--- a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
+++ b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
@@ -9,6 +9,10 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
@@ -16,6 +20,7 @@ import javax.servlet.http.HttpServletResponse;
import com.vaadin.Application;
import com.vaadin.RootRequiresMoreInformationException;
import com.vaadin.Version;
+import com.vaadin.annotations.LoadScripts;
import com.vaadin.external.json.JSONException;
import com.vaadin.external.json.JSONObject;
import com.vaadin.terminal.DeploymentConfiguration;
@@ -467,15 +472,15 @@ public abstract class BootstrapHandler implements RequestHandler {
page.write("<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\"/>\n");
page.write("<style type=\"text/css\">"
- + "html, body {height:100%;margin:0;}</style>");
+ + "html, body {height:100%;margin:0;}</style>\n");
// Add favicon links
if (themeName != null) {
String themeUri = getThemeUri(context, themeName);
page.write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\""
- + themeUri + "/favicon.ico\" />");
+ + themeUri + "/favicon.ico\" />\n");
page.write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\""
- + themeUri + "/favicon.ico\" />");
+ + themeUri + "/favicon.ico\" />\n");
}
Root root = context.getRoot();
@@ -484,7 +489,51 @@ public abstract class BootstrapHandler implements RequestHandler {
page.write("<title>"
+ AbstractApplicationServlet.safeEscapeForHtml(title)
- + "</title>");
+ + "</title>\n");
+
+ if (root != null) {
+ List<LoadScripts> loadScriptsAnnotations = getAnnotationsFor(
+ root.getClass(), LoadScripts.class);
+ Collections.reverse(loadScriptsAnnotations);
+ // Begin from the end as a class might requests scripts that depend
+ // on script loaded by a super class
+ for (int i = loadScriptsAnnotations.size() - 1; i >= 0; i--) {
+ LoadScripts loadScripts = loadScriptsAnnotations.get(i);
+ String[] value = loadScripts.value();
+ if (value != null) {
+ for (String script : value) {
+ page.write("<script type='text/javascript' src='");
+ page.write(script);
+ page.write("'></script>\n");
+ }
+ }
+ }
+
+ }
+ }
+
+ private static <T extends Annotation> List<T> getAnnotationsFor(
+ Class<?> type, Class<T> annotationType) {
+ List<T> list = new ArrayList<T>();
+ // Find from the class hierarchy
+ Class<?> currentType = type;
+ while (currentType != Object.class) {
+ T annotation = currentType.getAnnotation(annotationType);
+ if (annotation != null) {
+ list.add(annotation);
+ }
+ currentType = currentType.getSuperclass();
+ }
+
+ // Find from an implemented interface
+ for (Class<?> iface : type.getInterfaces()) {
+ T annotation = iface.getAnnotation(annotationType);
+ if (annotation != null) {
+ list.add(annotation);
+ }
+ }
+
+ return list;
}
/**
diff --git a/src/com/vaadin/terminal/gwt/server/ClientConnector.java b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
index 6830aa8e14..359e112738 100644
--- a/src/com/vaadin/terminal/gwt/server/ClientConnector.java
+++ b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
@@ -139,5 +139,5 @@ public interface ClientConnector extends Connector, RpcTarget {
public Iterator<Extension> getExtensionIterator();
- public void removeExtension(Extension feature);
+ public void removeExtension(Extension extension);
}
diff --git a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
index f5bef0b568..101e7f0cb3 100644
--- a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
+++ b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
@@ -296,7 +296,7 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
}
@Override
- public void removeExtension(Extension feature) {
+ public void removeExtension(Extension extension) {
// TODO Auto-generated method stub
}
diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
index aa61c68338..71e4727164 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
@@ -123,6 +123,9 @@ public class JsonCodec implements Serializable {
// Try to decode object using fields
if (value == JSONObject.NULL) {
return null;
+ } else if (targetType == JSONObject.class
+ || targetType == JSONArray.class) {
+ return value;
} else {
return decodeObject(targetType, (JSONObject) value, application);
}
@@ -400,7 +403,7 @@ public class JsonCodec implements Serializable {
boolean restrictToInternalTypes, JSONArray jsonArray,
Application application) throws JSONException {
HashSet<Object> set = new HashSet<Object>();
- set.addAll(decodeList(List.class, restrictToInternalTypes, jsonArray,
+ set.addAll(decodeList(targetType, restrictToInternalTypes, jsonArray,
application));
return set;
}
@@ -514,6 +517,8 @@ public class JsonCodec implements Serializable {
return connector.getConnectorId();
} else if (value instanceof Enum) {
return encodeEnum((Enum<?>) value, application);
+ } else if (value instanceof JSONArray || value instanceof JSONObject) {
+ return value;
} else {
// Any object that we do not know how to encode we encode by looping
// through fields
diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
index 6f278f7797..95565c4379 100644
--- a/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
+++ b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
@@ -79,6 +79,12 @@ public class ServerRpcMethodInvocation extends MethodInvocation {
}
}
+ if (invocationMethod == null) {
+ throw new IllegalStateException("Can't find method " + methodName
+ + " with " + parameterCount + " parameters in "
+ + targetType.getName());
+ }
+
return invocationMethod;
}
diff --git a/src/com/vaadin/ui/AbstractJavascriptComponent.java b/src/com/vaadin/ui/AbstractJavascriptComponent.java
new file mode 100644
index 0000000000..0a26c10239
--- /dev/null
+++ b/src/com/vaadin/ui/AbstractJavascriptComponent.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import com.vaadin.terminal.JavascriptRpcHelper;
+
+public class AbstractJavascriptComponent extends AbstractComponent {
+ private JavascriptRpcHelper rpcHelper = new JavascriptRpcHelper(this);
+
+ protected void registerCallback(String functionName,
+ JavascriptCallback javascriptCallback) {
+ rpcHelper.registerCallback(functionName, javascriptCallback);
+ }
+
+ protected void invokeCallback(String name, Object... arguments) {
+ rpcHelper.invokeCallback(name, arguments);
+ }
+}
diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java
index dae334fe4c..ae47d87eae 100644
--- a/src/com/vaadin/ui/DirtyConnectorTracker.java
+++ b/src/com/vaadin/ui/DirtyConnectorTracker.java
@@ -101,7 +101,7 @@ public class DirtyConnectorTracker implements Serializable {
}
markDirty(c);
for (ClientConnector child : AbstractClientConnector
- .getAllChildrenIteratable(c)) {
+ .getAllChildrenIterable(c)) {
markConnectorsDirtyRecursively(child);
}
}
diff --git a/src/com/vaadin/ui/JavascriptCallback.java b/src/com/vaadin/ui/JavascriptCallback.java
new file mode 100644
index 0000000000..89700b3faf
--- /dev/null
+++ b/src/com/vaadin/ui/JavascriptCallback.java
@@ -0,0 +1,14 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+
+public interface JavascriptCallback extends Serializable {
+ public void call(JSONArray arguments) throws JSONException;
+}
diff --git a/src/com/vaadin/ui/JavascriptManager.java b/src/com/vaadin/ui/JavascriptManager.java
new file mode 100644
index 0000000000..72295dce2b
--- /dev/null
+++ b/src/com/vaadin/ui/JavascriptManager.java
@@ -0,0 +1,58 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.terminal.AbstractExtension;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavascriptManagerState;
+
+public class JavascriptManager extends AbstractExtension {
+ private Map<String, JavascriptCallback> callbacks = new HashMap<String, JavascriptCallback>();
+
+ // Can not be defined in client package as this JSONArray is not available
+ // in GWT
+ public interface JavascriptCallbackRpc extends ServerRpc {
+ public void call(String name, JSONArray arguments);
+ }
+
+ public JavascriptManager() {
+ registerRpc(new JavascriptCallbackRpc() {
+ public void call(String name, JSONArray arguments) {
+ JavascriptCallback callback = callbacks.get(name);
+ // TODO handle situation if name is not registered
+ try {
+ callback.call(arguments);
+ } catch (JSONException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public JavascriptManagerState getState() {
+ return (JavascriptManagerState) super.getState();
+ }
+
+ public void addCallback(String name, JavascriptCallback javascriptCallback) {
+ callbacks.put(name, javascriptCallback);
+ if (getState().getNames().add(name)) {
+ requestRepaint();
+ }
+ }
+
+ public void removeCallback(String name) {
+ callbacks.remove(name);
+ if (getState().getNames().remove(name)) {
+ requestRepaint();
+ }
+ }
+
+}
diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java
index 8792bf1912..9814084cbc 100644
--- a/src/com/vaadin/ui/Root.java
+++ b/src/com/vaadin/ui/Root.java
@@ -408,6 +408,8 @@ public abstract class Root extends AbstractComponentContainer implements
private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
this);
+ private JavascriptManager javascriptManager;
+
private RootServerRpc rpc = new RootServerRpc() {
public void click(MouseEventDetails mouseDetails) {
fireEvent(new ClickEvent(Root.this, mouseDetails));
@@ -1590,4 +1592,14 @@ public abstract class Root extends AbstractComponentContainer implements
return dirtyConnectorTracker;
}
+ public JavascriptManager getJavascriptManager() {
+ if (javascriptManager == null) {
+ // Create and attach on first use
+ javascriptManager = new JavascriptManager();
+ addExtension(javascriptManager);
+ }
+
+ return javascriptManager;
+ }
+
}
diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java
index 3b12c097de..a83c9bd87a 100644
--- a/src/com/vaadin/ui/Table.java
+++ b/src/com/vaadin/ui/Table.java
@@ -1653,6 +1653,14 @@ public class Table extends AbstractSelect implements Action.Container,
super.requestRepaint();
}
+ @Override
+ public void requestRepaintAll() {
+ super.requestRepaintAll();
+
+ // Avoid sending a partial repaint (#8714)
+ refreshRowCache();
+ }
+
private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) {
int totalCachedRows = pageBuffer[CELL_ITEMID].length;
int totalRows = size();
diff --git a/tests/server-side/com/vaadin/tests/server/navigator/ClassBasedViewProviderTest.java b/tests/server-side/com/vaadin/tests/server/navigator/ClassBasedViewProviderTest.java
index 818229ab2f..fc0d1b60a0 100644
--- a/tests/server-side/com/vaadin/tests/server/navigator/ClassBasedViewProviderTest.java
+++ b/tests/server-side/com/vaadin/tests/server/navigator/ClassBasedViewProviderTest.java
@@ -12,17 +12,10 @@ import com.vaadin.ui.Label;
public class ClassBasedViewProviderTest extends TestCase {
- private ClassBasedViewProvider provider;
-
public static class TestView extends Label implements View {
- public boolean initialized = false;
public String parameters = null;
- public void init() {
- initialized = true;
- }
-
- public void navigateTo(String parameters, Object... internalParameters) {
+ public void navigateTo(String parameters) {
this.parameters = parameters;
}
@@ -32,86 +25,68 @@ public class ClassBasedViewProviderTest extends TestCase {
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- provider = new ClassBasedViewProvider();
- }
-
- public void testAddViewWithNullName() throws Exception {
+ public void testCreateProviderWithNullName() throws Exception {
try {
- provider.addView(null, TestView.class);
- fail("Should not be able to add view with null name");
+ new ClassBasedViewProvider(null, TestView.class);
+ fail("Should not be able to create view provider with null name");
} catch (IllegalArgumentException e) {
}
}
- public void testAddViewWithEmptyStringName() throws Exception {
- try {
- provider.addView("", TestView.class);
- fail("Should not be able to add view with empty name");
- } catch (IllegalArgumentException e) {
- }
+ public void testCreateProviderWithEmptyStringName() throws Exception {
+ new ClassBasedViewProvider("", TestView.class);
}
- public void testAddViewNull() throws Exception {
+ public void testCreateProviderNullViewClass() throws Exception {
try {
- provider.addView("test", null);
- fail("Should not be able to add null view");
+ new ClassBasedViewProvider("test", null);
+ fail("Should not be able to create view provider with null view class");
} catch (IllegalArgumentException e) {
}
}
- public void testAddViewSameName() throws Exception {
- try {
- provider.addView("test", TestView.class);
- provider.addView("test", TestView2.class);
- fail("Should not be able to add two views with same name");
- } catch (IllegalArgumentException e) {
- }
+ public void testViewNameGetter() throws Exception {
+ ClassBasedViewProvider provider1 = new ClassBasedViewProvider("",
+ TestView.class);
+ assertEquals("View name should be empty", "", provider1.getViewName());
+
+ ClassBasedViewProvider provider2 = new ClassBasedViewProvider("test",
+ TestView.class);
+ assertEquals("View name does not match", "test",
+ provider2.getViewName());
}
- public void testAddViewSameClass() throws Exception {
- try {
- provider.addView("test", TestView.class);
- provider.addView("test2", TestView.class);
- fail("Should not be able to add same view class with two different names");
- } catch (IllegalArgumentException e) {
- }
+ public void testViewClassGetter() throws Exception {
+ ClassBasedViewProvider provider = new ClassBasedViewProvider("test",
+ TestView.class);
+ assertEquals("Incorrect view class returned by getter", TestView.class,
+ provider.getViewClass());
}
public void testGetViewNameForNullString() throws Exception {
- assertNull(
- "Found view name for null view string in an empty view provider",
- provider.getViewName((String) null));
-
- provider.addView("test", TestView.class);
- assertNull("Found view name for null view string",
+ ClassBasedViewProvider provider = new ClassBasedViewProvider("test",
+ TestView.class);
+ assertNull("Received view name for null view string",
provider.getViewName((String) null));
}
- public void testGetViewNameForNullClass() throws Exception {
- assertNull("Found view name for null class",
- provider.getViewName((Class<View>) null));
- }
-
public void testGetViewNameForEmptyString() throws Exception {
- assertNull(
- "Found view name for empty view string in an empty provider",
- provider.getViewName(""));
- provider.addView("test", TestView.class);
- assertNull("Found view name for empty view string",
- provider.getViewName(""));
- }
+ ClassBasedViewProvider provider1 = new ClassBasedViewProvider("",
+ TestView.class);
+ assertEquals(
+ "Did not find view name for empty view string in a provider with empty string registered",
+ "", provider1.getViewName(""));
- public void testGetViewNameForClass() throws Exception {
- provider.addView("test", TestView.class);
- assertEquals("No view name found for view class", "test",
- provider.getViewName(TestView.class));
+ ClassBasedViewProvider provider2 = new ClassBasedViewProvider("test",
+ TestView.class);
+ assertNull(
+ "Found view name for empty view string when none registered",
+ provider2.getViewName(""));
}
public void testGetViewNameWithParameters() throws Exception {
- provider.addView("test", TestView.class);
+ ClassBasedViewProvider provider = new ClassBasedViewProvider("test",
+ TestView.class);
assertEquals("Incorrect view name found for view string", "test",
provider.getViewName("test"));
assertEquals(
@@ -122,87 +97,21 @@ public class ClassBasedViewProviderTest extends TestCase {
"test", provider.getViewName("test/params/are/here"));
}
- public void testGetViewNameMultipleRegisteredWithParameters()
- throws Exception {
- provider.addView("test", TestView.class);
- provider.addView("test2", TestView2.class);
- assertEquals("Incorrect view name found for view string", "test",
- provider.getViewName("test/test2/params"));
- }
-
- public void testGetViewNameNestedNames() throws Exception {
- provider.addView("test/subview", TestView2.class);
- provider.addView("test", TestView.class);
- assertEquals("Incorrect view name found for subview string",
- "test/subview", provider.getViewName("test/subview"));
- assertEquals(
- "Incorrect view name found for subview string with empty parameters",
- "test/subview", provider.getViewName("test/subview/"));
- assertEquals(
- "Incorrect view name found for subview string with parameters",
- "test/subview", provider.getViewName("test/subview/parameters"));
- assertEquals("Incorrect view name found for top level view string",
- "test", provider.getViewName("test"));
- assertEquals(
- "Incorrect view name found for top level view string with empty parameters",
- "test", provider.getViewName("test/"));
- assertEquals(
- "Incorrect view name found for top level view string with parameters starting like subview name",
- "test", provider.getViewName("test/subviewnothere"));
- }
-
- public void testGetViewClass() throws Exception {
- assertNull("View class found for empty view provider",
- provider.getViewClass("test"));
- provider.addView("test", TestView.class);
- assertEquals("View class not found", TestView.class,
- provider.getViewClass("test"));
- assertNull("View class found for unregistered view name",
- provider.getViewClass("test2"));
- }
-
- public void testGetViewSimple() throws Exception {
- assertNull("Found view in an empty view provider",
- provider.getViewName("test"));
+ public void testGetView() throws Exception {
+ ClassBasedViewProvider provider = new ClassBasedViewProvider("test",
+ TestView.class);
- provider.addView("test", TestView.class);
View view = provider.getView("test");
assertNotNull("Did not get view from a provider", view);
assertEquals("Incorrect view type", TestView.class, view.getClass());
- assertTrue("View not initialized", ((TestView) view).initialized);
- }
-
- public void testGetViewMultipleRegistered() throws Exception {
- provider.addView("test", TestView.class);
- provider.addView("test2", TestView2.class);
- assertEquals("Incorrect view type", TestView.class,
- provider.getView("test").getClass());
- assertEquals("Incorrect view type", TestView2.class,
- provider.getView("test2").getClass());
- assertEquals("Incorrect view type", TestView.class,
- provider.getView("test").getClass());
}
- public void testRemoveView() throws Exception {
- provider.addView("test", TestView.class);
- assertNotNull("Did not get view from a provider",
- provider.getView("test"));
- provider.removeView("test");
- assertNull("View class found for removed view name",
- provider.getViewClass("test"));
- assertNull("View name found for removed view",
- provider.getViewName(TestView.class));
- // cached view?
- assertNull(
- "Received view instance from a provider after removing view type",
- provider.getView("test"));
- }
+ public void testGetViewIncorrectViewName() throws Exception {
+ ClassBasedViewProvider provider = new ClassBasedViewProvider("test",
+ TestView.class);
- public void testGetViewCached() throws Exception {
- provider.addView("test", TestView.class);
- View view1 = provider.getView("test");
- View view2 = provider.getView("test");
- assertSame("View instance not cached", view1, view2);
+ View view = provider.getView("test2");
+ assertNull("Got view from a provider for incorrect view name", view);
}
}
diff --git a/tests/server-side/com/vaadin/tests/server/navigator/NavigatorTest.java b/tests/server-side/com/vaadin/tests/server/navigator/NavigatorTest.java
index d6a7cc68c7..ab983b4022 100644
--- a/tests/server-side/com/vaadin/tests/server/navigator/NavigatorTest.java
+++ b/tests/server-side/com/vaadin/tests/server/navigator/NavigatorTest.java
@@ -4,6 +4,8 @@
package com.vaadin.tests.server.navigator;
+import java.util.LinkedList;
+
import junit.framework.TestCase;
import org.easymock.EasyMock;
@@ -11,16 +13,146 @@ import org.easymock.IMocksControl;
import com.vaadin.navigator.FragmentManager;
import com.vaadin.navigator.Navigator;
+import com.vaadin.navigator.Navigator.SimpleViewDisplay;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener;
+import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.navigator.ViewDisplay;
import com.vaadin.navigator.ViewProvider;
+import com.vaadin.tests.server.navigator.ClassBasedViewProviderTest.TestView;
+import com.vaadin.tests.server.navigator.ClassBasedViewProviderTest.TestView2;
+import com.vaadin.ui.Root;
public class NavigatorTest extends TestCase {
// TODO test internal parameters (and absence of them)
// TODO test listeners blocking navigation, multiple listeners
+ public static class NullDisplay implements ViewDisplay {
+ public void showView(View view) {
+ // do nothing
+ }
+ }
+
+ public static class NullFragmentManager implements FragmentManager {
+ public String getFragment() {
+ return null;
+ }
+
+ public void setFragment(String fragment) {
+ // do nothing
+ }
+ }
+
+ public static class TestDisplay implements ViewDisplay {
+ private View currentView;
+
+ public void showView(View view) {
+ currentView = view;
+ }
+
+ public View getCurrentView() {
+ return currentView;
+ }
+ }
+
+ public static class TestNavigator extends Navigator {
+ public TestNavigator() {
+ super(new NullFragmentManager(), new TestDisplay());
+ }
+
+ public View getView(String viewAndParameters) {
+ navigateTo(viewAndParameters);
+ return ((TestDisplay) getDisplay()).getCurrentView();
+ }
+ }
+
+ public static class ViewChangeTestListener implements ViewChangeListener {
+ private final LinkedList<ViewChangeEvent> referenceEvents = new LinkedList<ViewChangeListener.ViewChangeEvent>();
+ private final LinkedList<Boolean> referenceIsCheck = new LinkedList<Boolean>();
+ private final LinkedList<Boolean> checkReturnValues = new LinkedList<Boolean>();
+
+ public void addExpectedIsViewChangeAllowed(ViewChangeEvent event,
+ boolean returnValue) {
+ referenceIsCheck.add(true);
+ referenceEvents.add(event);
+ checkReturnValues.add(returnValue);
+ }
+
+ public void addExpectedNavigatorViewChange(ViewChangeEvent event) {
+ referenceIsCheck.add(false);
+ referenceEvents.add(event);
+ }
+
+ public boolean isReady() {
+ return referenceEvents.isEmpty();
+ }
+
+ public boolean equalsReferenceEvent(ViewChangeEvent event,
+ ViewChangeEvent reference) {
+ if (event == null) {
+ return false;
+ }
+ if (reference.getNavigator() != event.getNavigator()) {
+ return false;
+ }
+ if (reference.getOldView() != event.getOldView()) {
+ return false;
+ }
+ if (reference.getNewView() != event.getNewView()) {
+ return false;
+ }
+ if (!stringEquals(reference.getViewName(), event.getViewName())) {
+ return false;
+ }
+ if (!stringEquals(reference.getFragmentParameters(),
+ event.getFragmentParameters())) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean stringEquals(String string1, String string2) {
+ if (string1 == null) {
+ return string2 == null;
+ } else {
+ return string1.equals(string2);
+ }
+ }
+
+ public boolean isViewChangeAllowed(ViewChangeEvent event) {
+ if (referenceEvents.isEmpty()) {
+ fail("Unexpected call to isViewChangeAllowed()");
+ }
+ ViewChangeEvent reference = referenceEvents.remove();
+ Boolean isCheck = referenceIsCheck.remove();
+ if (!isCheck) {
+ fail("Expected navigatorViewChanged(), received isViewChangeAllowed()");
+ }
+ // here to make sure exactly the correct values are removed from
+ // each queue
+ Boolean returnValue = checkReturnValues.remove();
+ if (!equalsReferenceEvent(event, reference)) {
+ fail("View change event does not match reference event");
+ }
+ return returnValue;
+ }
+
+ public void navigatorViewChanged(ViewChangeEvent event) {
+ if (referenceEvents.isEmpty()) {
+ fail("Unexpected call to navigatorViewChanged()");
+ }
+ ViewChangeEvent reference = referenceEvents.remove();
+ Boolean isCheck = referenceIsCheck.remove();
+ if (isCheck) {
+ fail("Expected isViewChangeAllowed(), received navigatorViewChanged()");
+ }
+ if (!equalsReferenceEvent(event, reference)) {
+ fail("View change event does not match reference event");
+ }
+ }
+ }
+
public void testBasicNavigation() {
IMocksControl control = EasyMock.createControl();
FragmentManager manager = control.createMock(FragmentManager.class);
@@ -32,7 +164,6 @@ public class NavigatorTest extends TestCase {
// prepare mocks: what to expect
EasyMock.expect(provider.getViewName("test1")).andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("");
view1.navigateTo(null);
display.showView(view1);
@@ -40,7 +171,6 @@ public class NavigatorTest extends TestCase {
EasyMock.expect(provider.getViewName("test2/")).andReturn("test2");
EasyMock.expect(provider.getView("test2")).andReturn(view2);
- view2.init();
EasyMock.expect(manager.getFragment()).andReturn("view1");
view2.navigateTo(null);
display.showView(view2);
@@ -49,7 +179,6 @@ public class NavigatorTest extends TestCase {
EasyMock.expect(provider.getViewName("test1/params"))
.andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("view2");
view1.navigateTo("params");
display.showView(view1);
@@ -75,25 +204,15 @@ public class NavigatorTest extends TestCase {
View view2 = control.createMock(View.class);
// prepare mocks: what to expect
- EasyMock.expect(provider.getViewName("test1")).andReturn("test1");
- EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
- EasyMock.expect(manager.getFragment()).andReturn("");
- view1.navigateTo(null);
- display.showView(view1);
- manager.setFragment("test1");
-
EasyMock.expect(provider.getViewName("test2")).andReturn("test2");
EasyMock.expect(provider.getView("test2")).andReturn(view2);
- view2.init();
EasyMock.expect(manager.getFragment()).andReturn("view1");
view2.navigateTo(null);
display.showView(view2);
manager.setFragment("test2");
- EasyMock.expect(provider.getViewName("test1")).andReturn("test1");
+ EasyMock.expect(provider.getViewName("")).andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("");
view1.navigateTo(null);
display.showView(view1);
@@ -102,7 +221,6 @@ public class NavigatorTest extends TestCase {
EasyMock.expect(provider.getViewName("test1/params"))
.andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("view2");
view1.navigateTo("params");
display.showView(view1);
@@ -113,8 +231,6 @@ public class NavigatorTest extends TestCase {
// create and test navigator
Navigator navigator = new Navigator(manager, display);
navigator.registerProvider(provider);
- // this also triggers navigation
- navigator.setMainView("test1");
navigator.navigateTo("test2");
navigator.navigateTo("");
@@ -128,43 +244,46 @@ public class NavigatorTest extends TestCase {
ViewProvider provider = control.createMock(ViewProvider.class);
View view1 = control.createMock(View.class);
View view2 = control.createMock(View.class);
- ViewChangeListener listener = control
- .createMock(ViewChangeListener.class);
+ ViewChangeTestListener listener = new ViewChangeTestListener();
+
+ // create navigator to test
+ Navigator navigator = new Navigator(manager, display);
// prepare mocks: what to expect
EasyMock.expect(provider.getViewName("test1")).andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
+ ViewChangeEvent event1 = new ViewChangeEvent(navigator, null, view1,
+ "test1", null);
+ listener.addExpectedIsViewChangeAllowed(event1, true);
EasyMock.expect(manager.getFragment()).andReturn("");
- EasyMock.expect(
- listener.isViewChangeAllowed(null, view1, "test1", null,
- new Object[0])).andReturn(true);
view1.navigateTo(null);
display.showView(view1);
manager.setFragment("test1");
- listener.navigatorViewChanged(null, view1);
+ listener.addExpectedNavigatorViewChange(event1);
EasyMock.expect(provider.getViewName("test2")).andReturn("test2");
EasyMock.expect(provider.getView("test2")).andReturn(view2);
- view2.init();
+ ViewChangeEvent event2 = new ViewChangeEvent(navigator, view1, view2,
+ "test2", null);
+ listener.addExpectedIsViewChangeAllowed(event2, true);
EasyMock.expect(manager.getFragment()).andReturn("view1");
- EasyMock.expect(
- listener.isViewChangeAllowed(view1, view2, "test2", null,
- new Object[0])).andReturn(true);
view2.navigateTo(null);
display.showView(view2);
manager.setFragment("test2");
- listener.navigatorViewChanged(view1, view2);
+ listener.addExpectedNavigatorViewChange(event2);
control.replay();
- // create and test navigator
- Navigator navigator = new Navigator(manager, display);
+ // test navigator
navigator.registerProvider(provider);
navigator.addListener(listener);
navigator.navigateTo("test1");
navigator.navigateTo("test2");
+
+ if (!listener.isReady()) {
+ fail("Missing listener calls");
+ }
}
public void testBlockNavigation() {
@@ -174,71 +293,60 @@ public class NavigatorTest extends TestCase {
ViewProvider provider = control.createMock(ViewProvider.class);
View view1 = control.createMock(View.class);
View view2 = control.createMock(View.class);
- ViewChangeListener listener1 = control
- .createMock(ViewChangeListener.class);
- ViewChangeListener listener2 = control
- .createMock(ViewChangeListener.class);
+ ViewChangeTestListener listener1 = new ViewChangeTestListener();
+ ViewChangeTestListener listener2 = new ViewChangeTestListener();
+
+ Navigator navigator = new Navigator(manager, display);
// prepare mocks: what to expect
// first listener blocks first view change
EasyMock.expect(provider.getViewName("test1")).andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("");
- EasyMock.expect(
- listener1.isViewChangeAllowed(null, view1, "test1", null,
- new Object[0])).andReturn(false);
+ ViewChangeEvent event1 = new ViewChangeEvent(navigator, null, view1,
+ "test1", null);
+ listener1.addExpectedIsViewChangeAllowed(event1, false);
// second listener blocks second view change
EasyMock.expect(provider.getViewName("test1/test")).andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("");
- EasyMock.expect(
- listener1.isViewChangeAllowed(null, view1, "test1", "test",
- new Object[0])).andReturn(true);
- EasyMock.expect(
- listener2.isViewChangeAllowed(null, view1, "test1", "test",
- new Object[0])).andReturn(false);
+ ViewChangeEvent event2 = new ViewChangeEvent(navigator, null, view1,
+ "test1", "test");
+ listener1.addExpectedIsViewChangeAllowed(event2, true);
+ listener2.addExpectedIsViewChangeAllowed(event2, false);
// both listeners allow view change
EasyMock.expect(provider.getViewName("test1/bar")).andReturn("test1");
EasyMock.expect(provider.getView("test1")).andReturn(view1);
- view1.init();
EasyMock.expect(manager.getFragment()).andReturn("");
- EasyMock.expect(
- listener1.isViewChangeAllowed(null, view1, "test1", "bar",
- new Object[0])).andReturn(true);
- EasyMock.expect(
- listener2.isViewChangeAllowed(null, view1, "test1", "bar",
- new Object[0])).andReturn(true);
+ ViewChangeEvent event3 = new ViewChangeEvent(navigator, null, view1,
+ "test1", "bar");
+ listener1.addExpectedIsViewChangeAllowed(event3, true);
+ listener2.addExpectedIsViewChangeAllowed(event3, true);
view1.navigateTo("bar");
display.showView(view1);
manager.setFragment("test1/bar");
- listener1.navigatorViewChanged(null, view1);
- listener2.navigatorViewChanged(null, view1);
+ listener1.addExpectedNavigatorViewChange(event3);
+ listener2.addExpectedNavigatorViewChange(event3);
// both listeners allow view change from non-null view
EasyMock.expect(provider.getViewName("test2")).andReturn("test2");
EasyMock.expect(provider.getView("test2")).andReturn(view2);
- view2.init();
EasyMock.expect(manager.getFragment()).andReturn("view1");
- EasyMock.expect(
- listener1.isViewChangeAllowed(view1, view2, "test2", null,
- new Object[0])).andReturn(true);
- EasyMock.expect(
- listener2.isViewChangeAllowed(view1, view2, "test2", null,
- new Object[0])).andReturn(true);
+ ViewChangeEvent event4 = new ViewChangeEvent(navigator, view1, view2,
+ "test2", null);
+ listener1.addExpectedIsViewChangeAllowed(event4, true);
+ listener2.addExpectedIsViewChangeAllowed(event4, true);
view2.navigateTo(null);
display.showView(view2);
manager.setFragment("test2");
- listener1.navigatorViewChanged(view1, view2);
- listener2.navigatorViewChanged(view1, view2);
+ listener1.addExpectedNavigatorViewChange(event4);
+ listener2.addExpectedNavigatorViewChange(event4);
control.replay();
- // create and test navigator
- Navigator navigator = new Navigator(manager, display);
+ // test navigator
navigator.registerProvider(provider);
navigator.addListener(listener1);
navigator.addListener(listener2);
@@ -247,6 +355,215 @@ public class NavigatorTest extends TestCase {
navigator.navigateTo("test1/test");
navigator.navigateTo("test1/bar");
navigator.navigateTo("test2");
+
+ if (!listener1.isReady()) {
+ fail("Missing listener calls for listener1");
+ }
+ if (!listener2.isReady()) {
+ fail("Missing listener calls for listener2");
+ }
+ }
+
+ public void testDefaultDisplayType() {
+ IMocksControl control = EasyMock.createControl();
+ Root root = control.createMock(Root.class);
+
+ Navigator navigator = new Navigator(root);
+
+ assertEquals("Default display should be a SimpleViewDisplay",
+ SimpleViewDisplay.class, navigator.getDisplay().getClass());
+ }
+
+ public void testAddViewInstance() throws Exception {
+ View view = new TestView();
+
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", view);
+
+ assertEquals("Registered view instance not returned by navigator",
+ view, navigator.getView("test"));
}
+ public void testAddViewInstanceSameName() throws Exception {
+ View view1 = new TestView();
+ View view2 = new TestView2();
+
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", view1);
+ navigator.addView("test", view2);
+
+ assertEquals(
+ "Adding second view with same name should override previous view",
+ view2, navigator.getView("test"));
+ }
+
+ public void testAddViewClass() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", TestView.class);
+
+ View view = navigator.getView("test");
+ assertNotNull("Received null view", view);
+ assertEquals("Received incorrect type of view", TestView.class,
+ view.getClass());
+ }
+
+ public void testAddViewClassSameName() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", TestView.class);
+ navigator.addView("test", TestView2.class);
+
+ assertEquals(
+ "Adding second view class with same name should override previous view",
+ TestView2.class, navigator.getView("test").getClass());
+ }
+
+ public void testAddViewInstanceAndClassSameName() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", TestView.class);
+ TestView2 view2 = new TestView2();
+ navigator.addView("test", view2);
+
+ assertEquals(
+ "Adding second view class with same name should override previous view",
+ view2, navigator.getView("test"));
+
+ navigator.addView("test", TestView.class);
+
+ assertEquals(
+ "Adding second view class with same name should override previous view",
+ TestView.class, navigator.getView("test").getClass());
+ }
+
+ public void testAddViewWithNullName() throws Exception {
+ Navigator navigator = new Navigator(new NullFragmentManager(),
+ new NullDisplay());
+
+ try {
+ navigator.addView(null, new TestView());
+ fail("addView() accepted null view name");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ navigator.addView(null, TestView.class);
+ fail("addView() accepted null view name");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testAddViewWithNullInstance() throws Exception {
+ Navigator navigator = new Navigator(new NullFragmentManager(),
+ new NullDisplay());
+
+ try {
+ navigator.addView("test", (View) null);
+ fail("addView() accepted null view instance");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testAddViewWithNullClass() throws Exception {
+ Navigator navigator = new Navigator(new NullFragmentManager(),
+ new NullDisplay());
+
+ try {
+ navigator.addView("test", (Class<View>) null);
+ fail("addView() accepted null view class");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testRemoveViewInstance() throws Exception {
+ View view = new TestView();
+
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", view);
+ navigator.removeView("test");
+
+ assertNull("View not removed", navigator.getView("test"));
+ }
+
+ public void testRemoveViewInstanceNothingElse() throws Exception {
+ View view = new TestView();
+ View view2 = new TestView2();
+
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", view);
+ navigator.addView("test2", view2);
+ navigator.removeView("test");
+
+ assertEquals("Removed extra views", view2, navigator.getView("test2"));
+ }
+
+ public void testRemoveViewClass() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", TestView.class);
+ navigator.removeView("test");
+
+ assertNull("View not removed", navigator.getView("test"));
+ }
+
+ public void testRemoveViewClassNothingElse() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test", TestView.class);
+ navigator.addView("test2", TestView2.class);
+ navigator.removeView("test");
+
+ assertEquals("Removed extra views", TestView2.class,
+ navigator.getView("test2").getClass());
+ }
+
+ public void testGetViewNestedNames() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test/subview", TestView2.class);
+ navigator.addView("test", TestView.class);
+
+ assertEquals("Incorrect view name found for subview string",
+ TestView2.class, navigator.getView("test/subview").getClass());
+ assertEquals(
+ "Incorrect view name found for subview string with empty parameters",
+ TestView2.class, navigator.getView("test/subview/").getClass());
+ assertEquals(
+ "Incorrect view name found for subview string with parameters",
+ TestView2.class, navigator.getView("test/subview/parameters")
+ .getClass());
+ assertEquals("Incorrect view name found for top level view string",
+ TestView.class, navigator.getView("test").getClass());
+ assertEquals(
+ "Incorrect view name found for top level view string with empty parameters",
+ TestView.class, navigator.getView("test/").getClass());
+ assertEquals(
+ "Incorrect view name found for top level view string with parameters starting like subview name",
+ TestView.class, navigator.getView("test/subviewnothere")
+ .getClass());
+ }
+
+ public void testGetViewLongestPrefixOrder() throws Exception {
+ TestNavigator navigator = new TestNavigator();
+
+ navigator.addView("test/subview", TestView2.class);
+ navigator.addView("test", TestView.class);
+
+ assertEquals("Incorrect view name found", TestView.class, navigator
+ .getView("test").getClass());
+
+ // other order
+
+ TestNavigator navigator2 = new TestNavigator();
+
+ navigator2.addView("test", TestView.class);
+ navigator2.addView("test/subview", TestView2.class);
+
+ assertEquals("Incorrect view name found", TestView.class, navigator2
+ .getView("test").getClass());
+ }
}
diff --git a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavascriptComponent.java b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavascriptComponent.java
new file mode 100644
index 0000000000..2240fc246b
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavascriptComponent.java
@@ -0,0 +1,73 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.tests.components.javascriptcomponent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.vaadin.annotations.LoadScripts;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.AbstractJavascriptComponent;
+import com.vaadin.ui.Root;
+
+@LoadScripts({ "/statictestfiles/jsconnector.js" })
+public class BasicJavascriptComponent extends AbstractTestRoot {
+
+ public interface ExampleClickRpc extends ServerRpc {
+ public void onClick(String message);
+ }
+
+ public static class SpecialState extends ComponentState {
+ private List<String> data;
+
+ public List<String> getData() {
+ return data;
+ }
+
+ public void setData(List<String> data) {
+ this.data = data;
+ }
+ }
+
+ public static class ExampleWidget extends AbstractJavascriptComponent {
+ public ExampleWidget() {
+ registerRpc(new ExampleClickRpc() {
+ public void onClick(String message) {
+ Root.getCurrentRoot().showNotification(
+ "Got a click: " + message);
+ }
+ });
+ getState().setData(Arrays.asList("a", "b", "c"));
+ }
+
+ @Override
+ public SpecialState getState() {
+ return (SpecialState) super.getState();
+ }
+ }
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ ExampleWidget c = new ExampleWidget();
+ c.setCaption("test caption");
+ c.setDescription("Some description");
+ addComponent(c);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // 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/table/TableInTabsheet.java b/tests/testbench/com/vaadin/tests/components/table/TableInTabsheet.java
new file mode 100644
index 0000000000..27e624b810
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/table/TableInTabsheet.java
@@ -0,0 +1,114 @@
+package com.vaadin.tests.components.table;
+
+import java.net.MalformedURLException;
+
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.gwt.client.ui.label.ContentMode;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.AbsoluteLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Table.Align;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.themes.Reindeer;
+
+public class TableInTabsheet extends AbstractTestRoot {
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ getRoot().setCaption("test");
+ VerticalLayout vPrinc = new VerticalLayout();
+ vPrinc.setStyleName(Reindeer.LAYOUT_BLUE);
+
+ vPrinc.addComponent(title());
+ vPrinc.addComponent(page());
+ vPrinc.addComponent(new Label("Dvlop Tecnologia."));
+ setContent(vPrinc);
+ }
+
+ private VerticalLayout title() {
+
+ VerticalLayout vP = new VerticalLayout();
+ vP.setStyleName(Reindeer.LAYOUT_BLACK);
+ Label tit = new Label("<h1> Tab/Table Test</h1>", ContentMode.XHTML);
+ vP.addComponent(tit);
+ return vP;
+
+ }
+
+ private VerticalLayout page() {
+
+ VerticalLayout vP = new VerticalLayout();
+ vP.setStyleName(Reindeer.LAYOUT_BLUE);
+ TabSheet t = new TabSheet();
+ t.setWidth(1000, Unit.PIXELS);
+
+ HorizontalLayout hP = new HorizontalLayout();
+ t.addTab(Ranking(), "Ranking");
+ try {
+
+ t.addTab(GDocs(""), "Dez 2011");
+ t.addTab(GDocs(""), "Jan 2012");
+ t.addTab(GDocs(""), "Abr 2012");
+
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ hP.addComponent(t);
+ vP.addComponent(hP);
+ return vP;
+
+ }
+
+ private AbsoluteLayout Ranking() {
+
+ AbsoluteLayout vT = new AbsoluteLayout();
+ vT.setHeight(500, Unit.PIXELS);
+ vT.setWidth(900, Unit.PIXELS);
+ vT.setStyleName(Reindeer.LAYOUT_BLUE);
+
+ final Table table = new Table("Ranking Oficial");
+
+ table.addContainerProperty("Atleta", String.class, null);
+ table.addContainerProperty("P", String.class, null);
+ table.addContainerProperty("Dez/11", Integer.class, null);
+ table.setColumnAlignment("Dez/11", Align.CENTER);
+ table.addContainerProperty("Jan/12", Integer.class, null);
+ table.setColumnAlignment("Jan/12", Align.CENTER);
+ table.addContainerProperty("Abr/12", String.class, null);
+ table.addContainerProperty("Total", Integer.class, null);
+ table.setColumnAlignment("Total", Align.CENTER);
+
+ table.addItem(new Object[] { "Araujo", "D.1", 8, 8, " ", 16 }, 1);
+ table.addItem(new Object[] { "Claudio", "D.2", 2, 10, " ", 12 }, 2);
+ table.setPageLength(12);
+
+ vT.addComponent(table, "left: 50px; top: 50px;");
+ return vT;
+
+ }
+
+ private VerticalLayout GDocs(String end) throws MalformedURLException {
+
+ VerticalLayout vT = new VerticalLayout();
+ vT.setHeight(500, Unit.PIXELS);
+ vT.setWidth(900, Unit.PIXELS);
+
+ return vT;
+
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Chaning to a different tab and then back to the first tab should properly render the table.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return Integer.valueOf(8714);
+ }
+
+}
diff --git a/tests/testbench/com/vaadin/tests/features/HelloWorldFeatureTest.java b/tests/testbench/com/vaadin/tests/extensions/HelloWorldExtensionTest.java
index 3d26b92648..b56e05b2a0 100644
--- a/tests/testbench/com/vaadin/tests/features/HelloWorldFeatureTest.java
+++ b/tests/testbench/com/vaadin/tests/extensions/HelloWorldExtensionTest.java
@@ -1,7 +1,7 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.tests.features;
+package com.vaadin.tests.extensions;
import com.vaadin.terminal.WrappedRequest;
import com.vaadin.tests.components.AbstractTestRoot;
@@ -9,7 +9,7 @@ import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.HelloWorldExtension;
-public class HelloWorldFeatureTest extends AbstractTestRoot {
+public class HelloWorldExtensionTest extends AbstractTestRoot {
@Override
protected void setup(WrappedRequest request) {
@@ -26,7 +26,7 @@ public class HelloWorldFeatureTest extends AbstractTestRoot {
@Override
protected String getTestDescription() {
- return "Testing basic Feature";
+ return "Testing basic Extension";
}
@Override
diff --git a/tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.html b/tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.html
new file mode 100644
index 0000000000..7bdb8cd50f
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.html
@@ -0,0 +1,47 @@
+<?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://localhost: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.extensions.JavascriptManagerTest?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsextensionsJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[4]</td>
+ <td>1. Got 4 arguments</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsextensionsJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[3]</td>
+ <td>2. Argument 1 as a number: 42</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsextensionsJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[2]</td>
+ <td>3. Argument 2 as a string: text</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsextensionsJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[1]</td>
+ <td>4. Argument 3.p as a boolean: true</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsextensionsJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[0]</td>
+ <td>5. Argument 4 is JSONObject.NULL: true</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.java b/tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.java
new file mode 100644
index 0000000000..c84d37cd0f
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/extensions/JavascriptManagerTest.java
@@ -0,0 +1,47 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.tests.extensions;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.JavascriptCallback;
+
+public class JavascriptManagerTest extends AbstractTestRoot {
+
+ private Log log = new Log(5);
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ addComponent(log);
+ getJavascriptManager().addCallback("testing", new JavascriptCallback() {
+ public void call(JSONArray arguments) throws JSONException {
+ log.log("Got " + arguments.length() + " arguments");
+ log.log("Argument 1 as a number: " + arguments.getInt(0));
+ log.log("Argument 2 as a string: " + arguments.getString(1));
+ log.log("Argument 3.p as a boolean: "
+ + arguments.getJSONObject(2).getBoolean("p"));
+ log.log("Argument 4 is JSONObject.NULL: "
+ + (arguments.get(3) == JSONObject.NULL));
+ }
+ });
+ executeJavaScript("window.testing(42, 'text', {p: true}, null)");
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Test javascript callback handling by adding a callback and invoking the javascript.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/tests/testbench/com/vaadin/tests/extensions/SimpleJavascriptExtensionTest.java b/tests/testbench/com/vaadin/tests/extensions/SimpleJavascriptExtensionTest.java
new file mode 100644
index 0000000000..92c134efb0
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/extensions/SimpleJavascriptExtensionTest.java
@@ -0,0 +1,95 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.tests.extensions;
+
+import com.vaadin.annotations.LoadScripts;
+import com.vaadin.terminal.AbstractJavascriptExtension;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Root;
+
+@LoadScripts({ "/statictestfiles/jsextension.js" })
+public class SimpleJavascriptExtensionTest extends AbstractTestRoot {
+
+ public static class SimpleJavascriptExtensionState extends SharedState {
+ private String prefix;
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+ }
+
+ public static interface SimpleJavascriptExtensionClientRpc extends
+ ClientRpc {
+ public void greet(String message);
+ }
+
+ public static interface SimpleJavascriptExtensionServerRpc extends
+ ServerRpc {
+ public void greet(String message);
+ }
+
+ public static class SimpleJavascriptExtension extends
+ AbstractJavascriptExtension {
+
+ public SimpleJavascriptExtension() {
+ registerRpc(new SimpleJavascriptExtensionServerRpc() {
+ public void greet(String message) {
+ Root.getCurrentRoot().showNotification(
+ getState().getPrefix() + message);
+ }
+ });
+ }
+
+ @Override
+ public SimpleJavascriptExtensionState getState() {
+ return (SimpleJavascriptExtensionState) super.getState();
+ }
+
+ public void setPrefix(String prefix) {
+ getState().setPrefix(prefix);
+ requestRepaint();
+ }
+
+ public void greet(String message) {
+ getRpcProxy(SimpleJavascriptExtensionClientRpc.class)
+ .greet(message);
+ }
+ }
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ final SimpleJavascriptExtension simpleJavascriptExtension = new SimpleJavascriptExtension();
+ simpleJavascriptExtension.setPrefix("Prefix: ");
+ addExtension(simpleJavascriptExtension);
+ addComponent(new Button("Send greeting", new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ simpleJavascriptExtension.greet("Greeted by button");
+ }
+ }));
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}