From: Leif Åstrand Date: Wed, 22 Aug 2012 17:14:30 +0000 (+0300) Subject: Support using public fields in state classes (#9324) X-Git-Tag: 7.0.0.beta1~221^2~12 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=38358ae25543433f8cc381101bc340adc7e5c67f;p=vaadin-framework.git Support using public fields in state classes (#9324) --- diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java index 9a5b83f460..a2e61947e8 100644 --- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java @@ -219,7 +219,7 @@ public class ConnectorBundleLoaderFactory extends Generator { writeClassLiteral(w, property.getBeanType()); w.print(", \""); w.print(escape(property.getName())); - w.print("\", new Invoker() {"); + w.println("\", new Invoker() {"); w.indent(); w.println("public Object invoke(Object bean, Object[] params) {"); diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java index ad6b1eb102..7515124a5a 100644 --- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java @@ -372,6 +372,7 @@ public class ConnectorBundle { HashSet properties = new HashSet(); properties.addAll(MethodProperty.findProperties(type)); + properties.addAll(FieldProperty.findProperties(type)); return properties; } diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/FieldProperty.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/FieldProperty.java new file mode 100644 index 0000000000..31555cc30b --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/FieldProperty.java @@ -0,0 +1,77 @@ +/* + * Copyright 2011 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.terminal.gwt.widgetsetutils.metadata; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JField; +import com.google.gwt.user.rebind.SourceWriter; + +public class FieldProperty extends Property { + + private FieldProperty(JClassType beanType, JField field) { + super(field.getName(), beanType, field.getType()); + } + + @Override + public void writeSetterBody(TreeLogger logger, SourceWriter w, + String beanVariable, String valueVariable) { + w.print("((%s) %s).%s = (%s)%s;", getBeanType() + .getQualifiedSourceName(), beanVariable, getName(), + getUnboxedPropertyTypeName(), valueVariable); + } + + @Override + public void writeGetterBody(TreeLogger logger, SourceWriter w, + String beanVariable) { + w.print("return ((%s) %s).%s;", getBeanType().getQualifiedSourceName(), + beanVariable, getName()); + } + + public static Collection findProperties(JClassType type) { + Collection properties = new ArrayList(); + + List fields = getPublicFields(type); + for (JField field : fields) { + properties.add(new FieldProperty(type, field)); + } + + return properties; + } + + private static List getPublicFields(JClassType type) { + Set names = new HashSet(); + ArrayList fields = new ArrayList(); + for (JClassType subType : type.getFlattenedSupertypeHierarchy()) { + JField[] subFields = subType.getFields(); + for (JField field : subFields) { + if (field.isPublic() && !field.isStatic() + && names.add(field.getName())) { + fields.add(field); + } + } + } + return fields; + } + +} diff --git a/client/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java b/client/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java index 4280db8bc9..57f8c16952 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java +++ b/client/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java @@ -43,10 +43,10 @@ public class LabelConnector extends AbstractComponentConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); boolean sinkOnloads = false; - switch (getState().getContentMode()) { + switch (getState().contentMode) { case PREFORMATTED: PreElement preElement = Document.get().createPreElement(); - preElement.setInnerText(getState().getText()); + preElement.setInnerText(getState().text); // clear existing content getWidget().setHTML(""); // add preformatted text to dom @@ -54,14 +54,14 @@ public class LabelConnector extends AbstractComponentConnector { break; case TEXT: - getWidget().setText(getState().getText()); + getWidget().setText(getState().text); break; case XHTML: case RAW: sinkOnloads = true; case XML: - getWidget().setHTML(getState().getText()); + getWidget().setHTML(getState().text); break; default: getWidget().setText(""); diff --git a/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java index 892f7ec526..1eee9c4f52 100644 --- a/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java +++ b/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java @@ -21,9 +21,10 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; @@ -55,6 +56,107 @@ import com.vaadin.ui.ConnectorTracker; */ public class JsonCodec implements Serializable { + public static interface BeanProperty { + public Object getValue(Object bean) throws Exception; + + public void setValue(Object bean, Object value) throws Exception; + + public String getName(); + + public Type getType(); + } + + private static class FieldProperty implements BeanProperty { + private final Field field; + + public FieldProperty(Field field) { + this.field = field; + } + + @Override + public Object getValue(Object bean) throws Exception { + return field.get(bean); + } + + @Override + public void setValue(Object bean, Object value) throws Exception { + field.set(bean, value); + } + + @Override + public String getName() { + return field.getName(); + } + + @Override + public Type getType() { + return field.getGenericType(); + } + + public static Collection find(Class type) + throws IntrospectionException { + Collection properties = new ArrayList(); + + Field[] fields = type.getFields(); + for (Field field : fields) { + if (!Modifier.isStatic(field.getModifiers())) { + properties.add(new FieldProperty(field)); + } + } + + return properties; + } + + } + + private static class MethodProperty implements BeanProperty { + private final PropertyDescriptor pd; + + public MethodProperty(PropertyDescriptor pd) { + this.pd = pd; + } + + @Override + public Object getValue(Object bean) throws Exception { + Method readMethod = pd.getReadMethod(); + return readMethod.invoke(bean); + } + + @Override + public void setValue(Object bean, Object value) throws Exception { + pd.getWriteMethod().invoke(bean, value); + } + + @Override + public String getName() { + String fieldName = pd.getWriteMethod().getName().substring(3); + fieldName = Character.toLowerCase(fieldName.charAt(0)) + + fieldName.substring(1); + return fieldName; + } + + public static Collection find(Class type) + throws IntrospectionException { + Collection properties = new ArrayList(); + + for (PropertyDescriptor pd : Introspector.getBeanInfo(type) + .getPropertyDescriptors()) { + if (pd.getReadMethod() == null || pd.getWriteMethod() == null) { + continue; + } + + properties.add(new MethodProperty(pd)); + } + return properties; + } + + @Override + public Type getType() { + return pd.getReadMethod().getGenericReturnType(); + } + + } + private static Map, String> typeToTransportType = new HashMap, String>(); /** @@ -468,27 +570,6 @@ public class JsonCodec implements Serializable { return set; } - /** - * Returns the name that should be used as field name in the JSON. We strip - * "set" from the setter, keeping the result - this is easy to do on both - * server and client, avoiding some issues with cASE. E.g setZIndex() - * becomes "zIndex". Also ensures that both getter and setter are present, - * returning null otherwise. - * - * @param pd - * @return the name to be used or null if both getter and setter are not - * found. - */ - static String getTransportFieldName(PropertyDescriptor pd) { - if (pd.getReadMethod() == null || pd.getWriteMethod() == null) { - return null; - } - String fieldName = pd.getWriteMethod().getName().substring(3); - fieldName = Character.toLowerCase(fieldName.charAt(0)) - + fieldName.substring(1); - return fieldName; - } - private static Object decodeObject(Type targetType, JSONObject serializedObject, ConnectorTracker connectorTracker) throws JSONException { @@ -497,31 +578,19 @@ public class JsonCodec implements Serializable { try { Object decodedObject = targetClass.newInstance(); - for (PropertyDescriptor pd : Introspector.getBeanInfo(targetClass) - .getPropertyDescriptors()) { + for (BeanProperty property : getProperties(targetClass)) { - String fieldName = getTransportFieldName(pd); - if (fieldName == null) { - continue; - } + String fieldName = property.getName(); Object encodedFieldValue = serializedObject.get(fieldName); - Type fieldType = pd.getReadMethod().getGenericReturnType(); + Type fieldType = property.getType(); Object decodedFieldValue = decodeInternalOrCustomType( fieldType, encodedFieldValue, connectorTracker); - pd.getWriteMethod().invoke(decodedObject, decodedFieldValue); + property.setValue(decodedObject, decodedFieldValue); } return decodedObject; - } catch (IllegalArgumentException e) { - throw new JSONException(e); - } catch (IllegalAccessException e) { - throw new JSONException(e); - } catch (InvocationTargetException e) { - throw new JSONException(e); - } catch (InstantiationException e) { - throw new JSONException(e); - } catch (IntrospectionException e) { + } catch (Exception e) { throw new JSONException(e); } } @@ -602,22 +671,27 @@ public class JsonCodec implements Serializable { return JSONObject.NULL; } + public static Collection getProperties(Class type) + throws IntrospectionException { + Collection properties = new ArrayList(); + + properties.addAll(MethodProperty.find(type)); + properties.addAll(FieldProperty.find(type)); + + return properties; + } + private static Object encodeObject(Object value, JSONObject diffState, ConnectorTracker connectorTracker) throws JSONException { JSONObject jsonMap = new JSONObject(); try { - for (PropertyDescriptor pd : Introspector.getBeanInfo( - value.getClass()).getPropertyDescriptors()) { - String fieldName = getTransportFieldName(pd); - if (fieldName == null) { - continue; - } - Method getterMethod = pd.getReadMethod(); + for (BeanProperty property : getProperties(value.getClass())) { + String fieldName = property.getName(); // We can't use PropertyDescriptor.getPropertyType() as it does // not support generics - Type fieldType = getterMethod.getGenericReturnType(); - Object fieldValue = getterMethod.invoke(value, (Object[]) null); + Type fieldType = property.getType(); + Object fieldValue = property.getValue(value); boolean equals = false; Object diffStateValue = null; if (diffState != null && diffState.has(fieldName)) { diff --git a/server/src/com/vaadin/ui/Label.java b/server/src/com/vaadin/ui/Label.java index 668b99a74c..10fa741058 100644 --- a/server/src/com/vaadin/ui/Label.java +++ b/server/src/com/vaadin/ui/Label.java @@ -168,7 +168,7 @@ public class Label extends AbstractComponent implements Property, public String getValue() { if (getPropertyDataSource() == null) { // Use internal value if we are running without a data source - return getState().getText(); + return getState().text; } return ConverterUtil.convertFromModel(getPropertyDataSource() .getValue(), String.class, getConverter(), getLocale()); @@ -189,7 +189,7 @@ public class Label extends AbstractComponent implements Property, + String.class.getName()); } if (getPropertyDataSource() == null) { - getState().setText((String) newStringValue); + getState().text = (String) newStringValue; requestRepaint(); } else { throw new IllegalStateException( @@ -277,7 +277,7 @@ public class Label extends AbstractComponent implements Property, * @see ContentMode */ public ContentMode getContentMode() { - return getState().getContentMode(); + return getState().contentMode; } /** @@ -293,7 +293,7 @@ public class Label extends AbstractComponent implements Property, throw new IllegalArgumentException("Content mode can not be null"); } - getState().setContentMode(contentMode); + getState().contentMode = contentMode; requestRepaint(); } @@ -384,7 +384,7 @@ public class Label extends AbstractComponent implements Property, @Override public void valueChange(Property.ValueChangeEvent event) { // Update the internal value from the data source - getState().setText(getValue()); + getState().text = getValue(); requestRepaint(); fireValueChange(); diff --git a/shared/src/com/vaadin/shared/ui/label/LabelState.java b/shared/src/com/vaadin/shared/ui/label/LabelState.java index 35e27bc63d..a91aeb0aa1 100644 --- a/shared/src/com/vaadin/shared/ui/label/LabelState.java +++ b/shared/src/com/vaadin/shared/ui/label/LabelState.java @@ -18,23 +18,6 @@ package com.vaadin.shared.ui.label; import com.vaadin.shared.ComponentState; public class LabelState extends ComponentState { - private ContentMode contentMode = ContentMode.TEXT; - private String text = ""; - - public ContentMode getContentMode() { - return contentMode; - } - - public void setContentMode(ContentMode contentMode) { - this.contentMode = contentMode; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - + public ContentMode contentMode = ContentMode.TEXT; + public String text = ""; } diff --git a/shared/src/com/vaadin/shared/ui/orderedlayout/AbstractOrderedLayoutState.java b/shared/src/com/vaadin/shared/ui/orderedlayout/AbstractOrderedLayoutState.java index 3fa2ad771c..35456ab9ac 100644 --- a/shared/src/com/vaadin/shared/ui/orderedlayout/AbstractOrderedLayoutState.java +++ b/shared/src/com/vaadin/shared/ui/orderedlayout/AbstractOrderedLayoutState.java @@ -25,7 +25,7 @@ import com.vaadin.shared.ui.AlignmentInfo; public class AbstractOrderedLayoutState extends AbstractLayoutState { private boolean spacing = false; - public HashMap childData = new HashMap(); + private HashMap childData = new HashMap(); private int marginsBitmask = 0; diff --git a/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java b/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java index 16cc0ede98..7775b667a1 100644 --- a/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java +++ b/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java @@ -1,7 +1,7 @@ package com.vaadin.terminal.gwt.server; /* - * Copyright 2011 Vaadin Ltd. + * Copyright 2011 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 @@ -15,9 +15,6 @@ package com.vaadin.terminal.gwt.server; * License for the specific language governing permissions and limitations under * the License. */ -import java.beans.BeanInfo; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; @@ -28,6 +25,7 @@ import junit.framework.TestCase; import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState; import com.vaadin.terminal.gwt.client.communication.JsonDecoder; import com.vaadin.terminal.gwt.client.communication.JsonEncoder; +import com.vaadin.terminal.gwt.server.JsonCodec.BeanProperty; /** * Tests for {@link JsonCodec}, {@link JsonEncoder}, {@link JsonDecoder} @@ -118,15 +116,9 @@ public class JSONSerializerTest extends TestCase { } private boolean equalsBean(Object o1, Object o2) throws Exception { - BeanInfo beanInfo = Introspector.getBeanInfo(o1.getClass()); - for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { - String fieldName = JsonCodec.getTransportFieldName(pd); - if (fieldName == null) { - continue; - } - - Object c1 = pd.getReadMethod().invoke(o1); - Object c2 = pd.getReadMethod().invoke(o2); + for (BeanProperty property : JsonCodec.getProperties(o1.getClass())) { + Object c1 = property.getValue(o1); + Object c2 = property.getValue(o2); if (!equals(c1, c2)) { return false; }