aboutsummaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2014-03-27 11:14:55 +0200
committerVaadin Code Review <review@vaadin.com>2014-04-01 08:15:56 +0000
commit7bfb2347ad32187444c194e7ebadae6a6d3597ca (patch)
treec94081d4cb9d2eba433c4cec3ddbcc27da57c643 /client
parenta636bb70c20277228468345017a2f8d42d39857c (diff)
downloadvaadin-framework-7bfb2347ad32187444c194e7ebadae6a6d3597ca.tar.gz
vaadin-framework-7bfb2347ad32187444c194e7ebadae6a6d3597ca.zip
Implement @OnStateChange (#12958)
Change-Id: I8ea2b781fab42458bf55a751c1229e391365e965
Diffstat (limited to 'client')
-rw-r--r--client/src/com/vaadin/client/annotations/OnStateChange.java50
-rw-r--r--client/src/com/vaadin/client/metadata/OnStateChangeMethod.java111
-rw-r--r--client/src/com/vaadin/client/metadata/TypeDataStore.java50
-rw-r--r--client/src/com/vaadin/client/ui/AbstractConnector.java35
-rw-r--r--client/src/com/vaadin/client/ui/button/ButtonConnector.java95
5 files changed, 293 insertions, 48 deletions
diff --git a/client/src/com/vaadin/client/annotations/OnStateChange.java b/client/src/com/vaadin/client/annotations/OnStateChange.java
new file mode 100644
index 0000000000..80c1095458
--- /dev/null
+++ b/client/src/com/vaadin/client/annotations/OnStateChange.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.client.communication.StateChangeEvent;
+
+/**
+ * Marks a method in Connector classes that should be used to handle changes to
+ * specific properties in the connector's shared state.
+ * <p>
+ * The annotated method will by called whenever at least one of the named state
+ * properties have changed. If multiple listened properties are changed by the
+ * same {@link StateChangeEvent}, the method will only be called once.
+ * <p>
+ * If there is no state variable with the provided name, the widgetset
+ * compilation will fail.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface OnStateChange {
+ /**
+ * Defines a list of property names to listen for.
+ *
+ * @return an array of property names, should contain at least one item
+ */
+ public String[] value();
+}
diff --git a/client/src/com/vaadin/client/metadata/OnStateChangeMethod.java b/client/src/com/vaadin/client/metadata/OnStateChangeMethod.java
new file mode 100644
index 0000000000..2ba06fd4eb
--- /dev/null
+++ b/client/src/com/vaadin/client/metadata/OnStateChangeMethod.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.metadata;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.annotations.OnStateChange;
+import com.vaadin.client.communication.StateChangeEvent;
+
+/**
+ * Encapsulates the data that the widgetset compiler generates for supporting a
+ * connector method annotated with {@link OnStateChange}
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class OnStateChangeMethod {
+
+ private final String methodName;
+ private final List<String> properties;
+ private final Class<?> declaringClass;
+
+ /**
+ * Creates a new instance based on a method name, a list of parameters names
+ * and a list of properties to listen for.
+ *
+ * @param methodName
+ * the name of the method to call
+ * @param properties
+ * an array of state property names to listen to
+ */
+ public OnStateChangeMethod(String methodName, String[] properties) {
+ this(null, methodName, properties);
+ }
+
+ /**
+ * Creates a new instance based on declaring class, a method name, a list of
+ * parameters names and a list of properties to listen for.
+ * <p>
+ * If the declaring class is <code>null</code>, the method is found based on
+ * the type of the connector that fired the state change event.
+ *
+ * @param declaringClass
+ * the class in which the target method is declared, or
+ * <code>null</code> to use the class of the connector firing the
+ * event
+ * @param methodName
+ * the name of the method to call
+ * @param properties
+ * an array of state property names to listen to
+ */
+ public OnStateChangeMethod(Class<?> declaringClass, String methodName,
+ String[] properties) {
+
+ this.methodName = methodName;
+
+ this.properties = Collections.unmodifiableList(Arrays
+ .asList(properties));
+
+ this.declaringClass = declaringClass;
+ }
+
+ /**
+ * Invokes the listener method for a state change.
+ *
+ * @param stateChangeEvent
+ * the state change event
+ */
+ public void invoke(StateChangeEvent stateChangeEvent) {
+ ServerConnector connector = (ServerConnector) stateChangeEvent
+ .getSource();
+
+ Class<?> declaringClass = this.declaringClass;
+ if (declaringClass == null) {
+ declaringClass = connector.getClass();
+ }
+ Type declaringType = TypeDataStore.getType(declaringClass);
+
+ try {
+ declaringType.getMethod(methodName).invoke(connector);
+ } catch (NoDataException e) {
+ throw new RuntimeException("Couldn't invoke @OnStateChange method "
+ + declaringType.getSignature() + "." + methodName, e);
+ }
+ }
+
+ /**
+ * Gets the list of state property names to listen for.
+ *
+ * @return the list of state property names to listen for
+ */
+ public List<String> getProperties() {
+ return properties;
+ }
+}
diff --git a/client/src/com/vaadin/client/metadata/TypeDataStore.java b/client/src/com/vaadin/client/metadata/TypeDataStore.java
index d5fbc94823..bc6610a6ff 100644
--- a/client/src/com/vaadin/client/metadata/TypeDataStore.java
+++ b/client/src/com/vaadin/client/metadata/TypeDataStore.java
@@ -23,6 +23,7 @@ import com.google.gwt.core.client.JsArrayString;
import com.vaadin.client.FastStringMap;
import com.vaadin.client.FastStringSet;
import com.vaadin.client.JsArrayObject;
+import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.JSONSerializer;
public class TypeDataStore {
@@ -37,6 +38,12 @@ public class TypeDataStore {
private final FastStringMap<JsArrayString> delegateToWidgetProperties = FastStringMap
.create();
+ /**
+ * Maps connector class -> state property name -> hander method data
+ */
+ private final FastStringMap<FastStringMap<JsArrayObject<OnStateChangeMethod>>> onStateChangeMethods = FastStringMap
+ .create();
+
private final FastStringSet delayedMethods = FastStringSet.create();
private final FastStringSet lastOnlyMethods = FastStringSet.create();
@@ -368,4 +375,47 @@ public class TypeDataStore {
return typeData[beanName] !== undefined ;
}-*/;
+ /**
+ * Gets data for all methods annotated with {@link OnStateChange} in the
+ * given connector type.
+ *
+ * @since 7.2
+ * @param type
+ * the connector type
+ * @return a map of state property names to handler method data
+ */
+ public static FastStringMap<JsArrayObject<OnStateChangeMethod>> getOnStateChangeMethods(
+ Class<?> type) {
+ return get().onStateChangeMethods.get(getType(type).getSignature());
+ }
+
+ /**
+ * Adds data about a method annotated with {@link OnStateChange} for the
+ * given connector type.
+ *
+ * @since 7.2
+ * @param clazz
+ * the connector type
+ * @param method
+ * the state change method data
+ */
+ public void addOnStateChangeMethod(Class<?> clazz,
+ OnStateChangeMethod method) {
+ FastStringMap<JsArrayObject<OnStateChangeMethod>> handlers = getOnStateChangeMethods(clazz);
+ if (handlers == null) {
+ handlers = FastStringMap.create();
+ onStateChangeMethods.put(getType(clazz).getSignature(), handlers);
+ }
+
+ for (String property : method.getProperties()) {
+ JsArrayObject<OnStateChangeMethod> propertyHandlers = handlers
+ .get(property);
+ if (propertyHandlers == null) {
+ propertyHandlers = JsArrayObject.createArray().cast();
+ handlers.put(property, propertyHandlers);
+ }
+
+ propertyHandlers.add(method);
+ }
+ }
}
diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java
index 6855c5cd2d..bd499ac4bc 100644
--- a/client/src/com/vaadin/client/ui/AbstractConnector.java
+++ b/client/src/com/vaadin/client/ui/AbstractConnector.java
@@ -18,6 +18,7 @@ package com.vaadin.client.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -27,6 +28,7 @@ import com.google.gwt.event.shared.HandlerManager;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.FastStringMap;
+import com.vaadin.client.JsArrayObject;
import com.vaadin.client.Profiler;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.Util;
@@ -35,8 +37,10 @@ import com.vaadin.client.communication.RpcProxy;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.metadata.NoDataException;
+import com.vaadin.client.metadata.OnStateChangeMethod;
import com.vaadin.client.metadata.Type;
import com.vaadin.client.metadata.TypeData;
+import com.vaadin.client.metadata.TypeDataStore;
import com.vaadin.shared.communication.ClientRpc;
import com.vaadin.shared.communication.ServerRpc;
import com.vaadin.shared.communication.SharedState;
@@ -290,6 +294,37 @@ public abstract class AbstractConnector implements ServerConnector,
}
updateEnabledState(isEnabled());
+
+ FastStringMap<JsArrayObject<OnStateChangeMethod>> handlers = TypeDataStore
+ .getOnStateChangeMethods(getClass());
+ if (handlers != null) {
+ Profiler.enter("AbstractConnector.onStateChanged @OnStateChange");
+
+ HashSet<OnStateChangeMethod> invokedMethods = new HashSet<OnStateChangeMethod>();
+
+ JsArrayString propertyNames = handlers.getKeys();
+ for (int i = 0; i < propertyNames.length(); i++) {
+ String propertyName = propertyNames.get(i);
+
+ if (stateChangeEvent.hasPropertyChanged(propertyName)) {
+ JsArrayObject<OnStateChangeMethod> propertyMethods = handlers
+ .get(propertyName);
+
+ for (int j = 0; j < propertyMethods.size(); j++) {
+ OnStateChangeMethod method = propertyMethods.get(j);
+
+ if (invokedMethods.add(method)) {
+
+ method.invoke(stateChangeEvent);
+
+ }
+ }
+ }
+ }
+
+ Profiler.leave("AbstractConnector.onStateChanged @OnStateChange");
+ }
+
Profiler.leave("AbstractConnector.onStateChanged");
}
diff --git a/client/src/com/vaadin/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/client/ui/button/ButtonConnector.java
index 94c2841c3c..32a457c1f1 100644
--- a/client/src/com/vaadin/client/ui/button/ButtonConnector.java
+++ b/client/src/com/vaadin/client/ui/button/ButtonConnector.java
@@ -26,8 +26,8 @@ import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.vaadin.client.EventHelper;
import com.vaadin.client.MouseEventDetailsBuilder;
+import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.VButton;
@@ -56,44 +56,38 @@ public class ButtonConnector extends AbstractComponentConnector implements
super.init();
getWidget().addClickHandler(this);
getWidget().client = getConnection();
- addStateChangeHandler("errorMessage", new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- if (null != getState().errorMessage) {
- if (getWidget().errorIndicatorElement == null) {
- getWidget().errorIndicatorElement = DOM.createSpan();
- getWidget().errorIndicatorElement
- .setClassName("v-errorindicator");
- }
- getWidget().wrapper.insertBefore(
- getWidget().errorIndicatorElement,
- getWidget().captionElement);
-
- } else if (getWidget().errorIndicatorElement != null) {
- getWidget().wrapper
- .removeChild(getWidget().errorIndicatorElement);
- getWidget().errorIndicatorElement = null;
- }
- }
- });
-
- addStateChangeHandler("resources", new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- if (getWidget().icon != null) {
- getWidget().wrapper.removeChild(getWidget().icon
- .getElement());
- getWidget().icon = null;
- }
- Icon icon = getIcon();
- if (icon != null) {
- getWidget().icon = icon;
- icon.setAlternateText(getState().iconAltText);
- getWidget().wrapper.insertBefore(icon.getElement(),
- getWidget().captionElement);
- }
+ }
+
+ @OnStateChange("errorMessage")
+ void setErrorMessage() {
+ if (null != getState().errorMessage) {
+ if (getWidget().errorIndicatorElement == null) {
+ getWidget().errorIndicatorElement = DOM.createSpan();
+ getWidget().errorIndicatorElement
+ .setClassName("v-errorindicator");
}
- });
+ getWidget().wrapper.insertBefore(getWidget().errorIndicatorElement,
+ getWidget().captionElement);
+
+ } else if (getWidget().errorIndicatorElement != null) {
+ getWidget().wrapper.removeChild(getWidget().errorIndicatorElement);
+ getWidget().errorIndicatorElement = null;
+ }
+ }
+
+ @OnStateChange("resources")
+ void onResourceChange() {
+ if (getWidget().icon != null) {
+ getWidget().wrapper.removeChild(getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+ Icon icon = getIcon();
+ if (icon != null) {
+ getWidget().icon = icon;
+ icon.setAlternateText(getState().iconAltText);
+ getWidget().wrapper.insertBefore(icon.getElement(),
+ getWidget().captionElement);
+ }
}
@Override
@@ -103,22 +97,27 @@ public class ButtonConnector extends AbstractComponentConnector implements
focusHandlerRegistration);
blurHandlerRegistration = EventHelper.updateBlurHandler(this,
blurHandlerRegistration);
+ }
- if (stateChangeEvent.hasPropertyChanged("caption")
- || stateChangeEvent.hasPropertyChanged("htmlContentAllowed")) {
- // Set text
- if (getState().htmlContentAllowed) {
- getWidget().setHtml(getState().caption);
- } else {
- getWidget().setText(getState().caption);
- }
+ @OnStateChange({ "caption", "htmlContentAllowed" })
+ void setCaption() {
+ String caption = getState().caption;
+ if (getState().htmlContentAllowed) {
+ getWidget().setHtml(caption);
+ } else {
+ getWidget().setText(caption);
}
+ }
- if (getWidget().icon != null
- && stateChangeEvent.hasPropertyChanged("iconAltText")) {
+ @OnStateChange("iconAltText")
+ void setIconAltText() {
+ if (getWidget().icon != null) {
getWidget().icon.setAlternateText(getState().iconAltText);
}
+ }
+ @OnStateChange("clickShortcutKeyCode")
+ void setClickShortcut() {
getWidget().clickShortcut = getState().clickShortcutKeyCode;
}