Add CssResource interface generator.

Patch by: bobv
Review by: rjrjr

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6344 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/doc/build.xml b/doc/build.xml
index 5ce1b14..fb7ac64 100644
--- a/doc/build.xml
+++ b/doc/build.xml
@@ -10,7 +10,7 @@
   <property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev-${build.host.platform}.jar" />
 
   <property name="USER_PKGS"
-          value="com.google.gwt.animation.client;com.google.gwt.benchmarks.client;com.google.gwt.core.client;com.google.gwt.core.ext;com.google.gwt.core.ext.soyc;com.google.gwt.core.ext.linker;com.google.gwt.core.ext.typeinfo;com.google.gwt.debug.client;com.google.gwt.dom.client;com.google.gwt.event.dom.client;com.google.gwt.event.logical.shared;com.google.gwt.event.shared;com.google.gwt.http.client;com.google.gwt.i18n.client;com.google.gwt.i18n.client.constants;com.google.gwt.i18n.rebind.format;com.google.gwt.i18n.rebind.keygen;com.google.gwt.json.client;com.google.gwt.junit.client;com.google.gwt.benchmarks.client;com.google.gwt.user.client;com.google.gwt.user.client.rpc;com.google.gwt.user.client.ui;com.google.gwt.user.datepicker.client;com.google.gwt.user.server.rpc;com.google.gwt.xml.client"/>
+          value="com.google.gwt.animation.client;com.google.gwt.benchmarks.client;com.google.gwt.core.client;com.google.gwt.core.ext;com.google.gwt.core.ext.soyc;com.google.gwt.core.ext.linker;com.google.gwt.core.ext.typeinfo;com.google.gwt.debug.client;com.google.gwt.dom.client;com.google.gwt.event.dom.client;com.google.gwt.event.logical.shared;com.google.gwt.event.shared;com.google.gwt.http.client;com.google.gwt.i18n.client;com.google.gwt.i18n.client.constants;com.google.gwt.i18n.rebind.format;com.google.gwt.i18n.rebind.keygen;com.google.gwt.json.client;com.google.gwt.junit.client;com.google.gwt.benchmarks.client;com.google.gwt.resources.client;com.google.gwt.resources.ext;com.google.gwt.user.client;com.google.gwt.user.client.rpc;com.google.gwt.user.client.ui;com.google.gwt.user.datepicker.client;com.google.gwt.user.server.rpc;com.google.gwt.xml.client"/>
   <property name="LANG_PKGS" value="java.lang;java.lang.annotation;java.util;java.io;java.sql" />
 
   <!--	Individual classes to include when we don't want to 
diff --git a/user/src/com/google/gwt/resources/client/CssResource.java b/user/src/com/google/gwt/resources/client/CssResource.java
index a35af8f..6f4baec 100644
--- a/user/src/com/google/gwt/resources/client/CssResource.java
+++ b/user/src/com/google/gwt/resources/client/CssResource.java
@@ -136,6 +136,10 @@
  * 
  * If a <code>String foo()</code> method were defined in <code>MyCss</code>, it
  * would return the string value "<code>foo</code>".
+ * <p>
+ * The utility tool <code>com.google.gwt.resources.css.InterfaceGenerator</code>
+ * can be used to automatically generate a Java interface from a
+ * CssResource-compatible CSS file.
  * 
  * @see <a href="http://code.google.com/p/google-web-toolkit/wiki/CssResource"
  *      >CssResource design doc</a>
diff --git a/user/src/com/google/gwt/resources/css/GenerateCssAst.java b/user/src/com/google/gwt/resources/css/GenerateCssAst.java
index 8749f3e..f9b37e4 100644
--- a/user/src/com/google/gwt/resources/css/GenerateCssAst.java
+++ b/user/src/com/google/gwt/resources/css/GenerateCssAst.java
@@ -614,9 +614,11 @@
   private static final String VALUE_FUNCTION_NAME = "value";
 
   /**
-   * Create a CssStylesheet from the contents of a URL.
+   * Create a CssStylesheet from the contents of one or more URLs. If multiple
+   * URLs are provided, the generated stylesheet will be created as though the
+   * contents of the URLs had been concatenated.
    */
