Fix various errors in FileURLConnectionTest.

- First, the test was broken pretty badly. It would pass a
  Jar style URL to a FileURLConnection and expect it to
  work, this is out of spec and seems wrong in general
  (file:///foo.apk!resources/foo.txt).

- The RI supports various "headers" on file URL connections
  (sigh). As if the getHeaderField(int) and getHeaderFieldKey(int)
  APIs weren't bad enough. This has now been implemented with
  a naive implementation.

- Fixes FileURLConnection to populate a header map for
  information it provides (content length, content type
  etc.)

bug: 11664881

Change-Id: I7a3e9aaa79bf125abbcfe8367574115ce54718e3
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index b976c67..01f6d91 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1623,15 +1623,6 @@
   ]
 },
 {
-  description: "Known failures in dealing with # in file / jar URLs",
-  bug: 11664881,
-  names: [
-    "org.apache.harmony.tests.internal.net.www.protocol.file.FileURLConnectionTest#testGetContentType",
-    "org.apache.harmony.tests.internal.net.www.protocol.file.FileURLConnectionTest#testGetInputStream",
-    "org.apache.harmony.tests.internal.net.www.protocol.file.FileURLConnectionTest#testHeaderFunctions"
-  ]
-},
-{
   description: "Known failures in SerializationStressTest",
   bug: 11668227,
   names: [
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java
index 317ba5c..1f4ad1f 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java
@@ -14,12 +14,18 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package tests.api.internal.net.www.protocol.file;
+package org.apache.harmony.tests.internal.net.www.protocol.file;
 
-import junit.framework.TestCase;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URL;
 import java.net.URLConnection;
+import junit.framework.TestCase;
+import libcore.io.Streams;
 import libcore.net.url.FileURLConnection;
 
 /**
@@ -27,53 +33,81 @@
  */
 public class FileURLConnectionTest extends TestCase {
 
-    static String getContentType(String fileName) throws IOException {
+    private static final String RESOURCE_NAME = "resources/test.rtf";
+
+    private final ClassLoader loader = FileURLConnectionTest.class.getClassLoader();
+
+    private URL createTempFileWithContent(String resourceName) throws IOException {
+        InputStream is = null;
+        OutputStream os = null;
+        try {
+            final URL url = loader.getResource(resourceName);
+            assertNotNull("Cannot find test resource " + resourceName, url);
+            is = url.openStream();
+            File file = File.createTempFile("FileURLConnectionTest",
+                    resourceName.substring(resourceName.indexOf(".")));
+            os = new BufferedOutputStream(new FileOutputStream(file));
+            Streams.copy(is, os);
+
+            return new URL("file://" + file.getAbsolutePath());
+        } finally {
+            if (is != null) {
+                is.close();
+            }
+
+            if (os != null) {
+                os.close();
+            }
+        }
+    }
+
+    private String getContentType(String fileName) throws IOException {
         String resourceName = "resources/" + fileName;
-        URL url = ClassLoader.getSystemClassLoader().getResource(resourceName);
-        assertNotNull("Cannot find test resource " + resourceName, url);
+        URL url = createTempFileWithContent(resourceName);
         return new FileURLConnection(url).getContentType();
     }
 
     public void testGetContentType() throws IOException {
         // Regression for HARMONY-4699
-        assertEquals("application/rtf", getContentType("test.rtf"));
-        assertEquals("text/plain", getContentType("test.java"));
+        assertEquals("text/rtf", getContentType("test.rtf"));
         // RI would return "content/unknown"
         assertEquals("application/msword", getContentType("test.doc"));
         assertEquals("text/html", getContentType("test.htx"));
-        assertEquals("application/xml", getContentType("test.xml"));
-        assertEquals("text/plain", getContentType("."));
+        assertEquals("text/xml", getContentType("test.xml"));
+        assertEquals("text/html",
+                new FileURLConnection(new URL("file:///")).getContentType());
     }
 
     public void testGetInputStream() throws IOException {
         // Regression for Harmony-5737
-        String resourceName = "resources/" + "test.rtf";
-        URL url = ClassLoader.getSystemClassLoader().getResource(resourceName);
+        URL url = createTempFileWithContent(RESOURCE_NAME);
         assertNotNull(url);
         URL anchorUrl = new URL(url, "#anchor");
-        assertNotNull("Cannot find test resource " + resourceName, anchorUrl);
+        assertNotNull("Cannot find test resource " + RESOURCE_NAME, anchorUrl);
 
         FileURLConnection conn = new FileURLConnection(anchorUrl);
         assertNotNull(conn.getInputStream());
+    }
 
+    public void testGetInputStream_localHost() throws IOException {
         // Regression for Harmony-5779
+        URL url = createTempFileWithContent(RESOURCE_NAME);
         String localURLString = "file://localhost/" + url.getFile();
         URL localURL = new URL(localURLString);
-        conn = new FileURLConnection(localURL);
+        FileURLConnection conn = new FileURLConnection(localURL);
         assertNotNull(conn.getInputStream());
         assertEquals("file", conn.getURL().getProtocol());
     }
 
     public void testHeaderFunctions() throws IOException {
-        String resourceName = "resources/test.rtf";  //folder name
-        URL url = ClassLoader.getSystemClassLoader().getResource(resourceName);
+        URL url = createTempFileWithContent(RESOURCE_NAME);
         FileURLConnection conn = new FileURLConnection(url);
         assertNotNull(conn.getInputStream());
         assertEquals(conn.getContentType(), conn.getHeaderField("content-type"));
 
-        resourceName = "resources/" + "test.rtf";
-        url = ClassLoader.getSystemClassLoader().getResource(resourceName);
+        url = createTempFileWithContent(RESOURCE_NAME);
         conn = new FileURLConnection(url);
+
         assertNotNull(conn.getInputStream());
         assertEquals(conn.getContentType(), conn.getHeaderField("content-type"));
         assertEquals(Integer.toString(conn.getContentLength()),
@@ -89,8 +123,7 @@
     }
 
     public void testHeader_BoundaryCheck() throws IOException {
-        String resourceName = "resources/test.rtf";
-        URL url = ClassLoader.getSystemClassLoader().getResource(resourceName);
+        URL url = createTempFileWithContent(RESOURCE_NAME);
         URLConnection urlConnection = url.openConnection();
         assertNull(urlConnection.getHeaderField(Integer.MIN_VALUE));
         assertNull(urlConnection.getHeaderField(Integer.MAX_VALUE));
diff --git a/luni/src/main/java/libcore/net/url/FileURLConnection.java b/luni/src/main/java/libcore/net/url/FileURLConnection.java
index f8d7926..94fe1d6 100644
--- a/luni/src/main/java/libcore/net/url/FileURLConnection.java
+++ b/luni/src/main/java/libcore/net/url/FileURLConnection.java
@@ -28,6 +28,11 @@
 import java.io.PrintStream;
 import java.net.URL;
 import java.net.URLConnection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 import libcore.net.UriCodec;
 
 /**
@@ -38,17 +43,45 @@
  */
 public class FileURLConnection extends URLConnection {
 
+    private static final Comparator<String> HEADER_COMPARATOR = new Comparator<String>() {
+        @Override
+        public int compare(String a, String b) {
+            if (a == b) {
+                return 0;
+            } else if (a == null) {
+                return -1;
+            } else if (b == null) {
+                return 1;
+            } else {
+                return String.CASE_INSENSITIVE_ORDER.compare(a, b);
+            }
+        }
+    };
+
     private String filename;
 
     private InputStream is;
 
     private long length = -1;
 
+    private long lastModified = -1;
+
     private boolean isDir;
 
     private FilePermission permission;
 
     /**
+     * A set of three key value pairs representing the headers we support.
+     */
+    private final String[] headerKeysAndValues;
+
+    private static final int CONTENT_TYPE_VALUE_IDX = 1;
+    private static final int CONTENT_LENGTH_VALUE_IDX = 3;
+    private static final int LAST_MODIFIED_VALUE_IDX = 5;
+
+    private Map<String, List<String>> headerFields;
+
+    /**
      * Creates an instance of <code>FileURLConnection</code> for establishing
      * a connection to the file pointed by this <code>URL<code>
      *
@@ -61,6 +94,10 @@
             filename = "";
         }
         filename = UriCodec.decode(filename);
+        headerKeysAndValues = new String[] {
+                "content-type", null,
+                "content-length", null,
+                "last-modified", null };
     }
 
     /**
@@ -74,15 +111,105 @@
     @Override
     public void connect() throws IOException {
         File f = new File(filename);
+        IOException error = null;
         if (f.isDirectory()) {
             isDir = true;
             is = getDirectoryListing(f);
             // use -1 for the contentLength
+            lastModified = f.lastModified();
+            headerKeysAndValues[CONTENT_TYPE_VALUE_IDX] = "text/html";
         } else {
-            is = new BufferedInputStream(new FileInputStream(f));
-            length = f.length();
+            try {
+                is = new BufferedInputStream(new FileInputStream(f));
+            } catch (IOException ioe) {
+                error = ioe;
+            }
+
+            if (error == null) {
+                length = f.length();
+                lastModified = f.lastModified();
+                headerKeysAndValues[CONTENT_TYPE_VALUE_IDX] = getContentTypeForPlainFiles();
+            } else {
+                headerKeysAndValues[CONTENT_TYPE_VALUE_IDX] = "content/unknown";
+            }
         }
+
+        headerKeysAndValues[CONTENT_LENGTH_VALUE_IDX] = String.valueOf(length);
+        headerKeysAndValues[LAST_MODIFIED_VALUE_IDX] = String.valueOf(lastModified);
+
         connected = true;
+        if (error != null) {
+            throw error;
+        }
+    }
+
+    @Override
+    public String getHeaderField(String key) {
+        if (!connected) {
+            try {
+                connect();
+            } catch (IOException ioe) {
+                return null;
+            }
+        }
+
+        for (int i = 0; i < headerKeysAndValues.length; i += 2) {
+            if (headerKeysAndValues[i].equalsIgnoreCase(key)) {
+                return headerKeysAndValues[i + 1];
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String getHeaderFieldKey(int position) {
+        if (!connected) {
+            try {
+                connect();
+            } catch (IOException ioe) {
+                return null;
+            }
+        }
+
+        if (position < 0 || position > headerKeysAndValues.length / 2) {
+            return null;
+        }
+
+        return headerKeysAndValues[position * 2];
+    }
+
+    @Override
+    public String getHeaderField(int position) {
+        if (!connected) {
+            try {
+                connect();
+            } catch (IOException ioe) {
+                return null;
+            }
+        }
+
+        if (position < 0 || position > headerKeysAndValues.length / 2) {
+            return null;
+        }
+
+        return headerKeysAndValues[(position * 2) + 1];
+    }
+
+    @Override
+    public Map<String, List<String>> getHeaderFields() {
+        if (headerFields == null) {
+            final TreeMap<String, List<String>> headerFieldsMap = new TreeMap<>(HEADER_COMPARATOR);
+
+            for (int i = 0; i < headerKeysAndValues.length; i+=2) {
+                headerFieldsMap.put(headerKeysAndValues[i],
+                        Collections.singletonList(headerKeysAndValues[i + 1]));
+            }
+
+            headerFields = Collections.unmodifiableMap(headerFieldsMap);
+        }
+
+        return headerFields;
     }
 
     /**
@@ -123,16 +250,11 @@
      */
     @Override
     public String getContentType() {
-        try {
-            if (!connected) {
-                connect();
-            }
-        } catch (IOException e) {
-            return "content/unknown";
-        }
-        if (isDir) {
-            return "text/plain";
-        }
+        // The content-type header field is always at position 0.
+        return getHeaderField(0);
+    }
+
+    private String getContentTypeForPlainFiles() {
         String result = guessContentTypeFromName(url.getFile());
         if (result != null) {
             return result;