/* * 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.uibinder.rebind.model; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.resources.css.ExtractClassNamesVisitor; import com.google.gwt.resources.css.GenerateCssAst; import com.google.gwt.resources.css.ast.CssStylesheet; import com.google.gwt.resources.ext.ResourceGeneratorUtil; import com.google.gwt.uibinder.attributeparsers.CssNameConverter; import com.google.gwt.uibinder.rebind.MortalLogger; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; 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 final CssNameConverter nameConverter = new CssNameConverter(); private final String packageName; private final String className; private final String name; 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; private Set<String> cssClassNames; private Set<String> normalizedCssClassNames; /** * Visible for testing only, get instances from {@link ImplicitClientBundle}. */ public ImplicitCssResource(String packageName, String className, String name, String[] source, JClassType extendedInterface, String body, MortalLogger logger, Set<JClassType> importTypes) { this.packageName = packageName; this.className = className; this.name = name; this.extendedInterface = extendedInterface; this.body = body; this.logger = logger; this.imports = Collections.unmodifiableSet(importTypes); sources = Arrays.asList(source); } /** * Returns the name of the CssResource interface. */ public String getClassName() { return className; } /** * Returns the set of CSS classnames in the underlying .css files. * * @throws UnableToCompleteException if the user has called for a .css file we * can't find. */ public Set<String> getCssClassNames() throws UnableToCompleteException { List<URL> urls = getExternalCss(); if (cssClassNames == null) { final File bodyFile = getGeneratedFile(); if (bodyFile != null) { try { urls.add(bodyFile.toURI().toURL()); } catch (MalformedURLException e) { throw new RuntimeException(e); } } assert urls.size() > 0; CssStylesheet sheet = GenerateCssAst.exec(logger.getTreeLogger(), urls.toArray(new URL[urls.size()])); cssClassNames = ExtractClassNamesVisitor.exec(sheet, imports.toArray(new JClassType[imports.size()])); } return cssClassNames; } /** * Returns the public interface that this CssResource implements. */ public JClassType getExtendedInterface() { return extendedInterface; } /** * Returns the set of CssResource types whose scopes are imported. */ public Set<JClassType> getImports() { return imports; } /** * Returns the name of this resource. This is both its method name in the * owning {@link ImplicitClientBundle} and its ui:field name. */ public String getName() { return name; } /** * Returns css class names with dashed-names normalized like so: dashedNames. */ public Set<String> getNormalizedCssClassNames() throws UnableToCompleteException { if (normalizedCssClassNames == null) { Set<String> rawNames = getCssClassNames(); normalizedCssClassNames = new HashSet<String>(); for (String rawName : rawNames) { normalizedCssClassNames.add(nameConverter.convertName(rawName)); } } return normalizedCssClassNames; } /** * Returns the package in which the generated CssResource interface should * reside. */ public String getPackageName() { return packageName; } /** * Returns the name of the generated type. */ public String getQualifiedSourceName() { if (packageName.length() == 0) { return name; } return String.format("%s.%s", packageName, className); } /** * Returns the name of the .css file(s), separate by white space. */ 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 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 * this package */ ClassLoader classLoader = ImplicitCssResource.class.getClassLoader(); String path = packageName.replace(".", "/"); List<URL> urls = new ArrayList<URL>(); for (String s : sources) { String resourcePath = path + '/' + s; URL found = classLoader.getResource(resourcePath); if (null == found) { logger.die("Unable to find resource: " + resourcePath); } 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", packageName, className), ".css"); f.deleteOnExit(); BufferedWriter out = new BufferedWriter(new FileWriter(f)); out.write(body); out.close(); generatedFile = f; } catch (IOException e) { throw new RuntimeException(e); } ResourceGeneratorUtil.addNamedFile(getBodyFileName(), generatedFile); } return generatedFile; } }