-  public static CssStylesheet exec(TreeLogger logger, URL[] stylesheets)
+  public static CssStylesheet exec(TreeLogger logger, URL... stylesheets)
       throws UnableToCompleteException {
     Parser p = new Parser();
     Errors errors = new Errors(logger);
@@ -652,8 +654,8 @@
    * Expresses an rgb function as a hex expression.
    * 
    * @param colors a sequence of LexicalUnits, assumed to be
-   *          <code>(VAL COMMA VAL COMMA VAL)</code>
-   *     where VAL can be an INT or a PERCENT (which is then converted to INT)
+   *          <code>(VAL COMMA VAL COMMA VAL)</code> where VAL can be an INT or
+   *          a PERCENT (which is then converted to INT)
    * @return the minimal hex expression for the RGB color values
    */
   private static Value colorValue(LexicalUnit colors) {
@@ -745,11 +747,11 @@
   /**
    * Return an integer value from 0-255 for a component of an RGB color.
    * 
-   * @param color typed value from the CSS parser, which may be an INTEGER or
-   *     a PERCENTAGE
+   * @param color typed value from the CSS parser, which may be an INTEGER or a
+   *          PERCENTAGE
    * @return integer value from 0-255
    * @throws IllegalArgumentException if the color is not an INTEGER or
-   *     PERCENTAGE value
+   *           PERCENTAGE value
    */
   private static int getRgbComponentValue(LexicalUnit color) {
     switch (color.getLexicalUnitType()) {
diff --git a/user/src/com/google/gwt/resources/css/InterfaceGenerator.java b/user/src/com/google/gwt/resources/css/InterfaceGenerator.java
new file mode 100644
index 0000000..ad0e63c
--- /dev/null
+++ b/user/src/com/google/gwt/resources/css/InterfaceGenerator.java
@@ -0,0 +1,262 @@
+/*
+ * 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.Generator;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.resources.client.CssResource.ClassName;
+import com.google.gwt.resources.css.ast.CssStylesheet;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.StringSourceWriter;
+import com.google.gwt.util.tools.ArgHandlerFile;
+import com.google.gwt.util.tools.ArgHandlerFlag;
+import com.google.gwt.util.tools.ArgHandlerString;
+import com.google.gwt.util.tools.ToolBase;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * A utility class for creating a Java interface declaration for a given CSS
+ * file.
+ */
+public class InterfaceGenerator extends ToolBase {
+
+  private static final Comparator<String> CSS_CLASS_COMPARATOR = new Comparator<String>() {
+    public int compare(String o1, String o2) {
+      return o1.compareToIgnoreCase(o2);
+    }
+  };
+
+  private static final TreeLogger.Type LOG_LEVEL = TreeLogger.WARN;
+
+  public static void main(String[] args) {
+    (new InterfaceGenerator()).execImpl(args);
+  }
+
+  private String interfaceName;
+  private File inputFile;
+  private TreeLogger logger;
+  private boolean standaloneFile;
+
+  private InterfaceGenerator() {
+    // -standalone
+    registerHandler(new ArgHandlerFlag() {
+
+      @Override
+      public String getPurpose() {
+        return "Add package and import statements to generated interface";
+      }
+
+      @Override
+      public String getTag() {
+        return "-standalone";
+      }
+
+      @Override
+      public boolean setFlag() {
+        standaloneFile = true;
+        logger.log(TreeLogger.DEBUG, "Creating standalone file");
+        return true;
+      }
+    });
+
+    // -typeName some.package.MyCssResource
+    registerHandler(new ArgHandlerString() {
+
+      @Override
+      public String getPurpose() {
+        return "The name of the generated CssResource subtype";
+      }
+
+      @Override
+      public String getTag() {
+        return "-typeName";
+      }
+
+      @Override
+      public String[] getTagArgs() {
+        return new String[] {"some.package.MyCssResource"};
+      }
+
+      @Override
+      public boolean isRequired() {
+        return true;
+      }
+
+      @Override
+      public boolean setString(String str) {
+        if (str.length() == 0) {
+          return false;
+        }
+        if (!Character.isJavaIdentifierStart(str.charAt(0))) {
+          return false;
+        }
+        for (int i = 1, j = str.length(); i < j; i++) {
+          char c = str.charAt(i);
+          if (!(Character.isJavaIdentifierPart(c) || c == '.')) {
+            return false;
+          }
+        }
+        interfaceName = str;
+        logger.log(TreeLogger.DEBUG, "interfaceName = " + interfaceName);
+        return true;
+      }
+    });
+
+    // -css in.css
+    registerHandler(new ArgHandlerFile() {
+
+      @Override
+      public String getPurpose() {
+        return "The input CSS file to process";
+      }
+
+      @Override
+      public String getTag() {
+        return "-css";
+      }
+
+      @Override
+      public boolean isRequired() {
+        return true;
+      }
+
+      @Override
+      public void setFile(File file) {
+        inputFile = file;
+        logger.log(TreeLogger.DEBUG, "inputFile = " + file.getAbsolutePath());
+      }
+    });
+  }
+
+  @Override
+  protected String getDescription() {
+    return "Create a CssResource interface based on a CSS file";
+  }
+
+  private void execImpl(String[] args) {
+    // Set up logging
+    PrintWriter logWriter = new PrintWriter(System.err);
+    logger = new PrintWriterTreeLogger(logWriter);
+    ((PrintWriterTreeLogger) logger).setMaxDetail(LOG_LEVEL);
+
+    // Process args or die
+    if (!processArgs(args)) {
+      System.exit(-1);
+    }
+
+    boolean error = false;
+    try {
+      System.out.println(process());
+    } catch (MalformedURLException e) {
+      logger.log(TreeLogger.ERROR, "Unable to load CSS", e);
+      error = true;
+    } catch (UnableToCompleteException e) {
+      logger.log(TreeLogger.ERROR, "Unable to process CSS", e);
+      error = true;
+    } finally {
+      // Make sure the logs are emitted
+      logWriter.flush();
+    }
+
+    System.exit(error ? -1 : 0);
+  }
+
+  /**
+   * Munge a CSS class name into a Java identifier.
+   */
+  private String methodName(String className) {
+    StringBuilder sb = new StringBuilder();
+    char c = className.charAt(0);
+    boolean nextUpCase = false;
+
+    if (Character.isJavaIdentifierStart(c)) {
+      sb.append(Character.toLowerCase(c));
+    }
+
+    for (int i = 1, j = className.length(); i < j; i++) {
+      c = className.charAt(i);
+      if (!Character.isJavaIdentifierPart(c)) {
+        nextUpCase = true;
+        continue;
+      }
+
+      if (nextUpCase) {
+        nextUpCase = false;
+        c = Character.toUpperCase(c);
+      }
+      sb.append(c);
+    }
+    return sb.toString();
+  }
+
+  private String process() throws MalformedURLException,
+      UnableToCompleteException {
+    // Create AST
+    CssStylesheet sheet = GenerateCssAst.exec(logger, inputFile.toURI().toURL());
+
+    // Sort all class names
+    Set<String> classNames = new TreeSet<String>(CSS_CLASS_COMPARATOR);
+    classNames.addAll(ExtractClassNamesVisitor.exec(sheet));
+
+    // Deduplicate method names
+    Set<String> methodNames = new HashSet<String>();
+
+    // Build the interface
+    SourceWriter sw = new StringSourceWriter();
+
+    int lastDot = interfaceName.lastIndexOf('.');
+    if (standaloneFile) {
+      sw.println("// DO NOT EDIT");
+      sw.println("// Automatically generated by "
+          + InterfaceGenerator.class.getName());
+      sw.println("package " + interfaceName.substring(0, lastDot) + ";");
+      sw.println("import " + CssResource.class.getCanonicalName() + ";");
+      sw.println("import " + ClassName.class.getCanonicalName() + ";");
+    }
+
+    sw.println("interface " + interfaceName.substring(lastDot + 1)
+        + " extends CssResource {");
+    sw.indent();
+    for (String className : classNames) {
+      String methodName = methodName(className);
+
+      while (!methodNames.add(methodName)) {
+        // Unusual, handles foo-bar and foo--bar
+        methodName += "_";
+      }
+
+      sw.println();
+      if (!methodName.equals(className)) {
+        sw.println("@ClassName(\"" + Generator.escape(className) + "\")");
+      }
+      sw.println("String " + methodName + "();");
+    }
+    sw.outdent();
+    sw.println("}");
+
+    return sw.toString();
+  }
+}
diff --git a/user/src/com/google/gwt/resources/css/Minify.java b/user/src/com/google/gwt/resources/css/Minify.java
index 5523312..b7fc260 100644
--- a/user/src/com/google/gwt/resources/css/Minify.java
+++ b/user/src/com/google/gwt/resources/css/Minify.java
@@ -117,7 +117,7 @@
     }
 
     try {
-      CssStylesheet sheet = GenerateCssAst.exec(logger, new URL[] {source});
+      CssStylesheet sheet = GenerateCssAst.exec(logger, source);
       TextOutput out = new DefaultTextOutput(!pretty);
       (new CssGenerationVisitor(out)).accept(sheet);
       System.out.println(out.toString());
diff --git a/user/src/com/google/gwt/resources/css/ast/CssSelector.java b/user/src/com/google/gwt/resources/css/ast/CssSelector.java
index b6067ce..44ebf53 100644
--- a/user/src/com/google/gwt/resources/css/ast/CssSelector.java
+++ b/user/src/com/google/gwt/resources/css/ast/CssSelector.java
@@ -21,7 +21,7 @@
  * An opaque view of a selector.
  */
 public class CssSelector extends CssNode {
-  public static final Pattern CLASS_SELECTOR_PATTERN = Pattern.compile("\\.([^ :>+#.]*)");
+  public static final Pattern CLASS_SELECTOR_PATTERN = Pattern.compile("\\.([^ \\[:>+#.]+)");
 
   /*
    * TODO: Evaluate whether or not we need to have a type hierarchy of
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
index b940167..522e044 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -78,7 +78,7 @@
 
   /**
    * @return the set of CSS classnames in the underlying .css files
-   *
+   * 
    * @throws UnableToCompleteException if the user has called for a .css file we
    *           can't find.
    */
@@ -89,7 +89,7 @@
       urls = getExternalCss();
     } else {
       try {
-        urls = new URL[] {getGeneratedFile().toURL()};
+        urls = new URL[] {getGeneratedFile().toURI().toURL()};
       } catch (MalformedURLException e) {
         throw new RuntimeException(e);
       }
diff --git a/user/test/com/google/gwt/resources/client/CSSResourceTest.java b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
index bdf8ee9..e1297ff 100644
--- a/user/test/com/google/gwt/resources/client/CSSResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
@@ -19,14 +19,12 @@
 import com.google.gwt.junit.client.GWTTestCase;
 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;
 
 /**
  * Contains various full-stack tests of the CssResource system.
  */
 public class CSSResourceTest extends GWTTestCase {
-
   interface ChildResources extends Resources {
     ChildResources INSTANCE = GWT.create(ChildResources.class);
 
@@ -64,6 +62,63 @@
     int rawInt();
   }
 
+  /**
+   * Regenerate this interface by running InterfaceGenerator over test.css.
+   */
+  interface FullTestCss extends CssResource {
+    String affectedBySprite();
+
+    String blahA();
+
+    String css3Color();
+
+    String externalA();
+
+    String externalB();
+
+    String externalC();
+
+    String extraSpriteClass();
+
+    @ClassName("FAIL")
+    String fAIL();
+
+    @ClassName("may-combine")
+    String mayCombine();
+
+    @ClassName("may-combine2")
+    String mayCombine2();
+
+    @ClassName("may-merge")
+    String mayMerge();
+
+    @ClassName("may-not-combine")
+    String mayNotCombine();
+
+    @ClassName("may-not-combine2")
+    String mayNotCombine2();
+
+    @ClassName("may-not-merge")
+    String mayNotMerge();
+
+    @ClassName("may-not-merge-or-combine-because-of-this")
+    String mayNotMergeOrCombineBecauseOfThis();
+
+    String multiClassA();
+
+    String multiClassB();
+
+    String replacement();
+
+    @ClassName("replacement-not-java-ident")
+    String replacementNotJavaIdent();
+
+    String spriteClass();
+
+    @ClassName("this-does-not-matter")
+    String thisDoesNotMatter();
+  }
+
   interface HasDescendants extends CssResource {
     String foo();
   }
@@ -116,8 +171,7 @@
     MyCssResourceB b();
 
     @Source("test.css")
-    @NotStrict
-    MyCssResourceWithSprite css();
+    FullTestCss css();
 
     @Source("32x32.png")
     DataResource dataMethod();
@@ -186,7 +240,7 @@
   }
 
   public void testCss() {
-    MyCssResourceWithSprite css = Resources.INSTANCE.css();
+    FullTestCss css = Resources.INSTANCE.css();
     String text = css.getText();
     report(text);
 
@@ -202,7 +256,7 @@
     assertFalse("replacement".equals(css.replacement()));
     assertTrue(text.contains("." + css.replacement()));
     assertTrue(text.contains("." + css.replacement() + ":after"));
-    assertTrue(text.contains("." + css.nameOverride()));
+    assertTrue(text.contains("." + css.replacementNotJavaIdent()));
 
     // Make sure renaming for multi-class selectors (.foo.bar) works
     assertFalse("multiClassA".equals(css.multiClassA()));
@@ -218,8 +272,8 @@
 
     // Check interestingly-named idents
     assertTrue(text.contains("\\-some-wacky-extension"));
-    assertTrue(text.contains(".ns\\:tag"));
-    assertTrue(text.contains(".ns\\:tag:pseudo"));
+    assertTrue(text.contains("ns\\:tag"));
+    assertTrue(text.contains("ns\\:tag:pseudo"));
 
     // Check escaped string values
     assertTrue(text.contains("\"Hello\\\\\\\" world\""));
@@ -247,12 +301,14 @@
     assertTrue(text.indexOf("static:PASSED") < text.indexOf("runtime:PASSED"));
     assertTrue(text.indexOf("before:merge") != -1);
     assertTrue(text.indexOf("before:merge") < text.indexOf("after:merge"));
-    assertTrue(text.indexOf(".may-combine,.may-combine2") != -1);
+    assertTrue(text.indexOf("." + css.mayCombine() + ",." + css.mayCombine2()) != -1);
     assertTrue(text.indexOf("merge:merge") != -1);
-    assertTrue(text.indexOf("merge:merge") < text.indexOf("may-not-combine"));
+    assertTrue(text.indexOf("merge:merge") < text.indexOf("."
+        + css.mayNotCombine()));
     assertTrue(text.indexOf("may-not-combine") < text.indexOf("prevent:true"));
     assertTrue(text.indexOf("prevent:true") < text.indexOf("prevent-merge:true"));
-    assertTrue(text.indexOf("prevent:true") < text.indexOf("may-not-combine2"));
+    assertTrue(text.indexOf("prevent:true") < text.indexOf("."
+        + css.mayNotCombine2()));
 
     // Check commonly-used CSS3 constructs
     assertTrue(text.contains("background-color:rgba(0,0,0,0.5);"));
diff --git a/user/test/com/google/gwt/resources/client/test.css b/user/test/com/google/gwt/resources/client/test.css
index 76059d4..e33311d 100644
--- a/user/test/com/google/gwt/resources/client/test.css
+++ b/user/test/com/google/gwt/resources/client/test.css
@@ -112,11 +112,11 @@
   top: literal("expression(document.compatMode==\"CSS1Compat\" ? documentElement.scrollTop:document.body.scrollTop \\ 2)");
 }
 
-.ns\:tag {
+ns\:tag {
   border: red;
 }
 
-.ns\:tag:pseudo {
+ns\:tag:pseudo {
   content : "blah";
 }
 
diff --git a/user/test/com/google/gwt/resources/css/CssNodeClonerTest.java b/user/test/com/google/gwt/resources/css/CssNodeClonerTest.java
index 517de81..7467a60 100644
--- a/user/test/com/google/gwt/resources/css/CssNodeClonerTest.java
+++ b/user/test/com/google/gwt/resources/css/CssNodeClonerTest.java
@@ -24,7 +24,6 @@
 import com.google.gwt.resources.css.ast.CssStylesheet;
 import com.google.gwt.resources.rg.CssTestCase;
 
-import java.net.URL;
 import java.util.List;
 
 /**
@@ -34,8 +33,8 @@
 
   public void testClone() throws UnableToCompleteException {
     CssStylesheet sheet = GenerateCssAst.exec(TreeLogger.NULL,
-        new URL[] {getClass().getClassLoader().getResource(
-            "com/google/gwt/resources/client/test.css")});
+        getClass().getClassLoader().getResource(
+            "com/google/gwt/resources/client/test.css"));
 
     CssStylesheet cloned = CssNodeCloner.clone(CssStylesheet.class, sheet);
 
@@ -45,8 +44,8 @@
 
   public void testCloneList() throws UnableToCompleteException {
     CssStylesheet sheet = GenerateCssAst.exec(TreeLogger.NULL,
-        new URL[] {getClass().getClassLoader().getResource(
-            "com/google/gwt/resources/client/test.css")});
+        getClass().getClassLoader().getResource(
+            "com/google/gwt/resources/client/test.css"));
 
     List<CssNode> cloned = CssNodeCloner.clone(CssNode.class, sheet.getNodes());
 
diff --git a/user/test/com/google/gwt/resources/rg/CssTestCase.java b/user/test/com/google/gwt/resources/rg/CssTestCase.java
index 33cbc0b..2974160 100644
--- a/user/test/com/google/gwt/resources/rg/CssTestCase.java
+++ b/user/test/com/google/gwt/resources/rg/CssTestCase.java
@@ -144,8 +144,8 @@
     CssStylesheet testSheet = null;
 
     try {
-      expectedSheet = GenerateCssAst.exec(logger, new URL[] {expected});
-      testSheet = GenerateCssAst.exec(logger, new URL[] {test});
+      expectedSheet = GenerateCssAst.exec(logger, expected);
+      testSheet = GenerateCssAst.exec(logger, test);
     } catch (UnableToCompleteException e) {
       fail("Unable to parse stylesheet");
     }