summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java91
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java50
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java54
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java3
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java25
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java15
-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
11 files changed, 494 insertions, 85 deletions
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java
index edbf5e260c..9b00142534 100644
--- a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java
@@ -46,8 +46,10 @@ import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.ServerConnector;
+import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.metadata.InvokationHandler;
+import com.vaadin.client.metadata.OnStateChangeMethod;
import com.vaadin.client.metadata.ProxyHandler;
import com.vaadin.client.metadata.TypeData;
import com.vaadin.client.metadata.TypeDataStore;
@@ -56,6 +58,7 @@ import com.vaadin.server.widgetsetutils.metadata.ClientRpcVisitor;
import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle;
import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor;
import com.vaadin.server.widgetsetutils.metadata.GeneratedSerializer;
+import com.vaadin.server.widgetsetutils.metadata.OnStateChangeVisitor;
import com.vaadin.server.widgetsetutils.metadata.Property;
import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor;
import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor;
@@ -463,6 +466,92 @@ public class ConnectorBundleLoaderFactory extends Generator {
writePropertyTypes(logger, w, bundle);
writeSerializers(logger, w, bundle);
writeDelegateToWidget(logger, w, bundle);
+ writeOnStateChangeHandlers(logger, w, bundle);
+ }
+
+ private void writeOnStateChangeHandlers(TreeLogger logger,
+ SplittingSourceWriter w, ConnectorBundle bundle)
+ throws UnableToCompleteException {
+ Map<JClassType, Set<JMethod>> needsOnStateChangeHandler = bundle
+ .getNeedsOnStateChangeHandler();
+ for (Entry<JClassType, Set<JMethod>> entry : needsOnStateChangeHandler
+ .entrySet()) {
+ JClassType connector = entry.getKey();
+
+ TreeLogger typeLogger = logger.branch(
+ Type.DEBUG,
+ "Generating @OnStateChange support for "
+ + connector.getName());
+
+ // Build map to speed up error checking
+ HashMap<String, Property> stateProperties = new HashMap<String, Property>();
+ JClassType stateType = ConnectorBundle
+ .findInheritedMethod(connector, "getState").getReturnType()
+ .isClassOrInterface();
+ for (Property property : bundle.getProperties(stateType)) {
+ stateProperties.put(property.getName(), property);
+ }
+
+ for (JMethod method : entry.getValue()) {
+ TreeLogger methodLogger = typeLogger.branch(Type.DEBUG,
+ "Processing method " + method.getName());
+
+ if (method.isPublic() || method.isProtected()) {
+ methodLogger
+ .log(Type.ERROR,
+ "@OnStateChange is only supported for methods with private or default visibility.");
+ throw new UnableToCompleteException();
+ }
+
+ OnStateChange onStateChange = method
+ .getAnnotation(OnStateChange.class);
+
+ String[] properties = onStateChange.value();
+
+ if (properties.length == 0) {
+ methodLogger.log(Type.ERROR,
+ "There are no properties to listen to");
+ throw new UnableToCompleteException();
+ }
+
+ // Verify that all properties do exist
+ for (String propertyName : properties) {
+ if (!stateProperties.containsKey(propertyName)) {
+ methodLogger.log(Type.ERROR,
+ "State class has no property named "
+ + propertyName);
+ throw new UnableToCompleteException();
+ }
+ }
+
+ if (method.getParameters().length != 0) {
+ methodLogger.log(Type.ERROR,
+ "Method should accept zero parameters");
+ throw new UnableToCompleteException();
+ }
+
+ // new OnStateChangeMethod(Class declaringClass, String
+ // methodName, String[], properties)
+ w.print("store.addOnStateChangeMethod(%s, new %s(",
+ getClassLiteralString(connector),
+ OnStateChangeMethod.class.getName());
+ if (!connector.equals(method.getEnclosingType())) {
+ w.print("%s, ",
+ getClassLiteralString(method.getEnclosingType()));
+ }
+ w.print("\"%s\", ", method.getName());
+
+ w.print("new String[] {");
+ for (String propertyName : properties) {
+ w.print("\"%s\", ", propertyName);
+ }
+ w.print("}");
+
+ w.println("));");
+
+ w.splitIfNeeded();
+ }
+ }
}
private void writeSuperClasses(SplittingSourceWriter w,
@@ -1109,7 +1198,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
List<TypeVisitor> visitors = Arrays.<TypeVisitor> asList(
new ConnectorInitVisitor(), new StateInitVisitor(),
new WidgetInitVisitor(), new ClientRpcVisitor(),
- new ServerRpcVisitor());
+ new ServerRpcVisitor(), new OnStateChangeVisitor());
for (TypeVisitor typeVisitor : visitors) {
typeVisitor.init(oracle);
}
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java
index 0064a24aef..f762a484b7 100644
--- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java
@@ -69,6 +69,7 @@ public class ConnectorBundle {
private final Map<JClassType, Set<JMethod>> needsInvoker = new HashMap<JClassType, Set<JMethod>>();
private final Map<JClassType, Set<JMethod>> needsParamTypes = new HashMap<JClassType, Set<JMethod>>();
private final Map<JClassType, Set<JMethod>> needsDelayedInfo = new HashMap<JClassType, Set<JMethod>>();
+ private final Map<JClassType, Set<JMethod>> needsOnStateChange = new HashMap<JClassType, Set<JMethod>>();
private final Set<Property> needsProperty = new HashSet<Property>();
private final Set<Property> needsDelegateToWidget = new HashSet<Property>();
@@ -199,7 +200,7 @@ public class ConnectorBundle {
JType type = iterator.next();
iterator.remove();
- if (hasSserializeSupport(type)) {
+ if (hasSerializeSupport(type)) {
continue;
}
@@ -516,7 +517,7 @@ public class ConnectorBundle {
}
public void setNeedsSerialize(JType type) {
- if (!hasSserializeSupport(type)) {
+ if (!hasSerializeSupport(type)) {
needsSerializeSupport.add(type);
}
}
@@ -557,12 +558,12 @@ public class ConnectorBundle {
return false;
}
- private boolean hasSserializeSupport(JType type) {
+ private boolean hasSerializeSupport(JType type) {
if (hasSerializeSupport.contains(type)) {
return true;
} else {
return previousBundle != null
- && previousBundle.hasSserializeSupport(type);
+ && previousBundle.hasSerializeSupport(type);
}
}
@@ -585,4 +586,45 @@ public class ConnectorBundle {
return Collections.unmodifiableSet(needsDelegateToWidget);
}
+ public void setNeedsOnStateChangeHandler(JClassType type, JMethod method) {
+ if (!isNeedsOnStateChangeHandler(type, method)) {
+ addMapping(needsOnStateChange, type, method);
+ }
+ }
+
+ private boolean isNeedsOnStateChangeHandler(JClassType type, JMethod method) {
+ if (hasMapping(needsOnStateChange, type, method)) {
+ return true;
+ } else {
+ return previousBundle != null
+ && previousBundle.isNeedsOnStateChangeHandler(type, method);
+ }
+ }
+
+ public Map<JClassType, Set<JMethod>> getNeedsOnStateChangeHandler() {
+ return Collections.unmodifiableMap(needsOnStateChange);
+ }
+
+ public static JMethod findInheritedMethod(JClassType type,
+ String methodName, JType... params) {
+
+ JClassType currentType = type;
+ while (currentType != null) {
+ JMethod method = currentType.findMethod(methodName, params);
+ if (method != null) {
+ return method;
+ }
+ currentType = currentType.getSuperclass();
+ }
+
+ JClassType[] interfaces = type.getImplementedInterfaces();
+ for (JClassType iface : interfaces) {
+ JMethod method = iface.findMethod(methodName, params);
+ if (method != null) {
+ return method;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java
new file mode 100644
index 0000000000..ea3e639486
--- /dev/null
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java
@@ -0,0 +1,54 @@
+/*
+ * 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.server.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.vaadin.client.annotations.OnStateChange;
+import com.vaadin.shared.ui.Connect;
+
+/**
+ * Visits Connector classes and check for methods with @OnStateChange
+ * annotations.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class OnStateChangeVisitor extends TypeVisitor {
+
+ @Override
+ public void visitConnector(TreeLogger logger, JClassType type,
+ ConnectorBundle bundle) throws UnableToCompleteException {
+ Connect connectAnnotation = type.getAnnotation(Connect.class);
+ if (connectAnnotation != null) {
+ // Find all the annotated methods in all the superclasses
+ JClassType connector = type;
+ while (connector != null) {
+ for (JMethod method : connector.getMethods()) {
+ if (method.getAnnotation(OnStateChange.class) != null) {
+ bundle.setNeedsInvoker(connector, method);
+ bundle.setNeedsOnStateChangeHandler(type, method);
+ }
+ }
+
+ connector = connector.getSuperclass();
+ }
+ }
+ }
+
+}
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java
index 56a404fbb5..f58d1c5d40 100644
--- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java
@@ -24,7 +24,8 @@ public class StateInitVisitor extends TypeVisitor {
@Override
public void visitConnector(TreeLogger logger, JClassType type,
ConnectorBundle bundle) {
- JMethod getState = findInheritedMethod(type, "getState");
+ JMethod getState = ConnectorBundle
+ .findInheritedMethod(type, "getState");
bundle.setNeedsReturnType(type, getState);
bundle.setNeedsSerialize(getState.getReturnType());
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java
index 0af3c6976d..aae610cdcd 100644
--- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java
@@ -18,8 +18,6 @@ package com.vaadin.server.widgetsetutils.metadata;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JMethod;
-import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
@@ -43,27 +41,4 @@ public abstract class TypeVisitor {
// Default does nothing
}
- protected JMethod findInheritedMethod(JClassType type, String methodName,
- JType... params) {
-
- JClassType currentType = type;
- while (currentType != null) {
- JMethod method = currentType.findMethod(methodName, params);
- if (method != null) {
- return method;
- }
- currentType = currentType.getSuperclass();
- }
-
- JClassType[] interfaces = type.getImplementedInterfaces();
- for (JClassType iface : interfaces) {
- JMethod method = iface.findMethod(methodName, params);
- if (method != null) {
- return method;
- }
- }
-
- return null;
- }
-
}
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java
index 4de9d2ae99..bac7f5a0f7 100644
--- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java
@@ -32,10 +32,11 @@ public class WidgetInitVisitor extends TypeVisitor {
ConnectorBundle bundle) throws UnableToCompleteException {
if (ConnectorBundle.isConnectedComponentConnector(type)) {
// The class in which createWidget is implemented
- JClassType createWidgetClass = findInheritedMethod(type,
- "createWidget").getEnclosingType();
+ JClassType createWidgetClass = ConnectorBundle.findInheritedMethod(
+ type, "createWidget").getEnclosingType();
- JMethod getWidget = findInheritedMethod(type, "getWidget");
+ JMethod getWidget = ConnectorBundle.findInheritedMethod(type,
+ "getWidget");
JClassType widgetType = getWidget.getReturnType().isClass();
// Needs GWT constructor if createWidget is not overridden
@@ -48,7 +49,8 @@ public class WidgetInitVisitor extends TypeVisitor {
}
// Check state properties for @DelegateToWidget
- JMethod getState = findInheritedMethod(type, "getState");
+ JMethod getState = ConnectorBundle.findInheritedMethod(type,
+ "getState");
JClassType stateType = getState.getReturnType().isClass();
Collection<Property> properties = bundle.getProperties(stateType);
@@ -63,8 +65,9 @@ public class WidgetInitVisitor extends TypeVisitor {
String methodName = DelegateToWidget.Helper
.getDelegateTarget(property.getName(),
delegateToWidget.value());
- JMethod delegatedSetter = findInheritedMethod(widgetType,
- methodName, property.getPropertyType());
+ JMethod delegatedSetter = ConnectorBundle
+ .findInheritedMethod(widgetType, methodName,
+ property.getPropertyType());
if (delegatedSetter == null) {
logger.log(
Type.ERROR,
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;
}