OSDN Git Service

2004-11-06 Noa Resare <noa@resare.com>
[pf3gnuchains/gcc-fork.git] / libjava / java / net / URLConnection.java
index 2065cad..3b3355c 100644 (file)
@@ -1,5 +1,5 @@
 /* URLConnection.java -- Abstract superclass for reading from URL's
-   Copyright (C) 1998, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -7,7 +7,7 @@ GNU Classpath is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
+
 GNU Classpath is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
@@ -38,27 +38,28 @@ exception statement from your version. */
 
 package java.net;
 
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.Permission;
 import java.security.AllPermission;
+import java.security.Permission;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
+import java.util.Collections;
 import java.util.Date;
-import java.util.Locale;
 import java.util.Hashtable;
+import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
 import gnu.gcj.io.MimeTypes;
 
+
 /**
  * Written using on-line Java Platform 1.2 API Specification, as well
  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
  * Status:  One guessContentTypeFrom... methods not implemented.
  *    getContent method assumes content type from response; see comment there.
  */
-
 /**
  * This class models a connection that retrieves the information pointed
  * to by a URL object.  This is typically a connection to a remote node
@@ -87,8 +88,8 @@ import gnu.gcj.io.MimeTypes;
  * by the actual content handlers as described in the description of that
  * method.
  *
- * @author Aaron M. Renn <arenn@urbanophile.com>
- * @author Warren Levy <warrenl@cygnus.com>
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Warren Levy (warrenl@cygnus.com)
  */
 public abstract class URLConnection
 {
@@ -98,24 +99,27 @@ public abstract class URLConnection
    * instance and store it here.
    */
   private static FileNameMap fileNameMap;
+
   /**
    * This is the ContentHandlerFactory set by the caller, if any
    */
   private static ContentHandlerFactory factory;
-  
+
   /**
    * This is the default value that will be used to determine whether or
    * not user interaction should be allowed.
    */
-  private static boolean defaultAllowUserInteraction = false;
-  
+  private static boolean defaultAllowUserInteraction;
+
   /**
    * This is the default flag indicating whether or not to use caches to
    * store the data returned from a server
    */
   private static boolean defaultUseCaches = true;
 
+  private static ContentHandlerFactory defaultFactory
+    = new gnu.java.net.DefaultContentHandlerFactory();
+
   /**
    * This variable determines whether or not interaction is allowed with
    * the user.  For example, to prompt for a username and password.
@@ -126,18 +130,18 @@ public abstract class URLConnection
    * Indicates whether or not a connection has been established to the
    * destination specified in the URL
    */
-  protected boolean connected = false;
-  
+  protected boolean connected;
+
   /**
    * Indicates whether or not input can be read from this URL
    */
   protected boolean doInput = true;
-  
+
   /**
    * Indicates whether or not output can be sent to this URL
    */
-  protected boolean doOutput = false;
-  
+  protected boolean doOutput;
+
   /**
    * If this flag is set, the protocol is allowed to cache data whenever
    * it can (caching is not guaranteed). If it is not set, the protocol
@@ -157,18 +161,19 @@ public abstract class URLConnection
    * modified more recently than the date set in this variable.  That date
    * should be specified as the number of seconds since 1/1/1970 GMT.
    */
-  protected long ifModifiedSince = 0L;
+  protected long ifModifiedSince;
 
   /**
    * This is the URL associated with this connection
    */
   protected URL url;
 
-  private static ContentHandler contentHandler;
   private static Hashtable handlers = new Hashtable();
-  private static Locale locale; 
-  private static SimpleDateFormat dateFormat1, dateFormat2, dateFormat3;
-  private static boolean dateformats_initialized = false;
+  private static SimpleDateFormat[] dateFormats;
+  private static boolean dateformats_initialized;
+
+  /* Cached ParsePosition, used when parsing dates. */
+  private ParsePosition position;
 
   /**
    * Creates a URL connection to a given URL. A real connection is not made.
@@ -189,6 +194,8 @@ public abstract class URLConnection
   /**
    * Establishes the actual connection to the URL associated with this
    * connection object
+   *
+   * @exception IOException if an error occurs
    */
   public abstract void connect() throws IOException;
 
@@ -231,7 +238,7 @@ public abstract class URLConnection
   /**
    * Returns the value of the content-encoding field or null if it is not
    * known or not present.
-   * 
+   *
    * @return The content-encoding field
    */
   public String getContentEncoding()
@@ -296,7 +303,7 @@ public abstract class URLConnection
    * Returns a String representing the value of the header field having
    * the named key.  Returns null if the header field does not exist.
    *
-   * @param The key of the header field
+   * @param name The key of the header field
    *
    * @return The value of the header field as a String
    */
@@ -308,13 +315,15 @@ public abstract class URLConnection
 
   /**
    * Returns a map of all sent header fields
-   * 
+   *
+   * @return all header fields
+   *
    * @since 1.4
    */
   public Map getHeaderFields()
   {
     // Subclasses for specific protocols override this.
-    return null;
+    return Collections.EMPTY_MAP;
   }
 
   /**
@@ -322,25 +331,28 @@ public abstract class URLConnection
    * is not present or cannot be parsed as an integer, the default value
    * will be returned.
    *
-   * @param name The name of the header field
-   * @param val The default value
+   * @param name The header field key to lookup
+   * @param defaultValue The defaule value if the header field is not found
+   * or can't be parsed.
    *
    * @return The value of the header field or the default value if the field
    * is missing or malformed
    */
-  public int getHeaderFieldInt(String name, int val)
+  public int getHeaderFieldInt(String name, int defaultValue)
   {
-    String str = getHeaderField(name);
+    String value = getHeaderField(name);
+
+    if (value == null)
+      return defaultValue;
+
     try
       {
-       if (str != null)
-         val = Integer.parseInt(str);
+       return Integer.parseInt(value);
       }
     catch (NumberFormatException e)
       {
-       ; // Do nothing; val is the default.
+       return defaultValue;
       }
-    return val;
   }
 
   /**
@@ -349,27 +361,37 @@ public abstract class URLConnection
    * value if the field is not present or cannot be converted to a date.
    *
    * @param name The name of the header field
-   * @param val The dafault date
+   * @param defaultValue The default date if the header field is not found
+   * or can't be converted.
    *
    * @return Returns the date value of the header filed or the default value
    * if the field is missing or malformed
    */
-  public long getHeaderFieldDate(String name, long val)
+  public long getHeaderFieldDate(String name, long defaultValue)
   {
     if (! dateformats_initialized)
       initializeDateFormats();
+
+    if (position == null)
+      position = new ParsePosition(0);
+
+    long result = defaultValue;
     String str = getHeaderField(name);
+
     if (str != null)
       {
-        Date date;
-       if ((date = dateFormat1.parse(str, new ParsePosition(0))) != null)
-         val = date.getTime();
-       else if ((date = dateFormat2.parse(str, new ParsePosition(0))) != null)
-         val = date.getTime();
-       else if ((date = dateFormat3.parse(str, new ParsePosition(0))) != null)
-         val = date.getTime();
+       for (int i = 0; i < dateFormats.length; i++)
+         {
+           SimpleDateFormat df = dateFormats[i];
+           position.setIndex(0);
+           position.setErrorIndex(0);
+           Date date = df.parse(str, position);
+           if (date != null)
+             return date.getTime();
+         }
       }
-    return val;
+
+    return result;
   }
 
   /**
@@ -378,7 +400,7 @@ public abstract class URLConnection
    * getHeaderField(int) method allows access to the corresponding value for
    * this tag.
    *
-   * @param index The index into the header field list to retrieve the key for. 
+   * @param index The index into the header field list to retrieve the key for.
    *
    * @return The header field key or null if index is past the end
    * of the headers.
@@ -390,41 +412,53 @@ public abstract class URLConnection
   }
 
   /**
-   * This method returns the content of the document pointed to by the URL
-   * as an Object.  The type of object depends on the MIME type of the
-   * object and particular content hander loaded.  Most text type content
-   * handlers will return a subclass of InputStream.  Images usually return
-   * a class that implements ImageProducer.  There is not guarantee what
-   * type of object will be returned, however.
-   * <p>
-   * This class first determines the MIME type of the content, then creates
-   * a ContentHandler object to process the input.  If the ContentHandlerFactory
-   * is set, then that object is called to load a content handler, otherwise
-   * a class called gnu.java.net.content.<content_type> is tried.
-   * The default class will also be used if the content handler factory returns
-   * a null content handler.
-   *
-   * @exception IOException If an error occurs
+   * This method returns the content of the document pointed to by the
+   * URL as an Object.  The type of object depends on the MIME type of
+   * the object and particular content hander loaded.  Most text type
+   * content handlers will return a subclass of
+   * <code>InputStream</code>.  Images usually return a class that
+   * implements <code>ImageProducer</code>.  There is not guarantee
+   * what type of object will be returned, however.
+   *
+   * <p>This class first determines the MIME type of the content, then
+   * creates a ContentHandler object to process the input.  If the
+   * <code>ContentHandlerFactory</code> is set, then that object is
+   * called to load a content handler, otherwise a class called
+   * gnu.java.net.content.&lt;content_type&gt; is tried.  If this
+   * handler does not exist, the method will simple return the
+   * <code>InputStream</code> returned by
+   * <code>getInputStream()</code>.  Note that the default
+   * implementation of <code>getInputStream()</code> throws a
+   * <code>UnknownServiceException</code> so subclasses are encouraged
+   * to override this method.</p>
+   *
+   * @exception IOException If an error with the connection occurs.
    * @exception UnknownServiceException If the protocol does not support the
-   * content type
+   * content type at all.
    */
   public Object getContent() throws IOException
   {
+    if (!connected)
+      connect();
+
     // FIXME: Doc indicates that other criteria should be applied as
     // heuristics to determine the true content type, e.g. see 
     // guessContentTypeFromName() and guessContentTypeFromStream methods
     // as well as FileNameMap class & fileNameMap field & get/set methods.
-    String cType = getContentType();
-    contentHandler = setContentHandler(cType);
-    if (contentHandler == null)
-      return getInputStream();
+    String type = getContentType();
+    ContentHandler ch = getContentHandler(type);
+
+    if (ch != null)
+      return ch.getContent(this);
 
-    return contentHandler.getContent(this);
+    return getInputStream();
   }
 
   /**
    * Retrieves the content of this URLConnection
    *
+   * @param classes The allowed classes for the content
+   *
    * @exception IOException If an error occurs
    * @exception UnknownServiceException If the protocol does not support the
    * content type
@@ -432,7 +466,7 @@ public abstract class URLConnection
   public Object getContent(Class[] classes) throws IOException
   {
     // FIXME: implement this
-    return getContent ();
+    return getContent();
   }
 
   /**
@@ -454,7 +488,7 @@ public abstract class URLConnection
   public Permission getPermission() throws IOException
   {
     // Subclasses may override this.
-    return new java.security.AllPermission();
+    return new AllPermission();
   }
 
   /**
@@ -469,8 +503,8 @@ public abstract class URLConnection
   public InputStream getInputStream() throws IOException
   {
     // Subclasses for specific protocols override this.
-    throw new UnknownServiceException("Protocol " + url.getProtocol() +
-                       " does not support input.");
+    throw new UnknownServiceException("Protocol " + url.getProtocol()
+                                      + " does not support input.");
   }
 
   /**
@@ -485,14 +519,14 @@ public abstract class URLConnection
   public OutputStream getOutputStream() throws IOException
   {
     // Subclasses for specific protocols override this.
-    throw new UnknownServiceException("Protocol " + url.getProtocol() +
-                       " does not support output.");
+    throw new UnknownServiceException("Protocol " + url.getProtocol()
+                                      + " does not support output.");
   }
 
   /**
    * The methods prints the value of this object as a String by calling the
    * toString() method of its associated URL.  Overrides Object.toString()
-   * 
+   *
    * @return A String representation of this object
    */
   public String toString()
@@ -504,15 +538,16 @@ public abstract class URLConnection
    * Returns the value of a flag indicating whether or not input is going
    * to be done for this connection.  This default to true unless the
    * doOutput flag is set to false, in which case this defaults to false.
-   * 
-   * @param doinput The new value of the doInput field
+   *
+   * @param input <code>true</code> if input is to be done,
+   * <code>false</code> otherwise
    *
    * @exception IllegalStateException If already connected
    */
   public void setDoInput(boolean input)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
 
     doInput = input;
   }
