Adds @GwtTransient, an annotation that tells GWT RPC
to treat a field as if it were marked with the Java
transient keyword, even though it's not.

Review by: robertvawter



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4422 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/GwtTransient.java b/user/src/com/google/gwt/user/client/rpc/GwtTransient.java
new file mode 100644
index 0000000..376e055
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/GwtTransient.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.google.gwt.user.client.rpc;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation means the same thing as the <code>transient</code> keyword,
+ * but it is ignored by all serialization systems other than GWT's. Usually the
+ * <code>transient</code> keyword should be used in preference to this
+ * annotation. However, for types used with multiple serialization systems, it
+ * can be useful.
+ */
+@Documented
+@Target(ElementType.FIELD)
+public @interface GwtTransient {
+}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index 687c07a..73c4564 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -31,6 +31,7 @@
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.user.client.rpc.GwtTransient;
 import com.google.gwt.user.client.rpc.IsSerializable;
 import com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.TypeParameterFlowInfo;
 import com.google.gwt.user.rebind.rpc.TypePaths.TypePath;
@@ -87,14 +88,14 @@
   private class TypeInfoComputed {
 
     /**
-     * <code>true</code> if the type is assignable to {@link IsSerializable}
-     * or {@link java.io.Serializable Serializable}.
+     * <code>true</code> if the type is assignable to {@link IsSerializable} or
+     * {@link java.io.Serializable Serializable}.
      */
     private final boolean autoSerializable;
 
     /**
-     * <code>true</code> if the this type directly implements one of the
-     * marker interfaces.
+     * <code>true</code> if the this type directly implements one of the marker
+     * interfaces.
      */
     private final boolean directlyImplementsMarker;
 
@@ -384,8 +385,8 @@
 
   /**
    * Return <code>true</code> if a class's fields should be considered for
-   * serialization. If it returns <code>false</code> then none of the fields
-   * of this class should be serialized.
+   * serialization. If it returns <code>false</code> then none of the fields of
+   * this class should be serialized.
    */
   static boolean shouldConsiderFieldsForSerialization(TreeLogger logger,
       JClassType type, boolean isSpeculative, TypeFilter filter) {
@@ -450,8 +451,8 @@
   }
 
   /**
-   * Returns <code>true</code> if the field qualifies for serialization
-   * without considering its type.
+   * Returns <code>true</code> if the field qualifies for serialization without
+   * considering its type.
    */
   static boolean shouldConsiderForSerialization(TreeLogger logger,
       boolean suppressNonStaticFinalFieldWarnings, JField field) {
@@ -459,6 +460,10 @@
       return false;
     }
 
+    if (field.isAnnotationPresent(GwtTransient.class)) {
+      return false;
+    }
+
     if (field.isFinal()) {
       logger.branch(suppressNonStaticFinalFieldWarnings ? TreeLogger.DEBUG
           : TreeLogger.WARN, "Field '" + field.toString()
@@ -711,7 +716,8 @@
 
     logSerializableTypes(logger, fieldSerializableTypes);
 
-    return new SerializableTypeOracleImpl(fieldSerializableTypes, possiblyInstantiatedTypes);
+    return new SerializableTypeOracleImpl(fieldSerializableTypes,
+        possiblyInstantiatedTypes);
   }
 
   /**
@@ -748,8 +754,8 @@
 
   /**
    * Same as
-   * {@link #checkTypeInstantiable(TreeLogger, JType, boolean, com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.TypePath)},
-   * except that returns the set of instantiable subtypes.
+   * {@link #checkTypeInstantiable(TreeLogger, JType, boolean, com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.TypePath)}
+   * , except that returns the set of instantiable subtypes.
    */
   boolean checkTypeInstantiable(TreeLogger logger, JType type,
       boolean isSpeculative, TypePath path, Set<JClassType> instSubtypes) {
@@ -1064,9 +1070,8 @@
 
       /*
        * If my super type did not check out, then I am not instantiable and we
-       * should error out... UNLESS I am *directly* serializable myself, in
-       * which case it's ok for me to be the root of a new instantiable
-       * hierarchy.
+       * should error out... UNLESS I amdirectly serializable myself, in which
+       * case it's ok for me to be the root of a new instantiable hierarchy.
        */
       if (!superTypeOk && !isDirectlySerializable(classOrInterface)) {
         return false;
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java b/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
index 6be308f..8936d73 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.rebind.rpc;
 
+import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JField;
@@ -114,13 +115,10 @@
     JField[] declFields = classType.getFields();
     assert (declFields != null);
     for (JField field : declFields) {
-      // TODO(mmendez): this is shared with the serializable type oracle
-      // builder, join with that
-      if (field.isStatic() || field.isTransient() || field.isFinal()) {
-        continue;
+      if (SerializableTypeOracleBuilder.shouldConsiderForSerialization(
+          TreeLogger.NULL, true, field)) {
+        fields.add(field);
       }
-
-      fields.add(field);
     }
 
     Collections.sort(fields, FIELD_COMPARATOR);
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index a3d9d13..e7b1258 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -105,6 +105,14 @@
 
   private static final int EXPOSURE_NONE = TypeParameterExposureComputer.EXPOSURE_NONE;
 
+  private static void addGwtTransient(Set<CompilationUnit> units) {
+    StringBuffer code = new StringBuffer();
+    code.append("package com.google.gwt.user.client.rpc;\n");
+    code.append("public @interface GwtTransient { }\n");
+    units.add(createMockCompilationUnit(
+        "com.google.gwt.user.client.rpc.GwtTransient", code));
+  }
+
   private static void addICRSE(Set<CompilationUnit> units) {
     StringBuffer code = new StringBuffer();
     code.append("package com.google.gwt.user.client.rpc;\n");
@@ -167,6 +175,7 @@
   }
 
   private static void addStandardClasses(Set<CompilationUnit> units) {
+    addGwtTransient(units);
     addJavaIoSerializable(units);
     addJavaLangObject(units);
     addJavaLangString(units);
@@ -2022,6 +2031,59 @@
   }
 
   /**
+   * Tests that STOB skips transient fields.
+   */
+  public void testTransient() throws UnableToCompleteException,
+      NotFoundException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import com.google.gwt.user.client.rpc.GwtTransient;\n");
+      code.append("import java.io.Serializable;\n");
+      code.append("public class A implements Serializable {\n");
+      code.append("  transient ServerOnly1 serverOnly1;\n");
+      code.append("  @GwtTransient ServerOnly2 serverOnly2;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("A", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("class ServerOnly1 implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ServerOnly1", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("class ServerOnly2 implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ServerOnly2", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JClassType a = to.getType("A");
+    JClassType serverOnly1 = to.getType("ServerOnly1");
+    JClassType serverOnly2 = to.getType("ServerOnly2");
+
+    SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, a);
+    SerializableTypeOracle so = sob.build(logger);
+
+    assertSerializableTypes(so, a);
+    assertInstantiable(so, a);
+    assertNotFieldSerializable(so, serverOnly1);
+    assertNotFieldSerializable(so, serverOnly2);
+  }
+
+  /**
    * Miscellaneous direct tests of {@link TypeConstrainer}.
    * 
    * @throws UnableToCompleteException