]> source.dussan.org Git - vaadin-framework.git/commitdiff
Support using public fields in state classes (#9324)
authorLeif Åstrand <leif@vaadin.com>
Wed, 22 Aug 2012 17:14:30 +0000 (20:14 +0300)
committerLeif Åstrand <leif@vaadin.com>
Wed, 22 Aug 2012 17:14:30 +0000 (20:14 +0300)
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/FieldProperty.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java
server/src/com/vaadin/terminal/gwt/server/JsonCodec.java
server/src/com/vaadin/ui/Label.java
shared/src/com/vaadin/shared/ui/label/LabelState.java
shared/src/com/vaadin/shared/ui/orderedlayout/AbstractOrderedLayoutState.java
tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java

index 9a5b83f46087c2d58c776a78eaea11dece708125..a2e61947e8006bac34b2dd7a1b3c339d8e6704e2 100644 (file)
@@ -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) {");
index ad6b1eb102afe45e8543467b138d0b1ad213879e..7515124a5a52c06bff6cfb8331a5e477fe0a15a9 100644 (file)
@@ -372,6 +372,7 @@ public class ConnectorBundle {
         HashSet<Property> properties = new HashSet<Property>();
 
         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 (file)
index 0000000..31555cc
--- /dev/null
@@ -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<FieldProperty> findProperties(JClassType type) {
+        Collection<FieldProperty> properties = new ArrayList<FieldProperty>();
+
+        List<JField> fields = getPublicFields(type);
+        for (JField field : fields) {
+            properties.add(new FieldProperty(type, field));
+        }
+
+        return properties;
+    }
+
+    private static List<JField> getPublicFields(JClassType type) {
+        Set<String> names = new HashSet<String>();
+        ArrayList<JField> fields = new ArrayList<JField>();
+        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;
+    }
+
+}
index 4280db8bc94992a73b519d203189f89da9cdec4e..57f8c16952137d79ccda0442d182a62095aa20f4 100644 (file)
@@ -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("");
index 892f7ec52650a2a0ca01142db259e856155db9e1..1eee9c4f529e25525fc60fef8919be8a83e5db80 100644 (file)
@@ -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<FieldProperty> find(Class<?> type)
+                throws IntrospectionException {
+            Collection<FieldProperty> properties = new ArrayList<FieldProperty>();
+
+            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<MethodProperty> find(Class<?> type)
+                throws IntrospectionException {
+            Collection<MethodProperty> properties = new ArrayList<MethodProperty>();
+
+            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<Class<?>, String> typeToTransportType = new HashMap<Class<?>, 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<BeanProperty> getProperties(Class<?> type)
+            throws IntrospectionException {
+        Collection<BeanProperty> properties = new ArrayList<BeanProperty>();
+
+        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)) {
index 668b99a74c38712b50126bde84e436d12b4fed3b..10fa7410586010c7062007c1235951f3adb695aa 100644 (file)
@@ -168,7 +168,7 @@ public class Label extends AbstractComponent implements Property<String>,
     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>,
                     + 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<String>,
      * @see ContentMode
      */
     public ContentMode getContentMode() {
-        return getState().getContentMode();
+        return getState().contentMode;
     }
 
     /**
@@ -293,7 +293,7 @@ public class Label extends AbstractComponent implements Property<String>,
             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<String>,
     @Override
     public void valueChange(Property.ValueChangeEvent event) {
         // Update the internal value from the data source
-        getState().setText(getValue());
+        getState().text = getValue();
         requestRepaint();
 
         fireValueChange();
index 35e27bc63d1abe0d524d32570665d5cd142d3ed4..a91aeb0aa1bd4be4345d7e7c1c7c43cabf87284f 100644 (file)
@@ -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 = "";
 }
index 3fa2ad771c2ee4ab8de8e264a641b488e7e5b543..35456ab9ac5f862dc9eaae169e8e860a939d1e00 100644 (file)
@@ -25,7 +25,7 @@ import com.vaadin.shared.ui.AlignmentInfo;
 public class AbstractOrderedLayoutState extends AbstractLayoutState {
     private boolean spacing = false;
 
-    public HashMap<Connector, ChildComponentData> childData = new HashMap<Connector, ChildComponentData>();
+    private HashMap<Connector, ChildComponentData> childData = new HashMap<Connector, ChildComponentData>();
 
     private int marginsBitmask = 0;
 
index 16cc0ede980dd6fd33d967c04dc90a0242b6849d..7775b667a1079496818af32ed3d900c822618c85 100644 (file)
@@ -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;
             }