@@ -541,7 +576,7 @@ public abstract class URLConnection
   public void setDoOutput(boolean output)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
 
     doOutput = output;
   }
@@ -616,7 +651,7 @@ public abstract class URLConnection
   public void setUseCaches(boolean usecaches)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
 
     useCaches = usecaches;
   }
@@ -647,7 +682,7 @@ public abstract class URLConnection
   public void setIfModifiedSince(long ifmodifiedsince)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
 
     ifModifiedSince = ifmodifiedsince;
   }
@@ -667,7 +702,10 @@ public abstract class URLConnection
   }
 
   /**
-   * Returns the default value of the useCaches field
+   * Returns the default value used to determine whether or not caching
+   * of documents will be done when possible.
+   *
+   * @return true if caches will be used, false otherwise
    */
   public boolean getDefaultUseCaches()
   {
@@ -680,56 +718,62 @@ public abstract class URLConnection
    *
    * @param use true to use caches if possible by default, false otherwise
    */
-  public void setDefaultUseCaches(boolean defaultusecaches)
+  public void setDefaultUseCaches(boolean use)
   {
-    defaultUseCaches = defaultusecaches;
+    defaultUseCaches = use;
   }
 
   /**
-   * Returns the default value used to determine whether or not caching
-   * of documents will be done when possible.
+   * Sets the value of the named request property
    *
-   * @param key Key of the property to set
-   * @param value Value of the Property to set
+   * @param key The name of the property
+   * @param value The value of the property
    *
    * @exception IllegalStateException If already connected
    * @exception NullPointerException If key is null
    *
    * @see URLConnection#getRequestProperty(String key)
    * @see URLConnection#addRequestProperty(String key, String value)
+   *
+   * @since 1.4
    */
   public void setRequestProperty(String key, String value)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
+
+    if (key == null)
+      throw new NullPointerException("key is null");
 
     // Do nothing unless overridden by subclasses that support setting
     // header fields in the request.
   }
 
   /**
-   * Sets the value of the named request property
+   * Adds a new request property by a key/value pair.
+   * This method does not overwrite existing properties with the same key.
    *
    * @param key Key of the property to add
    * @param value Value of the Property to add
    *
    * @exception IllegalStateException If already connected
    * @exception NullPointerException If key is null
-   * 
+   *
    * @see URLConnection#getRequestProperty(String key)
    * @see URLConnection#setRequestProperty(String key, String value)
-   * 
+   *
    * @since 1.4
    */
   public void addRequestProperty(String key, String value)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
 
