Revision: 3856
Author: [email protected]
Date: Thu Nov 19 15:57:15 2009
Log: Error message and test cleanup
http://codereview.appspot.com/157079

Changes error messages so they make sense both from linter and from cajoler..
And make some test code available to the ancillary tools.
Fix a semicolon warning issue after labeled blocks as in
   foo: {}
Add class=nocode to HTML error snippets so the filename is not prettified.

[email protected]

http://code.google.com/p/google-caja/source/detail?r=3856

Modified:
 /trunk/build.xml
 /trunk/src/com/google/caja/cajita-module.js
 /trunk/src/com/google/caja/parser/html/HtmlQuasiBuilder.java
 /trunk/src/com/google/caja/parser/js/LabeledStmtWrapper.java
 /trunk/src/com/google/caja/parser/js/Operation.java
 /trunk/src/com/google/caja/parser/js/Parser.java
 /trunk/src/com/google/caja/plugin/PluginMessageType.java
 /trunk/src/com/google/caja/render/JsMinimalPrinter.java
 /trunk/src/com/google/caja/reporting/HtmlSnippetProducer.java
 /trunk/src/com/google/caja/util/Maps.java
 /trunk/tests/com/google/caja/opensocial/DefaultGadgetRewriterTest.java
 /trunk/tests/com/google/caja/parser/js/ExpressionTest.java
 /trunk/tests/com/google/caja/plugin/CssValidatorTest.java
 /trunk/tests/com/google/caja/reporting/HtmlSnippetProducerTest.java
 /trunk/tests/com/google/caja/service/FetchedDataTest.java
 /trunk/tests/com/google/caja/service/TestHttpServletRequest.java
 /trunk/tests/com/google/caja/service/TestHttpServletResponse.java

=======================================
--- /trunk/build.xml    Wed Nov 18 16:37:58 2009
+++ /trunk/build.xml    Thu Nov 19 15:57:15 2009
@@ -26,7 +26,7 @@
   -   benchmarks :  runs the benchmarks
   -   clean      :  wipes generated files
   -   demos      :  demo files
-  -   docs       :  javadoc, jsdocs, and rule docs
+  -   docs       :  javadocs, jsdocs, and rule docs
   -   jars       :  build the binary distribution
   -   jars       :  build the binary distribution
   -   pluginc    :  the plugin compiler
@@ -112,7 +112,7 @@
   </path>
   <path id="classpath.javadocs">
     <path refid="classpath.compile"/>
-    <path refid="classpath.ant"/>
+    <path refid="classpath.tools"/>
   </path>

   <!--== Tasks Used Below ==-->
