diff options
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; } |