-    if (getRequestProperty (key) == null)
-      {
-        setRequestProperty (key, value);
-      }
+    if (key == null)
+      throw new NullPointerException("key is null");
+
+    // Do nothing unless overridden by subclasses that support adding
+    // header fields in the request.
   }
 
   /**
@@ -747,7 +791,7 @@ public abstract class URLConnection
   public String getRequestProperty(String key)
   {
     if (connected)
-      throw new IllegalStateException ("Already connected");
+      throw new IllegalStateException("Already connected");
 
     // Overridden by subclasses that support reading header fields from the
     // request.
@@ -765,9 +809,12 @@ public abstract class URLConnection
    */
   public Map getRequestProperties()
   {
+    if (connected)
+      throw new IllegalStateException("Already connected");
+
     // Overridden by subclasses that support reading header fields from the
     // request.
-    return null;
+    return Collections.EMPTY_MAP;
   }
 
   /**
@@ -778,14 +825,14 @@ public abstract class URLConnection
    * @param key The request property name the default is being set for
    * @param value The value to set the default to
    *
-   * @deprecated 1.3 The method setRequestProperty should be used instead
+   * @deprecated 1.3 The method setRequestProperty should be used instead.
+   * This method does nothing now.
    *
-   * @see URLConnectionr#setRequestProperty(String key, String value)
+   * @see URLConnection#setRequestProperty(String key, String value)
    */
   public static void setDefaultRequestProperty(String key, String value)
   {
-    // Do nothing unless overridden by subclasses that support setting
-    // default request properties.
+    // This method does nothing since JDK 1.3.
   }
 
   /**
@@ -796,14 +843,15 @@ public abstract class URLConnection
    * @param key The request property to return the default value of
    *
    * @return The value of the default property or null if not available
-   * 
-   * @deprecated 1.3 The method getRequestProperty should be used instead
+   *
+   * @deprecated 1.3 The method getRequestProperty should be used instead.
+   * This method does nothing now.
    *
    * @see URLConnection#getRequestProperty(String key)
    */
   public static String getDefaultRequestProperty(String key)
   {
-    // Overridden by subclasses that support default request properties.
+    // This method does nothing since JDK 1.3.
     return null;
   }
 
