This allows multiple source files to be specified for a ui:style, just
like any other CssResource.
It also allows you to put body text on a ui:style that has
sources. The body becomes just one more source file, the last
one--nice for local tweaks on centrally defined styles.
ResourceGeneratorUtil patch by bobv, reviewed by rjrjr
Remainder by rjrjr, reviewed by jgw
http://gwt-code-reviews.appspot.com/77819
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6479 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java b/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
index d2efb20..c0ea076 100644
--- a/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
+++ b/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
@@ -202,8 +202,8 @@
public static URL[] findResources(TreeLogger logger, ClassLoader classLoader,
ResourceContext context, JMethod method, String[] defaultSuffixes)
throws UnableToCompleteException {
- return findResources(logger, new ClassLoaderLocator(classLoader), context,
- method, defaultSuffixes, true);
+ return findResources(logger, new Locator[] {new ClassLoaderLocator(
+ classLoader)}, context, method, defaultSuffixes);
}
/**
@@ -281,34 +281,14 @@
public static URL[] findResources(TreeLogger logger, ResourceContext context,
JMethod method, String[] defaultSuffixes)
throws UnableToCompleteException {
- URL[] toReturn = null;
- Locator locator;
+ Locator[] locators = {
+ FileLocator.INSTANCE,
+ new ResourceOracleLocator(
+ context.getGeneratorContext().getResourcesOracle()),
+ new ClassLoaderLocator(Thread.currentThread().getContextClassLoader())};
- // If we have named files, attempt them first
- if (!namedFiles.isEmpty()) {
- toReturn = findResources(logger, FileLocator.INSTANCE, context, method,
- defaultSuffixes, false);
- }
-
- if (toReturn == null) {
- // Try to find the resources with ResourceOracle
- locator = new ResourceOracleLocator(
- context.getGeneratorContext().getResourcesOracle());
-
- // Don't report errors since we have a fallback mechanism
- toReturn = findResources(logger, locator, context, method,
- defaultSuffixes, false);
- }
-
- if (toReturn == null) {
- // Since not all resources were found, try with ClassLoader
- locator = new ClassLoaderLocator(
- Thread.currentThread().getContextClassLoader());
-
- // Do report hard failures
- toReturn = findResources(logger, locator, context, method,
- defaultSuffixes, true);
- }
+ URL[] toReturn = findResources(logger, locators, context, method,
+ defaultSuffixes);
return toReturn;
}
@@ -331,14 +311,10 @@
/**
* Main implementation of findResources.
- *
- * @param reportErrors controls whether or not the inability to locate any
- * given resource should be a hard error (throw an UnableToComplete)
- * or a soft error (return null).
*/
- private static URL[] findResources(TreeLogger logger, Locator locator,
- ResourceContext context, JMethod method, String[] defaultSuffixes,
- boolean reportErrors) throws UnableToCompleteException {
+ private static URL[] findResources(TreeLogger logger, Locator[] locators,
+ ResourceContext context, JMethod method, String[] defaultSuffixes)
+ throws UnableToCompleteException {
logger = logger.branch(TreeLogger.DEBUG, "Finding resources");
String locale;
@@ -360,21 +336,22 @@
if (defaultSuffixes != null) {
for (String extension : defaultSuffixes) {
logger.log(TreeLogger.SPAM, "Trying default extension " + extension);
- URL resourceUrl = tryFindResource(locator, getPathRelativeToPackage(
- method.getEnclosingType().getPackage(), method.getName()
- + extension), locale);
+ for (Locator locator : locators) {
+ URL resourceUrl = tryFindResource(locator,
+ getPathRelativeToPackage(
+ method.getEnclosingType().getPackage(), method.getName()
+ + extension), locale);
- if (resourceUrl != null) {
- // Early out because we found a hit
- return new URL[] {resourceUrl};
+ if (resourceUrl != null) {
+ // Take the first match
+ return new URL[] {resourceUrl};
+ }
}
}
}
- if (reportErrors) {
- logger.log(TreeLogger.ERROR, "No " + Source.class.getName()
- + " annotation and no resources found with default extensions");
- }
+ logger.log(TreeLogger.ERROR, "No " + Source.class.getName()
+ + " annotation and no resources found with default extensions");
toReturn = null;
error = true;
@@ -387,26 +364,31 @@
int tagIndex = 0;
for (String resource : resources) {
// Try to find the resource relative to the package.
- URL resourceURL = tryFindResource(locator, getPathRelativeToPackage(
- method.getEnclosingType().getPackage(), resource), locale);
+ URL resourceURL = null;
- // If we didn't find the resource relative to the package, assume it is
- // absolute.
- if (resourceURL == null) {
- resourceURL = tryFindResource(locator, resource, locale);
+ for (Locator locator : locators) {
+ resourceURL = tryFindResource(locator, getPathRelativeToPackage(
+ method.getEnclosingType().getPackage(), resource), locale);
+
+ /*
+ * If we didn't find the resource relative to the package, assume it
+ * is absolute.
+ */
+ if (resourceURL == null) {
+ resourceURL = tryFindResource(locator, resource, locale);
+ }
+
+ // If we have found a resource, take the first match
+ if (resourceURL != null) {
+ break;
+ }
}
if (resourceURL == null) {
error = true;
- if (reportErrors) {
- logger.log(TreeLogger.ERROR, "Resource " + resource
- + " not found. Is the name specified as Class.getResource()"
- + " would expect?");
- } else {
- // Speculative attempts should not emit errors
- logger.log(TreeLogger.DEBUG, "Stopping because " + resource
- + " not found");
- }
+ logger.log(TreeLogger.ERROR, "Resource " + resource
+ + " not found. Is the name specified as Class.getResource()"
+ + " would expect?");
}
toReturn[tagIndex++] = resourceURL;
@@ -414,11 +396,7 @@
}
if (error) {
- if (reportErrors) {
- throw new UnableToCompleteException();
- } else {
- return null;
- }
+ throw new UnableToCompleteException();
}
return toReturn;
diff --git a/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java b/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java
index 1db5074..b0a00b4 100644
--- a/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java
@@ -29,6 +29,7 @@
import com.google.gwt.uibinder.rebind.model.ImplicitDataResource;
import com.google.gwt.uibinder.rebind.model.ImplicitImageResource;
+import java.util.Collection;
import java.util.Set;
/**
@@ -96,7 +97,7 @@
// Write css methods
for (ImplicitCssResource css : bundleClass.getCssMethods()) {
- writer.write("@Source(\"%s\")", css.getSource());
+ writeCssSource(css);
writeCssImports(css);
writer.write("%s %s();", css.getClassName(), css.getName());
writer.newline();
@@ -136,6 +137,22 @@
}
}
+ private void writeCssSource(ImplicitCssResource css) {
+ Collection<String> sources = css.getSource();
+ if (sources.size() == 1) {
+ writer.write("@Source(\"%s\")", sources.iterator().next());
+ } else {
+ StringBuffer b = new StringBuffer();
+ for (String s : sources) {
+ if (b.length() > 0) {
+ b.append(", ");
+ }
+ b.append('"').append(s).append('"');
+ }
+ writer.write("@Source({%s})", b);
+ }
+ }
+
private void writeImageMethods() {
for (ImplicitImageResource image : bundleClass.getImageMethods()) {
if (null != image.getSource()) {
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
index d6dca10..26579e3 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -224,13 +224,12 @@
private void createStyle(XMLElement elem) throws UnableToCompleteException {
String body = elem.consumeUnescapedInnerText();
- if (body.length() > 0 && elem.hasAttribute(SOURCE_ATTRIBUTE)) {
- writer.die(
- "In %s, cannot use both a source attribute and inline css text.",
- elem);
- }
-
String source = elem.consumeAttribute(SOURCE_ATTRIBUTE);
+
+ if (0 == body.length() && 0 == source.length()) {
+ writer.die("%s must have either a src attribute or body text", elem);
+ }
+
String name = elem.consumeAttribute(FIELD_ATTRIBUTE, "style");
JClassType publicType = consumeCssResourceType(elem);
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
index f538c3f..6d59267 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -118,7 +118,7 @@
* was unset. The returned string is not escaped.
*
* @param name the attribute's full name (including prefix)
- * @return the attribute's value, or null
+ * @return the attribute's value, or ""
*/
public String consumeAttribute(String name) {
String value = elem.getAttribute(name);
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
index b5c5bb6..588921e 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
@@ -29,22 +29,34 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
* Models a method returning a CssResource on a generated ClientBundle.
*/
public class ImplicitCssResource {
+ private static List<String> explodeSource(String source) {
+ if (source.length() == 0) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(source.split("\\s+"));
+ }
+
private final String packageName;
private final String className;
private final String name;
- private final String source;
+ private final List<String> sources;
private final JClassType extendedInterface;
private final String body;
private final MortalLogger logger;
private final Set<JClassType> imports;
+
private File generatedFile;
ImplicitCssResource(String packageName, String className, String name,
@@ -57,20 +69,7 @@
this.body = body;
this.logger = logger;
this.imports = Collections.unmodifiableSet(importTypes);
-
- if (body.length() > 0) {
- assert "".equals(source); // Enforced for real by the parser
-
- /*
- * We're going to write the inline body to a temporary File and register
- * it with the CssResource world under the name in this.source, via
- * ResourceGeneratorUtil.addNamedFile(). When CssResourceGenerator sees
- * this name in an @Source annotation it will know to use the registered
- * file rather than load a resource.
- */
- source = String.format("uibinder:%s.%s.css", packageName, className);
- }
- this.source = source;
+ sources = explodeSource(source);
}
/**
@@ -87,20 +86,22 @@
* can't find.
*/
public Set<String> getCssClassNames() throws UnableToCompleteException {
- URL[] urls;
+ List<URL> urls = getExternalCss();
- if (body.length() == 0) {
- urls = getExternalCss();
- } else {
+ final File bodyFile = getGeneratedFile();
+ if (bodyFile != null) {
try {
- urls = new URL[] {getGeneratedFile().toURI().toURL()};
+ urls.add(bodyFile.toURI().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
+ assert urls.size() > 0;
- CssStylesheet sheet = GenerateCssAst.exec(logger.getTreeLogger(), urls);
- return ExtractClassNamesVisitor.exec(sheet, imports.toArray(new JClassType[imports.size()]));
+ CssStylesheet sheet = GenerateCssAst.exec(logger.getTreeLogger(),
+ urls.toArray(new URL[urls.size()]));
+ return ExtractClassNamesVisitor.exec(sheet,
+ imports.toArray(new JClassType[imports.size()]));
}
/**
@@ -133,11 +134,21 @@
/**
* @return the name of the .css file(s), separate by white space
*/
- public String getSource() {
- return source;
+ public Collection<String> getSource() {
+ if (body.length() == 0) {
+ return Collections.unmodifiableCollection(sources);
+ }
+
+ List<String> rtn = new ArrayList<String>(sources);
+ rtn.add(getBodyFileName());
+ return rtn;
}
- private URL[] getExternalCss() throws UnableToCompleteException {
+ private String getBodyFileName() {
+ return String.format("uibinder:%s.%s.css", packageName, className);
+ }
+
+ private List<URL> getExternalCss() throws UnableToCompleteException {
/*
* TODO(rjrjr,bobv) refactor ResourceGeneratorUtil.findResources so we can
* find them the same way ClientBundle does. For now, just look relative to
@@ -147,9 +158,7 @@
ClassLoader classLoader = ImplicitCssResource.class.getClassLoader();
String path = packageName.replace(".", "/");
- String[] sources = source.split(" ");
- URL[] urls = new URL[sources.length];
- int i = 0;
+ List<URL> urls = new ArrayList<URL>();
for (String s : sources) {
String resourcePath = path + '/' + s;
@@ -157,12 +166,16 @@
if (null == found) {
logger.die("Unable to find resource: " + resourcePath);
}
- urls[i++] = found;
+ urls.add(found);
}
return urls;
}
private File getGeneratedFile() {
+ if (body.length() == 0) {
+ return null;
+ }
+
if (generatedFile == null) {
try {
File f = File.createTempFile(String.format("uiBinder_%s_%s",
@@ -175,7 +188,7 @@
} catch (IOException e) {
throw new RuntimeException(e);
}
- ResourceGeneratorUtil.addNamedFile(getSource(), generatedFile);
+ ResourceGeneratorUtil.addNamedFile(getBodyFileName(), generatedFile);
}
return generatedFile;
}
diff --git a/user/src/com/google/gwt/uibinder/sample/client/Menu.css b/user/src/com/google/gwt/uibinder/sample/client/Menu.css
new file mode 100644
index 0000000..9bd75be
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/sample/client/Menu.css
@@ -0,0 +1,4 @@
+.menuBar {
+ border:solid;
+ background-color:white;
+}
diff --git a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css
index c712178..6ef8738 100644
--- a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css
+++ b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css
@@ -1,8 +1,3 @@
-.menuBar {
- border:solid;
- background-color:white;
-}
-
/*
* Demonstrates that the ui.xml has access to styles that
* do not back any declared CssResource api
diff --git a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml
index d8ffecd..b828a9c 100644
--- a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml
+++ b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml
@@ -71,9 +71,13 @@
<!--
Tests creating a CssResource from an external file.
-->
-<ui:style field='myStyle' src='WidgetBasedUi.css'
- type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'/>
-
+<ui:style field='myStyle' src='WidgetBasedUi.css Menu.css'
+ type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'>
+ .menuBar {
+ font-family: sans-serif;
+ }
+</ui:style>
+
<ui:style field='myOtherStyle' type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'>
/* because we extend WidgetBasedUi.Style and it's tagged @Shared, we can refine the menubar
style defined in other files */
diff --git a/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java
index 9528470..7a12ded 100644
--- a/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java
@@ -231,7 +231,7 @@
}
interface Bundle extends ClientBundle {
- @Source("WidgetBasedUi.css")
+ @Source({"WidgetBasedUi.css", "Menu.css"})
@NotStrict
WidgetBasedUi.Style style();
}