Formalize how default extensions are provided by ClientBundle resource types.
Patch by: bobv
Review by: rjrjr
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5978 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
index d3ab066..c8b112e 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
@@ -19,6 +19,9 @@
import com.google.gwt.dev.util.collect.Sets;
import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -45,8 +48,8 @@
}
/**
- * Returns <code>true</code> if the rhs array type can be assigned to the
- * lhs array type.
+ * Returns <code>true</code> if the rhs array type can be assigned to the lhs
+ * array type.
*/
private static boolean areArraysAssignable(JArrayType lhsType,
JArrayType rhsType) {
@@ -327,8 +330,8 @@
}
/**
- * Cached set of supertypes for this type (including itself). If null,
- * the set has not been calculated yet.
+ * Cached set of supertypes for this type (including itself). If null, the set
+ * has not been calculated yet.
*/
private Set<JClassType> flattenedSupertypes;
@@ -358,6 +361,65 @@
return null;
}
+ /**
+ * Find an annotation on a type or on one of its superclasses or
+ * superinterfaces.
+ * <p>
+ * This provides semantics similar to that of
+ * {@link java.lang.annotation.Inherited} except that it checks all types to
+ * which this type is assignable. {@code @Inherited} only works on
+ * superclasses, not superinterfaces.
+ * <p>
+ * Annotations present on the superclass chain will be returned preferentially
+ * over those found in the superinterface hierarchy. Note that the annotation
+ * does not need to be tagged with {@code @Inherited} in order to be returned
+ * from the superclass chain.
+ *
+ * @param annotationType the type of the annotation to look for
+ * @return the desired annotation or <code>null</code> if the annotation is
+ * not present in the type's type hierarchy
+ */
+ public <T extends Annotation> T findAnnotationInTypeHierarchy(
+ Class<T> annotationType) {
+
+ // Remember what we've seen to avoid loops
+ Set<JClassType> seen = new HashSet<JClassType>();
+
+ // Work queue
+ List<JClassType> searchTypes = new LinkedList<JClassType>();
+ searchTypes.add(this);
+
+ T toReturn = null;
+
+ while (!searchTypes.isEmpty()) {
+ JClassType current = searchTypes.remove(0);
+
+ if (!seen.add(current)) {
+ continue;
+ }
+
+ toReturn = current.getAnnotation(annotationType);
+ if (toReturn != null) {
+ /*
+ * First one wins. It might be desirable at some point to have a
+ * variation that can return more than one instance of the annotation if
+ * it is present on multiple supertypes.
+ */
+ break;
+ }
+
+ if (current.getSuperclass() != null) {
+ // Add the superclass at the front of the list
+ searchTypes.add(0, current.getSuperclass());
+ }
+
+ // Superinterfaces
+ Collections.addAll(searchTypes, current.getImplementedInterfaces());
+ }
+
+ return toReturn;
+ }
+
public abstract JConstructor findConstructor(JType[] paramTypes);
public abstract JField findField(String name);
@@ -446,12 +508,12 @@
Class<? extends Annotation> annotationClass);
/**
- * Returns <code>true</code> if this {@link JClassType} is assignable from
- * the specified {@link JClassType} parameter.
+ * Returns <code>true</code> if this {@link JClassType} is assignable from the
+ * specified {@link JClassType} parameter.
*
* @param possibleSubtype possible subtype of this {@link JClassType}
- * @return <code>true</code> if this {@link JClassType} is assignable from
- * the specified {@link JClassType} parameter
+ * @return <code>true</code> if this {@link JClassType} is assignable from the
+ * specified {@link JClassType} parameter
*
* @throws NullPointerException if <code>possibleSubtype</code> is
* <code>null</code>
@@ -487,7 +549,7 @@
* Determines if the class can be constructed using a simple <code>new</code>
* operation. Specifically, the class must
* <ul>
- * <li>be a class rather than an interface, </li>
+ * <li>be a class rather than an interface,</li>
* <li>have either no constructors or a parameterless constructor, and</li>
* <li>be a top-level class or a static nested class.</li>
* </ul>
diff --git a/user/src/com/google/gwt/resources/client/CssResource.java b/user/src/com/google/gwt/resources/client/CssResource.java
index dc3a446..908c777 100644
--- a/user/src/com/google/gwt/resources/client/CssResource.java
+++ b/user/src/com/google/gwt/resources/client/CssResource.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.resources.client;
+import com.google.gwt.resources.ext.DefaultExtensions;
import com.google.gwt.resources.ext.ResourceGeneratorType;
import com.google.gwt.resources.rg.CssResourceGenerator;
@@ -32,9 +33,9 @@
* <li>{@code String someClassName();} will allow the css class
* <code>.someClassName</code> to be obfuscated at runtime. The function will
* return the obfuscated class name.</li>
- * <li>{@code <primitive numeric type or String> someDefName();} will allow
- * access to the values defined by {@literal @def} rules within the CSS file.
- * The defined value must be a raw number, a CSS length, or a percentage value
+ * <li>{@code <primitive numeric type or String> someDefName();} will allow
+ * access to the values defined by {@literal @def} rules within the CSS file.
+ * The defined value must be a raw number, a CSS length, or a percentage value
* if it is to be returned as a numeric type.
* </ul>
*
@@ -94,6 +95,7 @@
* @see <a href="http://code.google.com/p/google-web-toolkit/wiki/CssResource"
* >CssResource design doc</a>
*/
+@DefaultExtensions(value = {".css"})
@ResourceGeneratorType(CssResourceGenerator.class)
public interface CssResource extends ResourcePrototype {
/**
diff --git a/user/src/com/google/gwt/resources/client/ExternalTextResource.java b/user/src/com/google/gwt/resources/client/ExternalTextResource.java
index 0e5d4cb..a1b0ba7 100644
--- a/user/src/com/google/gwt/resources/client/ExternalTextResource.java
+++ b/user/src/com/google/gwt/resources/client/ExternalTextResource.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.resources.client;
+import com.google.gwt.resources.ext.DefaultExtensions;
import com.google.gwt.resources.ext.ResourceGeneratorType;
import com.google.gwt.resources.rg.ExternalTextResourceGenerator;
@@ -23,6 +24,7 @@
* not inlined into the compiled output. This is suitable for resources that are
* not required as part of program initialization.
*/
+@DefaultExtensions(value = {".txt"})
@ResourceGeneratorType(ExternalTextResourceGenerator.class)
public interface ExternalTextResource extends ResourcePrototype {
void getText(ResourceCallback<TextResource> callback)
diff --git a/user/src/com/google/gwt/resources/client/ImageResource.java b/user/src/com/google/gwt/resources/client/ImageResource.java
index 4a30dd7..8095ed5 100644
--- a/user/src/com/google/gwt/resources/client/ImageResource.java
+++ b/user/src/com/google/gwt/resources/client/ImageResource.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.resources.client;
+import com.google.gwt.resources.ext.DefaultExtensions;
import com.google.gwt.resources.ext.ResourceGeneratorType;
import com.google.gwt.resources.rg.ImageResourceGenerator;
@@ -25,6 +26,7 @@
/**
* Provides access to image resources at runtime.
*/
+@DefaultExtensions(value = {".png", ".jpg", ".gif", ".bmp"})
@ResourceGeneratorType(ImageResourceGenerator.class)
public interface ImageResource extends ResourcePrototype {
diff --git a/user/src/com/google/gwt/resources/client/TextResource.java b/user/src/com/google/gwt/resources/client/TextResource.java
index 69ee17a..b1f41ed 100644
--- a/user/src/com/google/gwt/resources/client/TextResource.java
+++ b/user/src/com/google/gwt/resources/client/TextResource.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.resources.client;
+import com.google.gwt.resources.ext.DefaultExtensions;
import com.google.gwt.resources.ext.ResourceGeneratorType;
import com.google.gwt.resources.rg.TextResourceGenerator;
@@ -22,6 +23,7 @@
* A resource that contains text that should be incorporated into the compiled
* output.
*/
+@DefaultExtensions(value = {".txt"})
@ResourceGeneratorType(TextResourceGenerator.class)
public interface TextResource extends ResourcePrototype {
String getText();
diff --git a/user/src/com/google/gwt/resources/ext/DefaultExtensions.java b/user/src/com/google/gwt/resources/ext/DefaultExtensions.java
new file mode 100644
index 0000000..8c2047b
--- /dev/null
+++ b/user/src/com/google/gwt/resources/ext/DefaultExtensions.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 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.resources.ext;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies the default extensions for a resource type. This annotation is
+ * intended to allow external tooling to know which filename extensions the
+ * ClientBundle system will search for if no
+ * {@link com.google.gwt.resources.client.ClientBundle.Source} annotation is
+ * present on an accessor method.
+ */
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface DefaultExtensions {
+ String[] value();
+}
diff --git a/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java b/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
index 4f09b76..0c92052 100644
--- a/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
+++ b/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
@@ -20,6 +20,7 @@
import com.google.gwt.core.ext.SelectionProperty;
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.JPackage;
import com.google.gwt.dev.resource.Resource;
@@ -171,6 +172,10 @@
* will fall back to using the current thread's context ClassLoader. If it is
* necessary to alter the way in which resources are located, use the overload
* that accepts a ClassLoader.
+ * <p>
+ * If the method's return type declares the {@link DefaultExtensions}
+ * annotation, the value of this annotation will be used to find matching
+ * resource names if the method lacks an {@link Source} annotation.
*
* @param logger a TreeLogger that will be used to report errors or warnings
* @param context the ResourceContext in which the ResourceGenerator is
@@ -184,7 +189,16 @@
*/
public static URL[] findResources(TreeLogger logger, ResourceContext context,
JMethod method) throws UnableToCompleteException {
- return findResources(logger, context, method, new String[0]);
+ JClassType returnType = method.getReturnType().isClassOrInterface();
+ assert returnType != null;
+ DefaultExtensions annotation = returnType.findAnnotationInTypeHierarchy(DefaultExtensions.class);
+ String[] extensions;
+ if (annotation != null) {
+ extensions = annotation.value();
+ } else {
+ extensions = new String[0];
+ }
+ return findResources(logger, context, method, extensions);
}
/**
diff --git a/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java b/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
index d48a4ea..a18597e 100644
--- a/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
+++ b/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
@@ -418,40 +418,18 @@
private Class<? extends ResourceGenerator> findResourceGenerator(
TreeLogger logger, TypeOracle typeOracle, JMethod method)
throws UnableToCompleteException {
-
JClassType resourceType = method.getReturnType().isClassOrInterface();
assert resourceType != null;
- List<JClassType> searchTypes = new ArrayList<JClassType>();
- searchTypes.add(resourceType);
-
- ResourceGeneratorType generatorType = null;
-
- while (!searchTypes.isEmpty()) {
- JClassType current = searchTypes.remove(0);
- generatorType = current.getAnnotation(ResourceGeneratorType.class);
- if (generatorType != null) {
- break;
- }
-
- if (current.getSuperclass() != null) {
- searchTypes.add(current.getSuperclass());
- }
-
- for (JClassType t : current.getImplementedInterfaces()) {
- searchTypes.add(t);
- }
- }
-
- if (generatorType == null) {
+ ResourceGeneratorType annotation = resourceType.findAnnotationInTypeHierarchy(ResourceGeneratorType.class);
+ if (annotation == null) {
logger.log(TreeLogger.ERROR, "No @"
- + ResourceGeneratorType.class.getName()
- + " was specifed for resource type "
+ + ResourceGeneratorType.class.getName() + " was specifed for type "
+ resourceType.getQualifiedSourceName() + " or its supertypes");
throw new UnableToCompleteException();
}
- return generatorType.value();
+ return annotation.value();
}
/**
diff --git a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
index e8af11d..bb96a2b 100644
--- a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
@@ -1002,8 +1002,6 @@
*/
private static final int CONCAT_EXPRESSION_LIMIT = 20;
- private static final String[] DEFAULT_EXTENSIONS = new String[] {".css"};
-
/**
* These constants are used to cache obfuscated class names.
*/
@@ -1377,7 +1375,7 @@
}
URL[] resources = ResourceGeneratorUtil.findResources(logger, context,
- method, DEFAULT_EXTENSIONS);
+ method);
if (resources.length == 0) {
logger.log(TreeLogger.ERROR, "At least one source must be specified");
diff --git a/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java b/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java
index c5f3d31..ee62178 100644
--- a/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java
@@ -41,7 +41,6 @@
*/
public final class ExternalTextResourceGenerator extends
AbstractResourceGenerator {
- private static final String[] DEFAULT_EXTENSIONS = new String[] {".txt"};
private StringBuffer data;
private boolean first;
private String urlExpression;
@@ -112,8 +111,7 @@
ClientBundleRequirements requirements, JMethod method)
throws UnableToCompleteException {
- URL[] urls = ResourceGeneratorUtil.findResources(logger, context, method,
- DEFAULT_EXTENSIONS);
+ URL[] urls = ResourceGeneratorUtil.findResources(logger, context, method);
if (urls.length != 1) {
logger.log(TreeLogger.ERROR, "Exactly one resource must be specified",
diff --git a/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java b/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java
index b0df671..720116e 100644
--- a/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java
@@ -45,8 +45,6 @@
* Builds an image strip for all ImageResources defined within an ClientBundle.
*/
public final class ImageResourceGenerator extends AbstractResourceGenerator {
- private static final String[] DEFAULT_EXTENSIONS = new String[] {
- ".png", ".jpg", ".gif", ".bmp"};
private Map<String, ImageRect> imageRectsByName;
private Map<ImageRect, ImageBundleBuilder> buildersByImageRect;
private Map<RepeatStyle, ImageBundleBuilder> buildersByRepeatStyle;
@@ -199,7 +197,7 @@
ClientBundleRequirements requirements, JMethod method)
throws UnableToCompleteException {
URL[] resources = ResourceGeneratorUtil.findResources(logger, context,
- method, DEFAULT_EXTENSIONS);
+ method);
if (resources.length != 1) {
logger.log(TreeLogger.ERROR, "Exactly one image may be specified", null);
diff --git a/user/src/com/google/gwt/resources/rg/TextResourceGenerator.java b/user/src/com/google/gwt/resources/rg/TextResourceGenerator.java
index 88b3d33..10a8820 100644
--- a/user/src/com/google/gwt/resources/rg/TextResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/TextResourceGenerator.java
@@ -33,13 +33,11 @@
*/
public final class TextResourceGenerator extends AbstractResourceGenerator {
- private static final String[] DEFAULT_EXTENSIONS = new String[] {".txt"};
-
@Override
public String createAssignment(TreeLogger logger, ResourceContext context,
JMethod method) throws UnableToCompleteException {
URL[] resources = ResourceGeneratorUtil.findResources(logger, context,
- method, DEFAULT_EXTENSIONS);
+ method);
if (resources.length != 1) {
logger.log(TreeLogger.ERROR, "Exactly one resource must be specified",