Merge up from farewellSwt@6271 svn merge https://google-web-toolkit.googlecode.com/svn/branches/farewellSwt \ --ignore-ancestry -r6267:6271 . git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.0@6272 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/branch-info.txt b/branch-info.txt index 43681eb..7843c61 100644 --- a/branch-info.txt +++ b/branch-info.txt
@@ -2,3 +2,8 @@ ======================== svn cp https://google-web-toolkit.googlecode.com/svn/branches/farewellSwt \ https://google-web-toolkit.googlecode.com/svn/releases/2.0 + +Merged from farewellSwt +================ +svn merge https://google-web-toolkit.googlecode.com/svn/branches/farewellSwt \ + --ignore-ancestry -r6267:6271 .
diff --git a/dev/common.ant.xml b/dev/common.ant.xml index 178bc62..262d3ba 100755 --- a/dev/common.ant.xml +++ b/dev/common.ant.xml
@@ -4,7 +4,6 @@ <property.ensure name="gwt.core.root" location="../core" /> <property.ensure name="gwt.core.build" location="${project.build}/../core" /> - <property.ensure name="gwt.tools.soyc" location="${gwt.root}/tools/soyc-vis" /> <target name="compile" description="Compile all java files"> <mkdir dir="${javac.out}" /> @@ -29,9 +28,6 @@ <fileset dir="${javac.out}" /> <fileset dir="${gwt.core.build}/bin" /> <fileset file="${gwt.core.build}/alldeps.jar" /> - <fileset dir="${gwt.tools.soyc}/images"/> - <fileset file="${gwt.tools.soyc}/classLevel.css"/> - <fileset file="${gwt.tools.soyc}/roundedCorners.css"/> </sourcefiles> <targetfiles> <fileset file="${project.lib}"/> @@ -48,10 +44,6 @@ <fileset dir="${gwt.core.build}/bin" /> <zipfileset src="${gwt.core.build}/alldeps.jar" /> - <zipfileset dir="${gwt.tools.soyc}/images" prefix="com/google/gwt/soyc/resources/images"/> - <zipfileset file="${gwt.tools.soyc}/classLevel.css" prefix="com/google/gwt/soyc/resources/"/> - <zipfileset file="${gwt.tools.soyc}/roundedCorners.css" prefix="com/google/gwt/soyc/resources/"/> - <manifest> <attribute name="Main-Class" value="com.google.gwt.dev.GWTMain" /> </manifest>
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java b/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java index 615f333..982499a 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java
@@ -16,6 +16,7 @@ package com.google.gwt.core.ext.linker; import com.google.gwt.core.ext.Linker; +import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis.SoycArtifact; import java.util.ArrayList; import java.util.LinkedList; @@ -41,6 +42,12 @@ public abstract EmittedArtifact getDetailedStoriesFile(); /** + * Files containing the HTML dashboard. + */ + + public abstract List<SoycArtifact> getReportFiles(); + + /** * @return a file of size maps */ public abstract EmittedArtifact getSizeMapsFile(); @@ -69,9 +76,12 @@ allFiles()); LinkedList<EmittedArtifact> otherFiles = new LinkedList<EmittedArtifact>( o.allFiles()); - assert (myFiles.size() == otherFiles.size()); while (!myFiles.isEmpty()) { + if (otherFiles.isEmpty()) { + return 1; + } + EmittedArtifact myFile = myFiles.removeFirst(); EmittedArtifact otherFile = otherFiles.removeFirst(); if (myFile == null && otherFile == null) { @@ -93,6 +103,10 @@ } } + if (!otherFiles.isEmpty()) { + return -1; + } + return 0; } @@ -107,6 +121,7 @@ files.add(getDepFile()); files.add(getSizeMapsFile()); files.add(getDetailedStoriesFile()); + files.addAll(getReportFiles()); return files; } }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java index b9a8969..327c5ba 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java
@@ -19,6 +19,8 @@ import com.google.gwt.core.ext.linker.SyntheticArtifact; import com.google.gwt.core.linker.SoycReportLinker; +import java.util.List; + /** * An implementation of CompilationAnalysis. This class transforms SourceInfos * and related data into an API suitable for public consumption via the Linker @@ -48,9 +50,9 @@ private SoycArtifact detailedStoriesFile; /** - * File containing split points. + * Files containing the HTML dashboard. */ - private SoycArtifact splitPointsFile; + private List<SoycArtifact> reportFiles; /** * File containing size maps. @@ -58,16 +60,22 @@ private SoycArtifact sizeMapsFile; /** + * File containing split points. + */ + private SoycArtifact splitPointsFile; + + /** * Constructed by PermutationCompiler. */ public StandardCompilationAnalysis(SoycArtifact dependencies, SoycArtifact sizeMaps, SoycArtifact splitPoints, - SoycArtifact detailedStories) { + SoycArtifact detailedStories, List<SoycArtifact> reportFiles) { super(StandardLinkerContext.class); this.depFile = dependencies; this.sizeMapsFile = sizeMaps; this.splitPointsFile = splitPoints; this.detailedStoriesFile = detailedStories; + this.reportFiles = reportFiles; } @Override @@ -81,6 +89,11 @@ } @Override + public List<SoycArtifact> getReportFiles() { + return reportFiles; + } + + @Override public SoycArtifact getSizeMapsFile() { return sizeMapsFile; }
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java index e58edce..a8f648e 100644 --- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java +++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
@@ -108,7 +108,7 @@ protected void recordDependenciesImpl(TreeLogger logger, JProgram jprogram) { logger = logger.branch(TreeLogger.INFO, - "Creating Dependencies file for SOYC"); + "Creating dependencies file for the compile report"); ControlFlowAnalyzer dependencyAnalyzer = new ControlFlowAnalyzer(jprogram); dependencyAnalyzer.setDependencyRecorder(this);
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java index 597ad33..044de32 100644 --- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java +++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
@@ -40,7 +40,7 @@ TreeLogger logger) { logger = logger.branch(TreeLogger.TRACE, - "Creating Split Point Map file for SOYC"); + "Creating split point map file for the compile report"); try { OutputStreamWriter writer = new OutputStreamWriter(new GZIPOutputStream(
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java index f1708d2..9ad7601 100644 --- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java +++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
@@ -108,7 +108,7 @@ protected void recordStoriesImpl(TreeLogger logger, OutputStream out, List<Map<Range, SourceInfo>> sourceInfoMaps, String[] js) { - logger = logger.branch(TreeLogger.INFO, "Creating Stories file for SOYC"); + logger = logger.branch(TreeLogger.INFO, "Creating Stories file for the compile report"); this.js = js;
diff --git a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java index bb32174..2f63ff1 100644 --- a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java +++ b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
@@ -20,9 +20,17 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.ArtifactSet; +import com.google.gwt.core.ext.linker.CompilationResult; import com.google.gwt.core.ext.linker.LinkerOrder; +import com.google.gwt.core.ext.linker.SelectionProperty; import com.google.gwt.core.ext.linker.LinkerOrder.Order; import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis; +import com.google.gwt.soyc.SoycDashboard; +import com.google.gwt.soyc.io.ArtifactsOutputDirectory; + +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; /** * Converts SOYC report files into emitted private artifacts. @@ -32,13 +40,15 @@ @Override public String getDescription() { - return "Emit SOYC artifacts"; + return "Emit compile report artifacts"; } @Override public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException { ArtifactSet results = new ArtifactSet(artifacts); + boolean foundReports = false; + for (StandardCompilationAnalysis soycFiles : artifacts.find(StandardCompilationAnalysis.class)) { if (soycFiles.getDepFile() != null) { results.add(soycFiles.getDepFile()); @@ -50,8 +60,42 @@ results.add(soycFiles.getDetailedStoriesFile()); } results.add(soycFiles.getSplitPointsFile()); + if (!soycFiles.getReportFiles().isEmpty()) { + results.addAll(soycFiles.getReportFiles()); + foundReports = true; + } + } + + if (foundReports) { + // run the final step of the dashboard to generate top-level files + ArtifactsOutputDirectory out = new ArtifactsOutputDirectory(); + try { + new SoycDashboard(out).generateCrossPermutationFiles(extractPermutationDescriptions(artifacts)); + } catch (IOException e) { + logger.log(TreeLogger.ERROR, + "Error while generating a Story of Your Compile", e); + e.printStackTrace(); + } + results.addAll(out.getArtifacts()); } return results; } + private Map<String, String> extractPermutationDescriptions( + ArtifactSet artifacts) { + Map<String, String> permDescriptions = new TreeMap<String, String>(); + + for (CompilationResult res : artifacts.find(CompilationResult.class)) { + String permId = Integer.toString(res.getPermutationId()); + /* + * TODO(kprobst,spoon) support permutations that collapsed to the same + * compilation result + */ + Map<SelectionProperty, String> propertyMap = res.getPropertyMap().iterator().next(); + String permDesc = SymbolMapsLinker.propertyMapToString(propertyMap); + permDescriptions.put(permId, permDesc); + } + + return permDescriptions; + } }
diff --git a/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java b/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java index 3d3ed90..a3dcad3 100644 --- a/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java +++ b/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java
@@ -29,6 +29,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Map; import java.util.SortedMap; @@ -47,6 +48,33 @@ */ public static final String STRONG_NAME_SUFFIX = ".symbolMap"; + public static String propertyMapToString( + Map<SelectionProperty, String> propertyMap) { + StringWriter writer = new StringWriter(); + PrintWriter pw = new PrintWriter(writer); + printPropertyMap(pw, propertyMap); + pw.flush(); + return writer.toString(); + } + + private static void printPropertyMap(PrintWriter pw, + Map<SelectionProperty, String> map) { + boolean needsComma = false; + for (Map.Entry<SelectionProperty, String> entry : map.entrySet()) { + if (needsComma) { + pw.print(" , "); + } else { + needsComma = true; + } + + pw.print("'"); + pw.print(entry.getKey().getName()); + pw.print("' : '"); + pw.print(entry.getValue()); + pw.print("'"); + } + } + @Override public String getDescription() { return "Export CompilationResult symbol maps"; @@ -89,26 +117,11 @@ */ protected void doWriteSymbolMap(TreeLogger logger, CompilationResult result, PrintWriter pw) throws UnableToCompleteException { - pw.println("# { " + result.getPermutationId() + " }"); - + for (SortedMap<SelectionProperty, String> map : result.getPropertyMap()) { pw.print("# { "); - - boolean needsComma = false; - for (Map.Entry<SelectionProperty, String> entry : map.entrySet()) { - if (needsComma) { - pw.print(" , "); - } else { - needsComma = true; - } - - pw.print("'"); - pw.print(entry.getKey().getName()); - pw.print("' : '"); - pw.print(entry.getValue()); - pw.print("'"); - } + printPropertyMap(pw, map); pw.println(" }"); }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java index c37c6c3..3f83f66 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -20,6 +20,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.ArtifactSet; +import com.google.gwt.core.ext.linker.EmittedArtifact; import com.google.gwt.core.ext.linker.StatementRanges; import com.google.gwt.core.ext.linker.SymbolData; import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis; @@ -57,7 +58,6 @@ import com.google.gwt.dev.jjs.impl.CastNormalizer; import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer; import com.google.gwt.dev.jjs.impl.CodeSplitter; -import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder; import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer; import com.google.gwt.dev.jjs.impl.DeadCodeElimination; import com.google.gwt.dev.jjs.impl.EqualityNormalizer; @@ -85,6 +85,7 @@ import com.google.gwt.dev.jjs.impl.SourceGenerationVisitor; import com.google.gwt.dev.jjs.impl.TypeMap; import com.google.gwt.dev.jjs.impl.TypeTightener; +import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder; import com.google.gwt.dev.js.EvalFunctionsAtTopScope; import com.google.gwt.dev.js.JsBreakUpLargeVarStatements; import com.google.gwt.dev.js.JsIEBlockSizeVisitor; @@ -110,12 +111,16 @@ import com.google.gwt.dev.util.PerfLogger; import com.google.gwt.dev.util.TextOutput; import com.google.gwt.dev.util.Util; +import com.google.gwt.dev.util.collect.Lists; import com.google.gwt.dev.util.collect.Maps; +import com.google.gwt.soyc.SoycDashboard; +import com.google.gwt.soyc.io.ArtifactsOutputDirectory; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.xml.sax.SAXException; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; @@ -130,13 +135,15 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.zip.GZIPInputStream; + +import javax.xml.parsers.ParserConfigurationException; /** * Compiles the Java <code>JProgram</code> representation into its corresponding * JavaScript source. */ public class JavaToJavaScriptCompiler { - private static class PermutationResultImpl implements PermutationResult { private final ArtifactSet artifacts = new ArtifactSet(); private final byte[][] js; @@ -318,7 +325,7 @@ // Work around an IE7 bug, // http://code.google.com/p/google-web-toolkit/issues/detail?id=1440 // note, JsIEBlockTextTransformer now handles restructuring top level - // blocks, this class now handles non-top level blocks only. + // blocks, this class now handles non-top level blocks only. SelectionProperty userAgentProperty = null; for (PropertyOracle oracle : propertyOracles) { userAgentProperty = oracle.getSelectionProperty(logger, "user.agent"); @@ -327,9 +334,8 @@ } } // if user agent is known or ie6, split overly large blocks - boolean splitBlocks = userAgentProperty == null || ( - userAgentProperty != null && - "ie6".equals(userAgentProperty.getCurrentValue())); + boolean splitBlocks = userAgentProperty == null + || (userAgentProperty != null && "ie6".equals(userAgentProperty.getCurrentValue())); if (splitBlocks) { JsIEBlockSizeVisitor.exec(jsProgram); @@ -853,7 +859,7 @@ v = new JsSourceGenerationVisitorWithSizeBreakdown(out, jjsMap); } v.accept(jsProgram.getFragmentBlock(i)); - + /** * Reorder function decls to improve compression ratios. Also restructures * the top level blocks into sub-blocks if they exceed 32767 statements. @@ -929,10 +935,10 @@ SizeBreakdown[] sizeBreakdowns, List<Map<Range, SourceInfo>> sourceInfoMaps, SoycArtifact dependencies, JavaToJavaScriptMap jjsmap, Map<JsName, String> obfuscateMap) - throws IOException { + throws IOException, UnableToCompleteException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PerfLogger.start("Recording SOYC output"); + PerfLogger.start("Recording compile report output"); PerfLogger.start("Record split points"); SplitPointRecorder.recordSplitPoints(jprogram, baos, logger); @@ -964,8 +970,34 @@ PerfLogger.end(); + List<SoycArtifact> reportArtifacts = Lists.create(); + if (sizeBreakdowns != null) { + PerfLogger.start("Generating compile report"); + ArtifactsOutputDirectory outDir = new ArtifactsOutputDirectory(); + SoycDashboard dashboard = new SoycDashboard(outDir); + dashboard.startNewPermutation(Integer.toString(permutationId)); + try { + dashboard.readSplitPoints(openWithGunzip(splitPoints)); + if (sizeMaps != null) { + dashboard.readSizeMaps(openWithGunzip(sizeMaps)); + } + if (dependencies != null) { + dashboard.readDependencies(openWithGunzip(dependencies)); + } + } catch (ParserConfigurationException e) { + throw new InternalCompilerException( + "Error reading compile report information that was just generated", e); + } catch (SAXException e) { + throw new InternalCompilerException( + "Error reading compile report information that was just generated", e); + } + dashboard.generateForOnePermutation(); + reportArtifacts = outDir.getArtifacts(); + PerfLogger.end(); + } + return new StandardCompilationAnalysis(dependencies, sizeMaps, splitPoints, - detailedStories); + detailedStories, reportArtifacts); } /** @@ -1032,6 +1064,14 @@ } /** + * Open an emitted artifact and gunzip its contents. + */ + private static GZIPInputStream openWithGunzip(EmittedArtifact artifact) + throws IOException, UnableToCompleteException { + return new GZIPInputStream(artifact.getContents(TreeLogger.NULL)); + } + + /** * Dependency information is normally recorded during code splitting, and it * results in multiple dependency graphs. If the code splitter doesn't run, * then this method can be used instead to record a single dependency graph
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java index d8b78c9..c278399 100644 --- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoycDetailed.java
@@ -29,7 +29,7 @@ @Override public String getPurpose() { - return "Emit extra, detailed SOYC information at the expense of compile time"; + return "Emit extra, detailed compile-report information at the expense of compile time"; } @Override
diff --git a/dev/core/src/com/google/gwt/soyc/GlobalInformation.java b/dev/core/src/com/google/gwt/soyc/GlobalInformation.java index aaf9edf..34262db 100644 --- a/dev/core/src/com/google/gwt/soyc/GlobalInformation.java +++ b/dev/core/src/com/google/gwt/soyc/GlobalInformation.java
@@ -25,10 +25,11 @@ import java.util.TreeSet; /** - * Information global to the entire SOYC report generator. + * SOYC information about a compiled module. */ public class GlobalInformation { private static final SizeBreakdown[] EMPTY_SIZE_BREAKDOWN = new SizeBreakdown[0]; + public Map<String, Map<String, String>> dependencies = null; private HashMap<String, String> classToPackage = new HashMap<String, String>(); private HashMap<String, HashSet<String>> classToWhatItDependsOn = new HashMap<String, HashSet<String>>(); private Map<Integer, SizeBreakdown> exclusiveCodeBreakdowns = new HashMap<Integer, SizeBreakdown>(); @@ -38,11 +39,16 @@ "Leftovers code, code not in any other category", "leftovers"); private int numSplitPoints = 0; private Map<String, TreeSet<String>> packageToClasses = new TreeMap<String, TreeSet<String>>(); + private final String permutationId; private ArrayList<Integer> splitPointInitialLoadSequence = new ArrayList<Integer>(); private HashMap<Integer, String> splitPointToLocation = new HashMap<Integer, String>(); private SizeBreakdown totalCodeBreakdown = new SizeBreakdown("Total program", "total"); + public GlobalInformation(String permutationId) { + this.permutationId = permutationId; + } + public SizeBreakdown[] allSizeBreakdowns() { List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>(); breakdowns.add(totalCodeBreakdown); @@ -128,6 +134,10 @@ return packageToClasses; } + public String getPermutationId() { + return permutationId; + } + /** * Gets the initial load sequence. *
diff --git a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java index 9c63ffb..79d9258 100644 --- a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java +++ b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -17,13 +17,9 @@ package com.google.gwt.soyc; import com.google.gwt.dev.util.Util; +import com.google.gwt.soyc.io.OutputDirectory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; @@ -33,9 +29,6 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarInputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,24 +37,14 @@ */ public class MakeTopLevelHtmlForPerm { /** - * A dependency linker for exclusive fragments. It links to nothing. - */ - public class DependencyLinkerForExclusiveFragment implements DependencyLinker { - public String dependencyLinkForClass(String className, String permutationId) { - return null; - } - } - - /** * A dependency linker for the initial code download. It links to the * dependencies for the initial download. */ public class DependencyLinkerForInitialCode implements DependencyLinker { - public String dependencyLinkForClass(String className, String permutationId) { + public String dependencyLinkForClass(String className) { String packageName = globalInformation.getClassToPackage().get(className); assert packageName != null; - return dependenciesFileName("initial", packageName, permutationId) + "#" - + className; + return dependenciesFileName("initial", packageName) + "#" + className; } } @@ -70,8 +53,8 @@ * status pages. */ public class DependencyLinkerForLeftoversFragment implements DependencyLinker { - public String dependencyLinkForClass(String className, String permutationId) { - return leftoversStatusFileName(className, permutationId); + public String dependencyLinkForClass(String className) { + return leftoversStatusFileName(className); } } @@ -81,13 +64,22 @@ * */ public class DependencyLinkerForTotalBreakdown implements DependencyLinker { - public String dependencyLinkForClass(String className, String permutationId) { - return splitStatusFileName(className, permutationId); + public String dependencyLinkForClass(String className) { + return splitStatusFileName(className); + } + } + + /** + * A dependency linker that never links to anything. + */ + public static class NullDependencyLinker implements DependencyLinker { + public String dependencyLinkForClass(String className) { + return null; } } interface DependencyLinker { - String dependencyLinkForClass(String className, String permutationId); + String dependencyLinkForClass(String className); } /** @@ -110,10 +102,6 @@ */ private static final Pattern PATTERN_SP_INT = Pattern.compile("sp([0-9]+)"); - private static String RESOURCES_PATH = MakeTopLevelHtmlForPerm.class.getPackage().getName().replace( - '.', '/') - + "/resources/"; - public static String escapeXml(String unescaped) { String escaped = unescaped.replaceAll("\\&", "&"); escaped = escaped.replaceAll("\\<", "<"); @@ -123,6 +111,46 @@ return escaped; } + public static void makeTopLevelHtmlForAllPerms( + Map<String, String> allPermsInfo, OutputDirectory outDir) + throws IOException { + PrintWriter outFile = new PrintWriter(outDir.getOutputStream("index.html")); + + outFile.println("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">"); + outFile.println("<html>"); + outFile.println("<head>"); + outFile.println(" <title>Story of Your Compile - Top Level Dashboard for all Permutations</title>"); + outFile.println("<style type=\"text/css\">"); + outFile.println("body {background-color: #728FCE}"); + outFile.println("h2 {background-color: transparent}"); + outFile.println("</style>"); + outFile.println("</head>"); + + outFile.println("<body>"); + outFile.println("<center>"); + outFile.println("<h1>Story of Your Compile</h1>"); + outFile.println("<hr>"); + outFile.println("<h3>Story of Your Compile - Overview of Permutations</h3>"); + outFile.println("<hr>"); + + outFile.println("<div style='overflow:auto; background-color:white'>"); + outFile.println("<center>"); + for (String permutationId : allPermsInfo.keySet()) { + String permutationInfo = allPermsInfo.get(permutationId); + outFile.print("<p><a href=\"SoycDashboard" + "-" + permutationId + + "-index.html\">Permutation " + permutationId); + if (permutationInfo.length() > 0) { + outFile.println(" (" + permutationInfo + ")" + "</a>"); + } else { + outFile.println("</a>"); + } + } + outFile.println("</center>"); + outFile.println("</div>"); + addStandardHtmlEnding(outFile); + outFile.close(); + } + private static void addCenteredHeader(final PrintWriter outFile, String header) { outFile.println("<hr>"); outFile.println("<b>" + header + "</b>"); @@ -137,6 +165,37 @@ addCenteredHeader(outFile, headerLineForBreakdown(breakdown)); } + private static void addStandardHtmlEnding(final PrintWriter out) { + out.println("</div>"); + out.println("</body>"); + out.println("</html>"); + } + + private static void addStandardHtmlProlog(final PrintWriter out, + String title, String header) { + out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""); + out.println("\"http://www.w3.org/TR/html4/strict.dtd\">"); + out.println("<html>"); + out.println("<head>"); + out.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">"); + out.println("<title>" + title + "</title>"); + out.println("</head>"); + + out.println("<style type=\"text/css\">"); + out.println("body {background-color: #728FCE}"); + out.println("h2 {background-color: transparent}"); + out.println("p {background-color: fuchsia}"); + out.println("</style>"); + + out.println("<body>"); + out.println("<center>"); + out.println("<h2>" + title + "</h2>"); + if (header != null) { + addCenteredHeader(out, header); + } + out.println("</center>"); + } + private static String classesInPackageFileName(SizeBreakdown breakdown, String packageName, String permutationId) { return breakdown.getId() + "_" + packageName + "-" + permutationId @@ -167,155 +226,22 @@ /** * Global information for this permutation. */ - private GlobalInformation globalInformation = new GlobalInformation(); + private final GlobalInformation globalInformation; - /** - * Settings for this permutation. - */ - private Settings settings = new Settings(); + private final OutputDirectory outDir; - /** - * Default constructor. Will be used for all permutations. - */ - MakeTopLevelHtmlForPerm() { - this.globalInformation = new GlobalInformation(); - this.settings = new Settings(); - } - - /** - * Constructor for a specific permutation. - * - * @param globalInformation All the information about this permutation - */ - MakeTopLevelHtmlForPerm(final GlobalInformation globalInformation) { + MakeTopLevelHtmlForPerm(GlobalInformation globalInformation, + OutputDirectory outDir) { this.globalInformation = globalInformation; + this.outDir = outDir; } - public void copyFileOrDirectory(File srcPath, File dstPath, String classPath, - String inputFileName, boolean isDirectory) throws IOException { - if (srcPath.isDirectory()) { - if (!dstPath.exists()) { - dstPath.mkdir(); - } - String files[] = srcPath.list(); - for (int i = 0; i < files.length; i++) { - copyFileOrDirectory(new File(srcPath, files[i]), new File(dstPath, - files[i]), classPath, inputFileName, isDirectory); - } - } else { - if (!srcPath.exists()) { - copyFileOrDirectoryFromJar(classPath, inputFileName, dstPath, - isDirectory); - } else { - InputStream in = new FileInputStream(srcPath); - OutputStream out = new FileOutputStream(dstPath); - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - in.close(); - out.close(); - } - } - } - - public void copyFileOrDirectoryFromJar(String jarFileName, - String inputFileName, File dstPath, boolean isDirectory) - throws IOException { - - JarFile jarFile = new JarFile(jarFileName); - if (isDirectory) { - dstPath.mkdir(); - - JarInputStream jarFileIS = new JarInputStream(new FileInputStream( - jarFileName)); - JarEntry jarEntry = jarFileIS.getNextJarEntry(); - while (jarEntry != null) { - if (!inputFileName.endsWith("/")) { - inputFileName += "/"; - } - if ((jarEntry.getName().compareTo(inputFileName) != 0) - && (jarEntry.getName().startsWith(inputFileName))) { - File newDstPath = getOutFile(jarEntry.getName()); - copyFileOrDirectoryFromJar(jarFileName, jarEntry.getName(), - newDstPath, false); - } - jarEntry = jarFileIS.getNextJarEntry(); - } - jarFileIS.close(); - } else { - InputStream in = jarFile.getInputStream(jarFile.getEntry(inputFileName)); - OutputStream out = new FileOutputStream(dstPath); - - int c; - while ((c = in.read()) != -1) { - out.write(c); - } - in.close(); - out.close(); - jarFile.close(); - } - } - - public GlobalInformation getGlobalInformation() { - return globalInformation; - } - - public void makeBreakdownShell(SizeBreakdown breakdown, String permutationId) - throws IOException { - // this will contain the place holder iframes where the actual information - // is going to go. - + public void makeBreakdownShell(SizeBreakdown breakdown) throws IOException { Map<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl; Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl; - // copy from the bin directory to the current directory - String classPath = settings.resources.get(); - if (classPath == null) { - classPath = System.getProperty("java.class.path"); - } - if (!classPath.endsWith("/")) { - classPath += "/"; - } - String inputFileName = "roundedCorners.css"; - File inputFile = new File(classPath + RESOURCES_PATH + inputFileName); - if (!inputFile.exists()) { - inputFile = new File(classPath + inputFileName); - } - File outputFile = getOutFile("roundedCorners.css"); - copyFileOrDirectory(inputFile, outputFile, classPath, RESOURCES_PATH - + inputFileName, false); - - inputFileName = "classLevel.css"; - File inputFile2 = new File(classPath + RESOURCES_PATH + inputFileName); - if (!inputFile2.exists()) { - inputFile2 = new File(classPath + inputFileName); - } - File outputFile2 = getOutFile("classLevel.css"); - copyFileOrDirectory(inputFile2, outputFile2, classPath, RESOURCES_PATH - + inputFileName, false); - - inputFileName = "common.css"; - File inputFile3 = new File(classPath + RESOURCES_PATH + inputFileName); - if (!inputFile3.exists()) { - inputFile3 = new File(classPath + inputFileName); - } - File outputFile3 = getOutFile("common.css"); - copyFileOrDirectory(inputFile3, outputFile3, classPath, RESOURCES_PATH - + inputFileName, false); - - inputFileName = "images"; - File inputDir = new File(classPath + RESOURCES_PATH + "images"); - if (!inputDir.exists()) { - inputDir = new File(classPath + "images"); - } - File outputDir = getOutFile("images"); - copyFileOrDirectory(inputDir, outputDir, classPath, inputFileName, true); - - final PrintWriter outFile = new PrintWriter(getOutFile(shellFileName( - breakdown, permutationId))); + PrintWriter outFile = new PrintWriter(getOutFile(shellFileName(breakdown, + getPermutationId()))); outFile.println("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">"); outFile.println("<html>"); @@ -389,7 +315,7 @@ outFile.println("<div class='abs header'>Package breakdown</div>"); outFile.println("<div class='abs innerContent'>"); - String packageBreakdownFileName = makePackageHtml(breakdown, permutationId); + String packageBreakdownFileName = makePackageHtml(breakdown); outFile.println(" <iframe class='frame' src=\"" + packageBreakdownFileName + "\" scrolling=auto></iframe>"); outFile.println("</div>"); @@ -400,7 +326,7 @@ outFile.println("<div class='abs innerContent'>"); String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown, - nameToCodeColl, nameToLitColl, permutationId); + nameToCodeColl, nameToLitColl); outFile.println("<iframe class='frame' src=\"" + codeTypeBreakdownFileName + "\" scrolling=auto></iframe>"); outFile.println("</div>"); @@ -411,15 +337,15 @@ outFile.close(); } - public void makeCodeTypeClassesHtmls(SizeBreakdown breakdown, - String permutationId) throws IOException { + public void makeCodeTypeClassesHtmls(SizeBreakdown breakdown) + throws IOException { HashMap<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl; for (String codeType : nameToCodeColl.keySet()) { // construct file name String outFileName = breakdown.getId() + "_" + codeType + "-" - + permutationId + "Classes.html"; + + getPermutationId() + "Classes.html"; float maxSize = 0f; TreeMap<Float, String> sortedClasses = new TreeMap<Float, String>( @@ -540,28 +466,26 @@ } } - public void makeDependenciesHtml( - Map<String, Map<String, String>> allDependencies, String permutationId) - throws IOException { - for (String depGraphName : allDependencies.keySet()) { - makeDependenciesHtml(depGraphName, allDependencies.get(depGraphName), - permutationId); + public void makeDependenciesHtml() throws IOException { + for (String depGraphName : globalInformation.dependencies.keySet()) { + makeDependenciesHtml(depGraphName, + globalInformation.dependencies.get(depGraphName)); } } - public void makeLeftoverStatusPages(String permutationId) throws IOException { + public void makeLeftoverStatusPages() throws IOException { for (String className : globalInformation.getClassToPackage().keySet()) { - makeLeftoversStatusPage(className, permutationId); + makeLeftoversStatusPage(className); } } - public void makeLiteralsClassesTableHtmls(SizeBreakdown breakdown, - String permutationId) throws IOException { + public void makeLiteralsClassesTableHtmls(SizeBreakdown breakdown) + throws IOException { Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl; for (String literalType : nameToLitColl.keySet()) { - String outFileName = literalType + "-" + permutationId + "Lits.html"; + String outFileName = literalType + "-" + getPermutationId() + "Lits.html"; final PrintWriter outFile = new PrintWriter(getOutFile(breakdown.getId() + "_" + outFileName)); @@ -625,9 +549,8 @@ * Make size breakdowns for each package for one code collection. */ public void makePackageClassesHtmls(SizeBreakdown breakdown, - DependencyLinker depLinker, String permutationId) throws IOException { + DependencyLinker depLinker) throws IOException { for (String packageName : globalInformation.getPackageToClasses().keySet()) { - TreeMap<Float, String> sortedClasses = new TreeMap<Float, String>( Collections.reverseOrder()); float maxSize = 0f; @@ -642,7 +565,6 @@ } if (curSize != 0f) { - sumSize += curSize; sortedClasses.put(curSize, className); if (curSize > maxSize) { @@ -653,7 +575,7 @@ PrintWriter outFile = new PrintWriter( getOutFile(classesInPackageFileName(breakdown, packageName, - permutationId))); + getPermutationId()))); outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""); outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">"); @@ -726,9 +648,8 @@ outFile.println("<th class='barlabel'></th>"); outFile.println("<th style='width:100%' class='barlabel'></th>"); outFile.println("</thead>"); -// - for (Float size : sortedClasses.keySet()) { + for (Float size : sortedClasses.keySet()) { String className = sortedClasses.get(size); float ratio = (size / maxSize) * 85; if (ratio < 5) { @@ -736,12 +657,11 @@ } float perc = (size / sumSize) * 100; - String dependencyLink = depLinker.dependencyLinkForClass(className, - permutationId); + String dependencyLink = depLinker.dependencyLinkForClass(className); outFile.println("<tr>"); outFile.println("<td class=\"barlabel\">" + size + "</td>"); outFile.println("<td class=\"barlabel\">" + perc + "%</td>"); - if ((settings.displayDependencies) && (dependencyLink != null)) { + if (dependencyLink != null) { outFile.println("<td class=\"barlabel\"><a href=\"" + dependencyLink + "\" target=\"_top\">" + className + "</a></td>"); } else { @@ -761,54 +681,15 @@ } } - public void makeSplitStatusPages(String permutationId) throws IOException { + public void makeSplitStatusPages() throws IOException { for (String className : globalInformation.getClassToPackage().keySet()) { - makeSplitStatusPage(className, permutationId); + makeSplitStatusPage(className); } } - public void makeTopLevelHtmlForAllPerms() throws FileNotFoundException { - - PrintWriter outFile = new PrintWriter(getOutFile("index.html")); - - outFile.println("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">"); - outFile.println("<html>"); - outFile.println("<head>"); - outFile.println(" <title>Story of Your Compile - Top Level Dashboard for all Permutations</title>"); - outFile.println("<style type=\"text/css\">"); - outFile.println("body {background-color: #728FCE}"); - outFile.println("h2 {background-color: transparent}"); - outFile.println("</style>"); - outFile.println("</head>"); - - outFile.println("<body>"); - outFile.println("<center>"); - outFile.println("<h1>Story of Your Compile</h1>"); - outFile.println("<hr>"); - outFile.println("<h3>Story of Your Compile - Overview of Permutations</h3>"); - outFile.println("<hr>"); - - outFile.println("<div style='overflow:auto; background-color:white'>"); - outFile.println("<center>"); - for (String permutationId : settings.allPermsInfo.keySet()) { - String permutationInfo = settings.allPermsInfo.get(permutationId); - outFile.print("<p><a href=\"SoycDashboard" + "-" + permutationId - + "-index.html\">Permutation " + permutationId); - if (permutationInfo.length() > 0) { - outFile.println(" (" + permutationInfo + ")" + "</a>"); - } else { - outFile.println("</a>"); - } - } - outFile.println("</center>"); - outFile.println("</div>"); - addStandardHtmlEnding(outFile); - outFile.close(); - } - - public void makeTopLevelShell(String permutationId) throws IOException { + public void makeTopLevelShell() throws IOException { PrintWriter outFile = new PrintWriter(getOutFile("SoycDashboard" + "-" - + permutationId + "-index.html")); + + getPermutationId() + "-index.html")); outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""); outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">"); @@ -868,8 +749,10 @@ outFile.println("<body>"); outFile.println("<div class='abs mainHeader'>"); outFile.println("<h2>Story of Your Compile Dashboard</h2>"); - String permutationInfo = settings.allPermsInfo.get(permutationId); - outFile.print("<h3>Permutation " + permutationId); + // String permutationInfo = settings.allPermsInfo.get(getPermutationId()); + String permutationInfo = ""; // TODO(spoon) pass in permutation information + // here + outFile.print("<h3>Permutation " + getPermutationId()); if (permutationInfo.length() > 0) { outFile.println(" (" + permutationInfo + ")"); } @@ -928,7 +811,7 @@ breakdown = globalInformation.splitPointCodeBreakdown(i); } - String drillDownFileName = shellFileName(breakdown, permutationId); + String drillDownFileName = shellFileName(breakdown, getPermutationId()); String splitPointDescription = breakdown.getDescription(); int size = breakdown.sizeAllCode; @@ -962,14 +845,6 @@ outFile.close(); } - public void setGlobalInformation(GlobalInformation globalInformation) { - this.globalInformation = globalInformation; - } - - public void setSettings(Settings settings) { - this.settings = settings; - } - private void addDependenciesHtmlProlog(final PrintWriter out, String title, String header) { out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""); @@ -1023,65 +898,39 @@ } private void addLefttoversStatus(String className, String packageName, - PrintWriter out, String permutationId) { - if (settings.displayDependencies) { + PrintWriter out) { + if (globalInformation.dependencies != null) { out.println("<tr><td> <a href=\"" - + dependenciesFileName("total", packageName, permutationId) + "#" - + className + "\">See why it's live</a></td></tr>"); + + dependenciesFileName("total", packageName) + "#" + className + + "\">See why it's live</a></td></tr>"); for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) { out.println("<tr><td> <a href=\"" - + dependenciesFileName("sp" + sp, packageName, permutationId) + "#" - + className + "\">See why it's not exclusive to s.p. #" + sp + " (" + + dependenciesFileName("sp" + sp, packageName) + "#" + className + + "\">See why it's not exclusive to s.p. #" + sp + " (" + globalInformation.getSplitPointToLocation().get(sp) + ")</a></td></tr>"); } } } - private void addStandardHtmlEnding(final PrintWriter out) { - out.println("</div>"); - out.println("</body>"); - out.println("</html>"); - } - - private void addStandardHtmlProlog(final PrintWriter out, String title, - String header) { - out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""); - out.println("\"http://www.w3.org/TR/html4/strict.dtd\">"); - out.println("<html>"); - out.println("<head>"); - out.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">"); - out.println("<title>" + title + "</title>"); - out.println("</head>"); - - out.println("<style type=\"text/css\">"); - out.println("body {background-color: #728FCE}"); - out.println("h2 {background-color: transparent}"); - out.println("p {background-color: fuchsia}"); - out.println("</style>"); - - out.println("<body>"); - out.println("<center>"); - out.println("<h2>" + title + "</h2>"); - if (header != null) { - addCenteredHeader(out, header); - } - out.println("</center>"); - } - - private String dependenciesFileName(String depGraphName, String packageName, - String permutationId) { + private String dependenciesFileName(String depGraphName, String packageName) { return "methodDependencies-" + depGraphName + "-" + filename(packageName) - + "-" + permutationId + ".html"; + + "-" + getPermutationId() + ".html"; } /** * Return a {@link File} object for a file to be emitted into the output * directory. */ - private File getOutFile(String localFileName) { - File outDir = new File(settings.out.get()); - return new File(outDir, localFileName); + private OutputStream getOutFile(String localFileName) throws IOException { + return outDir.getOutputStream(localFileName); + } + + /** + * @return + */ + private String getPermutationId() { + return globalInformation.getPermutationId(); } /** @@ -1115,16 +964,15 @@ splitPoint); } - private String leftoversStatusFileName(String className, String permutationId) { - return "leftoverStatus-" + filename(className) + "-" + permutationId + private String leftoversStatusFileName(String className) { + return "leftoverStatus-" + filename(className) + "-" + getPermutationId() + ".html"; } private String makeCodeTypeHtml(SizeBreakdown breakdown, Map<String, CodeCollection> nameToCodeColl, - Map<String, LiteralsCollection> nameToLitColl, String permutationId) - throws IOException { - String outFileName = breakdown.getId() + "-" + permutationId + Map<String, LiteralsCollection> nameToLitColl) throws IOException { + String outFileName = breakdown.getId() + "-" + getPermutationId() + "_codeTypeBreakdown.html"; float maxSize = 0f; float sumSize = 0f; @@ -1167,7 +1015,7 @@ String codeType = sortedCodeTypes.get(size); String drillDownFileName = breakdown.getId() + "_" + codeType + "-" - + permutationId + "Classes.html"; + + getPermutationId() + "Classes.html"; float ratio = (size / maxSize) * 79; float perc = (size / sumSize) * 100; @@ -1210,7 +1058,7 @@ for (Float size : sortedLitTypes.keySet()) { String literal = sortedLitTypes.get(size); String drillDownFileName = breakdown.getId() + "_" + literal + "-" - + permutationId + "Lits.html"; + + getPermutationId() + "Lits.html"; float ratio = (size / maxSize) * 79; float perc = (size / sumSize) * 100; @@ -1241,8 +1089,7 @@ } private void makeDependenciesHtml(String depGraphName, - Map<String, String> dependencies, String permutationId) - throws FileNotFoundException { + Map<String, String> dependencies) throws IOException { String depGraphDescription = inferDepGraphDescription(depGraphName); PrintWriter outFile = null; String curPackageName = ""; @@ -1268,8 +1115,7 @@ outFile.close(); } - String outFileName = dependenciesFileName(depGraphName, curPackageName, - permutationId); + String outFileName = dependenciesFileName(depGraphName, curPackageName); outFile = new PrintWriter(getOutFile(outFileName)); String packageDescription = packageName.length() == 0 @@ -1305,11 +1151,10 @@ outFile.close(); } - private void makeLeftoversStatusPage(String className, String permutationId) - throws IOException { + private void makeLeftoversStatusPage(String className) throws IOException { String packageName = globalInformation.getClassToPackage().get(className); - PrintWriter out = new PrintWriter(getOutFile(leftoversStatusFileName( - className, permutationId))); + PrintWriter out = new PrintWriter( + getOutFile(leftoversStatusFileName(className))); addStandardHtmlProlog(out, "Leftovers page for " + className, null); @@ -1317,7 +1162,7 @@ out.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">"); out.println("<tr><td>This class has some leftover code, neither initial nor exclusive to any split point:</td></tr>"); - addLefttoversStatus(className, packageName, out, permutationId); + addLefttoversStatus(className, packageName, out); out.println("</table>"); addStandardHtmlEnding(out); @@ -1325,9 +1170,8 @@ out.close(); } - private String makePackageHtml(SizeBreakdown breakdown, String permutationId) - throws FileNotFoundException { - String outFileName = breakdown.getId() + "-" + permutationId + "_" + private String makePackageHtml(SizeBreakdown breakdown) throws IOException { + String outFileName = breakdown.getId() + "-" + getPermutationId() + "_" + "packageBreakdown.html"; Map<String, Integer> packageToPartialSize = breakdown.packageToSize; TreeMap<Integer, String> sortedPackages = new TreeMap<Integer, String>( @@ -1364,7 +1208,7 @@ for (int size : sortedPackages.keySet()) { String packageName = sortedPackages.get(size); String drillDownFileName = classesInPackageFileName(breakdown, - packageName, permutationId); + packageName, getPermutationId()); float ratio = (size / maxSize) * 79; if (ratio < 5) { @@ -1393,11 +1237,10 @@ return outFileName; } - private void makeSplitStatusPage(String className, String permutationId) - throws IOException { + private void makeSplitStatusPage(String className) throws IOException { String packageName = globalInformation.getClassToPackage().get(className); - PrintWriter out = new PrintWriter(getOutFile(splitStatusFileName(className, - permutationId))); + PrintWriter out = new PrintWriter( + getOutFile(splitStatusFileName(className))); addStandardHtmlProlog(out, "Split point status for " + className, null); @@ -1405,10 +1248,10 @@ out.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">"); if (globalInformation.getInitialCodeBreakdown().classToSize.containsKey(className)) { - if (settings.displayDependencies) { + if (globalInformation.dependencies != null) { out.println("<tr><td>Some code is initial (<a href=\"" - + dependenciesFileName("initial", packageName, permutationId) + "#" - + className + "\">see why</a>)</td></tr>"); + + dependenciesFileName("initial", packageName) + "#" + className + + "\">see why</a>)</td></tr>"); } else { out.println("<tr><td>Some code is initial</td></tr>"); } @@ -1419,7 +1262,7 @@ } if (globalInformation.getLeftoversBreakdown().classToSize.containsKey(className)) { out.println("<tr><td>Some code is left over:</td></tr>"); - addLefttoversStatus(className, packageName, out, permutationId); + addLefttoversStatus(className, packageName, out); } out.println("</table>"); @@ -1442,7 +1285,8 @@ return sps; } - private String splitStatusFileName(String className, String permutationId) { - return "splitStatus-" + filename(className) + "-" + permutationId + ".html"; + private String splitStatusFileName(String className) { + return "splitStatus-" + filename(className) + "-" + getPermutationId() + + ".html"; } }
diff --git a/dev/core/src/com/google/gwt/soyc/Settings.java b/dev/core/src/com/google/gwt/soyc/Settings.java index 7f5b9bb..01bd6e8 100644 --- a/dev/core/src/com/google/gwt/soyc/Settings.java +++ b/dev/core/src/com/google/gwt/soyc/Settings.java
@@ -16,15 +16,10 @@ package com.google.gwt.soyc; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.TreeMap; /** * Command-line settings for SOYC. @@ -123,10 +118,6 @@ break; // No setting wanted the remaining arguments } - if (settings.resources.get() == null) { - throw new ArgumentListException("The -resources option is required"); - } - if ((settings.soycDir.get() == null) && (settings.symbolMapsDir.get() == null)) { @@ -184,17 +175,16 @@ return help.toString(); } - public Map<String, String> allPermsInfo = new TreeMap<String, String>(); public String depFileName; - public Boolean displayDependencies = false; - public Boolean displaySplitPoints = false; public final Setting<String> out = addSetting(new StringSetting("-out", "dir", ".", "output directory")); public final Setting<String> resources = addSetting(new StringSetting( - "-resources", "jarfile", null, - " directory or jar file with CSS, etc., resources")); + "-resources", + "dir", + null, + "present only for backwards compatibility; directory or jar file with CSS, etc., resources")); public final Setting<String> soycDir = addSetting(new StringSetting( "-soycDir", "dir", null, " directory for soyc files")); @@ -207,47 +197,6 @@ "-symbolMapsDir", "dir", null, " directory or symbol maps files")); private List<Setting<?>> allSettings; - public void readPermutationInfo() throws FileNotFoundException { - - if (symbolMapsDir.get() == null) { - // get the permutation id from settings - String permutationId = storiesFileName; - permutationId = permutationId.replaceAll(".*/stories", ""); - permutationId = permutationId.replaceAll("\\.xml(\\.gz)?", ""); - allPermsInfo.put(permutationId, ""); - } else { - File dir = new File(symbolMapsDir.get()); - String files[] = dir.list(); - for (Integer i = 0; i < files.length; i++) { - String permFileName = symbolMapsDir.get() + "/" + files[i]; - FileReader fir = new FileReader(permFileName); - - Scanner sc = new Scanner(fir); - - String permutationId = ""; - String permutationInfo = ""; - int lineCount = 0; - while ((sc.hasNextLine()) && (lineCount < 2)) { - - String curLine = sc.nextLine(); - curLine = curLine.trim(); - - if (curLine.startsWith("# {")) { - curLine = curLine.replace("# {", ""); - curLine = curLine.replace("}", ""); - curLine = curLine.trim(); - if (lineCount == 0) { - permutationId = curLine; - } else { - permutationInfo = curLine; - } - lineCount++; - } - } - allPermsInfo.put(permutationId, permutationInfo); - } - } - } private <T> Setting<T> addSetting(Setting<T> setting) { if (allSettings == null) {
diff --git a/dev/core/src/com/google/gwt/soyc/SoycDashboard.java b/dev/core/src/com/google/gwt/soyc/SoycDashboard.java index 25aeffd..190256f 100644 --- a/dev/core/src/com/google/gwt/soyc/SoycDashboard.java +++ b/dev/core/src/com/google/gwt/soyc/SoycDashboard.java
@@ -17,6 +17,9 @@ package com.google.gwt.soyc; import com.google.gwt.soyc.MakeTopLevelHtmlForPerm.DependencyLinker; +import com.google.gwt.soyc.MakeTopLevelHtmlForPerm.NullDependencyLinker; +import com.google.gwt.soyc.io.FileSystemOutputDirectory; +import com.google.gwt.soyc.io.OutputDirectory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -26,6 +29,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -33,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Scanner; import java.util.TreeMap; import java.util.TreeSet; import java.util.Map.Entry; @@ -43,10 +48,9 @@ import javax.xml.parsers.SAXParserFactory; /** - * The command-line entry point for creating a SOYC report. + * The command-line entry point for creating a compile report. */ public class SoycDashboard { - private static class FormatException extends RuntimeException { public FormatException() { super(); @@ -62,136 +66,47 @@ } public static void main(final String[] args) { + Settings settings; try { - System.out.println("Generating the Story of Your Compile..."); - Settings settings = Settings.fromArgumentList(args); + settings = Settings.fromArgumentList(args); + } catch (Settings.ArgumentListException e) { + System.err.println(e.getMessage()); + System.err.println("Usage: " + + "java com.google.gwt.soyc.SoycDashboard -resources dir -soycDir dir -symbolMaps dir [-out dir]"); + System.err.println("(Legacy usage: " + + "java com.google.gwt.soyc.SoycDashboard options stories0.xml[.gz] [dependencies0.xml[.gz]] [splitpoints0.xml[.gz]])"); + System.err.println("Options:"); + System.err.println(Settings.settingsHelp()); + System.exit(1); + return; // not reached + } - MakeTopLevelHtmlForPerm makeTopLevelHtml = new MakeTopLevelHtmlForPerm(); - makeTopLevelHtml.setSettings(settings); + System.out.println("Generating the Story of Your Compile..."); - // read in all the symbol maps - settings.readPermutationInfo(); - makeTopLevelHtml.makeTopLevelHtmlForAllPerms(); - for (String permutationId : settings.allPermsInfo.keySet()) { - GlobalInformation globalInformation = new GlobalInformation(); - MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm = new MakeTopLevelHtmlForPerm( - globalInformation); - makeTopLevelHtmlForPerm.setSettings(settings); + OutputDirectory outDir = new FileSystemOutputDirectory(new File( + settings.out.get())); - String storiesFileName = settings.storiesFileName; - - String depFileName = settings.depFileName; - if (depFileName == null) { - depFileName = ""; + try { + Map<String, String> permInfo = readPermutationInfo(settings); + SoycDashboard dashboard = new SoycDashboard(outDir); + for (String permutationId : permInfo.keySet()) { + dashboard.startNewPermutation(permutationId); + if (settings.symbolMapsDir.get() == null) { + dashboard.readFromFilesNamed(settings.storiesFileName, + settings.depFileName, settings.splitPointsFileName); + } else { + String soycDir = settings.soycDir.get(); + dashboard.readFromFilesNamed(soycInputFile(soycDir, "stories", + permutationId), soycInputFile(soycDir, "dependencies", + permutationId), soycInputFile(soycDir, "splitPoints", + permutationId)); } - String splitPointsFileName = settings.splitPointsFileName; - if (splitPointsFileName == null) { - splitPointsFileName = ""; - } - - if (settings.symbolMapsDir.get() != null) { - storiesFileName = settings.soycDir.get() + "/stories" + permutationId - + ".xml.gz"; - - if (!(new File(storiesFileName).exists())) { - storiesFileName = settings.soycDir.get() + "/stories" - + permutationId + ".xml"; - } - depFileName = settings.soycDir.get() + "/dependencies" - + permutationId + ".xml.gz"; - if (!(new File(depFileName).exists())) { - depFileName = settings.soycDir.get() + "/dependencies" - + permutationId + ".xml"; - } - splitPointsFileName = settings.soycDir.get() + "/splitPoints" - + permutationId + ".xml.gz"; - if (!(new File(splitPointsFileName).exists())) { - splitPointsFileName = settings.soycDir.get() + "/splitPoints" - + permutationId + ".xml"; - } - } - - settings.displayDependencies = (new File(depFileName)).exists(); - settings.displaySplitPoints = (new File(splitPointsFileName)).exists(); - - new File(settings.out.get()).mkdir(); - if (settings.displayDependencies) { - /** - * handle dependencies - */ - Map<String, Map<String, String>> dependencies = new TreeMap<String, Map<String, String>>(); - DefaultHandler depHandler = parseXMLDocumentDependencies(dependencies); - SAXParserFactory depFactoryMain = SAXParserFactory.newInstance(); - depFactoryMain.setNamespaceAware(true); - SAXParser saxParser = depFactoryMain.newSAXParser(); - InputStream in = new FileInputStream(depFileName); - if (depFileName.endsWith(".gz")) { - in = new GZIPInputStream(in); - } - in = new BufferedInputStream(in); - saxParser.parse(in, depHandler); - - makeTopLevelHtmlForPerm.makeDependenciesHtml(dependencies, - permutationId); - } - - if (settings.displaySplitPoints) { - /** - * handle runAsync split points - */ - - DefaultHandler splitPointHandler = parseXMLDocumentSplitPoints(globalInformation); - SAXParserFactory splitPointsFactoryMain = SAXParserFactory.newInstance(); - splitPointsFactoryMain.setNamespaceAware(true); - - SAXParser saxParser = splitPointsFactoryMain.newSAXParser(); - InputStream in = new FileInputStream(splitPointsFileName); - if (depFileName.endsWith(".gz")) { - in = new GZIPInputStream(in); - } - in = new BufferedInputStream(in); - saxParser.parse(in, splitPointHandler); - } - - /** - * handle everything else - */ - - DefaultHandler handler = parseXMLDocumentSizeMap(globalInformation); - SAXParserFactory factoryMain = SAXParserFactory.newInstance(); - factoryMain.setNamespaceAware(true); - SAXParser saxParser = factoryMain.newSAXParser(); - InputStream in = new FileInputStream(storiesFileName); - if (storiesFileName.endsWith(".gz")) { - in = new GZIPInputStream(in); - } - in = new BufferedInputStream(in); - saxParser.parse(in, handler); - - // add to "All Other Code" if none of the special categories apply - for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { - updateAllOtherCodeType(breakdown.nameToCodeColl, globalInformation); - } - globalInformation.computePackageSizes(); - - // clean up the RPC categories - for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { - foldInRPCHeuristic(breakdown.nameToCodeColl); - } - - // generate all the html files - makeTopLevelHtmlForPerm.makeSplitStatusPages(permutationId); - makeTopLevelHtmlForPerm.makeLeftoverStatusPages(permutationId); - for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { - DependencyLinker linker = chooseDependencyLinker( - makeTopLevelHtmlForPerm, breakdown); - makeHTMLFiles(makeTopLevelHtmlForPerm, breakdown, linker, - permutationId); - } + dashboard.generateForOnePermutation(); System.out.println("Finished creating reports for permutation."); } - System.out.println("Finished creating reports. To see the dashboard, open index.html in your browser."); + dashboard.generateCrossPermutationFiles(permInfo); + System.out.println("Finished creating reports. To see the dashboard, open index.html in your browser."); } catch (ParserConfigurationException e) { System.err.println("Could not parse document. " + e.getMessage()); System.exit(1); @@ -204,45 +119,21 @@ } catch (IOException e) { System.err.println("Error creating html file. " + e.getMessage()); System.exit(1); - } catch (Settings.ArgumentListException e) { - System.err.println(e.getMessage()); - System.err.println("Usage: " - + "java com.google.gwt.soyc.SoycDashboard -resources dir -soycDir dir -symbolMaps dir [-out dir]"); - System.err.println("(Legacy usage: " - + "java com.google.gwt.soyc.SoycDashboard options stories0.xml[.gz] [dependencies0.xml[.gz]] [splitpoints0.xml[.gz]])"); - System.err.println("Options:"); - System.err.println(Settings.settingsHelp()); - System.exit(1); } } - private static Collection<SizeBreakdown> breakdownsForFragment( - Integer fragment, GlobalInformation globalInformation) { - List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>(); - breakdowns.add(globalInformation.getTotalCodeBreakdown()); - if (fragment == 0) { - breakdowns.add(globalInformation.getInitialCodeBreakdown()); + /** + * Open a file for reading. If the filename ends in .gz, then wrap the stream + * with a {@link GZIPInputStream}. + */ + public static InputStream openPossiblyGzippedFile(String filename) + throws IOException { + InputStream in = new FileInputStream(filename); + if (filename.endsWith(".gz")) { + in = new GZIPInputStream(in); } - if (fragment == (globalInformation.getNumSplitPoints() + 1)) { - breakdowns.add(globalInformation.getLeftoversBreakdown()); - } - if (fragment >= 1 && fragment <= globalInformation.getNumSplitPoints()) { - breakdowns.add(globalInformation.splitPointCodeBreakdown(fragment)); - } - return breakdowns; - } - - private static DependencyLinker chooseDependencyLinker( - MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, SizeBreakdown breakdown) { - if (breakdown == makeTopLevelHtmlForPerm.getGlobalInformation().getTotalCodeBreakdown()) { - return makeTopLevelHtmlForPerm.new DependencyLinkerForTotalBreakdown(); - } else if (breakdown == makeTopLevelHtmlForPerm.getGlobalInformation().getInitialCodeBreakdown()) { - return makeTopLevelHtmlForPerm.new DependencyLinkerForInitialCode(); - } else if (breakdown == makeTopLevelHtmlForPerm.getGlobalInformation().getLeftoversBreakdown()) { - return makeTopLevelHtmlForPerm.new DependencyLinkerForLeftoversFragment(); - } else { - return makeTopLevelHtmlForPerm.new DependencyLinkerForExclusiveFragment(); - } + in = new BufferedInputStream(in); + return in; } /* @@ -279,27 +170,6 @@ } } - /** - * Generates all the HTML files for one size breakdown. - * - * @param makeTopLevelHtmlForPerm - * @param breakdown - * @param depLinker - * @param permutationId - * @throws IOException - */ - private static void makeHTMLFiles( - MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, SizeBreakdown breakdown, - DependencyLinker depLinker, String permutationId) throws IOException { - makeTopLevelHtmlForPerm.makePackageClassesHtmls(breakdown, depLinker, - permutationId); - makeTopLevelHtmlForPerm.makeCodeTypeClassesHtmls(breakdown, permutationId); - makeTopLevelHtmlForPerm.makeLiteralsClassesTableHtmls(breakdown, - permutationId); - makeTopLevelHtmlForPerm.makeBreakdownShell(breakdown, permutationId); - makeTopLevelHtmlForPerm.makeTopLevelShell(permutationId); - } - private static DefaultHandler parseXMLDocumentDependencies( final Map<String, Map<String, String>> allDependencies) { DefaultHandler handler = new DefaultHandler() { @@ -357,7 +227,193 @@ return handler; } - private static DefaultHandler parseXMLDocumentSizeMap( + private static Map<String, String> readPermutationInfo(Settings settings) + throws FileNotFoundException { + Map<String, String> allPermsInfo = new TreeMap<String, String>(); + if (settings.symbolMapsDir.get() == null) { + String permutationId = settings.storiesFileName; + permutationId = permutationId.replaceAll(".*/stories", ""); + permutationId = permutationId.replaceAll("\\.xml(\\.gz)?", ""); + allPermsInfo.put(permutationId, ""); + } else { + File dir = new File(settings.symbolMapsDir.get()); + String files[] = dir.list(); + for (Integer i = 0; i < files.length; i++) { + String permFileName = settings.symbolMapsDir.get() + "/" + files[i]; + FileReader fir = new FileReader(permFileName); + + Scanner sc = new Scanner(fir); + + String permutationId = ""; + String permutationInfo = ""; + int lineCount = 0; + while ((sc.hasNextLine()) && (lineCount < 2)) { + + String curLine = sc.nextLine(); + curLine = curLine.trim(); + + if (curLine.startsWith("# {")) { + curLine = curLine.replace("# {", ""); + curLine = curLine.replace("}", ""); + curLine = curLine.trim(); + if (lineCount == 0) { + permutationId = curLine; + } else { + permutationInfo = curLine; + } + lineCount++; + } + } + allPermsInfo.put(permutationId, permutationInfo); + } + } + return allPermsInfo; + } + + private static String soycInputFile(String soycDir, String baseName, + String permutationId) { + String name = soycDir + "/" + baseName + permutationId + ".xml.gz"; + if (new File(name).exists()) { + return name; + } + return soycDir + "/" + baseName + permutationId + ".xml"; + } + + /** + * Global information for the current permutation being emitted. + */ + private GlobalInformation globalInformation; + + /** + * HTML emitter for the current permutation being emitted. + */ + private MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm; + + private final OutputDirectory outDir; + + public SoycDashboard(OutputDirectory outDir) { + this.outDir = outDir; + } + + public void generateCrossPermutationFiles(Map<String, String> permInfo) + throws IOException { + StaticResources.emit(outDir); + MakeTopLevelHtmlForPerm.makeTopLevelHtmlForAllPerms(permInfo, outDir); + } + + public void generateForOnePermutation() throws IOException { + if (globalInformation.dependencies != null) { + makeTopLevelHtmlForPerm.makeDependenciesHtml(); + } + + if (globalInformation.getNumSplitPoints() > 0) { + makeTopLevelHtmlForPerm.makeSplitStatusPages(); + makeTopLevelHtmlForPerm.makeLeftoverStatusPages(); + } + for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { + DependencyLinker linker = chooseDependencyLinker(breakdown); + makeHTMLFiles(makeTopLevelHtmlForPerm, breakdown, linker); + } + } + + public void readDependencies(InputStream stream) + throws ParserConfigurationException, SAXException, IOException { + globalInformation.dependencies = new TreeMap<String, Map<String, String>>(); + DefaultHandler depHandler = parseXMLDocumentDependencies(globalInformation.dependencies); + SAXParserFactory depFactoryMain = SAXParserFactory.newInstance(); + depFactoryMain.setNamespaceAware(true); + SAXParser saxParser = depFactoryMain.newSAXParser(); + saxParser.parse(stream, depHandler); + } + + public void readSizeMaps(InputStream stream) + throws ParserConfigurationException, SAXException, IOException { + DefaultHandler handler = parseXMLDocumentSizeMap(globalInformation); + SAXParserFactory factoryMain = SAXParserFactory.newInstance(); + factoryMain.setNamespaceAware(true); + SAXParser saxParser = factoryMain.newSAXParser(); + saxParser.parse(stream, handler); + + // Now clean up the information that has been read in various ways + globalInformation.computePackageSizes(); + + // add to "All Other Code" if none of the special categories apply + for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { + updateAllOtherCodeType(breakdown.nameToCodeColl); + } + + // clean up the RPC categories + for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { + foldInRPCHeuristic(breakdown.nameToCodeColl); + } + } + + public void readSplitPoints(InputStream stream) + throws ParserConfigurationException, SAXException, IOException { + DefaultHandler splitPointHandler = parseXMLDocumentSplitPoints(); + SAXParserFactory splitPointsFactoryMain = SAXParserFactory.newInstance(); + splitPointsFactoryMain.setNamespaceAware(true); + + SAXParser saxParser = splitPointsFactoryMain.newSAXParser(); + saxParser.parse(stream, splitPointHandler); + } + + public void startNewPermutation(String permutationId) { + globalInformation = new GlobalInformation(permutationId); + makeTopLevelHtmlForPerm = new MakeTopLevelHtmlForPerm(globalInformation, + outDir); + } + + private Collection<SizeBreakdown> breakdownsForFragment(Integer fragment) { + List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>(); + breakdowns.add(globalInformation.getTotalCodeBreakdown()); + if (fragment == 0) { + breakdowns.add(globalInformation.getInitialCodeBreakdown()); + } + if (fragment == (globalInformation.getNumSplitPoints() + 1)) { + breakdowns.add(globalInformation.getLeftoversBreakdown()); + } + if (fragment >= 1 && fragment <= globalInformation.getNumSplitPoints()) { + breakdowns.add(globalInformation.splitPointCodeBreakdown(fragment)); + } + return breakdowns; + } + + private DependencyLinker chooseDependencyLinker(SizeBreakdown breakdown) { + if (globalInformation.dependencies == null) { + // no dependencies are available + return new NullDependencyLinker(); + } + + if (breakdown == globalInformation.getTotalCodeBreakdown()) { + if (globalInformation.getNumSplitPoints() > 0) { + return makeTopLevelHtmlForPerm.new DependencyLinkerForTotalBreakdown(); + } else { + return makeTopLevelHtmlForPerm.new DependencyLinkerForInitialCode(); + } + } else if (breakdown == globalInformation.getInitialCodeBreakdown()) { + return makeTopLevelHtmlForPerm.new DependencyLinkerForInitialCode(); + } else if (breakdown == globalInformation.getLeftoversBreakdown()) { + assert globalInformation.getNumSplitPoints() > 0; + return makeTopLevelHtmlForPerm.new DependencyLinkerForLeftoversFragment(); + } else { + return new NullDependencyLinker(); + } + } + + /** + * Generates all the HTML files for one size breakdown. + */ + private void makeHTMLFiles(MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, + SizeBreakdown breakdown, DependencyLinker depLinker) throws IOException { + makeTopLevelHtmlForPerm.makePackageClassesHtmls(breakdown, depLinker); + makeTopLevelHtmlForPerm.makeCodeTypeClassesHtmls(breakdown); + makeTopLevelHtmlForPerm.makeLiteralsClassesTableHtmls(breakdown); + makeTopLevelHtmlForPerm.makeBreakdownShell(breakdown); + makeTopLevelHtmlForPerm.makeTopLevelShell(); + } + + private DefaultHandler parseXMLDocumentSizeMap( final GlobalInformation globalInformation) { return new DefaultHandler() { int fragment = -1; @@ -393,8 +449,7 @@ } catch (NumberFormatException e) { throw new FormatException(e); } - for (SizeBreakdown breakdown : breakdownsForFragment(fragment, - globalInformation)) { + for (SizeBreakdown breakdown : breakdownsForFragment(fragment)) { breakdown.sizeAllCode += size; } } else if (localName.compareTo("size") == 0) { @@ -491,17 +546,14 @@ private void recordSize(String refType, String ref, int size, GlobalInformation globalInformation) { - for (SizeBreakdown breakdown : breakdownsForFragment(fragment, - globalInformation)) { + for (SizeBreakdown breakdown : breakdownsForFragment(fragment)) { accountForSize(breakdown, refType, ref, size, globalInformation); } } }; } - private static DefaultHandler parseXMLDocumentSplitPoints( - final GlobalInformation globalInformation) { - + private DefaultHandler parseXMLDocumentSplitPoints() { DefaultHandler handler = new DefaultHandler() { private boolean inInitialLoadSequence = false; @@ -558,12 +610,25 @@ return handler; } + private void readFromFilesNamed(String storiesFileName, + String dependenciesFileName, String splitPointsFileName) + throws ParserConfigurationException, SAXException, IOException { + if (dependenciesFileName != null && new File(dependenciesFileName).exists()) { + readDependencies(openPossiblyGzippedFile(dependenciesFileName)); + } + + if (splitPointsFileName != null && new File(splitPointsFileName).exists()) { + readSplitPoints(openPossiblyGzippedFile(splitPointsFileName)); + } + + readSizeMaps(openPossiblyGzippedFile(storiesFileName)); + } + /* * assigns code to "all other code" if none of the special categories apply */ - private static void updateAllOtherCodeType( - final HashMap<String, CodeCollection> nameToCodeColl, - GlobalInformation globalInformation) { + private void updateAllOtherCodeType( + final HashMap<String, CodeCollection> nameToCodeColl) { // all classes not in any of the other categories for (String className : globalInformation.getClassToPackage().keySet()) { if ((!nameToCodeColl.get("widget").classes.contains(className)) @@ -576,5 +641,4 @@ } } } - }
diff --git a/dev/core/src/com/google/gwt/soyc/StaticResources.java b/dev/core/src/com/google/gwt/soyc/StaticResources.java new file mode 100644 index 0000000..5e63e6c --- /dev/null +++ b/dev/core/src/com/google/gwt/soyc/StaticResources.java
@@ -0,0 +1,61 @@ +/* + * 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.soyc; + +import com.google.gwt.soyc.io.OutputDirectory; +import com.google.gwt.util.tools.Utility; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This class handles static resources such as CSS and GIF files that support + * the generated HTML. The resources are expected to be available via this + * class's class loader. + */ +public class StaticResources { + /** + * A list of all static resources. Storing it this way allows the resources to + * be loaded via a Java class loader, which is often convenient. Class loaders + * cannot be iterated over. + */ + private static String[] resourceNames = new String[] { + "classLevel.css", "common.css", "roundedCorners.css", "images/1bl.gif", + "images/1br.gif", "images/1tl.gif", "images/1tr.gif", "images/bb.gif", + "images/blc.gif", "images/brc.gif", "images/l.gif", "images/r.gif", + "images/roundedbox_lo.gif", "images/roundedbox_lu.gif", + "images/roundedbox_ro.gif", "images/roundedbox_ru.gif", "images/tb.gif", + "images/tlc.gif", "images/trc.gif",}; + + public static void emit(OutputDirectory outDir) throws IOException { + String prefix = StaticResources.class.getPackage().getName().replace('.', + '/') + + "/resources/"; + ClassLoader loader = StaticResources.class.getClassLoader(); + for (String resourceName : resourceNames) { + InputStream in = loader.getResourceAsStream(prefix + resourceName); + if (in == null) { + throw new Error("Could not find resource via my class loader: " + + resourceName); + } + OutputStream out = outDir.getOutputStream(resourceName); + Utility.streamOut(in, out, 10240); + in.close(); + out.close(); + } + } +}
diff --git a/dev/core/src/com/google/gwt/soyc/io/ArtifactsOutputDirectory.java b/dev/core/src/com/google/gwt/soyc/io/ArtifactsOutputDirectory.java new file mode 100644 index 0000000..fb774b7 --- /dev/null +++ b/dev/core/src/com/google/gwt/soyc/io/ArtifactsOutputDirectory.java
@@ -0,0 +1,87 @@ +/* + * 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.soyc.io; + +import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis.SoycArtifact; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * An {@link OutputDirectory} that writes its output as a list of GWT compiler + * artifacts. + */ +public class ArtifactsOutputDirectory implements OutputDirectory { + /** + * An in-memory output stream. When it is closed, its contents are saved to an + * artifact. + */ + private class OutputStreamForArtifact extends OutputStream { + private static final String OUTPUT_DIRECTORY_NAME = "compile-report"; + + private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private boolean closed = false; + private final String path; + + public OutputStreamForArtifact(String path) { + this.path = path; + } + + @Override + public void close() { + if (!closed) { + closed = true; + SoycArtifact newArtifact = new SoycArtifact(OUTPUT_DIRECTORY_NAME + "/" + + path, baos.toByteArray()); + newArtifact.setPrivate(false); + artifacts.add(newArtifact); + baos = null; + } + } + + @Override + public void write(byte b[]) throws IOException { + baos.write(b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + baos.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + baos.write(b); + } + } + + private List<SoycArtifact> artifacts = new ArrayList<SoycArtifact>(); + + /** + * Return the list of artifacts that have been written so far. + */ + public List<SoycArtifact> getArtifacts() { + return artifacts; + } + + public OutputStream getOutputStream(String path) throws IOException { + return new OutputStreamForArtifact(path); + } + +}
diff --git a/dev/core/src/com/google/gwt/soyc/io/FileSystemOutputDirectory.java b/dev/core/src/com/google/gwt/soyc/io/FileSystemOutputDirectory.java new file mode 100644 index 0000000..6914c00 --- /dev/null +++ b/dev/core/src/com/google/gwt/soyc/io/FileSystemOutputDirectory.java
@@ -0,0 +1,38 @@ +/* + * 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.soyc.io; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * An {@link OutputDirectory} that writes directly to the file system. + */ +public class FileSystemOutputDirectory implements OutputDirectory { + private final File outDir; + + public FileSystemOutputDirectory(File outDir) { + this.outDir = outDir; + } + + public OutputStream getOutputStream(String path) throws IOException { + File outFile = new File(outDir, path); + outFile.getParentFile().mkdirs(); + return new FileOutputStream(outFile); + } +}
diff --git a/dev/core/src/com/google/gwt/soyc/io/OutputDirectory.java b/dev/core/src/com/google/gwt/soyc/io/OutputDirectory.java new file mode 100644 index 0000000..e80777d --- /dev/null +++ b/dev/core/src/com/google/gwt/soyc/io/OutputDirectory.java
@@ -0,0 +1,28 @@ +/* + * 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.soyc.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An abstraction over output directories. The SOYC dashboard writes to this + * interface so that it can, depending on how it is invoked, both write to the + * filesystem and generate GWT compiler artifacts. + */ +public interface OutputDirectory { + OutputStream getOutputStream(String path) throws IOException; +}
diff --git a/tools/soyc-vis/classLevel.css b/dev/core/src/com/google/gwt/soyc/resources/classLevel.css similarity index 100% rename from tools/soyc-vis/classLevel.css rename to dev/core/src/com/google/gwt/soyc/resources/classLevel.css
diff --git a/tools/soyc-vis/common.css b/dev/core/src/com/google/gwt/soyc/resources/common.css similarity index 100% rename from tools/soyc-vis/common.css rename to dev/core/src/com/google/gwt/soyc/resources/common.css
diff --git a/tools/soyc-vis/images/1bl.gif b/dev/core/src/com/google/gwt/soyc/resources/images/1bl.gif similarity index 100% rename from tools/soyc-vis/images/1bl.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/1bl.gif Binary files differ
diff --git a/tools/soyc-vis/images/1br.gif b/dev/core/src/com/google/gwt/soyc/resources/images/1br.gif similarity index 100% rename from tools/soyc-vis/images/1br.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/1br.gif Binary files differ
diff --git a/tools/soyc-vis/images/1tl.gif b/dev/core/src/com/google/gwt/soyc/resources/images/1tl.gif similarity index 100% rename from tools/soyc-vis/images/1tl.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/1tl.gif Binary files differ
diff --git a/tools/soyc-vis/images/1tr.gif b/dev/core/src/com/google/gwt/soyc/resources/images/1tr.gif similarity index 100% rename from tools/soyc-vis/images/1tr.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/1tr.gif Binary files differ
diff --git a/tools/soyc-vis/images/bb.gif b/dev/core/src/com/google/gwt/soyc/resources/images/bb.gif similarity index 100% rename from tools/soyc-vis/images/bb.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/bb.gif Binary files differ
diff --git a/tools/soyc-vis/images/blc.gif b/dev/core/src/com/google/gwt/soyc/resources/images/blc.gif similarity index 100% rename from tools/soyc-vis/images/blc.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/blc.gif Binary files differ
diff --git a/tools/soyc-vis/images/brc.gif b/dev/core/src/com/google/gwt/soyc/resources/images/brc.gif similarity index 100% rename from tools/soyc-vis/images/brc.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/brc.gif Binary files differ
diff --git a/tools/soyc-vis/images/l.gif b/dev/core/src/com/google/gwt/soyc/resources/images/l.gif similarity index 100% rename from tools/soyc-vis/images/l.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/l.gif Binary files differ
diff --git a/tools/soyc-vis/images/r.gif b/dev/core/src/com/google/gwt/soyc/resources/images/r.gif similarity index 100% rename from tools/soyc-vis/images/r.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/r.gif Binary files differ
diff --git a/tools/soyc-vis/images/roundedbox_lo.gif b/dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_lo.gif similarity index 100% rename from tools/soyc-vis/images/roundedbox_lo.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_lo.gif Binary files differ
diff --git a/tools/soyc-vis/images/roundedbox_lu.gif b/dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_lu.gif similarity index 100% rename from tools/soyc-vis/images/roundedbox_lu.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_lu.gif Binary files differ
diff --git a/tools/soyc-vis/images/roundedbox_ro.gif b/dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_ro.gif similarity index 100% rename from tools/soyc-vis/images/roundedbox_ro.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_ro.gif Binary files differ
diff --git a/tools/soyc-vis/images/roundedbox_ru.gif b/dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_ru.gif similarity index 100% rename from tools/soyc-vis/images/roundedbox_ru.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/roundedbox_ru.gif Binary files differ
diff --git a/tools/soyc-vis/images/tb.gif b/dev/core/src/com/google/gwt/soyc/resources/images/tb.gif similarity index 100% rename from tools/soyc-vis/images/tb.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/tb.gif Binary files differ
diff --git a/tools/soyc-vis/images/tlc.gif b/dev/core/src/com/google/gwt/soyc/resources/images/tlc.gif similarity index 100% rename from tools/soyc-vis/images/tlc.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/tlc.gif Binary files differ
diff --git a/tools/soyc-vis/images/trc.gif b/dev/core/src/com/google/gwt/soyc/resources/images/trc.gif similarity index 100% rename from tools/soyc-vis/images/trc.gif rename to dev/core/src/com/google/gwt/soyc/resources/images/trc.gif Binary files differ
diff --git a/tools/soyc-vis/roundedCorners.css b/dev/core/src/com/google/gwt/soyc/resources/roundedCorners.css similarity index 100% rename from tools/soyc-vis/roundedCorners.css rename to dev/core/src/com/google/gwt/soyc/resources/roundedCorners.css
diff --git a/plugins/ie/oophm/oophm/oophm.vcproj b/plugins/ie/oophm/oophm/oophm.vcproj index a0cae8c..288d48c 100644 --- a/plugins/ie/oophm/oophm/oophm.vcproj +++ b/plugins/ie/oophm/oophm/oophm.vcproj
@@ -56,7 +56,7 @@ <Tool Name="VCCLCompilerTool" Optimization="0" - AdditionalIncludeDirectories="../../../common" + AdditionalIncludeDirectories=""$(ProjectDir)";../../../platform/Win;../../../common" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GWT_DEBUGLEVEL=Spam" MinimalRebuild="true" BasicRuntimeChecks="3" @@ -151,7 +151,7 @@ <Tool Name="VCCLCompilerTool" Optimization="0" - AdditionalIncludeDirectories="../../../common" + AdditionalIncludeDirectories=""$(ProjectDir)";../../../platform/Win;../../../common" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GWT_DEBUGLEVEL=Spam" MinimalRebuild="true" BasicRuntimeChecks="3" @@ -342,7 +342,7 @@ Name="VCCLCompilerTool" Optimization="2" FavorSizeOrSpeed="1" - AdditionalIncludeDirectories="../../../platform/Win;../../../common" + AdditionalIncludeDirectories=""$(ProjectDir)";../../../platform/Win;../../../common" PreprocessorDefinitions="_WINDOWS;GWT_DEBUGDISABLE" RuntimeLibrary="0" UsePrecompiledHeader="0"
diff --git a/plugins/ie/oophm/oophm/plugin.cpp b/plugins/ie/oophm/oophm/plugin.cpp index a372cdf..d48dc7d 100644 --- a/plugins/ie/oophm/oophm/plugin.cpp +++ b/plugins/ie/oophm/oophm/plugin.cpp
@@ -92,7 +92,12 @@ return S_OK; } - std::string tabKey = ""; // TODO(jat): add support for tab identity + // Use the current thread ID as a proxy for tab ID. + DWORD threadId = GetCurrentThreadId(); + char buf[20]; + snprintf(buf, sizeof(buf), "%lu", threadId); + std::string tabKey = buf; + std::string sessionKey = BSTRToUTF8(bsessionKey); std::string moduleName = BSTRToUTF8(bmoduleName); IOmNavigator* navigator;
diff --git a/plugins/ie/prebuilt/oophm.dll b/plugins/ie/prebuilt/oophm.dll old mode 100644 new mode 100755 index ca398aa..f2aac8a --- a/plugins/ie/prebuilt/oophm.dll +++ b/plugins/ie/prebuilt/oophm.dll Binary files differ
diff --git a/plugins/ie/prebuilt/oophm64.dll b/plugins/ie/prebuilt/oophm64.dll old mode 100644 new mode 100755 Binary files differ
diff --git a/tools/soyc-vis/build.xml b/tools/soyc-vis/build.xml index 51b8833..24274b4 100644 --- a/tools/soyc-vis/build.xml +++ b/tools/soyc-vis/build.xml
@@ -13,13 +13,9 @@ </target> <target name="compile"> - <mkdir dir="${javac.out}/com/google/gwt/soyc/resources/images"/> - <copy todir="${javac.out}/com/google/gwt/soyc/resources/images"> - <fileset dir="images"/> + <copy todir="${javac.out}/com/google/gwt/soyc/resources"> + <fileset dir="${gwt.root}/dev/core/src/com/google/gwt/soyc/resources"/> </copy> - <copy file="classLevel.css" tofile="${javac.out}/com/google/gwt/soyc/resources/classLevel.css"/> - <copy file="roundedCorners.css" tofile="${javac.out}/com/google/gwt/soyc/resources/roundedCorners.css"/> - <copy file="common.css" tofile="${javac.out}/com/google/gwt/soyc/resources/common.css"/> </target> <target name="build" depends="compile">
diff --git a/user/javadoc/com/google/gwt/examples/AsyncJUnitExample.java b/user/javadoc/com/google/gwt/examples/AsyncJUnitExample.java index fb73afb..275ff84 100644 --- a/user/javadoc/com/google/gwt/examples/AsyncJUnitExample.java +++ b/user/javadoc/com/google/gwt/examples/AsyncJUnitExample.java
@@ -30,6 +30,11 @@ * Tests the Timer class asynchronously. */ public void testTimer() { + + // Set a delay period significantly longer than the + // event is expected to take. + delayTestFinish(500); + // Setup an asynchronous event handler. Timer timer = new Timer() { @Override @@ -41,10 +46,6 @@ } }; - // Set a delay period significantly longer than the - // event is expected to take. - delayTestFinish(500); - // Schedule the event and return control to the test system. timer.schedule(100); }
diff --git a/user/src/com/google/gwt/junit/BatchingStrategy.java b/user/src/com/google/gwt/junit/BatchingStrategy.java index 1932854..14a5a11 100644 --- a/user/src/com/google/gwt/junit/BatchingStrategy.java +++ b/user/src/com/google/gwt/junit/BatchingStrategy.java
@@ -19,6 +19,7 @@ import com.google.gwt.junit.client.impl.JUnitHost.TestInfo; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -37,6 +38,24 @@ * @return an ordered list of test blocks to run */ public abstract List<TestInfo[]> getTestBlocks(String syntheticModuleName); + + /** + * Get the set of tests for this module, minus tests that should not be + * executed. + * + * @return the set of tests to execute + */ + protected final Set<TestInfo> getTestsForModule(String syntheticModuleName) { + Set<TestInfo> toExecute = GWTTestCase.getTestsForModule(syntheticModuleName).getTests(); + Set<TestInfo> toRemove = new HashSet<TestInfo>(); + for (TestInfo info : toExecute) { + if (JUnitShell.mustNotExecuteTest(info)) { + toRemove.add(info); + } + } + toExecute.removeAll(toRemove); + return toExecute; + } } /** @@ -46,8 +65,7 @@ class NoBatchingStrategy extends BatchingStrategy { @Override public List<TestInfo[]> getTestBlocks(String syntheticModuleName) { - Set<TestInfo> allTestsInModule = GWTTestCase.getTestsForModule( - syntheticModuleName).getTests(); + Set<TestInfo> allTestsInModule = getTestsForModule(syntheticModuleName); List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>(); for (TestInfo testInfo : allTestsInModule) { testBlocks.add(new TestInfo[] {testInfo}); @@ -62,8 +80,7 @@ class ClassBatchingStrategy extends BatchingStrategy { @Override public List<TestInfo[]> getTestBlocks(String syntheticModuleName) { - Set<TestInfo> allTestsInModule = GWTTestCase.getTestsForModule( - syntheticModuleName).getTests(); + Set<TestInfo> allTestsInModule = getTestsForModule(syntheticModuleName); List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>(); String lastTestClass = null; List<TestInfo> lastTestBlock = null; @@ -96,11 +113,12 @@ class ModuleBatchingStrategy extends BatchingStrategy { @Override public List<TestInfo[]> getTestBlocks(String syntheticModuleName) { - Set<TestInfo> allTestsInModule = GWTTestCase.getTestsForModule( - syntheticModuleName).getTests(); - TestInfo[] testBlock = allTestsInModule.toArray(new TestInfo[allTestsInModule.size()]); + Set<TestInfo> allTestsInModule = getTestsForModule(syntheticModuleName); List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>(); - testBlocks.add(testBlock); + if (allTestsInModule.size() > 0) { + TestInfo[] testBlock = allTestsInModule.toArray(new TestInfo[allTestsInModule.size()]); + testBlocks.add(testBlock); + } return testBlocks; } }
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java index 59bf1e6..6158789 100644 --- a/user/src/com/google/gwt/junit/JUnitShell.java +++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -396,19 +396,34 @@ * @return the list of remote user agents */ public static String[] getRemoteUserAgents() { - return getUnitTestShell().remoteUserAgents; + if (unitTestShell == null) { + return null; + } + return unitTestShell.remoteUserAgents; } /** * Checks if a testCase should not be executed. Currently, a test is either * executed on all clients (mentioned in this test) or on no clients. * - * @param testCase current testCase. + * @param testInfo the test info to check * @return true iff the test should not be executed on any of the specified * clients. */ - public static boolean mustNotExecuteTest(TestCase testCase) { - return getUnitTestShell().mustNotExecuteTest(getBannedPlatforms(testCase)); + public static boolean mustNotExecuteTest(TestInfo testInfo) { + if (unitTestShell == null) { + throw new IllegalStateException( + "mustNotExecuteTest cannot be called before runTest()"); + } + try { + Class<?> testClass = TestCase.class.getClassLoader().loadClass( + testInfo.getTestClass()); + return unitTestShell.mustNotExecuteTest(getBannedPlatforms(testClass, + testInfo.getTestMethod())); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Could not load test class: " + + testInfo.getTestClass()); + } } /** @@ -488,16 +503,19 @@ } /** - * returns the set of banned {@code Platform} for a test method. + * Returns the set of banned {@code Platform} for a test method. + * + * @param testClass the testClass + * @param methodName the name of the test method */ - private static Set<Platform> getBannedPlatforms(TestCase testCase) { - Class<?> testClass = testCase.getClass(); + private static Set<Platform> getBannedPlatforms(Class<?> testClass, + String methodName) { Set<Platform> bannedSet = EnumSet.noneOf(Platform.class); if (testClass.isAnnotationPresent(DoNotRunWith.class)) { bannedSet.addAll(Arrays.asList(testClass.getAnnotation(DoNotRunWith.class).value())); } try { - Method testMethod = testClass.getMethod(testCase.getName()); + Method testMethod = testClass.getMethod(methodName); if (testMethod.isAnnotationPresent(DoNotRunWith.class)) { bannedSet.addAll(Arrays.asList(testMethod.getAnnotation( DoNotRunWith.class).value())); @@ -536,6 +554,10 @@ // TODO: install a shutdown hook? Not necessary with GWTShell. unitTestShell.lastLaunchFailed = false; } + if (unitTestShell.thread != Thread.currentThread()) { + throw new IllegalThreadStateException( + "JUnitShell can only be accessed from the thread that created it."); + } return unitTestShell; } @@ -641,10 +663,16 @@ private long testMethodTimeout; /** + * The thread that created the JUnitShell. + */ + private Thread thread; + + /** * Enforce the singleton pattern. The call to {@link GWTShell}'s ctor forces * server mode and disables processing extra arguments as URLs to be shown. */ private JUnitShell() { + thread = Thread.currentThread(); setRunTomcat(true); setHeadless(true); } @@ -900,7 +928,8 @@ private void runTestImpl(GWTTestCase testCase, TestResult testResult) throws UnableToCompleteException { - if (mustNotExecuteTest(testCase)) { + if (mustNotExecuteTest(getBannedPlatforms(testCase.getClass(), + testCase.getName()))) { return; }
diff --git a/user/src/com/google/gwt/junit/client/GWTTestCase.java b/user/src/com/google/gwt/junit/client/GWTTestCase.java index 4ad6866..176c8a0 100644 --- a/user/src/com/google/gwt/junit/client/GWTTestCase.java +++ b/user/src/com/google/gwt/junit/client/GWTTestCase.java
@@ -273,11 +273,6 @@ public void setName(String name) { super.setName(name); - // If we can't run this test, don't add it to the map of all tests to batch. - if (JUnitShell.mustNotExecuteTest(this)) { - return; - } - synchronized (ALL_GWT_TESTS_LOCK) { // Once the name is set, we can add ourselves to the global set. String syntheticModuleName = getSyntheticModuleName();
diff --git a/user/src/com/google/gwt/resources/Resources.gwt.xml b/user/src/com/google/gwt/resources/Resources.gwt.xml index 1db713a..3c66d64 100644 --- a/user/src/com/google/gwt/resources/Resources.gwt.xml +++ b/user/src/com/google/gwt/resources/Resources.gwt.xml
@@ -16,6 +16,8 @@ <module> <!-- Pull in the necessary base support, including user.agent detection --> <inherits name="com.google.gwt.core.Core" /> + <!-- Pull in StyleInjector for CssResource --> + <inherits name="com.google.gwt.dom.DOM" /> <!-- Used by ExternalTextResource --> <inherits name="com.google.gwt.http.HTTP" />
diff --git a/user/src/com/google/gwt/resources/client/CssResource.java b/user/src/com/google/gwt/resources/client/CssResource.java index 908c777..5020448 100644 --- a/user/src/com/google/gwt/resources/client/CssResource.java +++ b/user/src/com/google/gwt/resources/client/CssResource.java
@@ -285,6 +285,16 @@ } /** + * Calls + * {@link com.google.gwt.dom.client.StyleInjector#injectStylesheet(String)} to + * inject the contents of the CssResource into the DOM. Repeated calls to this + * method on an instance of a CssResources will have no effect. + * + * @return <code>true</code> if this method mutated the DOM. + */ + boolean ensureInjected(); + + /** * Provides the contents of the CssResource. */ String getText();
diff --git a/user/src/com/google/gwt/resources/css/ClassRenamer.java b/user/src/com/google/gwt/resources/css/ClassRenamer.java new file mode 100644 index 0000000..a78ccbd --- /dev/null +++ b/user/src/com/google/gwt/resources/css/ClassRenamer.java
@@ -0,0 +1,271 @@ +/* + * 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.css; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.resources.client.CssResource.ClassName; +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssCompilerException; +import com.google.gwt.resources.css.ast.CssDef; +import com.google.gwt.resources.css.ast.CssSelector; +import com.google.gwt.resources.css.ast.CssStylesheet; +import com.google.gwt.resources.css.ast.CssVisitor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; + +/** + * Renames class selectors to their obfuscated names. + */ +public class ClassRenamer extends CssVisitor { + + /** + * A tag to indicate that an externally-defined CSS class has no JMethod that + * is used to access it. + */ + private static final Replacement UNREFERENCED_EXTERNAL = new Replacement( + null, null); + + /* + * TODO: Replace with Pair<A, B>. + */ + private static class Replacement { + + private JMethod method; + private String obfuscatedClassName; + + public Replacement(JMethod method, String obfuscatedClassName) { + this.method = method; + this.obfuscatedClassName = obfuscatedClassName; + } + + public JMethod getMethod() { + return method; + } + + public String getObfuscatedClassName() { + return obfuscatedClassName; + } + + /** + * For debugging use only. + */ + public String toString() { + if (this == UNREFERENCED_EXTERNAL) { + return "Unreferenced external class name"; + } else { + return method.getName() + "=" + obfuscatedClassName; + } + } + } + + /** + * Records replacements that have actually been performed. + */ + private final Map<JMethod, String> actualReplacements = new IdentityHashMap<JMethod, String>(); + private final Set<String> cssDefs = new HashSet<String>(); + + /** + * The task-list of replacements to perform in the stylesheet. + */ + private final Map<String, Replacement> potentialReplacements; + private final TreeLogger logger; + private final Set<JMethod> missingClasses; + private final boolean strict; + private final Set<String> unknownClasses = new HashSet<String>(); + + public ClassRenamer(TreeLogger logger, + Map<String, Map<JMethod, String>> classReplacementsWithPrefix, + boolean strict, Set<String> externalClasses) { + this.logger = logger.branch(TreeLogger.DEBUG, "Replacing CSS class names"); + this.strict = strict; + + potentialReplacements = computeReplacements(classReplacementsWithPrefix, + externalClasses); + + // Require a definition for all classes in the default namespace + assert classReplacementsWithPrefix.containsKey(""); + missingClasses = new HashSet<JMethod>( + classReplacementsWithPrefix.get("").keySet()); + } + + @Override + public void endVisit(CssDef x, Context ctx) { + cssDefs.add(x.getKey()); + } + + @Override + public void endVisit(CssSelector x, Context ctx) { + + String sel = x.getSelector(); + int originalLength = sel.length(); + + Matcher ma = CssSelector.CLASS_SELECTOR_PATTERN.matcher(sel); + StringBuilder sb = new StringBuilder(originalLength); + int start = 0; + + while (ma.find()) { + String sourceClassName = ma.group(1); + + Replacement entry = potentialReplacements.get(sourceClassName); + + if (entry == null) { + unknownClasses.add(sourceClassName); + continue; + + } else if (entry == UNREFERENCED_EXTERNAL) { + // An @external without an accessor method. This is OK. + continue; + } + + JMethod method = entry.getMethod(); + String obfuscatedClassName = entry.getObfuscatedClassName(); + + // Consume the interstitial portion of the original selector + sb.append(sel.subSequence(start, ma.start(1))); + sb.append(obfuscatedClassName); + start = ma.end(1); + + actualReplacements.put(method, obfuscatedClassName); + missingClasses.remove(method); + } + + if (start != 0) { + // Consume the remainder and update the selector + sb.append(sel.subSequence(start, originalLength)); + x.setSelector(sb.toString()); + } + } + + @Override + public void endVisit(CssStylesheet x, Context ctx) { + boolean stop = false; + + // Skip names corresponding to @def entries. They too can be declared as + // String accessors. + List<JMethod> toRemove = new ArrayList<JMethod>(); + for (JMethod method : missingClasses) { + if (cssDefs.contains(method.getName())) { + toRemove.add(method); + } + } + for (JMethod method : toRemove) { + missingClasses.remove(method); + } + + if (!missingClasses.isEmpty()) { + stop = true; + TreeLogger errorLogger = logger.branch(TreeLogger.INFO, + "The following obfuscated style classes were missing from " + + "the source CSS file:"); + for (JMethod m : missingClasses) { + String name = m.getName(); + ClassName className = m.getAnnotation(ClassName.class); + if (className != null) { + name = className.value(); + } + errorLogger.log(TreeLogger.ERROR, name + ": Fix by adding ." + name + + "{}"); + } + } + + if (strict && !unknownClasses.isEmpty()) { + stop = true; + TreeLogger errorLogger = logger.branch(TreeLogger.ERROR, + "The following unobfuscated classes were present in a strict CssResource:"); + for (String s : unknownClasses) { + errorLogger.log(TreeLogger.ERROR, s); + } + errorLogger.log(TreeLogger.INFO, "Fix by adding String accessor " + + "method(s) to the CssResource interface for obfuscated classes, " + + "or using an @external declaration for unobfuscated classes."); + } + + if (stop) { + throw new CssCompilerException("Missing a CSS replacement"); + } + } + + /** + * Reports the replacements that were actually performed by this visitor. + */ + public Map<JMethod, String> getReplacements() { + return actualReplacements; + } + + /** + * Flatten class name lookups to speed selector rewriting. + * + * @param classReplacementsWithPrefix a map of local prefixes to the + * obfuscated names of imported methods. If a CssResource makes use + * of the {@link CssResource.Import} annotation, the keys of this map + * will correspond to the {@link CssResource.ImportedWithPrefix} + * value defined on the imported CssResource. The zero-length string + * key holds the obfuscated names for the CssResource that is being + * generated. + * @return A flattened version of the classReplacementWithPrefix map, where + * the keys are the source class name (with prefix included), and + * values have the obfuscated class name and associated JMethod. + */ + private Map<String, Replacement> computeReplacements( + Map<String, Map<JMethod, String>> classReplacementsWithPrefix, + Set<String> externalClasses) { + + Map<String, Replacement> toReturn = new HashMap<String, Replacement>(); + + for (String externalClass : externalClasses) { + toReturn.put(externalClass, UNREFERENCED_EXTERNAL); + } + + for (Map.Entry<String, Map<JMethod, String>> outerEntry : classReplacementsWithPrefix.entrySet()) { + String prefix = outerEntry.getKey(); + + for (Map.Entry<JMethod, String> entry : outerEntry.getValue().entrySet()) { + JMethod method = entry.getKey(); + String sourceClassName = method.getName(); + String obfuscatedClassName = entry.getValue(); + + ClassName className = method.getAnnotation(ClassName.class); + if (className != null) { + sourceClassName = className.value(); + } + + sourceClassName = prefix + sourceClassName; + + if (externalClasses.contains(sourceClassName)) { + /* + * It simplifies the sanity-checking logic to treat external classes + * as though they were simply obfuscated to exactly the value the user + * wants. + */ + obfuscatedClassName = sourceClassName; + } + + toReturn.put(sourceClassName, new Replacement(method, + obfuscatedClassName)); + } + } + return Collections.unmodifiableMap(toReturn); + } +}
diff --git a/user/src/com/google/gwt/resources/css/DefsCollector.java b/user/src/com/google/gwt/resources/css/DefsCollector.java new file mode 100644 index 0000000..4cb11c8 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/DefsCollector.java
@@ -0,0 +1,40 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssDef; +import com.google.gwt.resources.css.ast.CssVisitor; + +import java.util.HashSet; +import java.util.Set; + +/** + * Collects the names of all user-defined {@literal @def} constants in the + * stylesheet. + */ +public class DefsCollector extends CssVisitor { + private final Set<String> defs = new HashSet<String>(); + + @Override + public void endVisit(CssDef x, Context ctx) { + defs.add(x.getKey()); + } + + public Set<String> getDefs() { + return defs; + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/ExternalClassesCollector.java b/user/src/com/google/gwt/resources/css/ExternalClassesCollector.java new file mode 100644 index 0000000..d6cb7bf --- /dev/null +++ b/user/src/com/google/gwt/resources/css/ExternalClassesCollector.java
@@ -0,0 +1,39 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssExternalSelectors; +import com.google.gwt.resources.css.ast.CssVisitor; + +import java.util.HashSet; +import java.util.Set; + +/** + * Collects all {@code @external} declarations in the stylesheet. + */ +public class ExternalClassesCollector extends CssVisitor { + private final Set<String> classes = new HashSet<String>(); + + @Override + public void endVisit(CssExternalSelectors x, Context ctx) { + classes.addAll(x.getClasses()); + } + + public Set<String> getClasses() { + return classes; + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/IfEvaluator.java b/user/src/com/google/gwt/resources/css/IfEvaluator.java new file mode 100644 index 0000000..5e02ad3 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/IfEvaluator.java
@@ -0,0 +1,85 @@ +/* + * 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.css; + +import com.google.gwt.core.ext.BadPropertyValueException; +import com.google.gwt.core.ext.ConfigurationProperty; +import com.google.gwt.core.ext.PropertyOracle; +import com.google.gwt.core.ext.SelectionProperty; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssCompilerException; +import com.google.gwt.resources.css.ast.CssIf; +import com.google.gwt.resources.css.ast.CssModVisitor; +import com.google.gwt.resources.css.ast.CssNode; + +import java.util.Arrays; + +/** + * Statically evaluates {@literal @if} rules. + */ +public class IfEvaluator extends CssModVisitor { + private final TreeLogger logger; + private final PropertyOracle oracle; + + public IfEvaluator(TreeLogger logger, PropertyOracle oracle) { + this.logger = logger.branch(TreeLogger.DEBUG, + "Replacing property-based @if blocks"); + this.oracle = oracle; + } + + @Override + public void endVisit(CssIf x, Context ctx) { + if (x.getExpression() != null) { + // This gets taken care of by the runtime substitution visitor + } else { + try { + String propertyName = x.getPropertyName(); + String propValue = null; + try { + SelectionProperty selProp = oracle.getSelectionProperty(logger, + propertyName); + propValue = selProp.getCurrentValue(); + } catch (BadPropertyValueException e) { + ConfigurationProperty confProp = oracle.getConfigurationProperty(propertyName); + propValue = confProp.getValues().get(0); + } + + /* + * If the deferred binding property's value is in the list of values in + * the @if rule, move the rules into the @if's context. + */ + if (Arrays.asList(x.getPropertyValues()).contains(propValue) + ^ x.isNegated()) { + for (CssNode n : x.getNodes()) { + ctx.insertBefore(n); + } + } else { + // Otherwise, move the else block into the if statement's position + for (CssNode n : x.getElseNodes()) { + ctx.insertBefore(n); + } + } + + // Always delete @if rules that we can statically evaluate + ctx.removeMe(); + } catch (BadPropertyValueException e) { + logger.log(TreeLogger.ERROR, "Unable to evaluate @if block", e); + throw new CssCompilerException("Unable to parse CSS", e); + } + } + } +}
diff --git a/user/src/com/google/gwt/resources/css/MergeIdenticalSelectorsVisitor.java b/user/src/com/google/gwt/resources/css/MergeIdenticalSelectorsVisitor.java new file mode 100644 index 0000000..08a03ed --- /dev/null +++ b/user/src/com/google/gwt/resources/css/MergeIdenticalSelectorsVisitor.java
@@ -0,0 +1,92 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssIf; +import com.google.gwt.resources.css.ast.CssMediaRule; +import com.google.gwt.resources.css.ast.CssModVisitor; +import com.google.gwt.resources.css.ast.CssNode; +import com.google.gwt.resources.css.ast.CssRule; +import com.google.gwt.resources.css.ast.CssSelector; +import com.google.gwt.resources.rg.CssResourceGenerator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Merges rules that have matching selectors. + */ +public class MergeIdenticalSelectorsVisitor extends CssModVisitor { + private final Map<String, CssRule> canonicalRules = new HashMap<String, CssRule>(); + private final List<CssRule> rulesInOrder = new ArrayList<CssRule>(); + + @Override + public boolean visit(CssIf x, Context ctx) { + visitInNewContext(x.getNodes()); + visitInNewContext(x.getElseNodes()); + return false; + } + + @Override + public boolean visit(CssMediaRule x, Context ctx) { + visitInNewContext(x.getNodes()); + return false; + } + + @Override + public boolean visit(CssRule x, Context ctx) { + // Assumed to run immediately after SplitRulesVisitor + assert x.getSelectors().size() == 1; + CssSelector sel = x.getSelectors().get(0); + + if (canonicalRules.containsKey(sel.getSelector())) { + CssRule canonical = canonicalRules.get(sel.getSelector()); + + // Check everything between the canonical rule and this rule for common + // properties. If there are common properties, it would be unsafe to + // promote the rule. + boolean hasCommon = false; + int index = rulesInOrder.indexOf(canonical) + 1; + assert index != 0; + + for (Iterator<CssRule> i = rulesInOrder.listIterator(index); i.hasNext() + && !hasCommon;) { + hasCommon = CssResourceGenerator.haveCommonProperties(i.next(), x); + } + + if (!hasCommon) { + // It's safe to promote the rule + canonical.getProperties().addAll(x.getProperties()); + ctx.removeMe(); + return false; + } + } + + canonicalRules.put(sel.getSelector(), x); + rulesInOrder.add(x); + return false; + } + + private void visitInNewContext(List<CssNode> nodes) { + MergeIdenticalSelectorsVisitor v = new MergeIdenticalSelectorsVisitor(); + v.acceptWithInsertRemove(nodes); + rulesInOrder.addAll(v.rulesInOrder); + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/MergeRulesByContentVisitor.java b/user/src/com/google/gwt/resources/css/MergeRulesByContentVisitor.java new file mode 100644 index 0000000..ea3e276 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/MergeRulesByContentVisitor.java
@@ -0,0 +1,93 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssIf; +import com.google.gwt.resources.css.ast.CssMediaRule; +import com.google.gwt.resources.css.ast.CssModVisitor; +import com.google.gwt.resources.css.ast.CssNode; +import com.google.gwt.resources.css.ast.CssProperty; +import com.google.gwt.resources.css.ast.CssRule; +import com.google.gwt.resources.rg.CssResourceGenerator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Merges rules that have identical content. + */ +public class MergeRulesByContentVisitor extends CssModVisitor { + private Map<String, CssRule> rulesByContents = new HashMap<String, CssRule>(); + private final List<CssRule> rulesInOrder = new ArrayList<CssRule>(); + + @Override + public boolean visit(CssIf x, Context ctx) { + visitInNewContext(x.getNodes()); + visitInNewContext(x.getElseNodes()); + return false; + } + + @Override + public boolean visit(CssMediaRule x, Context ctx) { + visitInNewContext(x.getNodes()); + return false; + } + + @Override + public boolean visit(CssRule x, Context ctx) { + StringBuilder b = new StringBuilder(); + for (CssProperty p : x.getProperties()) { + b.append(p.getName()).append(":").append(p.getValues().getExpression()); + } + + String content = b.toString(); + CssRule canonical = rulesByContents.get(content); + + // Check everything between the canonical rule and this rule for common + // properties. If there are common properties, it would be unsafe to + // promote the rule. + if (canonical != null) { + boolean hasCommon = false; + int index = rulesInOrder.indexOf(canonical) + 1; + assert index != 0; + + for (Iterator<CssRule> i = rulesInOrder.listIterator(index); i.hasNext() + && !hasCommon;) { + hasCommon = CssResourceGenerator.haveCommonProperties(i.next(), x); + } + + if (!hasCommon) { + canonical.getSelectors().addAll(x.getSelectors()); + ctx.removeMe(); + return false; + } + } + + rulesByContents.put(content, x); + rulesInOrder.add(x); + return false; + } + + private void visitInNewContext(List<CssNode> nodes) { + MergeRulesByContentVisitor v = new MergeRulesByContentVisitor(); + v.acceptWithInsertRemove(nodes); + rulesInOrder.addAll(v.rulesInOrder); + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/RequirementsCollector.java b/user/src/com/google/gwt/resources/css/RequirementsCollector.java new file mode 100644 index 0000000..125e348 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/RequirementsCollector.java
@@ -0,0 +1,53 @@ +/* + * 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.css; + +import com.google.gwt.core.ext.BadPropertyValueException; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssCompilerException; +import com.google.gwt.resources.css.ast.CssIf; +import com.google.gwt.resources.css.ast.CssVisitor; +import com.google.gwt.resources.ext.ClientBundleRequirements; + +/** + * Analyzes a stylesheet to update the ClientBundleRequirements interface. + */ +public class RequirementsCollector extends CssVisitor { + private final TreeLogger logger; + private final ClientBundleRequirements requirements; + + public RequirementsCollector(TreeLogger logger, + ClientBundleRequirements requirements) { + this.logger = logger.branch(TreeLogger.DEBUG, + "Scanning CSS for requirements"); + this.requirements = requirements; + } + + @Override + public void endVisit(CssIf x, Context ctx) { + String propertyName = x.getPropertyName(); + if (propertyName != null) { + try { + requirements.addPermutationAxis(propertyName); + } catch (BadPropertyValueException e) { + logger.log(TreeLogger.ERROR, "Unknown deferred-binding property " + + propertyName, e); + throw new CssCompilerException("Unknown deferred-binding property", e); + } + } + } +}
diff --git a/user/src/com/google/gwt/resources/css/RtlVisitor.java b/user/src/com/google/gwt/resources/css/RtlVisitor.java new file mode 100644 index 0000000..3994231 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/RtlVisitor.java
@@ -0,0 +1,290 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssCompilerException; +import com.google.gwt.resources.css.ast.CssModVisitor; +import com.google.gwt.resources.css.ast.CssNoFlip; +import com.google.gwt.resources.css.ast.CssProperty; +import com.google.gwt.resources.css.ast.CssRule; +import com.google.gwt.resources.css.ast.CssProperty.IdentValue; +import com.google.gwt.resources.css.ast.CssProperty.NumberValue; +import com.google.gwt.resources.css.ast.CssProperty.Value; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; + +/** + * Applies RTL transforms to a stylesheet. + */ +public class RtlVisitor extends CssModVisitor { + /** + * Records if we're currently visiting a CssRule whose only selector is + * "body". + */ + private boolean inBodyRule; + + @Override + public void endVisit(CssProperty x, Context ctx) { + String name = x.getName(); + + if (name.equalsIgnoreCase("left")) { + x.setName("right"); + } else if (name.equalsIgnoreCase("right")) { + x.setName("left"); + } else if (name.endsWith("-left")) { + int len = name.length(); + x.setName(name.substring(0, len - 4) + "right"); + } else if (name.endsWith("-right")) { + int len = name.length(); + x.setName(name.substring(0, len - 5) + "left"); + } else if (name.contains("-right-")) { + x.setName(name.replace("-right-", "-left-")); + } else if (name.contains("-left-")) { + x.setName(name.replace("-left-", "-right-")); + } else { + List<Value> values = new ArrayList<Value>(x.getValues().getValues()); + invokePropertyHandler(x.getName(), values); + x.setValue(new CssProperty.ListValue(values)); + } + } + + @Override + public boolean visit(CssNoFlip x, Context ctx) { + return false; + } + + @Override + public boolean visit(CssRule x, Context ctx) { + inBodyRule = x.getSelectors().size() == 1 + && x.getSelectors().get(0).getSelector().equals("body"); + return true; + } + + void propertyHandlerBackground(List<Value> values) { + /* + * The first numeric value will be treated as the left position only if we + * havn't seen any value that could potentially be the left value. + */ + boolean seenLeft = false; + + for (ListIterator<Value> it = values.listIterator(); it.hasNext();) { + Value v = it.next(); + Value maybeFlipped = flipLeftRightIdentValue(v); + NumberValue nv = v.isNumberValue(); + if (v != maybeFlipped) { + it.set(maybeFlipped); + seenLeft = true; + + } else if (isIdent(v, "center")) { + seenLeft = true; + + } else if (!seenLeft && (nv != null)) { + seenLeft = true; + if ("%".equals(nv.getUnits())) { + float position = 100f - nv.getValue(); + it.set(new NumberValue(position, "%")); + break; + } + } + } + } + + void propertyHandlerBackgroundPosition(List<Value> values) { + propertyHandlerBackground(values); + } + + Value propertyHandlerBackgroundPositionX(Value v) { + ArrayList<Value> list = new ArrayList<Value>(1); + list.add(v); + propertyHandlerBackground(list); + return list.get(0); + } + + /** + * Note there should be no propertyHandlerBorder(). The CSS spec states that + * the border property must set all values at once. + */ + void propertyHandlerBorderColor(List<Value> values) { + swapFour(values); + } + + void propertyHandlerBorderStyle(List<Value> values) { + swapFour(values); + } + + void propertyHandlerBorderWidth(List<Value> values) { + swapFour(values); + } + + Value propertyHandlerClear(Value v) { + return propertyHandlerFloat(v); + } + + Value propertyHandlerCursor(Value v) { + IdentValue identValue = v.isIdentValue(); + if (identValue == null) { + return v; + } + + String ident = identValue.getIdent().toLowerCase(); + if (!ident.endsWith("-resize")) { + return v; + } + + StringBuffer newIdent = new StringBuffer(); + + if (ident.length() == 9) { + if (ident.charAt(0) == 'n') { + newIdent.append('n'); + ident = ident.substring(1); + } else if (ident.charAt(0) == 's') { + newIdent.append('s'); + ident = ident.substring(1); + } else { + return v; + } + } + + if (ident.length() == 8) { + if (ident.charAt(0) == 'e') { + newIdent.append("w-resize"); + } else if (ident.charAt(0) == 'w') { + newIdent.append("e-resize"); + } else { + return v; + } + return new IdentValue(newIdent.toString()); + } else { + return v; + } + } + + Value propertyHandlerDirection(Value v) { + if (inBodyRule) { + if (isIdent(v, "ltr")) { + return new IdentValue("rtl"); + } else if (isIdent(v, "rtl")) { + return new IdentValue("ltr"); + } + } + return v; + } + + Value propertyHandlerFloat(Value v) { + return flipLeftRightIdentValue(v); + } + + void propertyHandlerMargin(List<Value> values) { + swapFour(values); + } + + void propertyHandlerPadding(List<Value> values) { + swapFour(values); + } + + Value propertyHandlerPageBreakAfter(Value v) { + return flipLeftRightIdentValue(v); + } + + Value propertyHandlerPageBreakBefore(Value v) { + return flipLeftRightIdentValue(v); + } + + Value propertyHandlerTextAlign(Value v) { + return flipLeftRightIdentValue(v); + } + + private Value flipLeftRightIdentValue(Value v) { + if (isIdent(v, "right")) { + return new IdentValue("left"); + + } else if (isIdent(v, "left")) { + return new IdentValue("right"); + } + return v; + } + + /** + * Reflectively invokes a propertyHandler method for the named property. + * Dashed names are transformed into camel-case names; only letters following + * a dash will be capitalized when looking for a method to prevent + * <code>fooBar<code> and <code>foo-bar</code> from colliding. + */ + private void invokePropertyHandler(String name, List<Value> values) { + // See if we have a property-handler function + try { + String[] parts = name.toLowerCase().split("-"); + StringBuffer methodName = new StringBuffer("propertyHandler"); + for (String part : parts) { + methodName.append(Character.toUpperCase(part.charAt(0))); + methodName.append(part, 1, part.length()); + } + + try { + // Single-arg for simplicity + Method m = getClass().getDeclaredMethod(methodName.toString(), + Value.class); + assert Value.class.isAssignableFrom(m.getReturnType()); + Value newValue = (Value) m.invoke(this, values.get(0)); + values.set(0, newValue); + } catch (NoSuchMethodException e) { + // OK + } + + try { + // Or the whole List for completeness + Method m = getClass().getDeclaredMethod(methodName.toString(), + List.class); + m.invoke(this, values); + } catch (NoSuchMethodException e) { + // OK + } + + } catch (SecurityException e) { + throw new CssCompilerException( + "Unable to invoke property handler function for " + name, e); + } catch (IllegalArgumentException e) { + throw new CssCompilerException( + "Unable to invoke property handler function for " + name, e); + } catch (IllegalAccessException e) { + throw new CssCompilerException( + "Unable to invoke property handler function for " + name, e); + } catch (InvocationTargetException e) { + throw new CssCompilerException( + "Unable to invoke property handler function for " + name, e); + } + } + + private boolean isIdent(Value value, String query) { + IdentValue v = value.isIdentValue(); + return v != null && v.getIdent().equalsIgnoreCase(query); + } + + /** + * Swaps the second and fourth values in a list of four values. + */ + private void swapFour(List<Value> values) { + if (values.size() == 4) { + Collections.swap(values, 1, 3); + } + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/SplitRulesVisitor.java b/user/src/com/google/gwt/resources/css/SplitRulesVisitor.java new file mode 100644 index 0000000..646bed7 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/SplitRulesVisitor.java
@@ -0,0 +1,45 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssModVisitor; +import com.google.gwt.resources.css.ast.CssNodeCloner; +import com.google.gwt.resources.css.ast.CssProperty; +import com.google.gwt.resources.css.ast.CssRule; +import com.google.gwt.resources.css.ast.CssSelector; + +/** + * Splits rules with compound selectors into multiple rules. + */ +public class SplitRulesVisitor extends CssModVisitor { + @Override + public void endVisit(CssRule x, Context ctx) { + if (x.getSelectors().size() == 1) { + return; + } + + for (CssSelector sel : x.getSelectors()) { + CssRule newRule = new CssRule(); + newRule.getSelectors().add(sel); + newRule.getProperties().addAll( + CssNodeCloner.clone(CssProperty.class, x.getProperties())); + ctx.insertBefore(newRule); + } + ctx.removeMe(); + return; + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/Spriter.java b/user/src/com/google/gwt/resources/css/Spriter.java new file mode 100644 index 0000000..6e48d37 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/Spriter.java
@@ -0,0 +1,150 @@ +/* + * 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.css; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.resources.client.ImageResource; +import com.google.gwt.resources.client.ImageResource.ImageOptions; +import com.google.gwt.resources.client.ImageResource.RepeatStyle; +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssCompilerException; +import com.google.gwt.resources.css.ast.CssModVisitor; +import com.google.gwt.resources.css.ast.CssProperty; +import com.google.gwt.resources.css.ast.CssRule; +import com.google.gwt.resources.css.ast.CssSprite; +import com.google.gwt.resources.css.ast.CssProperty.ExpressionValue; +import com.google.gwt.resources.css.ast.CssProperty.IdentValue; +import com.google.gwt.resources.ext.ResourceContext; + +import java.util.List; + +/** + * Replaces CssSprite nodes with CssRule nodes that will display the sprited + * image. The real trick with spriting the images is to reuse the ImageResource + * processing framework by requiring the sprite to be defined in terms of an + * ImageResource. + */ +public class Spriter extends CssModVisitor { + private final ResourceContext context; + private final TreeLogger logger; + + public Spriter(TreeLogger logger, ResourceContext context) { + this.logger = logger.branch(TreeLogger.DEBUG, + "Creating image sprite classes"); + this.context = context; + } + + @Override + public void endVisit(CssSprite x, Context ctx) { + JClassType bundleType = context.getClientBundleType(); + String functionName = x.getResourceFunction(); + + if (functionName == null) { + logger.log(TreeLogger.ERROR, "The @sprite rule " + x.getSelectors() + + " must specify the " + CssSprite.IMAGE_PROPERTY_NAME + " property"); + throw new CssCompilerException("No image property specified"); + } + + // Find the image accessor method + JMethod imageMethod = null; + JMethod[] allMethods = bundleType.getOverridableMethods(); + for (int i = 0; imageMethod == null && i < allMethods.length; i++) { + JMethod candidate = allMethods[i]; + // If the function name matches and takes no parameters + if (candidate.getName().equals(functionName) + && candidate.getParameters().length == 0) { + // We have a match + imageMethod = candidate; + } + } + + // Method unable to be located + if (imageMethod == null) { + logger.log(TreeLogger.ERROR, "Unable to find ImageResource method " + + functionName + " in " + bundleType.getQualifiedSourceName()); + throw new CssCompilerException("Cannot find image function"); + } + + JClassType imageResourceType = context.getGeneratorContext().getTypeOracle().findType( + ImageResource.class.getName()); + assert imageResourceType != null; + + if (!imageResourceType.isAssignableFrom(imageMethod.getReturnType().isClassOrInterface())) { + logger.log(TreeLogger.ERROR, "The return type of " + functionName + + " is not assignable to " + imageResourceType.getSimpleSourceName()); + throw new CssCompilerException("Incorrect return type for " + + CssSprite.IMAGE_PROPERTY_NAME + " method"); + } + + ImageOptions options = imageMethod.getAnnotation(ImageOptions.class); + RepeatStyle repeatStyle; + if (options != null) { + repeatStyle = options.repeatStyle(); + } else { + repeatStyle = RepeatStyle.None; + } + + String instance = "(" + context.getImplementationSimpleSourceName() + + ".this." + functionName + "())"; + + CssRule replacement = new CssRule(); + replacement.getSelectors().addAll(x.getSelectors()); + List<CssProperty> properties = replacement.getProperties(); + + if (repeatStyle == RepeatStyle.None + || repeatStyle == RepeatStyle.Horizontal) { + properties.add(new CssProperty("height", new ExpressionValue(instance + + ".getHeight() + \"px\""), false)); + } + + if (repeatStyle == RepeatStyle.None || repeatStyle == RepeatStyle.Vertical) { + properties.add(new CssProperty("width", new ExpressionValue(instance + + ".getWidth() + \"px\""), false)); + } + properties.add(new CssProperty("overflow", new IdentValue("hidden"), false)); + + String repeatText; + switch (repeatStyle) { + case None: + repeatText = " no-repeat"; + break; + case Horizontal: + repeatText = " repeat-x"; + break; + case Vertical: + repeatText = " repeat-y"; + break; + case Both: + repeatText = " repeat"; + break; + default: + throw new RuntimeException("Unknown repeatStyle " + repeatStyle); + } + + String backgroundExpression = "\"url(\\\"\" + " + instance + + ".getURL() + \"\\\") -\" + " + instance + ".getLeft() + \"px -\" + " + + instance + ".getTop() + \"px " + repeatText + "\""; + properties.add(new CssProperty("background", new ExpressionValue( + backgroundExpression), false)); + + // Retain any user-specified properties + properties.addAll(x.getProperties()); + + ctx.replaceMe(replacement); + } +}
diff --git a/user/src/com/google/gwt/resources/css/SubstitutionCollector.java b/user/src/com/google/gwt/resources/css/SubstitutionCollector.java new file mode 100644 index 0000000..d6cf2a7 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/SubstitutionCollector.java
@@ -0,0 +1,51 @@ +/* + * 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.css; + +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssDef; +import com.google.gwt.resources.css.ast.CssEval; +import com.google.gwt.resources.css.ast.CssUrl; +import com.google.gwt.resources.css.ast.CssVisitor; + +import java.util.HashMap; +import java.util.Map; + +/** + * Collects all user-defined constant nodes in the stylesheet. + */ +public class SubstitutionCollector extends CssVisitor { + private final Map<String, CssDef> substitutions = new HashMap<String, CssDef>(); + + @Override + public void endVisit(CssDef x, Context ctx) { + substitutions.put(x.getKey(), x); + } + + @Override + public void endVisit(CssEval x, Context ctx) { + substitutions.put(x.getKey(), x); + } + + @Override + public void endVisit(CssUrl x, Context ctx) { + substitutions.put(x.getKey(), x); + } + + public Map<String, CssDef> getSubstitutions() { + return substitutions; + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java b/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java new file mode 100644 index 0000000..9cbbcc4 --- /dev/null +++ b/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java
@@ -0,0 +1,119 @@ +/* + * 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.css; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.resources.client.DataResource; +import com.google.gwt.resources.css.ast.Context; +import com.google.gwt.resources.css.ast.CssCompilerException; +import com.google.gwt.resources.css.ast.CssDef; +import com.google.gwt.resources.css.ast.CssProperty; +import com.google.gwt.resources.css.ast.CssUrl; +import com.google.gwt.resources.css.ast.CssVisitor; +import com.google.gwt.resources.css.ast.CssProperty.ExpressionValue; +import com.google.gwt.resources.css.ast.CssProperty.IdentValue; +import com.google.gwt.resources.css.ast.CssProperty.ListValue; +import com.google.gwt.resources.css.ast.CssProperty.Value; +import com.google.gwt.resources.ext.ResourceContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +/** + * Substitute symbolic replacements into string values. + */ +public class SubstitutionReplacer extends CssVisitor { + private final ResourceContext context; + private final TreeLogger logger; + private final Map<String, CssDef> substitutions; + + public SubstitutionReplacer(TreeLogger logger, ResourceContext context, + Map<String, CssDef> substitutions) { + this.context = context; + this.logger = logger; + this.substitutions = substitutions; + } + + @Override + public void endVisit(CssProperty x, Context ctx) { + if (x.getValues() == null) { + // Nothing to do + return; + } + + List<Value> values = new ArrayList<Value>(x.getValues().getValues()); + + for (ListIterator<Value> i = values.listIterator(); i.hasNext();) { + IdentValue v = i.next().isIdentValue(); + + if (v == null) { + // Don't try to substitute into anything other than idents + continue; + } + + String value = v.getIdent(); + CssDef def = substitutions.get(value); + + if (def == null) { + continue; + } else if (def instanceof CssUrl) { + assert def.getValues().size() == 1; + assert def.getValues().get(0).isIdentValue() != null; + String functionName = def.getValues().get(0).isIdentValue().getIdent(); + + // Find the method + JMethod methods[] = context.getClientBundleType().getOverridableMethods(); + boolean foundMethod = false; + if (methods != null) { + for (JMethod method : methods) { + if (method.getName().equals(functionName)) { + foundMethod = true; + break; + } + } + } + + if (!foundMethod) { + logger.log(TreeLogger.ERROR, "Unable to find DataResource method " + + functionName + " in " + + context.getClientBundleType().getQualifiedSourceName()); + throw new CssCompilerException("Cannot find data function"); + } + + String instance = "((" + DataResource.class.getName() + ")(" + + context.getImplementationSimpleSourceName() + ".this." + + functionName + "()))"; + + StringBuilder expression = new StringBuilder(); + expression.append("\"url('\" + "); + expression.append(instance).append(".getUrl()"); + expression.append(" + \"')\""); + i.set(new ExpressionValue(expression.toString())); + + } else { + i.remove(); + for (Value defValue : def.getValues()) { + i.add(defValue); + } + } + } + + x.setValue(new ListValue(values)); + } +}
diff --git a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java index adbf29c..85846d4 100644 --- a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java +++ b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
@@ -19,7 +19,6 @@ import com.google.gwt.core.ext.ConfigurationProperty; import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.PropertyOracle; -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; @@ -31,43 +30,39 @@ import com.google.gwt.dev.util.DefaultTextOutput; import com.google.gwt.dev.util.Util; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.StyleInjector; import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.resources.client.CssResource; -import com.google.gwt.resources.client.DataResource; -import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.CssResource.ClassName; import com.google.gwt.resources.client.CssResource.Import; import com.google.gwt.resources.client.CssResource.ImportedWithPrefix; import com.google.gwt.resources.client.CssResource.NotStrict; import com.google.gwt.resources.client.CssResource.Shared; import com.google.gwt.resources.client.CssResource.Strict; -import com.google.gwt.resources.client.ImageResource.ImageOptions; -import com.google.gwt.resources.client.ImageResource.RepeatStyle; +import com.google.gwt.resources.css.ClassRenamer; import com.google.gwt.resources.css.CssGenerationVisitor; +import com.google.gwt.resources.css.DefsCollector; +import com.google.gwt.resources.css.ExternalClassesCollector; import com.google.gwt.resources.css.GenerateCssAst; +import com.google.gwt.resources.css.IfEvaluator; +import com.google.gwt.resources.css.MergeIdenticalSelectorsVisitor; +import com.google.gwt.resources.css.MergeRulesByContentVisitor; +import com.google.gwt.resources.css.RequirementsCollector; +import com.google.gwt.resources.css.RtlVisitor; +import com.google.gwt.resources.css.SplitRulesVisitor; +import com.google.gwt.resources.css.Spriter; +import com.google.gwt.resources.css.SubstitutionCollector; +import com.google.gwt.resources.css.SubstitutionReplacer; import com.google.gwt.resources.css.ast.CollapsedNode; -import com.google.gwt.resources.css.ast.Context; import com.google.gwt.resources.css.ast.CssCompilerException; import com.google.gwt.resources.css.ast.CssDef; -import com.google.gwt.resources.css.ast.CssEval; -import com.google.gwt.resources.css.ast.CssExternalSelectors; import com.google.gwt.resources.css.ast.CssIf; -import com.google.gwt.resources.css.ast.CssMediaRule; -import com.google.gwt.resources.css.ast.CssModVisitor; -import com.google.gwt.resources.css.ast.CssNoFlip; import com.google.gwt.resources.css.ast.CssNode; -import com.google.gwt.resources.css.ast.CssNodeCloner; import com.google.gwt.resources.css.ast.CssProperty; import com.google.gwt.resources.css.ast.CssRule; -import com.google.gwt.resources.css.ast.CssSelector; -import com.google.gwt.resources.css.ast.CssSprite; import com.google.gwt.resources.css.ast.CssStylesheet; -import com.google.gwt.resources.css.ast.CssUrl; -import com.google.gwt.resources.css.ast.CssVisitor; import com.google.gwt.resources.css.ast.HasNodes; import com.google.gwt.resources.css.ast.CssProperty.DotPathValue; -import com.google.gwt.resources.css.ast.CssProperty.ExpressionValue; -import com.google.gwt.resources.css.ast.CssProperty.IdentValue; import com.google.gwt.resources.css.ast.CssProperty.ListValue; import com.google.gwt.resources.css.ast.CssProperty.NumberValue; import com.google.gwt.resources.css.ast.CssProperty.Value; @@ -79,341 +74,23 @@ import com.google.gwt.user.rebind.StringSourceWriter; import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URL; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.regex.Matcher; import java.util.zip.Adler32; /** * Provides implementations of CSSResources. */ public final class CssResourceGenerator extends AbstractResourceGenerator { - static class ClassRenamer extends CssVisitor { - - /** - * A tag to indicate that an externally-defined CSS class has no JMethod - * that is used to access it. - */ - private static final Replacement UNREFERENCED_EXTERNAL = new Replacement( - null, null); - - /* - * TODO: Replace with Pair<A, B>. - */ - private static class Replacement { - - private JMethod method; - private String obfuscatedClassName; - - public Replacement(JMethod method, String obfuscatedClassName) { - this.method = method; - this.obfuscatedClassName = obfuscatedClassName; - } - - public JMethod getMethod() { - return method; - } - - public String getObfuscatedClassName() { - return obfuscatedClassName; - } - - /** - * For debugging use only. - */ - public String toString() { - if (this == UNREFERENCED_EXTERNAL) { - return "Unreferenced external class name"; - } else { - return method.getName() + "=" + obfuscatedClassName; - } - } - } - - /** - * Records replacements that have actually been performed. - */ - private final Map<JMethod, String> actualReplacements = new IdentityHashMap<JMethod, String>(); - private final Set<String> cssDefs = new HashSet<String>(); - - /** - * The task-list of replacements to perform in the stylesheet. - */ - private final Map<String, Replacement> potentialReplacements; - private final TreeLogger logger; - private final Set<JMethod> missingClasses; - private final boolean strict; - private final Set<String> unknownClasses = new HashSet<String>(); - - public ClassRenamer(TreeLogger logger, - Map<String, Map<JMethod, String>> classReplacementsWithPrefix, - boolean strict, Set<String> externalClasses) { - this.logger = logger.branch(TreeLogger.DEBUG, "Replacing CSS class names"); - this.strict = strict; - - potentialReplacements = computeReplacements(classReplacementsWithPrefix, - externalClasses); - - // Require a definition for all classes in the default namespace - assert classReplacementsWithPrefix.containsKey(""); - missingClasses = new HashSet<JMethod>( - classReplacementsWithPrefix.get("").keySet()); - } - - @Override - public void endVisit(CssDef x, Context ctx) { - cssDefs.add(x.getKey()); - } - - @Override - public void endVisit(CssSelector x, Context ctx) { - - String sel = x.getSelector(); - int originalLength = sel.length(); - - Matcher ma = CssSelector.CLASS_SELECTOR_PATTERN.matcher(sel); - StringBuilder sb = new StringBuilder(originalLength); - int start = 0; - - while (ma.find()) { - String sourceClassName = ma.group(1); - - Replacement entry = potentialReplacements.get(sourceClassName); - - if (entry == null) { - unknownClasses.add(sourceClassName); - continue; - - } else if (entry == UNREFERENCED_EXTERNAL) { - // An @external without an accessor method. This is OK. - continue; - } - - JMethod method = entry.getMethod(); - String obfuscatedClassName = entry.getObfuscatedClassName(); - - // Consume the interstitial portion of the original selector - sb.append(sel.subSequence(start, ma.start(1))); - sb.append(obfuscatedClassName); - start = ma.end(1); - - actualReplacements.put(method, obfuscatedClassName); - missingClasses.remove(method); - } - - if (start != 0) { - // Consume the remainder and update the selector - sb.append(sel.subSequence(start, originalLength)); - x.setSelector(sb.toString()); - } - } - - @Override - public void endVisit(CssStylesheet x, Context ctx) { - boolean stop = false; - - // Skip names corresponding to @def entries. They too can be declared as - // String accessors. - List<JMethod> toRemove = new ArrayList<JMethod>(); - for (JMethod method : missingClasses) { - if (cssDefs.contains(method.getName())) { - toRemove.add(method); - } - } - for (JMethod method : toRemove) { - missingClasses.remove(method); - } - - if (!missingClasses.isEmpty()) { - stop = true; - TreeLogger errorLogger = logger.branch(TreeLogger.INFO, - "The following obfuscated style classes were missing from " - + "the source CSS file:"); - for (JMethod m : missingClasses) { - String name = m.getName(); - ClassName className = m.getAnnotation(ClassName.class); - if (className != null) { - name = className.value(); - } - errorLogger.log(TreeLogger.ERROR, name + ": Fix by adding ." + name - + "{}"); - } - } - - if (strict && !unknownClasses.isEmpty()) { - stop = true; - TreeLogger errorLogger = logger.branch(TreeLogger.ERROR, - "The following unobfuscated classes were present in a strict CssResource:"); - for (String s : unknownClasses) { - errorLogger.log(TreeLogger.ERROR, s); - } - errorLogger.log(TreeLogger.INFO, "Fix by adding String accessor " - + "method(s) to the CssResource interface for obfuscated classes, " - + "or using an @external declaration for unobfuscated classes."); - } - - if (stop) { - throw new CssCompilerException("Missing a CSS replacement"); - } - } - - /** - * Reports the replacements that were actually performed by this visitor. - */ - public Map<JMethod, String> getReplacements() { - return actualReplacements; - } - - /** - * Flatten class name lookups to speed selector rewriting. - * - * @param classReplacementsWithPrefix a map of local prefixes to the - * obfuscated names of imported methods. If a CssResource makes use - * of the {@link Import} annotation, the keys of this map will - * correspond to the {@link ImportedWithPrefix} value defined on - * the imported CssResource. The zero-length string key holds the - * obfuscated names for the CssResource that is being generated. - * @return A flattened version of the classReplacementWithPrefix map, where - * the keys are the source class name (with prefix included), and - * values have the obfuscated class name and associated JMethod. - */ - private Map<String, Replacement> computeReplacements( - Map<String, Map<JMethod, String>> classReplacementsWithPrefix, - Set<String> externalClasses) { - - Map<String, Replacement> toReturn = new HashMap<String, Replacement>(); - - for (String externalClass : externalClasses) { - toReturn.put(externalClass, UNREFERENCED_EXTERNAL); - } - - for (Map.Entry<String, Map<JMethod, String>> outerEntry : classReplacementsWithPrefix.entrySet()) { - String prefix = outerEntry.getKey(); - - for (Map.Entry<JMethod, String> entry : outerEntry.getValue().entrySet()) { - JMethod method = entry.getKey(); - String sourceClassName = method.getName(); - String obfuscatedClassName = entry.getValue(); - - ClassName className = method.getAnnotation(ClassName.class); - if (className != null) { - sourceClassName = className.value(); - } - - sourceClassName = prefix + sourceClassName; - - if (externalClasses.contains(sourceClassName)) { - /* - * It simplifies the sanity-checking logic to treat external classes - * as though they were simply obfuscated to exactly the value the - * user wants. - */ - obfuscatedClassName = sourceClassName; - } - - toReturn.put(sourceClassName, new Replacement(method, - obfuscatedClassName)); - } - } - return Collections.unmodifiableMap(toReturn); - } - } - - static class DefsCollector extends CssVisitor { - private final Set<String> defs = new HashSet<String>(); - - @Override - public void endVisit(CssDef x, Context ctx) { - defs.add(x.getKey()); - } - } - - /** - * Collects all {@code @external} declarations in the stylesheet. - */ - static class ExternalClassesCollector extends CssVisitor { - private final Set<String> classes = new HashSet<String>(); - - @Override - public void endVisit(CssExternalSelectors x, Context ctx) { - classes.addAll(x.getClasses()); - } - - public Set<String> getClasses() { - return classes; - } - } - - /** - * Statically evaluates {@literal @if} rules. - */ - static class IfEvaluator extends CssModVisitor { - private final TreeLogger logger; - private final PropertyOracle oracle; - - public IfEvaluator(TreeLogger logger, PropertyOracle oracle) { - this.logger = logger.branch(TreeLogger.DEBUG, - "Replacing property-based @if blocks"); - this.oracle = oracle; - } - - @Override - public void endVisit(CssIf x, Context ctx) { - if (x.getExpression() != null) { - // This gets taken care of by the runtime substitution visitor - } else { - try { - String propertyName = x.getPropertyName(); - String propValue = null; - try { - SelectionProperty selProp = oracle.getSelectionProperty(logger, - propertyName); - propValue = selProp.getCurrentValue(); - } catch (BadPropertyValueException e) { - ConfigurationProperty confProp = oracle.getConfigurationProperty(propertyName); - propValue = confProp.getValues().get(0); - } - - /* - * If the deferred binding property's value is in the list of values - * in the @if rule, move the rules into the @if's context. - */ - if (Arrays.asList(x.getPropertyValues()).contains(propValue) - ^ x.isNegated()) { - for (CssNode n : x.getNodes()) { - ctx.insertBefore(n); - } - } else { - // Otherwise, move the else block into the if statement's position - for (CssNode n : x.getElseNodes()) { - ctx.insertBefore(n); - } - } - - // Always delete @if rules that we can statically evaluate - ctx.removeMe(); - } catch (BadPropertyValueException e) { - logger.log(TreeLogger.ERROR, "Unable to evaluate @if block", e); - throw new CssCompilerException("Unable to parse CSS", e); - } - } - } - } @SuppressWarnings("serial") static class JClassOrderComparator implements Comparator<JClassType>, @@ -424,654 +101,6 @@ } /** - * Merges rules that have matching selectors. - */ - static class MergeIdenticalSelectorsVisitor extends CssModVisitor { - private final Map<String, CssRule> canonicalRules = new HashMap<String, CssRule>(); - private final List<CssRule> rulesInOrder = new ArrayList<CssRule>(); - - @Override - public boolean visit(CssIf x, Context ctx) { - visitInNewContext(x.getNodes()); - visitInNewContext(x.getElseNodes()); - return false; - } - - @Override - public boolean visit(CssMediaRule x, Context ctx) { - visitInNewContext(x.getNodes()); - return false; - } - - @Override - public boolean visit(CssRule x, Context ctx) { - // Assumed to run immediately after SplitRulesVisitor - assert x.getSelectors().size() == 1; - CssSelector sel = x.getSelectors().get(0); - - if (canonicalRules.containsKey(sel.getSelector())) { - CssRule canonical = canonicalRules.get(sel.getSelector()); - - // Check everything between the canonical rule and this rule for common - // properties. If there are common properties, it would be unsafe to - // promote the rule. - boolean hasCommon = false; - int index = rulesInOrder.indexOf(canonical) + 1; - assert index != 0; - - for (Iterator<CssRule> i = rulesInOrder.listIterator(index); i.hasNext() - && !hasCommon;) { - hasCommon = haveCommonProperties(i.next(), x); - } - - if (!hasCommon) { - // It's safe to promote the rule - canonical.getProperties().addAll(x.getProperties()); - ctx.removeMe(); - return false; - } - } - - canonicalRules.put(sel.getSelector(), x); - rulesInOrder.add(x); - return false; - } - - private void visitInNewContext(List<CssNode> nodes) { - MergeIdenticalSelectorsVisitor v = new MergeIdenticalSelectorsVisitor(); - v.acceptWithInsertRemove(nodes); - rulesInOrder.addAll(v.rulesInOrder); - } - } - - /** - * Merges rules that have identical content. - */ - static class MergeRulesByContentVisitor extends CssModVisitor { - private Map<String, CssRule> rulesByContents = new HashMap<String, CssRule>(); - private final List<CssRule> rulesInOrder = new ArrayList<CssRule>(); - - @Override - public boolean visit(CssIf x, Context ctx) { - visitInNewContext(x.getNodes()); - visitInNewContext(x.getElseNodes()); - return false; - } - - @Override - public boolean visit(CssMediaRule x, Context ctx) { - visitInNewContext(x.getNodes()); - return false; - } - - @Override - public boolean visit(CssRule x, Context ctx) { - StringBuilder b = new StringBuilder(); - for (CssProperty p : x.getProperties()) { - b.append(p.getName()).append(":").append(p.getValues().getExpression()); - } - - String content = b.toString(); - CssRule canonical = rulesByContents.get(content); - - // Check everything between the canonical rule and this rule for common - // properties. If there are common properties, it would be unsafe to - // promote the rule. - if (canonical != null) { - boolean hasCommon = false; - int index = rulesInOrder.indexOf(canonical) + 1; - assert index != 0; - - for (Iterator<CssRule> i = rulesInOrder.listIterator(index); i.hasNext() - && !hasCommon;) { - hasCommon = haveCommonProperties(i.next(), x); - } - - if (!hasCommon) { - canonical.getSelectors().addAll(x.getSelectors()); - ctx.removeMe(); - return false; - } - } - - rulesByContents.put(content, x); - rulesInOrder.add(x); - return false; - } - - private void visitInNewContext(List<CssNode> nodes) { - MergeRulesByContentVisitor v = new MergeRulesByContentVisitor(); - v.acceptWithInsertRemove(nodes); - rulesInOrder.addAll(v.rulesInOrder); - } - } - - static class RequirementsCollector extends CssVisitor { - private final TreeLogger logger; - private final ClientBundleRequirements requirements; - - public RequirementsCollector(TreeLogger logger, - ClientBundleRequirements requirements) { - this.logger = logger.branch(TreeLogger.DEBUG, - "Scanning CSS for requirements"); - this.requirements = requirements; - } - - @Override - public void endVisit(CssIf x, Context ctx) { - String propertyName = x.getPropertyName(); - if (propertyName != null) { - try { - requirements.addPermutationAxis(propertyName); - } catch (BadPropertyValueException e) { - logger.log(TreeLogger.ERROR, "Unknown deferred-binding property " - + propertyName, e); - throw new CssCompilerException("Unknown deferred-binding property", e); - } - } - } - } - - static class RtlVisitor extends CssModVisitor { - /** - * Records if we're currently visiting a CssRule whose only selector is - * "body". - */ - private boolean inBodyRule; - - @Override - public void endVisit(CssProperty x, Context ctx) { - String name = x.getName(); - - if (name.equalsIgnoreCase("left")) { - x.setName("right"); - } else if (name.equalsIgnoreCase("right")) { - x.setName("left"); - } else if (name.endsWith("-left")) { - int len = name.length(); - x.setName(name.substring(0, len - 4) + "right"); - } else if (name.endsWith("-right")) { - int len = name.length(); - x.setName(name.substring(0, len - 5) + "left"); - } else if (name.contains("-right-")) { - x.setName(name.replace("-right-", "-left-")); - } else if (name.contains("-left-")) { - x.setName(name.replace("-left-", "-right-")); - } else { - List<Value> values = new ArrayList<Value>(x.getValues().getValues()); - invokePropertyHandler(x.getName(), values); - x.setValue(new CssProperty.ListValue(values)); - } - } - - @Override - public boolean visit(CssNoFlip x, Context ctx) { - return false; - } - - @Override - public boolean visit(CssRule x, Context ctx) { - inBodyRule = x.getSelectors().size() == 1 - && x.getSelectors().get(0).getSelector().equals("body"); - return true; - } - - void propertyHandlerBackground(List<Value> values) { - /* - * The first numeric value will be treated as the left position only if we - * havn't seen any value that could potentially be the left value. - */ - boolean seenLeft = false; - - for (ListIterator<Value> it = values.listIterator(); it.hasNext();) { - Value v = it.next(); - Value maybeFlipped = flipLeftRightIdentValue(v); - NumberValue nv = v.isNumberValue(); - if (v != maybeFlipped) { - it.set(maybeFlipped); - seenLeft = true; - - } else if (isIdent(v, "center")) { - seenLeft = true; - - } else if (!seenLeft && (nv != null)) { - seenLeft = true; - if ("%".equals(nv.getUnits())) { - float position = 100f - nv.getValue(); - it.set(new NumberValue(position, "%")); - break; - } - } - } - } - - void propertyHandlerBackgroundPosition(List<Value> values) { - propertyHandlerBackground(values); - } - - Value propertyHandlerBackgroundPositionX(Value v) { - ArrayList<Value> list = new ArrayList<Value>(1); - list.add(v); - propertyHandlerBackground(list); - return list.get(0); - } - - /** - * Note there should be no propertyHandlerBorder(). The CSS spec states that - * the border property must set all values at once. - */ - void propertyHandlerBorderColor(List<Value> values) { - swapFour(values); - } - - void propertyHandlerBorderStyle(List<Value> values) { - swapFour(values); - } - - void propertyHandlerBorderWidth(List<Value> values) { - swapFour(values); - } - - Value propertyHandlerClear(Value v) { - return propertyHandlerFloat(v); - } - - Value propertyHandlerCursor(Value v) { - IdentValue identValue = v.isIdentValue(); - if (identValue == null) { - return v; - } - - String ident = identValue.getIdent().toLowerCase(); - if (!ident.endsWith("-resize")) { - return v; - } - - StringBuffer newIdent = new StringBuffer(); - - if (ident.length() == 9) { - if (ident.charAt(0) == 'n') { - newIdent.append('n'); - ident = ident.substring(1); - } else if (ident.charAt(0) == 's') { - newIdent.append('s'); - ident = ident.substring(1); - } else { - return v; - } - } - - if (ident.length() == 8) { - if (ident.charAt(0) == 'e') { - newIdent.append("w-resize"); - } else if (ident.charAt(0) == 'w') { - newIdent.append("e-resize"); - } else { - return v; - } - return new IdentValue(newIdent.toString()); - } else { - return v; - } - } - - Value propertyHandlerDirection(Value v) { - if (inBodyRule) { - if (isIdent(v, "ltr")) { - return new IdentValue("rtl"); - } else if (isIdent(v, "rtl")) { - return new IdentValue("ltr"); - } - } - return v; - } - - Value propertyHandlerFloat(Value v) { - return flipLeftRightIdentValue(v); - } - - void propertyHandlerMargin(List<Value> values) { - swapFour(values); - } - - void propertyHandlerPadding(List<Value> values) { - swapFour(values); - } - - Value propertyHandlerPageBreakAfter(Value v) { - return flipLeftRightIdentValue(v); - } - - Value propertyHandlerPageBreakBefore(Value v) { - return flipLeftRightIdentValue(v); - } - - Value propertyHandlerTextAlign(Value v) { - return flipLeftRightIdentValue(v); - } - - private Value flipLeftRightIdentValue(Value v) { - if (isIdent(v, "right")) { - return new IdentValue("left"); - - } else if (isIdent(v, "left")) { - return new IdentValue("right"); - } - return v; - } - - /** - * Reflectively invokes a propertyHandler method for the named property. - * Dashed names are transformed into camel-case names; only letters - * following a dash will be capitalized when looking for a method to prevent - * <code>fooBar<code> and <code>foo-bar</code> from colliding. - */ - private void invokePropertyHandler(String name, List<Value> values) { - // See if we have a property-handler function - try { - String[] parts = name.toLowerCase().split("-"); - StringBuffer methodName = new StringBuffer("propertyHandler"); - for (String part : parts) { - methodName.append(Character.toUpperCase(part.charAt(0))); - methodName.append(part, 1, part.length()); - } - - try { - // Single-arg for simplicity - Method m = getClass().getDeclaredMethod(methodName.toString(), - Value.class); - assert Value.class.isAssignableFrom(m.getReturnType()); - Value newValue = (Value) m.invoke(this, values.get(0)); - values.set(0, newValue); - } catch (NoSuchMethodException e) { - // OK - } - - try { - // Or the whole List for completeness - Method m = getClass().getDeclaredMethod(methodName.toString(), - List.class); - m.invoke(this, values); - } catch (NoSuchMethodException e) { - // OK - } - - } catch (SecurityException e) { - throw new CssCompilerException( - "Unable to invoke property handler function for " + name, e); - } catch (IllegalArgumentException e) { - throw new CssCompilerException( - "Unable to invoke property handler function for " + name, e); - } catch (IllegalAccessException e) { - throw new CssCompilerException( - "Unable to invoke property handler function for " + name, e); - } catch (InvocationTargetException e) { - throw new CssCompilerException( - "Unable to invoke property handler function for " + name, e); - } - } - - private boolean isIdent(Value value, String query) { - IdentValue v = value.isIdentValue(); - return v != null && v.getIdent().equalsIgnoreCase(query); - } - - /** - * Swaps the second and fourth values in a list of four values. - */ - private void swapFour(List<Value> values) { - if (values.size() == 4) { - Collections.swap(values, 1, 3); - } - } - } - - /** - * Splits rules with compound selectors into multiple rules. - */ - static class SplitRulesVisitor extends CssModVisitor { - @Override - public void endVisit(CssRule x, Context ctx) { - if (x.getSelectors().size() == 1) { - return; - } - - for (CssSelector sel : x.getSelectors()) { - CssRule newRule = new CssRule(); - newRule.getSelectors().add(sel); - newRule.getProperties().addAll( - CssNodeCloner.clone(CssProperty.class, x.getProperties())); - ctx.insertBefore(newRule); - } - ctx.removeMe(); - return; - } - } - - /** - * Replaces CssSprite nodes with CssRule nodes that will display the sprited - * image. The real trick with spriting the images is to reuse the - * ImageResource processing framework by requiring the sprite to be defined in - * terms of an ImageResource. - */ - static class Spriter extends CssModVisitor { - private final ResourceContext context; - private final TreeLogger logger; - - public Spriter(TreeLogger logger, ResourceContext context) { - this.logger = logger.branch(TreeLogger.DEBUG, - "Creating image sprite classes"); - this.context = context; - } - - @Override - public void endVisit(CssSprite x, Context ctx) { - JClassType bundleType = context.getClientBundleType(); - String functionName = x.getResourceFunction(); - - if (functionName == null) { - logger.log(TreeLogger.ERROR, "The @sprite rule " + x.getSelectors() - + " must specify the " + CssSprite.IMAGE_PROPERTY_NAME - + " property"); - throw new CssCompilerException("No image property specified"); - } - - // Find the image accessor method - JMethod imageMethod = null; - JMethod[] allMethods = bundleType.getOverridableMethods(); - for (int i = 0; imageMethod == null && i < allMethods.length; i++) { - JMethod candidate = allMethods[i]; - // If the function name matches and takes no parameters - if (candidate.getName().equals(functionName) - && candidate.getParameters().length == 0) { - // We have a match - imageMethod = candidate; - } - } - - // Method unable to be located - if (imageMethod == null) { - logger.log(TreeLogger.ERROR, "Unable to find ImageResource method " - + functionName + " in " + bundleType.getQualifiedSourceName()); - throw new CssCompilerException("Cannot find image function"); - } - - JClassType imageResourceType = context.getGeneratorContext().getTypeOracle().findType( - ImageResource.class.getName()); - assert imageResourceType != null; - - if (!imageResourceType.isAssignableFrom(imageMethod.getReturnType().isClassOrInterface())) { - logger.log(TreeLogger.ERROR, "The return type of " + functionName - + " is not assignable to " - + imageResourceType.getSimpleSourceName()); - throw new CssCompilerException("Incorrect return type for " - + CssSprite.IMAGE_PROPERTY_NAME + " method"); - } - - ImageOptions options = imageMethod.getAnnotation(ImageOptions.class); - RepeatStyle repeatStyle; - if (options != null) { - repeatStyle = options.repeatStyle(); - } else { - repeatStyle = RepeatStyle.None; - } - - String instance = "(" + context.getImplementationSimpleSourceName() - + ".this." + functionName + "())"; - - CssRule replacement = new CssRule(); - replacement.getSelectors().addAll(x.getSelectors()); - List<CssProperty> properties = replacement.getProperties(); - - if (repeatStyle == RepeatStyle.None - || repeatStyle == RepeatStyle.Horizontal) { - properties.add(new CssProperty("height", new ExpressionValue(instance - + ".getHeight() + \"px\""), false)); - } - - if (repeatStyle == RepeatStyle.None - || repeatStyle == RepeatStyle.Vertical) { - properties.add(new CssProperty("width", new ExpressionValue(instance - + ".getWidth() + \"px\""), false)); - } - properties.add(new CssProperty("overflow", new IdentValue("hidden"), - false)); - - String repeatText; - switch (repeatStyle) { - case None: - repeatText = " no-repeat"; - break; - case Horizontal: - repeatText = " repeat-x"; - break; - case Vertical: - repeatText = " repeat-y"; - break; - case Both: - repeatText = " repeat"; - break; - default: - throw new RuntimeException("Unknown repeatStyle " + repeatStyle); - } - - String backgroundExpression = "\"url(\\\"\" + " + instance - + ".getURL() + \"\\\") -\" + " + instance - + ".getLeft() + \"px -\" + " + instance + ".getTop() + \"px " - + repeatText + "\""; - properties.add(new CssProperty("background", new ExpressionValue( - backgroundExpression), false)); - - // Retain any user-specified properties - properties.addAll(x.getProperties()); - - ctx.replaceMe(replacement); - } - } - - static class SubstitutionCollector extends CssVisitor { - private final Map<String, CssDef> substitutions = new HashMap<String, CssDef>(); - - @Override - public void endVisit(CssDef x, Context ctx) { - substitutions.put(x.getKey(), x); - } - - @Override - public void endVisit(CssEval x, Context ctx) { - substitutions.put(x.getKey(), x); - } - - @Override - public void endVisit(CssUrl x, Context ctx) { - substitutions.put(x.getKey(), x); - } - } - - /** - * Substitute symbolic replacements into string values. - */ - static class SubstitutionReplacer extends CssVisitor { - private final ResourceContext context; - private final TreeLogger logger; - private final Map<String, CssDef> substitutions; - - public SubstitutionReplacer(TreeLogger logger, ResourceContext context, - Map<String, CssDef> substitutions) { - this.context = context; - this.logger = logger; - this.substitutions = substitutions; - } - - @Override - public void endVisit(CssProperty x, Context ctx) { - if (x.getValues() == null) { - // Nothing to do - return; - } - - List<Value> values = new ArrayList<Value>(x.getValues().getValues()); - - for (ListIterator<Value> i = values.listIterator(); i.hasNext();) { - IdentValue v = i.next().isIdentValue(); - - if (v == null) { - // Don't try to substitute into anything other than idents - continue; - } - - String value = v.getIdent(); - CssDef def = substitutions.get(value); - - if (def == null) { - continue; - } else if (def instanceof CssUrl) { - assert def.getValues().size() == 1; - assert def.getValues().get(0).isIdentValue() != null; - String functionName = def.getValues().get(0).isIdentValue().getIdent(); - - // Find the method - JMethod methods[] = context.getClientBundleType().getOverridableMethods(); - boolean foundMethod = false; - if (methods != null) { - for (JMethod method : methods) { - if (method.getName().equals(functionName)) { - foundMethod = true; - break; - } - } - } - - if (!foundMethod) { - logger.log(TreeLogger.ERROR, "Unable to find DataResource method " - + functionName + " in " - + context.getClientBundleType().getQualifiedSourceName()); - throw new CssCompilerException("Cannot find data function"); - } - - String instance = "((" + DataResource.class.getName() + ")(" - + context.getImplementationSimpleSourceName() + ".this." - + functionName + "()))"; - - StringBuilder expression = new StringBuilder(); - expression.append("\"url('\" + "); - expression.append(instance).append(".getUrl()"); - expression.append(" + \"')\""); - i.set(new ExpressionValue(expression.toString())); - - } else { - i.remove(); - for (Value defValue : def.getValues()) { - i.add(defValue); - } - } - } - - x.setValue(new ListValue(values)); - } - } - - /** * A lookup table of base-32 chars we use to encode CSS idents. Because CSS * class selectors may be case-insensitive, we don't have enough characters to * use a base-64 encoding. @@ -1096,13 +125,7 @@ private static final String KEY_CLASS_PREFIX = "prefix"; private static final String KEY_CLASS_COUNTER = "counter"; - public static void main(String[] args) { - for (int i = 0; i < 1000; i++) { - System.out.println(makeIdent(i)); - } - } - - static boolean haveCommonProperties(CssRule a, CssRule b) { + public static boolean haveCommonProperties(CssRule a, CssRule b) { if (a.getProperties().size() == 0 || b.getProperties().size() == 0) { return false; } @@ -1149,6 +172,12 @@ return false; } + public static void main(String[] args) { + for (int i = 0; i < 1000; i++) { + System.out.println(makeIdent(i)); + } + } + /** * Create a Java expression that evaluates to a string representation of the * given node. Visible only for testing. @@ -1377,6 +406,10 @@ } } + // Methods defined by CssResource interface + writeEnsureInjected(sw); + writeGetName(method, sw); + sw.println("public String getText() {"); sw.indent(); boolean strict = isStrict(logger, context, method); @@ -1388,12 +421,6 @@ sw.outdent(); sw.println("}"); - sw.println("public String getName() {"); - sw.indent(); - sw.println("return \"" + method.getName() + "\";"); - sw.outdent(); - sw.println("}"); - /* * getOverridableMethods is used to handle CssResources extending * non-CssResource types. See the discussion in computeReplacementsForType. @@ -1763,7 +790,7 @@ SubstitutionCollector collector = new SubstitutionCollector(); collector.accept(sheet); - (new SubstitutionReplacer(logger, context, collector.substitutions)).accept(sheet); + (new SubstitutionReplacer(logger, context, collector.getSubstitutions())).accept(sheet); // Evaluate @if statements based on deferred binding properties (new IfEvaluator(logger, @@ -1834,7 +861,7 @@ String name = toImplement.getName(); // TODO: Annotation for override - CssDef def = collector.substitutions.get(name); + CssDef def = collector.getSubstitutions().get(name); if (def == null) { logger.log(TreeLogger.ERROR, "No @def rule for name " + name); throw new UnableToCompleteException(); @@ -1884,6 +911,28 @@ sw.println("}"); } + private void writeEnsureInjected(SourceWriter sw) { + sw.println("private boolean injected;"); + sw.println("public boolean ensureInjected() {"); + sw.indent(); + sw.println("if (!injected) {"); + sw.indentln("injected = true;"); + sw.indentln(StyleInjector.class.getName() + ".injectStylesheet(getText());"); + sw.indentln("return true;"); + sw.println("}"); + sw.println("return false;"); + sw.outdent(); + sw.println("}"); + } + + private void writeGetName(JMethod method, SourceWriter sw) { + sw.println("public String getName() {"); + sw.indent(); + sw.println("return \"" + method.getName() + "\";"); + sw.outdent(); + sw.println("}"); + } + /** * Write all of the user-defined methods in the CssResource subtype. */ @@ -1895,22 +944,23 @@ // Get list of @defs DefsCollector collector = new DefsCollector(); collector.accept(sheet); + Set<String> defs = collector.getDefs(); for (JMethod toImplement : methods) { String name = toImplement.getName(); - if ("getName".equals(name) || "getText".equals(name)) { + if ("getName".equals(name) || "getText".equals(name) + || "ensureInjected".equals(name)) { continue; } // Bomb out if there is a collision between @def and a style name - if (collector.defs.contains(name) - && obfuscatedClassNames.containsKey(toImplement)) { + if (defs.contains(name) && obfuscatedClassNames.containsKey(toImplement)) { logger.log(TreeLogger.ERROR, "@def shadows CSS class name: " + name + ". Fix by renaming the @def name or the CSS class name."); throw new UnableToCompleteException(); } - if (collector.defs.contains(toImplement.getName()) + if (defs.contains(toImplement.getName()) && toImplement.getParameters().length == 0) { writeDefAssignment(logger, sw, toImplement, sheet); } else if (toImplement.getReturnType().equals(stringType)
diff --git a/user/test/com/google/gwt/animation/client/AnimationTest.java b/user/test/com/google/gwt/animation/client/AnimationTest.java index 1ed09cf..1249dfc 100644 --- a/user/test/com/google/gwt/animation/client/AnimationTest.java +++ b/user/test/com/google/gwt/animation/client/AnimationTest.java
@@ -141,6 +141,7 @@ public void testCancelBeforeStarted() { final TestAnimation anim = new TestAnimation(); double curTime = Duration.currentTimeMillis(); + delayTestFinish(20 * DELAY_MULTIPLIER); anim.run(10 * DELAY_MULTIPLIER, curTime + 10 * DELAY_MULTIPLIER); // Check progress @@ -168,9 +169,6 @@ finishTest(); } }.schedule(15 * DELAY_MULTIPLIER); - - // Wait for test to finish - delayTestFinish(20 * DELAY_MULTIPLIER); } /** @@ -178,6 +176,7 @@ */ public void testCancelWhenComplete() { final TestAnimation anim = new TestAnimation(); + delayTestFinish(25 * DELAY_MULTIPLIER); anim.run(10 * DELAY_MULTIPLIER); // Check progress @@ -204,9 +203,6 @@ finishTest(); } }.schedule(20 * DELAY_MULTIPLIER); - - // Wait for test to finish - delayTestFinish(25 * DELAY_MULTIPLIER); } /** @@ -214,6 +210,7 @@ */ public void testCancelWhileRunning() { final TestAnimation anim = new TestAnimation(); + delayTestFinish(20 * DELAY_MULTIPLIER); anim.run(50 * DELAY_MULTIPLIER); // Check progress @@ -239,9 +236,6 @@ finishTest(); } }.schedule(15 * DELAY_MULTIPLIER); - - // Wait for test to finish - delayTestFinish(20 * DELAY_MULTIPLIER); } /** @@ -328,6 +322,7 @@ final TestAnimation animPast = new TestAnimation(); final TestAnimation animFuture = new TestAnimation(); + delayTestFinish(50 * DELAY_MULTIPLIER); // Run animations double curTime = Duration.currentTimeMillis(); animNow.run(30 * DELAY_MULTIPLIER); @@ -416,8 +411,6 @@ animFuture.assertStarted(true); animFuture.assertCompleted(false); animFuture.assertProgressRange(0.0, 1.0); - - finishTest(); } }.schedule(35 * DELAY_MULTIPLIER); @@ -440,8 +433,5 @@ finishTest(); } }.schedule(45 * DELAY_MULTIPLIER); - - // Wait for the test to finish - delayTestFinish(50 * DELAY_MULTIPLIER); } }
diff --git a/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java b/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java index 26357df..05e80d0 100644 --- a/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java +++ b/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java
@@ -46,7 +46,7 @@ } // Repeated runAsync using a Timer - public void runAsync1(final int attempt) { + private void runAsync1(final int attempt) { log("runAsync1: attempt = " + attempt); GWT.runAsync(new MyRunAsyncCallback() { public void onFailure(Throwable caught) {
diff --git a/user/test/com/google/gwt/junit/BatchingStrategyTest.java b/user/test/com/google/gwt/junit/BatchingStrategyTest.java index bd86c18..8a44b09 100644 --- a/user/test/com/google/gwt/junit/BatchingStrategyTest.java +++ b/user/test/com/google/gwt/junit/BatchingStrategyTest.java
@@ -26,10 +26,35 @@ import java.util.Set; /** - * Tests of {@link BatchingStrategy}. + * Tests of {@link BatchingStrategy}. This test must run after a + * {@link GWTTestCase} to ensure that JUnitShell is already initialized. */ public class BatchingStrategyTest extends TestCase { + private static class TestClass0 { + public void testMethod0() { + } + + public void testMethod1() { + } + } + + private static class TestClass1 { + public void testMethod2() { + } + + public void testMethod3() { + } + + public void testMethod4() { + } + } + + private static class TestClass2 { + public void testMethod5() { + } + } + /** * The synthetic name of the module used for this test. */ @@ -41,17 +66,17 @@ private static TestModuleInfo fakeModuleInfo; private static final TestInfo TEST_INFO_0_0 = new TestInfo( - FAKE_MODULE_SYNTHETIC_NAME, "testClass0", "testMethod0"); + FAKE_MODULE_SYNTHETIC_NAME, TestClass0.class.getName(), "testMethod0"); private static final TestInfo TEST_INFO_0_1 = new TestInfo( - FAKE_MODULE_SYNTHETIC_NAME, "testClass0", "testMethod1"); + FAKE_MODULE_SYNTHETIC_NAME, TestClass0.class.getName(), "testMethod1"); private static final TestInfo TEST_INFO_1_2 = new TestInfo( - FAKE_MODULE_SYNTHETIC_NAME, "testClass1", "testMethod2"); + FAKE_MODULE_SYNTHETIC_NAME, TestClass1.class.getName(), "testMethod2"); private static final TestInfo TEST_INFO_1_3 = new TestInfo( - FAKE_MODULE_SYNTHETIC_NAME, "testClass1", "testMethod3"); + FAKE_MODULE_SYNTHETIC_NAME, TestClass1.class.getName(), "testMethod3"); private static final TestInfo TEST_INFO_1_4 = new TestInfo( - FAKE_MODULE_SYNTHETIC_NAME, "testClass1", "testMethod4"); + FAKE_MODULE_SYNTHETIC_NAME, TestClass1.class.getName(), "testMethod4"); private static final TestInfo TEST_INFO_2_5 = new TestInfo( - FAKE_MODULE_SYNTHETIC_NAME, "testClass2", "testMethod5"); + FAKE_MODULE_SYNTHETIC_NAME, TestClass2.class.getName(), "testMethod5"); public void testClassBatchingStrategy() { List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>();
diff --git a/user/test/com/google/gwt/junit/JUnitSuite.java b/user/test/com/google/gwt/junit/JUnitSuite.java index ec1c25b..40f468e 100644 --- a/user/test/com/google/gwt/junit/JUnitSuite.java +++ b/user/test/com/google/gwt/junit/JUnitSuite.java
@@ -27,17 +27,19 @@ public static Test suite() { GWTTestSuite suite = new GWTTestSuite("Test for suite for com.google.gwt.junit"); - suite.addTestSuite(FakeMessagesMakerTest.class); - suite.addTestSuite(BatchingStrategyTest.class); - suite.addTestSuite(JUnitMessageQueueTest.class); - suite.addTestSuite(GWTTestCaseNoClientTest.class); - suite.addTestSuite(BenchmarkNoClientTest.class); - // client // Suppressed due to flakiness on Linux // suite.addTestSuite(BenchmarkTest.class); suite.addTestSuite(GWTTestCaseTest.class); + // Must run after a GWTTestCase so JUnitShell is initialized. + suite.addTestSuite(BatchingStrategyTest.class); + + suite.addTestSuite(FakeMessagesMakerTest.class); + suite.addTestSuite(JUnitMessageQueueTest.class); + suite.addTestSuite(GWTTestCaseNoClientTest.class); + suite.addTestSuite(BenchmarkNoClientTest.class); + // These two are intended only to be run manually. See class comments // suite.addTestSuite(ParallelRemoteTest.class); // suite.addTestSuite(TestManualAsync.class);
diff --git a/user/test/com/google/gwt/resources/ResourcesSuite.java b/user/test/com/google/gwt/resources/ResourcesSuite.java index 7826df2..98538b9 100644 --- a/user/test/com/google/gwt/resources/ResourcesSuite.java +++ b/user/test/com/google/gwt/resources/ResourcesSuite.java
@@ -21,10 +21,10 @@ import com.google.gwt.resources.client.ImageResourceTest; import com.google.gwt.resources.client.NestedBundleTest; import com.google.gwt.resources.client.TextResourceTest; +import com.google.gwt.resources.css.CssNodeClonerTest; +import com.google.gwt.resources.css.CssReorderTest; +import com.google.gwt.resources.css.CssRtlTest; import com.google.gwt.resources.css.ExtractClassNamesVisitorTest; -import com.google.gwt.resources.rg.CssNodeClonerTest; -import com.google.gwt.resources.rg.CssReorderTest; -import com.google.gwt.resources.rg.CssRtlTest; import junit.framework.Test;
diff --git a/user/test/com/google/gwt/resources/client/CSSResourceTest.java b/user/test/com/google/gwt/resources/client/CSSResourceTest.java index b0c4664..3bb69ec 100644 --- a/user/test/com/google/gwt/resources/client/CSSResourceTest.java +++ b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
@@ -135,6 +135,10 @@ @Strict HasDescendants descendants(); + // Make sure an empty, no-op CssResource works + @Strict + CssResource empty(); + @Source("16x16.png") ImageResource spriteMethod(); } @@ -292,6 +296,17 @@ assertFalse("10".equals(defines.overrideIntClass())); } + public void testEnsureInjected() { + Resources r = GWT.create(Resources.class); + assertTrue(r.empty().ensureInjected()); + + r = GWT.create(Resources.class); + assertFalse(r.empty().ensureInjected()); + + r = GWT.create(ChildResources.class); + assertTrue(r.empty().ensureInjected()); + } + public void testMultipleBundles() { Resources r1 = GWT.create(Resources.class); SiblingResources r2 = GWT.create(SiblingResources.class);
diff --git a/user/test/com/google/gwt/resources/client/ImageResourceTest.java b/user/test/com/google/gwt/resources/client/ImageResourceTest.java index 21e53fe..9da158a 100644 --- a/user/test/com/google/gwt/resources/client/ImageResourceTest.java +++ b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
@@ -113,6 +113,7 @@ assertEquals(a.getLeft(), b.getTop()); assertEquals(a.getLeft(), c.getTop()); + delayTestFinish(1000); // See if the size of the image strip is what we expect Image i = new Image(a.getURL()); i.addLoadHandler(new LoadHandler() { @@ -127,7 +128,6 @@ }); RootPanel.get().add(i); - delayTestFinish(1000); } public void testPacking() {
diff --git a/user/test/com/google/gwt/resources/client/TextResourceTest.java b/user/test/com/google/gwt/resources/client/TextResourceTest.java index 2b4ad40..2c9093f 100644 --- a/user/test/com/google/gwt/resources/client/TextResourceTest.java +++ b/user/test/com/google/gwt/resources/client/TextResourceTest.java
@@ -47,6 +47,8 @@ public void testExternal() throws ResourceException { final Resources r = GWT.create(Resources.class); + delayTestFinish(2000); + ResourceCallback<TextResource> c = new ResourceCallback<TextResource>() { public void onError(ResourceException e) { @@ -61,7 +63,6 @@ } }; - delayTestFinish(2000); r.helloWorldExternal().getText(c); }
diff --git a/user/test/com/google/gwt/resources/client/empty.css b/user/test/com/google/gwt/resources/client/empty.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/user/test/com/google/gwt/resources/client/empty.css
diff --git a/user/test/com/google/gwt/resources/rg/CssNodeClonerTest.java b/user/test/com/google/gwt/resources/css/CssNodeClonerTest.java similarity index 96% rename from user/test/com/google/gwt/resources/rg/CssNodeClonerTest.java rename to user/test/com/google/gwt/resources/css/CssNodeClonerTest.java index 24c5042..517de81 100644 --- a/user/test/com/google/gwt/resources/rg/CssNodeClonerTest.java +++ b/user/test/com/google/gwt/resources/css/CssNodeClonerTest.java
@@ -13,16 +13,16 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.resources.rg; +package com.google.gwt.resources.css; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.resources.css.GenerateCssAst; import com.google.gwt.resources.css.ast.CssNode; import com.google.gwt.resources.css.ast.CssNodeCloner; import com.google.gwt.resources.css.ast.CssProperty; import com.google.gwt.resources.css.ast.CssSelector; import com.google.gwt.resources.css.ast.CssStylesheet; +import com.google.gwt.resources.rg.CssTestCase; import java.net.URL; import java.util.List;
diff --git a/user/test/com/google/gwt/resources/rg/CssReorderTest.java b/user/test/com/google/gwt/resources/css/CssReorderTest.java similarity index 82% rename from user/test/com/google/gwt/resources/rg/CssReorderTest.java rename to user/test/com/google/gwt/resources/css/CssReorderTest.java index 2777548..0b3a62e 100644 --- a/user/test/com/google/gwt/resources/rg/CssReorderTest.java +++ b/user/test/com/google/gwt/resources/css/CssReorderTest.java
@@ -13,14 +13,12 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.resources.rg; +package com.google.gwt.resources.css; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.resources.css.ast.CssVisitor; -import com.google.gwt.resources.rg.CssResourceGenerator.MergeIdenticalSelectorsVisitor; -import com.google.gwt.resources.rg.CssResourceGenerator.MergeRulesByContentVisitor; -import com.google.gwt.resources.rg.CssResourceGenerator.SplitRulesVisitor; +import com.google.gwt.resources.rg.CssTestCase; /** * Tests CSS reordering visitors.
diff --git a/user/test/com/google/gwt/resources/rg/CssRtlTest.java b/user/test/com/google/gwt/resources/css/CssRtlTest.java similarity index 94% rename from user/test/com/google/gwt/resources/rg/CssRtlTest.java rename to user/test/com/google/gwt/resources/css/CssRtlTest.java index ac0bc8e..fa75951 100644 --- a/user/test/com/google/gwt/resources/rg/CssRtlTest.java +++ b/user/test/com/google/gwt/resources/css/CssRtlTest.java
@@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.resources.rg; +package com.google.gwt.resources.css; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.resources.css.ast.CssVisitor; -import com.google.gwt.resources.rg.CssResourceGenerator.RtlVisitor; +import com.google.gwt.resources.rg.CssTestCase; /** * This is a static test of the automatic RTL support.
diff --git a/user/test/com/google/gwt/resources/rg/backgroundProperties_expected.css b/user/test/com/google/gwt/resources/css/backgroundProperties_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/backgroundProperties_expected.css rename to user/test/com/google/gwt/resources/css/backgroundProperties_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/backgroundProperties_test.css b/user/test/com/google/gwt/resources/css/backgroundProperties_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/backgroundProperties_test.css rename to user/test/com/google/gwt/resources/css/backgroundProperties_test.css
diff --git a/user/test/com/google/gwt/resources/rg/cursorProperties_expected.css b/user/test/com/google/gwt/resources/css/cursorProperties_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/cursorProperties_expected.css rename to user/test/com/google/gwt/resources/css/cursorProperties_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/cursorProperties_test.css b/user/test/com/google/gwt/resources/css/cursorProperties_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/cursorProperties_test.css rename to user/test/com/google/gwt/resources/css/cursorProperties_test.css
diff --git a/user/test/com/google/gwt/resources/rg/directionProperty_expected.css b/user/test/com/google/gwt/resources/css/directionProperty_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/directionProperty_expected.css rename to user/test/com/google/gwt/resources/css/directionProperty_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/directionProperty_test.css b/user/test/com/google/gwt/resources/css/directionProperty_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/directionProperty_test.css rename to user/test/com/google/gwt/resources/css/directionProperty_test.css
diff --git a/user/test/com/google/gwt/resources/rg/fourValuedProperties_expected.css b/user/test/com/google/gwt/resources/css/fourValuedProperties_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/fourValuedProperties_expected.css rename to user/test/com/google/gwt/resources/css/fourValuedProperties_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/fourValuedProperties_test.css b/user/test/com/google/gwt/resources/css/fourValuedProperties_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/fourValuedProperties_test.css rename to user/test/com/google/gwt/resources/css/fourValuedProperties_test.css
diff --git a/user/test/com/google/gwt/resources/rg/leftRightProperties_expected.css b/user/test/com/google/gwt/resources/css/leftRightProperties_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/leftRightProperties_expected.css rename to user/test/com/google/gwt/resources/css/leftRightProperties_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/leftRightProperties_test.css b/user/test/com/google/gwt/resources/css/leftRightProperties_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/leftRightProperties_test.css rename to user/test/com/google/gwt/resources/css/leftRightProperties_test.css
diff --git a/user/test/com/google/gwt/resources/rg/noflip_expected.css b/user/test/com/google/gwt/resources/css/noflip_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/noflip_expected.css rename to user/test/com/google/gwt/resources/css/noflip_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/noflip_test.css b/user/test/com/google/gwt/resources/css/noflip_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/noflip_test.css rename to user/test/com/google/gwt/resources/css/noflip_test.css
diff --git a/user/test/com/google/gwt/resources/rg/propertyMerging_expected.css b/user/test/com/google/gwt/resources/css/propertyMerging_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/propertyMerging_expected.css rename to user/test/com/google/gwt/resources/css/propertyMerging_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/propertyMerging_test.css b/user/test/com/google/gwt/resources/css/propertyMerging_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/propertyMerging_test.css rename to user/test/com/google/gwt/resources/css/propertyMerging_test.css
diff --git a/user/test/com/google/gwt/resources/rg/selectorMerging_expected.css b/user/test/com/google/gwt/resources/css/selectorMerging_expected.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/selectorMerging_expected.css rename to user/test/com/google/gwt/resources/css/selectorMerging_expected.css
diff --git a/user/test/com/google/gwt/resources/rg/selectorMerging_test.css b/user/test/com/google/gwt/resources/css/selectorMerging_test.css similarity index 100% rename from user/test/com/google/gwt/resources/rg/selectorMerging_test.css rename to user/test/com/google/gwt/resources/css/selectorMerging_test.css
diff --git a/user/test/com/google/gwt/resources/rg/CssTestCase.java b/user/test/com/google/gwt/resources/rg/CssTestCase.java index 01e7555..33cbc0b 100644 --- a/user/test/com/google/gwt/resources/rg/CssTestCase.java +++ b/user/test/com/google/gwt/resources/rg/CssTestCase.java
@@ -38,13 +38,17 @@ * modifications to the CSS AST. */ public class CssTestCase extends TestCase { + /* + * NB: This class is in the resources.rg package so that it can acess + * package-protected methods in CssResourceGenerator. + */ /** * Triggers an assertion if a CssNode is traversed more than once. * * @see CssTestCase#assertNoAliasing(CssNode) */ - protected static class AliasDetector extends CssVisitor { + public static class AliasDetector extends CssVisitor { private final Map<CssNode, Void> seen = new IdentityHashMap<CssNode, Void>(); @Override
diff --git a/user/test/com/google/gwt/user/client/CommandExecutorTest.java b/user/test/com/google/gwt/user/client/CommandExecutorTest.java index ca18dc7..1e970d9 100644 --- a/user/test/com/google/gwt/user/client/CommandExecutorTest.java +++ b/user/test/com/google/gwt/user/client/CommandExecutorTest.java
@@ -114,13 +114,13 @@ } }); + delayTestFinish(TEST_FINISH_DELAY_MILLIS); ce.submit(new Command() { public void execute() { finishTest(); } }); - delayTestFinish(TEST_FINISH_DELAY_MILLIS); ce.doExecuteCommands(Duration.currentTimeMillis()); } };
diff --git a/user/test/com/google/gwt/user/client/CookieTest.java b/user/test/com/google/gwt/user/client/CookieTest.java index 7ecef1b..a51ef3d 100644 --- a/user/test/com/google/gwt/user/client/CookieTest.java +++ b/user/test/com/google/gwt/user/client/CookieTest.java
@@ -76,6 +76,7 @@ Cookies.setCookie(lateCookie, "late", expiresLate); Cookies.setCookie(sessionCookie, "forever", null); + delayTestFinish(6 * 1000); // Wait until the cookie expires before checking it Timer timer = new Timer() { @Override @@ -96,7 +97,6 @@ } }; timer.schedule(5010); - delayTestFinish(6 * 1000); } /**
diff --git a/user/test/com/google/gwt/user/client/WindowTest.java b/user/test/com/google/gwt/user/client/WindowTest.java index f25219c..b50e698 100644 --- a/user/test/com/google/gwt/user/client/WindowTest.java +++ b/user/test/com/google/gwt/user/client/WindowTest.java
@@ -149,6 +149,7 @@ final Label largeDOM = new Label(); largeDOM.setPixelSize(oldClientWidth + 100, oldClientHeight + 100); RootPanel.get().add(largeDOM); + delayTestFinish(200); DeferredCommand.addCommand(new Command() { public void execute() { int newClientHeight = Window.getClientHeight(); @@ -159,7 +160,6 @@ finishTest(); } }); - delayTestFinish(200); } /**
diff --git a/user/test/com/google/gwt/user/client/rpc/InheritanceTest.java b/user/test/com/google/gwt/user/client/rpc/InheritanceTest.java index 6023899..6544b75 100644 --- a/user/test/com/google/gwt/user/client/rpc/InheritanceTest.java +++ b/user/test/com/google/gwt/user/client/rpc/InheritanceTest.java
@@ -180,6 +180,7 @@ } public void testSerializationExceptionPreventsCall() { + delayTestFinish(TEST_DELAY); final boolean serializationExceptionCaught[] = new boolean[1]; new Timer() { @Override @@ -189,7 +190,6 @@ finishTest(); } }.schedule(TEST_DELAY / 2); - delayTestFinish(TEST_DELAY); InheritanceTestServiceAsync service = getServiceAsync(); service.echo(new AnonymousClassInterface() {
diff --git a/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java b/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java index d794eea..3cc558c 100644 --- a/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java +++ b/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
@@ -102,6 +102,7 @@ UIObjectTest.assertDebugId("myDialogBox-content", DOM.getParent(content.getElement())); + delayTestFinish(250); // Check the header IDs DeferredCommand.addCommand(new Command() { public void execute() { @@ -110,7 +111,6 @@ finishTest(); } }); - delayTestFinish(250); } @Override
diff --git a/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java b/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java index 8e12014..98f8eb5 100644 --- a/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java +++ b/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java
@@ -48,6 +48,7 @@ panel.setOpen(true); + delayTestFinish(500); // Allow the animation time to finish Timer t = new Timer() { @Override @@ -59,7 +60,6 @@ } }; t.schedule(450); - delayTestFinish(500); } public void testAttachDetachOrder() {
diff --git a/user/test/com/google/gwt/user/client/ui/HistoryTest.java b/user/test/com/google/gwt/user/client/ui/HistoryTest.java index 34f5373..0d1bd85 100644 --- a/user/test/com/google/gwt/user/client/ui/HistoryTest.java +++ b/user/test/com/google/gwt/user/client/ui/HistoryTest.java
@@ -125,6 +125,7 @@ * Verify that no events are issued via newItem if there were not reqeuested. */ public void testNoEvents() { + delayTestFinish(5000); addHistoryListenerImpl(new HistoryListener() { { timer = new Timer() { @@ -139,7 +140,6 @@ fail("onHistoryChanged should not have been called"); } }); - delayTestFinish(5000); History.newItem("testNoEvents", false); } @@ -197,9 +197,10 @@ * called once per {@link History#newItem(String)}. */ public void testHistoryChangedCount() { + delayTestFinish(5000); timer = new Timer() { private int count = 0; - + public void run() { if (count++ == 0) { // verify that duplicates don't issue another event @@ -222,7 +223,6 @@ timer.schedule(500); } }); - delayTestFinish(5000); History.newItem("testHistoryChangedCount"); }
diff --git a/user/test/com/google/gwt/user/client/ui/ImageTest.java b/user/test/com/google/gwt/user/client/ui/ImageTest.java index 995c57d..156feae 100644 --- a/user/test/com/google/gwt/user/client/ui/ImageTest.java +++ b/user/test/com/google/gwt/user/client/ui/ImageTest.java
@@ -99,6 +99,7 @@ final Image image = new Image("counting-forwards.png", 12, 13, 8, 8); assertEquals("clipped", getCurrentImageStateName(image)); + delayTestFinish(5000); image.addLoadListener(new LoadListener() { private int onLoadEventCount = 0; @@ -121,7 +122,6 @@ } }); - delayTestFinish(5000); RootPanel.get().add(image); } @@ -134,6 +134,7 @@ final Image image = new Image("counting-forwards.png"); assertEquals("unclipped", getCurrentImageStateName(image)); + delayTestFinish(5000); image.addLoadListener(new LoadListener() { private int onLoadEventCount = 0; @@ -157,7 +158,6 @@ } }); - delayTestFinish(5000); RootPanel.get().add(image); } @@ -169,6 +169,7 @@ public void disabledTestCreateImage() { final Image image = new Image("counting-forwards.png"); + delayTestFinish(5000); image.addLoadListener(new LoadListener() { private int onLoadEventCount = 0; @@ -185,7 +186,6 @@ } }); - delayTestFinish(5000); RootPanel.get().add(image); assertEquals(0, image.getOriginLeft()); assertEquals(0, image.getOriginTop()); @@ -202,6 +202,7 @@ public void disabledTestSetUrlAndLoadEventsOnUnclippedImage() { final Image image = new Image(); + delayTestFinish(5000); image.addLoadListener(new LoadListener() { private int onLoadEventCount = 0; @@ -218,7 +219,6 @@ } }); - delayTestFinish(5000); RootPanel.get().add(image); image.setUrl("counting-backwards.png"); } @@ -233,6 +233,7 @@ public void disabledTestSetUrlAndVisibleRectOnUnclippedImage() { final Image image = new Image("counting-backwards.png"); + delayTestFinish(5000); image.addLoadListener(new LoadListener() { private int onLoadEventCount = 0; @@ -256,7 +257,6 @@ } }); - delayTestFinish(5000); RootPanel.get().add(image); assertEquals("unclipped", getCurrentImageStateName(image)); } @@ -273,6 +273,7 @@ public void testCreateClippedImage() { final Image image = new Image("counting-forwards.png", 16, 16, 16, 16); + delayTestFinish(5000); final TestLoadListener listener = new TestLoadListener(image) { private int onLoadEventCount = 0; @@ -303,7 +304,6 @@ }); image.addErrorHandler(new TestErrorHandler(image)); - delayTestFinish(5000); RootPanel.get().add(image); assertEquals(16, image.getOriginLeft()); assertEquals(16, image.getOriginTop()); @@ -344,6 +344,7 @@ @DoNotRunWith({Platform.Htmlunit}) public void testSetUrlAndVisibleRectOnClippedImage() { final Image image = new Image("counting-backwards.png", 12, 12, 12, 12); + delayTestFinish(5000); final TestLoadListener listener = new TestLoadListener(image) { private int onLoadEventCount = 0; @@ -381,7 +382,6 @@ }); image.addErrorHandler(new TestErrorHandler(image)); - delayTestFinish(5000); RootPanel.get().add(image); assertEquals("clipped", getCurrentImageStateName(image)); image.setUrlAndVisibleRect("counting-forwards.png", 0, 16, 16, 16); @@ -396,6 +396,7 @@ public void testSetVisibleRectAndLoadEventsOnClippedImage() { final Image image = new Image("counting-backwards.png", 16, 16, 16, 16); + delayTestFinish(5000); final TestLoadListener listener = new TestLoadListener(image) { private int onLoadEventCount = 0; @@ -422,7 +423,6 @@ }); image.addErrorHandler(new TestErrorHandler(image)); - delayTestFinish(5000); RootPanel.get().add(image); image.setVisibleRect(0, 0, 16, 16); image.setVisibleRect(0, 0, 16, 16);
diff --git a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java index 5b116de..97e9501 100644 --- a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java +++ b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
@@ -49,6 +49,7 @@ list.ensureDebugId("myList"); UIObjectTest.assertDebugId("myList", list.getElement()); + delayTestFinish(250); DeferredCommand.addCommand(new Command() { public void execute() { UIObjectTest.assertDebugIdContents("myList-item0", "option0"); @@ -58,7 +59,6 @@ finishTest(); } }); - delayTestFinish(250); } public void testInsert() {
diff --git a/user/test/com/google/gwt/user/client/ui/MenuBarTest.java b/user/test/com/google/gwt/user/client/ui/MenuBarTest.java index 1785aeb..a33f64c 100644 --- a/user/test/com/google/gwt/user/client/ui/MenuBarTest.java +++ b/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
@@ -236,6 +236,7 @@ bar.ensureDebugId("myMenu"); UIObjectTest.assertDebugId("myMenu", bar.getElement()); + delayTestFinish(250); DeferredCommand.addCommand(new Command() { public void execute() { UIObjectTest.assertDebugIdContents("myMenu-item0", "top0"); @@ -248,7 +249,6 @@ finishTest(); } }); - delayTestFinish(250); } /**
diff --git a/user/test/com/google/gwt/user/client/ui/PopupTest.java b/user/test/com/google/gwt/user/client/ui/PopupTest.java index 0ec6231..eb64dc0 100644 --- a/user/test/com/google/gwt/user/client/ui/PopupTest.java +++ b/user/test/com/google/gwt/user/client/ui/PopupTest.java
@@ -333,6 +333,7 @@ } }.schedule(1000); + delayTestFinish(5000); // Give time for any errors to occur new Timer() { @Override @@ -340,7 +341,5 @@ finishTest(); } }.schedule(2000); - - delayTestFinish(5000); } }
diff --git a/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java b/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java index cce59a8..d504f19 100644 --- a/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java +++ b/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java
@@ -228,6 +228,7 @@ public void testSetTextBeforeInit() { final RichTextArea richTextArea = new RichTextArea(); richTextArea.setText("foo"); + delayTestFinish(RICH_TEXT_ASYNC_DELAY); richTextArea.addInitializeHandler(new InitializeHandler() { public void onInitialize(InitializeEvent event) { assertEquals("foo", richTextArea.getText()); @@ -236,6 +237,5 @@ }); RootPanel.get().add(richTextArea); assertEquals("foo", richTextArea.getText()); - delayTestFinish(RICH_TEXT_ASYNC_DELAY); } }
diff --git a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java index 0e6ab61..4bf82e6 100644 --- a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java +++ b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
@@ -1,12 +1,12 @@ /* * 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 @@ -75,6 +75,8 @@ UIObjectTest.assertDebugId("myStack-content2", DOM.getParent(c.getElement())); + delayTestFinish(5000); + // Check the header IDs DeferredCommand.addCommand(new Command() { public void execute() { @@ -97,7 +99,6 @@ finishTest(); } }); - delayTestFinish(5000); } /**
diff --git a/user/test/com/google/gwt/user/client/ui/TabPanelTest.java b/user/test/com/google/gwt/user/client/ui/TabPanelTest.java index 75c634e..0db70a3 100644 --- a/user/test/com/google/gwt/user/client/ui/TabPanelTest.java +++ b/user/test/com/google/gwt/user/client/ui/TabPanelTest.java
@@ -202,6 +202,7 @@ p.add(new Button("foo"), "foo"); p.add(new Button("bar"), "bar"); + this.delayTestFinish(1000); // Make sure selecting a tab fires both events in the right order. p.addTabListener(new TabListener() { private boolean onBeforeFired; @@ -217,7 +218,6 @@ } }); - this.delayTestFinish(1000); p.selectTab(1); }