=======================================
--- /trunk/src/com/google/caja/cajita-module.js Fri Nov 13 13:37:15 2009
+++ /trunk/src/com/google/caja/cajita-module.js Thu Nov 19 15:57:15 2009
@@ -157,6 +157,7 @@
     var xhr = bridal.makeXhr();
     xhr.onreadystatechange = function() {
       if (xhr.readyState === 4) {
+        xhr.onreadystatechange = noop;  // avoid memory leak
         if (xhr.status === 200) {
           var savedModuleHandler = ___.getNewModuleHandler();
           ___.setNewModuleHandler(___.primFreeze({
@@ -183,7 +184,6 @@
           //TODO: validate the response before eval it
           eval(xhr.responseText);
           ___.setNewModuleHandler(savedModuleHandler);
-          xhr.onreadystatechange = noop;  // avoid memory leak
         } else {
           r.resolve(Q.reject(
               "Retrieving the module " + mid + " failed, "
=======================================
--- /trunk/src/com/google/caja/parser/html/HtmlQuasiBuilder.java Fri Nov 13 11:43:08 2009 +++ /trunk/src/com/google/caja/parser/html/HtmlQuasiBuilder.java Thu Nov 19 15:57:15 2009
@@ -360,6 +360,9 @@

       String quasiIdentifier = m.group(1);
       Object binding = bindings.get(quasiIdentifier);
+      if (!(binding instanceof String)) {
+        throw new ClassCastException("@" + quasiIdentifier);
+      }
       Escaping.escapeXml((String) binding, false, sb);
     } while (m.find());
     sb.append(unescaped, pos, unescaped.length());
=======================================
--- /trunk/src/com/google/caja/parser/js/LabeledStmtWrapper.java Thu Nov 12 13:05:03 2009 +++ /trunk/src/com/google/caja/parser/js/LabeledStmtWrapper.java Thu Nov 19 15:57:15 2009
@@ -68,6 +68,11 @@
     }
     body.render(rc);
   }
+
+  @Override
+  public boolean isTerminal() {
+    return body.isTerminal();
+  }

   public boolean hasHangingConditional() {
     return body.hasHangingConditional();
=======================================
--- /trunk/src/com/google/caja/parser/js/Operation.java Wed Nov 18 16:37:58 2009 +++ /trunk/src/com/google/caja/parser/js/Operation.java Thu Nov 19 15:57:15 2009
@@ -533,6 +533,15 @@
         } else {
           return left;
         }
+      } else {
+        bv = right.conditionResult();
+        // foo != bar && true -> foo != bar
+        if (bv != null && bv.booleanValue() == (op == Operator.LOGICAL_AND)
+            && "boolean".equals(left.typeOf())
+            && "boolean".equals(right.typeOf())
+            && right.simplifyForSideEffect() == null) {
+          return left;
+        }
       }
     } else if (op == Operator.MEMBER_ACCESS) {
       if (left instanceof StringLiteral) {
@@ -578,6 +587,19 @@
       if (lhs instanceof Number && rhs instanceof Number) {
         double a = ((Number) lhs).doubleValue();
         double b = ((Number) rhs).doubleValue();
+        if (isIntOp(op) && !Double.isNaN(a) && !Double.isNaN(b)) {
+          long result;
+          switch (op) {
+            case BITWISE_AND: result = toInt32(a)  &   toInt32(b); break;
+            case BITWISE_OR:  result = toInt32(a)  |   toInt32(b); break;
+            case BITWISE_XOR: result = toInt32(a)  ^   toInt32(b); break;
+            case LSHIFT:      result = toInt32(a)  <<  toUint32(b); break;
+            case RSHIFT:      result = toInt32(a)  >>  toUint32(b); break;
+            case RUSHIFT:     result = toUint32(a) >>> toUint32(b); break;
+            default: return this;
+          }
+          return new IntegerLiteral(pos, result);
+        }
         double result;
         switch (op) {
           case ADDITION: result = a + b; break;
@@ -640,4 +662,26 @@
     }
     return null;
   }
-}
+
+  private static boolean isIntOp(Operator op) {
+    switch (op) {
+      case BITWISE_AND:
+      case BITWISE_OR:
+      case BITWISE_XOR:
+      case LSHIFT:
+      case RSHIFT:
+      case RUSHIFT:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  private static long toInt32(double n) {
+    return (int) n;
+  }
+
+  private static long toUint32(double n) {
+    return ((long) n) & 0xffffffffL;
+  }
+}
=======================================
--- /trunk/src/com/google/caja/parser/js/Parser.java Mon Nov 16 17:29:55 2009 +++ /trunk/src/com/google/caja/parser/js/Parser.java Thu Nov 19 15:57:15 2009
@@ -1256,6 +1256,9 @@
   }

   private static boolean isTerminal(Statement s) {
+    if (s instanceof LabeledStmtWrapper) {
+      return isTerminal(((LabeledStmtWrapper) s).getBody());
+    }
     return ((s instanceof Loop && !(s instanceof DoWhileLoop))
             || s instanceof Conditional || s instanceof FunctionDeclaration
             || s instanceof Block || s instanceof TryStmt
@@ -1265,9 +1268,7 @@

   private Statement parseTerminatedStatement() throws ParseException {
     Statement s = parseStatement();
-    if (!isTerminal(s)) {
-      checkSemicolon();
-    }
+    if (!isTerminal(s)) { checkSemicolon(); }
     return s;
   }

=======================================
--- /trunk/src/com/google/caja/plugin/PluginMessageType.java Wed Sep 9 11:41:56 2009 +++ /trunk/src/com/google/caja/plugin/PluginMessageType.java Thu Nov 19 15:57:15 2009
@@ -32,7 +32,7 @@
       "%s: access not allowed to global %s", MessageLevel.FATAL_ERROR),
   UNSAFE_ACCESS(
"%s: unsafe access to protected namespace: %s", MessageLevel.FATAL_ERROR),
-  UNKNOWN_TAG("%s: removing unknown tag %s", MessageLevel.WARNING),
+  UNKNOWN_TAG("%s: unknown tag %s", MessageLevel.WARNING),
   UNSAFE_TAG("%s: removing disallowed tag %s", MessageLevel.WARNING),
   MISSING_ATTRIBUTE("%s: expected param %s on %s", MessageLevel.ERROR),
   UNKNOWN_ATTRIBUTE("%s: removing unknown attribute %s on %s",
@@ -50,7 +50,7 @@
DISALLOWED_URI("%s: url %s cannot be linked to", MessageLevel.FATAL_ERROR),
   MALFORMED_URL("%s: malformed url %s", MessageLevel.FATAL_ERROR),
   MALFORMED_CSS_PROPERTY_VALUE(
- "%s: removing css property %s with bad value: %s", MessageLevel.WARNING),
+      "%s: css property %s has bad value: %s", MessageLevel.WARNING),
   DISALLOWED_CSS_PROPERTY_IN_SELECTOR(
       "%s: css property %s not allowed in :visited selector at %s",
       MessageLevel.ERROR),
=======================================
--- /trunk/src/com/google/caja/render/JsMinimalPrinter.java Mon Nov 2 13:56:19 2009 +++ /trunk/src/com/google/caja/render/JsMinimalPrinter.java Thu Nov 19 15:57:15 2009
@@ -44,7 +44,7 @@
   }

   /** Visible for testing.  Should not be used by clients. */
-  void setLineLengthLimit(int lineLengthLimit) {
+  public void setLineLengthLimit(int lineLengthLimit) {
     this.lineLengthLimit = lineLengthLimit;
   }

=======================================
--- /trunk/src/com/google/caja/reporting/HtmlSnippetProducer.java Wed Aug 6 20:25:46 2008 +++ /trunk/src/com/google/caja/reporting/HtmlSnippetProducer.java Thu Nov 19 15:57:15 2009
@@ -58,7 +58,7 @@
     StringBuilder filename = new StringBuilder();
     pos.source().format(mc, filename);

-    out.append("<a href=\"#\" class=\"filepos\" onclick=\"selectLine(")
+ out.append("<a href=\"#\" class=\"filepos nocode\" onclick=\"selectLine(")
       .append(html(js(pos.source().getUri().toString())))
       .append(",")
       .append(String.valueOf(pos.startLineNo()))
=======================================
--- /trunk/src/com/google/caja/util/Maps.java   Wed Nov 18 16:37:58 2009
+++ /trunk/src/com/google/caja/util/Maps.java   Thu Nov 19 15:57:15 2009
@@ -16,6 +16,7 @@

 import java.util.Collections;
 import java.util.Comparator;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
@@ -94,25 +95,59 @@

   public static final class ImmutableMapBuilder<K, V> {
     private Map<K, V> map;
+    private boolean canUseEnumMap = true;
+    @SuppressWarnings("unchecked")
+    private Class<? extends Enum> enumKeyType;
     ImmutableMapBuilder(Map<K, V> emptyMap) { this.map = emptyMap; }

     public ImmutableMapBuilder<K, V> put(K key, V value) {
+      if (canUseEnumMap) {
+        if (enumKeyType != null) {
+ if (!enumKeyType.isInstance(key)) { // Values from different enums
+            canUseEnumMap = false;
+            enumKeyType = null;
+          }
+        } else if (key instanceof Enum) {
+          enumKeyType = Enum.class.cast(key).getClass();
+        } else {
+          canUseEnumMap = false;
+        }
+      }
       map.put(key, value);
       return this;
     }

     public ImmutableMapBuilder<K, V> putAll(Map<K, V> map) {
-      map.putAll(map);
+      if (canUseEnumMap) {
+        for (Map.Entry<K, V> e : map.entrySet()) {
+          put(e.getKey(), e.getValue());
+        }
+      } else {
+        map.putAll(map);
+      }
       return this;
     }

     public Map<K, V> create() {
-      Map<K, V> map = this.map;
+      if (this.map.isEmpty()) { return Collections.<K, V>emptyMap(); }
+      Map<K, V> map;
+      if (canUseEnumMap) {
+        map = Maps.<K, V>makeEnumMap(enumKeyType);
+        map.putAll(this.map);
+      } else {
+        map = this.map;
+      }
       if (map == null) { throw new IllegalStateException(); }
       this.map = null;
       return Collections.unmodifiableMap(map);
     }
   }
+
+ // This is legit because enumKeyType above is both an enum type (checked at
+  // runtime in the EnumMap ctor) and is the type of a subclass of K.
+  @SuppressWarnings("unchecked")
+  private static <K, V>
+  Map<K, V> makeEnumMap(Class<? extends Enum> t) { return new EnumMap(t); }

   private Maps() {}
 }
=======================================
--- /trunk/tests/com/google/caja/opensocial/DefaultGadgetRewriterTest.java Fri Nov 13 13:37:15 2009 +++ /trunk/tests/com/google/caja/opensocial/DefaultGadgetRewriterTest.java Thu Nov 19 15:57:15 2009
@@ -1,4 +1,5 @@
 // Copyright (C) 2007 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
@@ -130,7 +131,7 @@
   public final void testStylesSanitized() throws Exception {
     assertRewritesWithMessage(
         "<p style=\"color: expression(foo)\">Bar</p>",
- "removing css property color with bad value: ==>expression(foo)<==",
+        "css property color has bad value: ==>expression(foo)<==",
         MessageLevel.WARNING, false /* should not fail */);
   }

=======================================
--- /trunk/tests/com/google/caja/parser/js/ExpressionTest.java Wed Nov 18 16:37:58 2009 +++ /trunk/tests/com/google/caja/parser/js/ExpressionTest.java Thu Nov 19 15:57:15 2009
@@ -166,6 +166,15 @@
     assertFolded("!void foo()", "!(void foo())");
     assertFolded("false", "!(4,true)");
     assertFolded("! (foo() || true)", "!(foo()||true)");
+    assertFolded("false", "false && foo()");
+    assertFolded("true", "true || foo()");
+    assertFolded("foo()", "false || foo()");
+    assertFolded("foo()", "true && foo()");
+    assertFolded("foo != bar", "foo != bar && true");
+    assertFolded("foo != bar", "foo != bar || false");
+    // Can't fold.  foo() might return non-boolean
+    assertFolded("foo() && true", "foo() && true");
+    assertFolded("foo() || false", "foo() || false");
     assertFolded("true", "'foo' == 'foo'");
     assertFolded("true", "'foo' === 'foo'");
     assertFolded("false", "'foo' == 'bar'");
@@ -187,6 +196,24 @@
     assertFolded("1.0", "1 % -3");
     assertFolded("-1.0", "-1 % 3");
     assertFolded("-1.0", "-1 % -3");
+    assertFolded("1024", "1 << 10");
+    assertFolded("" + (1 << 20), "1 << 20");
+    assertFolded("2", "4 >> 1");
+    assertFolded("1", "4 >> 2");
+    assertFolded("0", "4 >> 3");
+    assertFolded("-1", "-1 >> 1");
+    assertFolded("" + (-1 >>> 1), "-1 >>> 1");
+    assertFolded("1", "4 >>> 2");
+    assertFolded("0", "1 & 2");
+    assertFolded("2", "2 & 3");
+    assertFolded("2", "3 & 2");
+    assertFolded("3", "1 | 2");
+    assertFolded("7", "6 | 5");
+    assertFolded("11", "3 | 9");
+    assertFolded("0", "0 ^ 0");
+    assertFolded("0", "1 ^ 1");
+    assertFolded("3", "1 ^ 2");
+    assertFolded("-2", "-1 ^ 1");
     assertFolded("4.0", "4.0");
     assertFolded("4.0", "+4.0");
     assertFolded("-1", "~0");
=======================================
--- /trunk/tests/com/google/caja/plugin/CssValidatorTest.java Wed Sep 9 11:41:56 2009 +++ /trunk/tests/com/google/caja/plugin/CssValidatorTest.java Thu Nov 19 15:57:15 2009
@@ -199,7 +199,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value: "
+            "WARNING: css property font has bad value: "
             + "status-bar  ==><==  caption");

     // size and family
@@ -235,7 +235,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " -12pt  ==>url('Arial')<==");
     runTest("p, dl { font: twelve Arial; }",
             "StyleSheet\n"
@@ -292,7 +292,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " ==>150Arial<==");
     runTest("p, dl { font: 150/Arial; }",
             "StyleSheet\n"
@@ -304,7 +304,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " 150 /  ==>Arial<==");
     runTest("p, dl { font: medium Arial; }",
             "StyleSheet\n"
@@ -385,7 +385,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " italic  ==>bolderer<==  150% Arial");
     runTest("p, dl { font: italix bolder 150% Arial; }",
             "StyleSheet\n"
@@ -397,7 +397,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " ==>italix<==  bolder 150% Arial");

     // font-size also matches by previous terms
@@ -431,7 +431,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value: inherit");
+            "WARNING: css property font has bad value: inherit");

     // weight size family
     runTest("p, dl { font: 800 150% Arial; }",
@@ -517,7 +517,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " ==>abnormal<==  150% Arial");

     // with line-height following /
@@ -564,7 +564,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " ==>abnormal<==  150% / 175% Arial");
     runTest("p, dl { font: normal 800 150%/ Arial; }",
             "StyleSheet\n"
@@ -576,7 +576,7 @@
             + "      SimpleSelector\n"
             + "        IdentLiteral : dl\n"
             + "    EmptyDeclaration",
-            "WARNING: removing css property font with bad value:"
+            "WARNING: css property font has bad value:"
             + " normal 800 150% /  ==>Arial<==");
     runTest("p, dl { font: normal 800 150%/17.5 Arial; }",
             "StyleSheet\n"
@@ -1356,7 +1356,7 @@
             + "    Selector\n"
             + "      SimpleSelector\n"
             + "        IdentLiteral : p\n",
-            "WARNING: removing css property filter with bad value:"
+            "WARNING: css property filter has bad value:"
             + " ==>progid:foo.bar()<==");
