Change-Id: I8ea2b781fab42458bf55a751c1229e391365e965tags/7.2.0.beta1
import com.google.gwt.user.rebind.SourceWriter; | import com.google.gwt.user.rebind.SourceWriter; | ||||
import com.vaadin.client.JsArrayObject; | import com.vaadin.client.JsArrayObject; | ||||
import com.vaadin.client.ServerConnector; | import com.vaadin.client.ServerConnector; | ||||
import com.vaadin.client.annotations.OnStateChange; | |||||
import com.vaadin.client.metadata.ConnectorBundleLoader; | import com.vaadin.client.metadata.ConnectorBundleLoader; | ||||
import com.vaadin.client.metadata.InvokationHandler; | import com.vaadin.client.metadata.InvokationHandler; | ||||
import com.vaadin.client.metadata.OnStateChangeMethod; | |||||
import com.vaadin.client.metadata.ProxyHandler; | import com.vaadin.client.metadata.ProxyHandler; | ||||
import com.vaadin.client.metadata.TypeData; | import com.vaadin.client.metadata.TypeData; | ||||
import com.vaadin.client.metadata.TypeDataStore; | import com.vaadin.client.metadata.TypeDataStore; | ||||
import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle; | import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle; | ||||
import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor; | import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor; | ||||
import com.vaadin.server.widgetsetutils.metadata.GeneratedSerializer; | 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.Property; | ||||
import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor; | import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor; | ||||
import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor; | import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor; | ||||
writePropertyTypes(logger, w, bundle); | writePropertyTypes(logger, w, bundle); | ||||
writeSerializers(logger, w, bundle); | writeSerializers(logger, w, bundle); | ||||
writeDelegateToWidget(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, | private void writeSuperClasses(SplittingSourceWriter w, | ||||
List<TypeVisitor> visitors = Arrays.<TypeVisitor> asList( | List<TypeVisitor> visitors = Arrays.<TypeVisitor> asList( | ||||
new ConnectorInitVisitor(), new StateInitVisitor(), | new ConnectorInitVisitor(), new StateInitVisitor(), | ||||
new WidgetInitVisitor(), new ClientRpcVisitor(), | new WidgetInitVisitor(), new ClientRpcVisitor(), | ||||
new ServerRpcVisitor()); | |||||
new ServerRpcVisitor(), new OnStateChangeVisitor()); | |||||
for (TypeVisitor typeVisitor : visitors) { | for (TypeVisitor typeVisitor : visitors) { | ||||
typeVisitor.init(oracle); | typeVisitor.init(oracle); | ||||
} | } |
private final Map<JClassType, Set<JMethod>> needsInvoker = new HashMap<JClassType, Set<JMethod>>(); | 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>> needsParamTypes = new HashMap<JClassType, Set<JMethod>>(); | ||||
private final Map<JClassType, Set<JMethod>> needsDelayedInfo = 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> needsProperty = new HashSet<Property>(); | ||||
private final Set<Property> needsDelegateToWidget = new HashSet<Property>(); | private final Set<Property> needsDelegateToWidget = new HashSet<Property>(); | ||||
JType type = iterator.next(); | JType type = iterator.next(); | ||||
iterator.remove(); | iterator.remove(); | ||||
if (hasSserializeSupport(type)) { | |||||
if (hasSerializeSupport(type)) { | |||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
public void setNeedsSerialize(JType type) { | public void setNeedsSerialize(JType type) { | ||||
if (!hasSserializeSupport(type)) { | |||||
if (!hasSerializeSupport(type)) { | |||||
needsSerializeSupport.add(type); | needsSerializeSupport.add(type); | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
private boolean hasSserializeSupport(JType type) { | |||||
private boolean hasSerializeSupport(JType type) { | |||||
if (hasSerializeSupport.contains(type)) { | if (hasSerializeSupport.contains(type)) { | ||||
return true; | return true; | ||||
} else { | } else { | ||||
return previousBundle != null | return previousBundle != null | ||||
&& previousBundle.hasSserializeSupport(type); | |||||
&& previousBundle.hasSerializeSupport(type); | |||||
} | } | ||||
} | } | ||||
return Collections.unmodifiableSet(needsDelegateToWidget); | 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; | |||||
} | |||||
} | } |
/* | |||||
* 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(); | |||||
} | |||||
} | |||||
} | |||||
} |
@Override | @Override | ||||
public void visitConnector(TreeLogger logger, JClassType type, | public void visitConnector(TreeLogger logger, JClassType type, | ||||
ConnectorBundle bundle) { | ConnectorBundle bundle) { | ||||
JMethod getState = findInheritedMethod(type, "getState"); | |||||
JMethod getState = ConnectorBundle | |||||
.findInheritedMethod(type, "getState"); | |||||
bundle.setNeedsReturnType(type, getState); | bundle.setNeedsReturnType(type, getState); | ||||
bundle.setNeedsSerialize(getState.getReturnType()); | bundle.setNeedsSerialize(getState.getReturnType()); |
import com.google.gwt.core.ext.TreeLogger; | import com.google.gwt.core.ext.TreeLogger; | ||||
import com.google.gwt.core.ext.UnableToCompleteException; | import com.google.gwt.core.ext.UnableToCompleteException; | ||||
import com.google.gwt.core.ext.typeinfo.JClassType; | 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.NotFoundException; | ||||
import com.google.gwt.core.ext.typeinfo.TypeOracle; | import com.google.gwt.core.ext.typeinfo.TypeOracle; | ||||
// Default does nothing | // 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; | |||||
} | |||||
} | } |
ConnectorBundle bundle) throws UnableToCompleteException { | ConnectorBundle bundle) throws UnableToCompleteException { | ||||
if (ConnectorBundle.isConnectedComponentConnector(type)) { | if (ConnectorBundle.isConnectedComponentConnector(type)) { | ||||
// The class in which createWidget is implemented | // 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(); | JClassType widgetType = getWidget.getReturnType().isClass(); | ||||
// Needs GWT constructor if createWidget is not overridden | // Needs GWT constructor if createWidget is not overridden | ||||
} | } | ||||
// Check state properties for @DelegateToWidget | // Check state properties for @DelegateToWidget | ||||
JMethod getState = findInheritedMethod(type, "getState"); | |||||
JMethod getState = ConnectorBundle.findInheritedMethod(type, | |||||
"getState"); | |||||
JClassType stateType = getState.getReturnType().isClass(); | JClassType stateType = getState.getReturnType().isClass(); | ||||
Collection<Property> properties = bundle.getProperties(stateType); | Collection<Property> properties = bundle.getProperties(stateType); | ||||
String methodName = DelegateToWidget.Helper | String methodName = DelegateToWidget.Helper | ||||
.getDelegateTarget(property.getName(), | .getDelegateTarget(property.getName(), | ||||
delegateToWidget.value()); | delegateToWidget.value()); | ||||
JMethod delegatedSetter = findInheritedMethod(widgetType, | |||||
methodName, property.getPropertyType()); | |||||
JMethod delegatedSetter = ConnectorBundle | |||||
.findInheritedMethod(widgetType, methodName, | |||||
property.getPropertyType()); | |||||
if (delegatedSetter == null) { | if (delegatedSetter == null) { | ||||
logger.log( | logger.log( | ||||
Type.ERROR, | Type.ERROR, |
/* | |||||
* 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(); | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
import com.vaadin.client.FastStringMap; | import com.vaadin.client.FastStringMap; | ||||
import com.vaadin.client.FastStringSet; | import com.vaadin.client.FastStringSet; | ||||
import com.vaadin.client.JsArrayObject; | import com.vaadin.client.JsArrayObject; | ||||
import com.vaadin.client.annotations.OnStateChange; | |||||
import com.vaadin.client.communication.JSONSerializer; | import com.vaadin.client.communication.JSONSerializer; | ||||
public class TypeDataStore { | public class TypeDataStore { | ||||
private final FastStringMap<JsArrayString> delegateToWidgetProperties = FastStringMap | private final FastStringMap<JsArrayString> delegateToWidgetProperties = FastStringMap | ||||
.create(); | .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 delayedMethods = FastStringSet.create(); | ||||
private final FastStringSet lastOnlyMethods = FastStringSet.create(); | private final FastStringSet lastOnlyMethods = FastStringSet.create(); | ||||
return typeData[beanName] !== undefined ; | 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); | |||||
} | |||||
} | |||||
} | } |
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.HashSet; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Set; | import java.util.Set; | ||||
import com.google.web.bindery.event.shared.HandlerRegistration; | import com.google.web.bindery.event.shared.HandlerRegistration; | ||||
import com.vaadin.client.ApplicationConnection; | import com.vaadin.client.ApplicationConnection; | ||||
import com.vaadin.client.FastStringMap; | import com.vaadin.client.FastStringMap; | ||||
import com.vaadin.client.JsArrayObject; | |||||
import com.vaadin.client.Profiler; | import com.vaadin.client.Profiler; | ||||
import com.vaadin.client.ServerConnector; | import com.vaadin.client.ServerConnector; | ||||
import com.vaadin.client.Util; | import com.vaadin.client.Util; | ||||
import com.vaadin.client.communication.StateChangeEvent; | import com.vaadin.client.communication.StateChangeEvent; | ||||
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; | import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; | ||||
import com.vaadin.client.metadata.NoDataException; | import com.vaadin.client.metadata.NoDataException; | ||||
import com.vaadin.client.metadata.OnStateChangeMethod; | |||||
import com.vaadin.client.metadata.Type; | import com.vaadin.client.metadata.Type; | ||||
import com.vaadin.client.metadata.TypeData; | import com.vaadin.client.metadata.TypeData; | ||||
import com.vaadin.client.metadata.TypeDataStore; | |||||
import com.vaadin.shared.communication.ClientRpc; | import com.vaadin.shared.communication.ClientRpc; | ||||
import com.vaadin.shared.communication.ServerRpc; | import com.vaadin.shared.communication.ServerRpc; | ||||
import com.vaadin.shared.communication.SharedState; | import com.vaadin.shared.communication.SharedState; | ||||
} | } | ||||
updateEnabledState(isEnabled()); | 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"); | Profiler.leave("AbstractConnector.onStateChanged"); | ||||
} | } | ||||
import com.google.gwt.user.client.DOM; | import com.google.gwt.user.client.DOM; | ||||
import com.vaadin.client.EventHelper; | import com.vaadin.client.EventHelper; | ||||
import com.vaadin.client.MouseEventDetailsBuilder; | import com.vaadin.client.MouseEventDetailsBuilder; | ||||
import com.vaadin.client.annotations.OnStateChange; | |||||
import com.vaadin.client.communication.StateChangeEvent; | import com.vaadin.client.communication.StateChangeEvent; | ||||
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; | |||||
import com.vaadin.client.ui.AbstractComponentConnector; | import com.vaadin.client.ui.AbstractComponentConnector; | ||||
import com.vaadin.client.ui.Icon; | import com.vaadin.client.ui.Icon; | ||||
import com.vaadin.client.ui.VButton; | import com.vaadin.client.ui.VButton; | ||||
super.init(); | super.init(); | ||||
getWidget().addClickHandler(this); | getWidget().addClickHandler(this); | ||||
getWidget().client = getConnection(); | 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 | @Override | ||||
focusHandlerRegistration); | focusHandlerRegistration); | ||||
blurHandlerRegistration = EventHelper.updateBlurHandler(this, | blurHandlerRegistration = EventHelper.updateBlurHandler(this, | ||||
blurHandlerRegistration); | 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); | getWidget().icon.setAlternateText(getState().iconAltText); | ||||
} | } | ||||
} | |||||
@OnStateChange("clickShortcutKeyCode") | |||||
void setClickShortcut() { | |||||
getWidget().clickShortcut = getState().clickShortcutKeyCode; | getWidget().clickShortcut = getState().clickShortcutKeyCode; | ||||
} | } | ||||