Automatically generate unique names for our CSS code
This avoids the need to keep renaming the "gerritN.cache.css" file when we modify it, and instead name it uniquely based upon its own file content. This safely permits long lived caching of the CSS, without needing to worry about it at development time. Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
20
src/main/java/com/google/gerrit/CssBundle.gwt.xml
Normal file
20
src/main/java/com/google/gerrit/CssBundle.gwt.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
Copyright (C) 2009 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<module>
|
||||
<generate-with class="com.google.gerrit.rebind.css.CssGenerator">
|
||||
<when-type-assignable class="com.google.gerrit.client.css.CssBundle" />
|
||||
</generate-with>
|
||||
</module>
|
||||
@@ -22,12 +22,11 @@
|
||||
<inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
|
||||
|
||||
<inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
|
||||
<inherits name='com.google.gerrit.CssBundle'/>
|
||||
<inherits name='com.google.gerrit.UserAgent'/>
|
||||
<extend-property name="locale" values="en"/>
|
||||
|
||||
|
||||
<entry-point class='com.google.gerrit.client.Gerrit'/>
|
||||
<stylesheet src='gerrit6.cache.css' />
|
||||
|
||||
<servlet path='/Gerrit'
|
||||
class='com.google.gerrit.server.HostPageServlet'/>
|
||||
|
||||
@@ -175,6 +175,7 @@ public class Gerrit implements EntryPoint {
|
||||
|
||||
public void onModuleLoad() {
|
||||
UserAgent.assertNotInIFrame();
|
||||
loadCSS();
|
||||
initHistoryHooks();
|
||||
populateBottomMenu();
|
||||
|
||||
@@ -195,6 +196,11 @@ public class Gerrit implements EntryPoint {
|
||||
});
|
||||
}
|
||||
|
||||
private static void loadCSS() {
|
||||
final GerritCss css = GWT.create(GerritCss.class);
|
||||
css.load().inject();
|
||||
}
|
||||
|
||||
private static ArrayList<JavaScriptObject> historyHooks;
|
||||
|
||||
private static native void initHistoryHooks()
|
||||
|
||||
21
src/main/java/com/google/gerrit/client/GerritCss.java
Normal file
21
src/main/java/com/google/gerrit/client/GerritCss.java
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
//
|
||||
// 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.gerrit.client;
|
||||
|
||||
import com.google.gerrit.client.css.CssBundle;
|
||||
|
||||
/** References <code>GerritCss.css</code>. */
|
||||
public interface GerritCss extends CssBundle {
|
||||
}
|
||||
21
src/main/java/com/google/gerrit/client/css/CssBundle.java
Normal file
21
src/main/java/com/google/gerrit/client/css/CssBundle.java
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
//
|
||||
// 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.gerrit.client.css;
|
||||
|
||||
/** References a cached CSS file. */
|
||||
public interface CssBundle {
|
||||
/** @return a reference to the bundle's CSS file. */
|
||||
CssReference load();
|
||||
}
|
||||
44
src/main/java/com/google/gerrit/client/css/CssReference.java
Normal file
44
src/main/java/com/google/gerrit/client/css/CssReference.java
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
//
|
||||
// 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.gerrit.client.css;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Document;
|
||||
import com.google.gwt.dom.client.LinkElement;
|
||||
|
||||
/** Reference to a CSS file under the module base. */
|
||||
public class CssReference {
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* Create a new reference to a CSS file.
|
||||
*
|
||||
* @param name name of the CSS file, under the module base. Typically this is
|
||||
* a MD-5 or SHA-1 hash of the file content, with
|
||||
* <code>.cache.css</code> suffix, to permit long-lived caching of the
|
||||
* resource by browers and edge caches.
|
||||
*/
|
||||
public CssReference(final String name) {
|
||||
url = GWT.getModuleBaseURL() + name;
|
||||
}
|
||||
|
||||
/** Inject this resource into the document as a style sheet. */
|
||||
public void inject() {
|
||||
final LinkElement style = Document.get().createLinkElement();
|
||||
style.setRel("stylesheet");
|
||||
style.setHref(url);
|
||||
Document.get().getElementsByTagName("head").getItem(0).appendChild(style);
|
||||
}
|
||||
}
|
||||
177
src/main/java/com/google/gerrit/rebind/css/CssGenerator.java
Normal file
177
src/main/java/com/google/gerrit/rebind/css/CssGenerator.java
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
//
|
||||
// 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.gerrit.rebind.css;
|
||||
|
||||
import com.google.gerrit.client.css.CssReference;
|
||||
import com.google.gwt.core.ext.Generator;
|
||||
import com.google.gwt.core.ext.GeneratorContext;
|
||||
import com.google.gwt.core.ext.TreeLogger;
|
||||
import com.google.gwt.core.ext.UnableToCompleteException;
|
||||
import com.google.gwt.core.ext.typeinfo.JArrayType;
|
||||
import com.google.gwt.core.ext.typeinfo.JClassType;
|
||||
import com.google.gwt.core.ext.typeinfo.JPackage;
|
||||
import com.google.gwt.core.ext.typeinfo.JType;
|
||||
import com.google.gwt.core.ext.typeinfo.TypeOracle;
|
||||
import com.google.gwt.dev.util.Util;
|
||||
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
|
||||
import com.google.gwt.user.rebind.SourceWriter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class CssGenerator extends Generator {
|
||||
private static final String LDR_SUFFIX = "_Loader";
|
||||
|
||||
@Override
|
||||
public String generate(TreeLogger logger, final GeneratorContext ctx,
|
||||
final String requestedClass) throws UnableToCompleteException {
|
||||
logger =
|
||||
logger.branch(TreeLogger.DEBUG,
|
||||
"Generating client proxy for css loading interface '"
|
||||
+ requestedClass + "'", null);
|
||||
|
||||
final TypeOracle typeOracle = ctx.getTypeOracle();
|
||||
final JClassType ldrInf = typeOracle.findType(requestedClass);
|
||||
if (ldrInf == null) {
|
||||
logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
|
||||
+ requestedClass + "'", null);
|
||||
throw new UnableToCompleteException();
|
||||
}
|
||||
|
||||
if (ldrInf.isInterface() == null) {
|
||||
logger.log(TreeLogger.ERROR, ldrInf.getQualifiedSourceName()
|
||||
+ " is not an interface", null);
|
||||
throw new UnableToCompleteException();
|
||||
}
|
||||
|
||||
final SourceWriter w = getSourceWriter(logger, ctx, ldrInf);
|
||||
if (w != null) {
|
||||
try {
|
||||
writeClass(logger, ctx, w, ldrInf);
|
||||
} catch (Exception e) {
|
||||
logger.log(TreeLogger.ERROR, "Cannot create '"
|
||||
+ ldrInf.getQualifiedSourceName() + "'", e);
|
||||
throw new UnableToCompleteException();
|
||||
}
|
||||
}
|
||||
return getProxyQualifiedName(ldrInf);
|
||||
}
|
||||
|
||||
private void writeClass(TreeLogger logger, final GeneratorContext ctx,
|
||||
final SourceWriter w, final JClassType ldrInf) throws IOException,
|
||||
UnableToCompleteException {
|
||||
final String srcname =
|
||||
ldrInf.getQualifiedSourceName().replace('.', '/') + ".css";
|
||||
|
||||
final InputStream in =
|
||||
getClass().getClassLoader().getResourceAsStream(srcname);
|
||||
if (in == null) {
|
||||
throw new FileNotFoundException(srcname);
|
||||
}
|
||||
|
||||
final ByteArrayOutputStream tmp = new ByteArrayOutputStream();
|
||||
try {
|
||||
final byte[] buf = new byte[2048];
|
||||
int n;
|
||||
while ((n = in.read(buf)) >= 0) {
|
||||
tmp.write(buf, 0, n);
|
||||
}
|
||||
tmp.close();
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
final byte[] rawcss = tmp.toByteArray();
|
||||
final String outname = Util.computeStrongName(rawcss) + ".cache.css";
|
||||
|
||||
final OutputStream out = ctx.tryCreateResource(logger, outname);
|
||||
if (out != null) {
|
||||
out.write(rawcss);
|
||||
ctx.commitResource(logger, out);
|
||||
}
|
||||
|
||||
w.println("public CssReference load() {");
|
||||
w.indent();
|
||||
w.println("return new CssReference(\"" + escape(outname) + "\");");
|
||||
w.outdent();
|
||||
w.println("}");
|
||||
w.commit(logger);
|
||||
}
|
||||
|
||||
private static SourceWriter getSourceWriter(final TreeLogger logger,
|
||||
final GeneratorContext ctx, final JClassType ldrInf) {
|
||||
final JPackage servicePkg = ldrInf.getPackage();
|
||||
final String pkgn = servicePkg == null ? "" : servicePkg.getName();
|
||||
final PrintWriter pw;
|
||||
final ClassSourceFileComposerFactory cf;
|
||||
|
||||
pw = ctx.tryCreate(logger, pkgn, getProxySimpleName(ldrInf));
|
||||
if (pw == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
cf = new ClassSourceFileComposerFactory(pkgn, getProxySimpleName(ldrInf));
|
||||
cf.addImport(CssReference.class.getName());
|
||||
cf.addImplementedInterface(ldrInf.getErasedType().getQualifiedSourceName());
|
||||
return cf.createSourceWriter(ctx, pw);
|
||||
}
|
||||
|
||||
private static String getProxyQualifiedName(final JClassType ldrInf) {
|
||||
final String[] name = synthesizeTopLevelClassName(ldrInf, LDR_SUFFIX);
|
||||
return name[0].length() == 0 ? name[1] : name[0] + "." + name[1];
|
||||
}
|
||||
|
||||
private static String getProxySimpleName(final JClassType ldrInf) {
|
||||
return synthesizeTopLevelClassName(ldrInf, LDR_SUFFIX)[1];
|
||||
}
|
||||
|
||||
private static String[] synthesizeTopLevelClassName(JClassType type,
|
||||
String suffix) {
|
||||
// Gets the basic name of the type. If it's a nested type, the type name
|
||||
// will contains dots.
|
||||
//
|
||||
String className;
|
||||
String packageName;
|
||||
|
||||
JType leafType = type.getLeafType();
|
||||
if (leafType.isPrimitive() != null) {
|
||||
className = leafType.getSimpleSourceName();
|
||||
packageName = "";
|
||||
} else {
|
||||
JClassType classOrInterface = leafType.isClassOrInterface();
|
||||
assert (classOrInterface != null);
|
||||
className = classOrInterface.getName();
|
||||
packageName = classOrInterface.getPackage().getName();
|
||||
}
|
||||
|
||||
JArrayType isArray = type.isArray();
|
||||
if (isArray != null) {
|
||||
className += "_Array_Rank_" + isArray.getRank();
|
||||
}
|
||||
|
||||
// Add the meaningful suffix.
|
||||
//
|
||||
className += suffix;
|
||||
|
||||
// Make it a top-level name.
|
||||
//
|
||||
className = className.replace('.', '_');
|
||||
|
||||
return new String[] {packageName, className};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user