@@ -819,10 +867,9 @@ public abstract class URLConnection
    * @exception SecurityException If a security manager exists and its
    * checkSetFactory method doesn't allow the operation
    */
-  public static synchronized void setContentHandlerFactory
-                                    (ContentHandlerFactory fac)
+  public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory)
   {
-    if (factory != null)
+    if (URLConnection.factory != null)
       throw new Error("ContentHandlerFactory already set");
 
     // Throw an exception if an extant security mgr precludes
@@ -831,7 +878,7 @@ public abstract class URLConnection
     if (s != null)
       s.checkSetFactory();
 
-    factory = fac;
+    URLConnection.factory = factory;
   }
 
   /**
@@ -848,20 +895,20 @@ public abstract class URLConnection
    */
   public static String guessContentTypeFromName(String filename)
   {
-    int dot = filename.lastIndexOf (".");
+    int dot = filename.lastIndexOf(".");
     
     if (dot != -1)
       {
        if (dot == filename.length())
-         return ("application/octet-stream");
+         return "application/octet-stream";
        else
-         filename = filename.substring (dot + 1);
+         filename = filename.substring(dot + 1);
       }
     
-    String type = MimeTypes.getMimeTypeFromExtension (filename);
+    String type = MimeTypes.getMimeTypeFromExtension(filename);
     
     if (type == null)
-      return("application/octet-stream");
+      return"application/octet-stream";
 
     return type;
   }
