diff options
author | Leif Åstrand <leif@vaadin.com> | 2014-03-27 11:14:55 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2014-04-01 08:15:56 +0000 |
commit | 7bfb2347ad32187444c194e7ebadae6a6d3597ca (patch) | |
tree | c94081d4cb9d2eba433c4cec3ddbcc27da57c643 /client-compiler | |
parent | a636bb70c20277228468345017a2f8d42d39857c (diff) | |
download | vaadin-framework-7bfb2347ad32187444c194e7ebadae6a6d3597ca.tar.gz vaadin-framework-7bfb2347ad32187444c194e7ebadae6a6d3597ca.zip |
Implement @OnStateChange (#12958)
Change-Id: I8ea2b781fab42458bf55a751c1229e391365e965
Diffstat (limited to 'client-compiler')
6 files changed, 201 insertions, 37 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, |