runTest("p { filter: progid:dximagetransform.microsoft.alpha(opaquity=50) }",
             "StyleSheet\n"
@@ -1364,7 +1364,7 @@
             + "    Selector\n"
             + "      SimpleSelector\n"
             + "        IdentLiteral : p\n",
-            "WARNING: removing css property filter with bad value:"
+            "WARNING: css property filter has bad value:"
+ " ==>progid:dximagetransform.microsoft.alpha(opaquity=50)<==");
   }

@@ -1397,7 +1397,7 @@
             + "    Selector\n"
             + "      SimpleSelector\n"
             + "        IdentLiteral : p\n",
- "WARNING: removing css property color with bad value: ==>yelow<==");
+            "WARNING: css property color has bad value: ==>yelow<==");
   }

   public final void testHtmlStarHack() throws Exception {
=======================================
--- /trunk/tests/com/google/caja/reporting/HtmlSnippetProducerTest.java Thu Jul 23 09:16:30 2009 +++ /trunk/tests/com/google/caja/reporting/HtmlSnippetProducerTest.java Thu Nov 19 15:57:15 2009
@@ -32,7 +32,7 @@
         pos, MessagePart.Factory.valueOf("http://<h1>foo</h1>")));
     assertEquals(
         ""
-        + "<a href=\"#\" class=\"filepos\""
+        + "<a href=\"#\" class=\"filepos nocode\""
+ " onclick=\"selectLine(&#39;test:///testSnippetEscaped&#39;,1)\">"
         + "testSnippetEscaped:1</a>"
         + ": &lt;style&gt;background: url("
=======================================
--- /trunk/tests/com/google/caja/service/FetchedDataTest.java Thu Oct 8 15:42:37 2009 +++ /trunk/tests/com/google/caja/service/FetchedDataTest.java Thu Nov 19 15:57:15 2009
@@ -58,7 +58,7 @@
     testUrl = URI.create("http://www.example.com/";).toURL();
   }

