-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simple JavascriptResource implementation
- Loading branch information
Showing
6 changed files
with
291 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
*.class | ||
# Eclipse files | ||
.classpath | ||
.project | ||
.settings | ||
|
||
#Maven stuff | ||
target/ | ||
|
||
# Package Files # | ||
# Generic java stuff | ||
*.class | ||
*.jar | ||
*.war | ||
*.ear |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>com.fathomdb</groupId> | ||
<version>1.0-SNAPSHOT</version> | ||
|
||
<artifactId>gwt-utils</artifactId> | ||
<packaging>jar</packaging> | ||
<name>GWT Utilities</name> | ||
|
||
<properties> | ||
<gwtVersion>2.4.0</gwtVersion> | ||
<closureVersion>r1918</closureVersion> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.google.gwt</groupId> | ||
<artifactId>gwt-user</artifactId> | ||
<version>${gwtVersion}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.google.guava</groupId> | ||
<artifactId>guava-gwt</artifactId> | ||
<version>12.0-rc2</version> | ||
</dependency> | ||
|
||
<!-- For Javascript resource generation --> | ||
<dependency> | ||
<groupId>com.google.gwt</groupId> | ||
<artifactId>gwt-dev</artifactId> | ||
<version>${gwtVersion}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.google.javascript</groupId> | ||
<artifactId>closure-compiler</artifactId> | ||
<version>${closureVersion}</version> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<resources> | ||
<resource> | ||
<directory>src/main/java</directory> | ||
</resource> | ||
</resources> | ||
|
||
<plugins> | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>gwt-maven-plugin</artifactId> | ||
<version>${gwtVersion}</version> | ||
<executions> | ||
<execution> | ||
<!-- <goals> <goal>compile</goal> </goals> --> | ||
</execution> | ||
</executions> | ||
<configuration> | ||
<module>com.fathomdb.gwt.utils.GwtUtils</module> | ||
<runTarget>GwtUtils.html</runTarget> | ||
<hostedWebapp>${webappDirectory}</hostedWebapp> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
|
||
</build> | ||
|
||
|
||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<module rename-to='GwtUtils'> | ||
<inherits name='com.google.gwt.user.User' /> | ||
|
||
<source path='resources' /> | ||
</module> |
38 changes: 38 additions & 0 deletions
38
src/main/java/com/fathomdb/gwt/utils/resources/JavascriptInjector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.fathomdb.gwt.utils.resources; | ||
|
||
import com.google.gwt.dom.client.Document; | ||
import com.google.gwt.dom.client.Element; | ||
import com.google.gwt.dom.client.HeadElement; | ||
import com.google.gwt.dom.client.ScriptElement; | ||
|
||
public class JavascriptInjector { | ||
|
||
private static HeadElement head; | ||
|
||
public static void inject(String javascript) { | ||
HeadElement head = getHead(); | ||
ScriptElement element = createScriptElement(); | ||
element.setText(javascript); | ||
head.appendChild(element); | ||
} | ||
|
||
private static ScriptElement createScriptElement() { | ||
ScriptElement script = Document.get().createScriptElement(); | ||
|
||
script.setAttribute("type", "text/javascript"); | ||
script.setAttribute("language", "javascript"); | ||
|
||
return script; | ||
} | ||
|
||
private static HeadElement getHead() { | ||
if (head == null) { | ||
Element element = Document.get().getElementsByTagName("head").getItem(0); | ||
assert element != null : "HTML Head element required"; | ||
HeadElement head = HeadElement.as(element); | ||
JavascriptInjector.head = head; | ||
} | ||
return JavascriptInjector.head; | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
src/main/java/com/fathomdb/gwt/utils/resources/JavascriptResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.fathomdb.gwt.utils.resources; | ||
|
||
import com.fathomdb.gwt.utils.rg.JavascriptResourceGenerator; | ||
import com.google.gwt.resources.client.ResourcePrototype; | ||
import com.google.gwt.resources.ext.ResourceGeneratorType; | ||
|
||
@ResourceGeneratorType(JavascriptResourceGenerator.class) | ||
public interface JavascriptResource extends ResourcePrototype { | ||
/** | ||
* 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 JavascriptResource. | ||
*/ | ||
String getText(); | ||
|
||
} |
142 changes: 142 additions & 0 deletions
142
src/main/java/com/fathomdb/gwt/utils/rg/JavascriptResourceGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package com.fathomdb.gwt.utils.rg; | ||
|
||
import java.io.IOException; | ||
import java.net.URL; | ||
|
||
import org.apache.tools.ant.filters.StringInputStream; | ||
|
||
import com.fathomdb.gwt.utils.resources.JavascriptInjector; | ||
import com.fathomdb.gwt.utils.resources.JavascriptResource; | ||
import com.google.gwt.core.ext.Generator; | ||
import com.google.gwt.core.ext.TreeLogger; | ||
import com.google.gwt.core.ext.UnableToCompleteException; | ||
import com.google.gwt.core.ext.typeinfo.JMethod; | ||
import com.google.gwt.dev.util.Util; | ||
import com.google.gwt.resources.ext.AbstractResourceGenerator; | ||
import com.google.gwt.resources.ext.ResourceContext; | ||
import com.google.gwt.resources.ext.ResourceGeneratorUtil; | ||
import com.google.gwt.user.rebind.SourceWriter; | ||
import com.google.gwt.user.rebind.StringSourceWriter; | ||
import com.google.javascript.jscomp.CompilationLevel; | ||
import com.google.javascript.jscomp.Compiler; | ||
import com.google.javascript.jscomp.CompilerOptions; | ||
import com.google.javascript.jscomp.JSSourceFile; | ||
|
||
public class JavascriptResourceGenerator extends AbstractResourceGenerator { | ||
/** | ||
* Java compiler has a limit of 2^16 bytes for encoding string constants in a class file. Since the max size of a | ||
* character is 4 bytes, we'll limit the number of characters to (2^14 - 1) to fit within one record. | ||
*/ | ||
private static final int MAX_STRING_CHUNK = 16383; | ||
|
||
@Override | ||
public String createAssignment(TreeLogger logger, ResourceContext context, JMethod method) | ||
throws UnableToCompleteException { | ||
URL[] resources = ResourceGeneratorUtil.findResources(logger, context, method); | ||
|
||
if (resources.length != 1) { | ||
logger.log(TreeLogger.ERROR, "Exactly one resource must be specified", null); | ||
throw new UnableToCompleteException(); | ||
} | ||
|
||
URL resource = resources[0]; | ||
|
||
SourceWriter sw = new StringSourceWriter(); | ||
// Write the expression to create the subtype. | ||
sw.println("new " + JavascriptResource.class.getName() + "() {"); | ||
sw.indent(); | ||
|
||
// Convenience when examining the generated code. | ||
sw.println("// " + resource.toExternalForm()); | ||
|
||
// Methods defined by JavascriptResource interface | ||
writeEnsureInjected(sw); | ||
writeGetText(sw, resource); | ||
writeGetName(sw, method); | ||
|
||
sw.outdent(); | ||
sw.println("}"); | ||
|
||
return sw.toString(); | ||
} | ||
|
||
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(JavascriptInjector.class.getName() + ".inject(getText());"); | ||
sw.indentln("return true;"); | ||
sw.println("}"); | ||
sw.println("return false;"); | ||
sw.outdent(); | ||
sw.println("}"); | ||
} | ||
|
||
private void writeGetText(SourceWriter sw, URL resource) throws UnableToCompleteException { | ||
sw.println("public String getText() {"); | ||
sw.indent(); | ||
|
||
String toWrite = Util.readURLAsString(resource); | ||
|
||
toWrite = maybeMinify(resource.toExternalForm(), toWrite); | ||
|
||
if (toWrite.length() > MAX_STRING_CHUNK) { | ||
writeLongString(sw, toWrite); | ||
} else { | ||
sw.println("return \"" + Generator.escape(toWrite) + "\";"); | ||
} | ||
sw.outdent(); | ||
sw.println("}"); | ||
} | ||
|
||
private String maybeMinify(String fileName, String js) { | ||
// TODO: Error handling | ||
// TODO: Don't bother if it's small anyway?? | ||
// TODO: Use heuristics to check if already minified (look for comments or whitespace?) | ||
// TODO: Skip in hosted mode? | ||
// TODO: Make external? | ||
// TODO: When external, bundle? (Like images?) | ||
Compiler compiler = new Compiler(); | ||
JSSourceFile input; | ||
try { | ||
input = JSSourceFile.fromInputStream(fileName, new StringInputStream(js)); | ||
} catch (IOException e) { | ||
throw new IllegalStateException(); | ||
} | ||
JSSourceFile[] externs = new JSSourceFile[0]; | ||
JSSourceFile[] inputs = { input }; | ||
CompilerOptions options = new CompilerOptions(); | ||
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options); | ||
/* Result result = */compiler.compile(externs, inputs, options); | ||
|
||
return compiler.toSource(); | ||
} | ||
|
||
private void writeGetName(SourceWriter sw, JMethod method) throws UnableToCompleteException { | ||
sw.println("public String getName() {"); | ||
sw.indent(); | ||
sw.println("return \"" + method.getName() + "\";"); | ||
sw.outdent(); | ||
sw.println("}"); | ||
} | ||
|
||
/** | ||
* A single constant that is too long will crash the compiler with an out of memory error. Break up the constant and | ||
* generate code that appends using a buffer. | ||
*/ | ||
private void writeLongString(SourceWriter sw, String toWrite) { | ||
sw.println("StringBuilder builder = new StringBuilder();"); | ||
int offset = 0; | ||
int length = toWrite.length(); | ||
while (offset < length - 1) { | ||
int subLength = Math.min(MAX_STRING_CHUNK, length - offset); | ||
sw.print("builder.append(\""); | ||
sw.print(Generator.escape(toWrite.substring(offset, offset + subLength))); | ||
sw.println("\");"); | ||
offset += subLength; | ||
} | ||
sw.println("return builder.toString();"); | ||
} | ||
} |