@@ -870,7 +917,7 @@ public abstract class URLConnection
    * Returns the MIME type of a stream based on the first few characters
    * at the beginning of the stream.  This routine can be used to determine
    * the MIME type if a server is believed to be returning an incorrect
-   * MIME type.  This method returns "application/octet-stream" if it 
+   * MIME type.  This method returns "application/octet-stream" if it
    * cannot determine the MIME type.
    * <p>
    * NOTE: Overriding MIME types sent from the server can be obnoxious
@@ -912,12 +959,12 @@ public abstract class URLConnection
    *
    * @exception SecurityException If a security manager exists and its
    * checkSetFactory method doesn't allow the operation
-   * 
+   *
    * @since 1.2
    */
   public static void setFileNameMap(FileNameMap map)
   {
-    // Throw an exception if an extant security mgr precludes
+    // Throw an exception if an extant security manager precludes
     // setting the factory.
     SecurityManager s = System.getSecurityManager();
     if (s != null)
@@ -926,14 +973,14 @@ public abstract class URLConnection
     fileNameMap = map;
   }
 
-  private ContentHandler setContentHandler(String contentType)
+  private ContentHandler getContentHandler(String contentType)
   {
-    ContentHandler handler;
-
     // No content type so just handle it as the default.
-    if (contentType == null || contentType == "")
+    if (contentType == null || contentType.equals(""))
       return null;
 
+    ContentHandler handler;
+
     // See if a handler has been cached for this content type.
     // For efficiency, if a content type has been searched for but not
     // found, it will be in the hash table but as the contentType String
@@ -944,12 +991,17 @@ public abstract class URLConnection
       else
        return null;
 
-    // If a non-default factory has been set, use it to find the content type.
+    // If a non-default factory has been set, use it.
     if (factory != null)
       handler = factory.createContentHandler(contentType);
 
-    // Non-default factory may have returned null or a factory wasn't set.
-    // Use the default search algorithm to find a handler for this content type.
+    // Now try default factory. Using this factory to instantiate built-in
+    // content handlers is preferable  
+    if (handler == null)
+      handler = defaultFactory.createContentHandler(contentType);
+
+    // User-set factory has not returned a handler. Use the default search 
+    // algorithm.
     if (handler == null)
       {
        // Get the list of packages to check and append our default handler
@@ -958,11 +1010,10 @@ public abstract class URLConnection
        // ever be needed (or available).
        String propVal = System.getProperty("java.content.handler.pkgs");
        propVal = (propVal == null) ? "" : (propVal + "|");
-       propVal = propVal + "gnu.gcj.content|sun.net.www.content";
+       propVal = propVal + "gnu.java.net.content|sun.net.www.content";
 
        // Replace the '/' character in the content type with '.' and
        // all other non-alphabetic, non-numeric characters with '_'.
-       StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
        char[] cArray = contentType.toCharArray();
        for (int i = 0; i < cArray.length; i++)
          {
@@ -976,6 +1027,7 @@ public abstract class URLConnection
        String contentClass = new String(cArray);
 
        // See if a class of this content type exists in any of the packages.
+       StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
        do
          {
            String facName = pkgPrefix.nextToken() + "." + contentClass;
@@ -994,7 +1046,7 @@ public abstract class URLConnection
       }
 
     // Update the hashtable with the new content handler.
-    if (handler != null && handler instanceof ContentHandler)
+    if (handler instanceof ContentHandler)
       {
        handlers.put(contentType, handler);
        return handler;
@@ -1009,16 +1061,18 @@ public abstract class URLConnection
   // We don't put these in a static initializer, because it creates problems
   // with initializer co-dependency: SimpleDateFormat's constructors eventually 
   // depend on URLConnection (via the java.text.*Symbols classes).
-  private synchronized void initializeDateFormats()
+  private static synchronized void initializeDateFormats()
   {
     if (dateformats_initialized)
       return;
-    locale = new Locale("En", "Us", "Unix");
-    dateFormat1 = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", 
-                                       locale);
-    dateFormat2 = new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", 
-                                       locale);
-    dateFormat3 = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
+
+    Locale locale = new Locale("En", "Us", "Unix");
+    dateFormats = new SimpleDateFormat[3];
+    dateFormats[0] =
+      new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale);
+    dateFormats[1] =
+      new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale);
+    dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
     dateformats_initialized = true;
   }
 }