-  public void testSimpleContent() throws Exception {
+  public final void testSimpleContent() throws Exception {
     FetchedData fd = new FetchedData(
         new TestURLConnection(testUrl, "abcdef", "text/html"));
     assertEquals("text/html", fd.getContentType());
@@ -76,7 +76,7 @@
     assertEquals(expectedCharSet, fd.getCharSet());
   }

-  public void testCharSetParsing() throws Exception {
+  public final void testCharSetParsing() throws Exception {
     assertCharSet(
         "iso-8859-1",
         "text/html;charset=iso-8859-1");
=======================================
--- /trunk/tests/com/google/caja/service/TestHttpServletRequest.java Fri Nov 13 11:43:08 2009 +++ /trunk/tests/com/google/caja/service/TestHttpServletRequest.java Thu Nov 19 15:57:15 2009
@@ -14,18 +14,20 @@

 package com.google.caja.service;

+import com.google.caja.util.Maps;
 import com.google.caja.util.Strings;

 import java.io.BufferedReader;
-import java.io.UnsupportedEncodingException;
 import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
 import java.io.IOException;
 import java.net.URLDecoder;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.Enumeration;
-import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -39,15 +41,15 @@
 /**
  * @author [email protected] (Jasvir Nagra)
  */
-final class TestHttpServletRequest implements HttpServletRequest {
+public final class TestHttpServletRequest implements HttpServletRequest {
   private final String queryString;
-  private final Hashtable<String, List<String>> params
-      = new Hashtable<String, List<String>>();
+  private final Map<String, List<String>> params = Maps.newHashMap();
   private final byte[] content;
   private final String contentType;
   private final String characterEncoding;
-
-  TestHttpServletRequest(String queryString) {
+  private final Map<String, String> headers = Maps.newHashMap();
+
+  public TestHttpServletRequest(String queryString) {
     this.queryString = queryString;
     this.content = new byte[0];
     this.contentType = null;
@@ -80,17 +82,20 @@

public String getAuthType() { throw new UnsupportedOperationException(); } public Cookie[] getCookies() { throw new UnsupportedOperationException(); }
+  @SuppressWarnings("deprecation")
   public long getDateHeader(String a) {
-    throw new UnsupportedOperationException();
+    String h = headers.get(a);
+    return h != null ? new Date(h).getTime() : -1;
   }
   public String getHeader(String a) {
-    throw new UnsupportedOperationException();
+    return headers.get(a);
   }
   public Enumeration<String> getHeaderNames() {
-    throw new UnsupportedOperationException();
+    return enumeration(headers.keySet().iterator());
   }
   public int getIntHeader(String arg0) {
-    throw new UnsupportedOperationException();
+    String h = headers.get(arg0);
+    return h != null ? Integer.valueOf(h) : -1;
   }
   public String getMethod() { throw new UnsupportedOperationException(); }
public String getPathInfo() { throw new UnsupportedOperationException(); }
@@ -146,7 +151,9 @@
   public String getParameter(String k) {
     return params.containsKey(k) ? params.get(k).get(0) : null;
   }
-  public Enumeration<?> getParameterNames() { return params.keys(); }
+  public Enumeration<String> getParameterNames() {
+    return enumeration(params.keySet().iterator());
+  }
   public String[] getParameterValues(String k) {
     List<String> vals = params.get(k);
     return vals != null ? vals.toArray(new String[0]) : null;
@@ -223,4 +230,11 @@
   public void setCharacterEncoding(String encodingName) {
     throw new UnsupportedOperationException();
   }
-}
+
+  private static <T> Enumeration<T> enumeration(final Iterator<T> it) {
+    return new Enumeration<T>() {
+      public boolean hasMoreElements() { return it.hasNext(); }
+      public T nextElement() { return it.next(); }
+    };
+  }
+}
=======================================
--- /trunk/tests/com/google/caja/service/TestHttpServletResponse.java Fri Nov 13 11:43:08 2009 +++ /trunk/tests/com/google/caja/service/TestHttpServletResponse.java Thu Nov 19 15:57:15 2009
@@ -14,6 +14,7 @@

 package com.google.caja.service;

+import com.google.caja.util.Maps;
 import com.google.caja.util.Strings;

 import java.io.ByteArrayOutputStream;
@@ -24,8 +25,8 @@
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.util.Date;
-import java.util.Hashtable;
 import java.util.Locale;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;

@@ -36,9 +37,9 @@
 /**
  * @author [email protected] (Jasvir Nagra)
  */
-final class TestHttpServletResponse implements HttpServletResponse {
+public final class TestHttpServletResponse implements HttpServletResponse {
   private int status = 200;
- private Hashtable<String, String> headers = new Hashtable<String, String>();
+  private Map<String, String> headers = Maps.newLinkedHashMap();
   private Object output;
public void addCookie(Cookie a) { throw new UnsupportedOperationException(); } public boolean containsHeader(String n) { return headers.containsKey(n); }
@@ -66,14 +67,14 @@
     throw new UnsupportedOperationException();
   }
   public void setDateHeader(String arg0, long arg1) {
-    throw new UnsupportedOperationException();
+    setHeader(arg0, new Date(arg1).toString());
   }
   public void setHeader(String k, String v) {
     if (output != null) { throw new IllegalStateException(); }
     headers.put(Strings.toLowerCase(k), v);
   }
   public void setIntHeader(String arg0, int arg1) {
-    throw new UnsupportedOperationException();
+    setHeader(arg0, "" + arg1);
   }
   public void setStatus(int status) {
     if (output != null) { throw new IllegalStateException(); }
@@ -167,4 +168,6 @@
   public void setLocale(Locale arg0) {
     throw new UnsupportedOperationException();
   }
-}
+
+  public Map<String, String> getHeaders() { return headers; }
+}

Reply via email to