Merge from mainline.
[official-gcc.git] / libjava / java / net / URLConnection.java
blobb5e4cb0624149426de42b5caccc64be2966c82d3
1 /* URLConnection.java -- Abstract superclass for reading from URL's
2 Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.net;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.OutputStream;
44 import java.security.AllPermission;
45 import java.security.Permission;
46 import java.text.ParsePosition;
47 import java.text.SimpleDateFormat;
48 import java.util.Collections;
49 import java.util.Date;
50 import java.util.Hashtable;
51 import java.util.Locale;
52 import java.util.Map;
53 import java.util.StringTokenizer;
54 import gnu.gcj.io.MimeTypes;
57 /**
58 * Written using on-line Java Platform 1.2 API Specification, as well
59 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
60 * Status: One guessContentTypeFrom... methods not implemented.
61 * getContent method assumes content type from response; see comment there.
63 /**
64 * This class models a connection that retrieves the information pointed
65 * to by a URL object. This is typically a connection to a remote node
66 * on the network, but could be a simple disk read.
67 * <p>
68 * A URLConnection object is normally created by calling the openConnection()
69 * method of a URL object. This method is somewhat misnamed because it does
70 * not actually open the connection. Instead, it return an unconnected
71 * instance of this object. The caller then has the opportunity to set
72 * various connection options prior to calling the actual connect() method.
73 * <p>
74 * After the connection has been opened, there are a number of methods in
75 * this class that access various attributes of the data, typically
76 * represented by headers sent in advance of the actual data itself.
77 * <p>
78 * Also of note are the getInputStream and getContent() methods which allow
79 * the caller to retrieve the actual data from the connection. Note that
80 * for some types of connections, writing is also allowed. The setDoOutput()
81 * method must be called prior to connecing in order to enable this, then
82 * the getOutputStream method called after the connection in order to
83 * obtain a stream to write the output to.
84 * <p>
85 * The getContent() method is of particular note. This method returns an
86 * Object that encapsulates the data returned. There is no way do determine
87 * the type of object that will be returned in advance. This is determined
88 * by the actual content handlers as described in the description of that
89 * method.
91 * @author Aaron M. Renn (arenn@urbanophile.com)
92 * @author Warren Levy (warrenl@cygnus.com)
94 public abstract class URLConnection
96 /**
97 * This is an object that maps filenames to MIME types. The interface
98 * to do this is implemented by this class, so just create an empty
99 * instance and store it here.
101 private static FileNameMap fileNameMap;
104 * This is the ContentHandlerFactory set by the caller, if any
106 private static ContentHandlerFactory factory;
109 * This is the default value that will be used to determine whether or
110 * not user interaction should be allowed.
112 private static boolean defaultAllowUserInteraction;
115 * This is the default flag indicating whether or not to use caches to
116 * store the data returned from a server
118 private static boolean defaultUseCaches = true;
120 private static ContentHandlerFactory defaultFactory
121 = new gnu.java.net.DefaultContentHandlerFactory();
124 * This variable determines whether or not interaction is allowed with
125 * the user. For example, to prompt for a username and password.
127 protected boolean allowUserInteraction;
130 * Indicates whether or not a connection has been established to the
131 * destination specified in the URL
133 protected boolean connected;
136 * Indicates whether or not input can be read from this URL
138 protected boolean doInput = true;
141 * Indicates whether or not output can be sent to this URL
143 protected boolean doOutput;
146 * If this flag is set, the protocol is allowed to cache data whenever
147 * it can (caching is not guaranteed). If it is not set, the protocol
148 * must a get a fresh copy of the data.
149 * <p>
150 * This field is set by the setUseCaches method and returned by the
151 * getUseCaches method.
153 * Its default value is that determined by the last invocation of
154 * setDefaultUseCaches
156 protected boolean useCaches;
159 * If this value is non-zero, then the connection will only attempt to
160 * fetch the document pointed to by the URL if the document has been
161 * modified more recently than the date set in this variable. That date
162 * should be specified as the number of seconds since 1/1/1970 GMT.
164 protected long ifModifiedSince;
167 * This is the URL associated with this connection
169 protected URL url;
171 private static Hashtable handlers = new Hashtable();
172 private static SimpleDateFormat[] dateFormats;
173 private static boolean dateformats_initialized;
175 /* Cached ParsePosition, used when parsing dates. */
176 private ParsePosition position;
179 * Creates a URL connection to a given URL. A real connection is not made.
180 * Use #connect to do this.
182 * @param url The Object to create the URL connection to
184 * @see URLConnection#connect()
186 protected URLConnection(URL url)
188 // Set up all our instance variables
189 this.url = url;
190 allowUserInteraction = defaultAllowUserInteraction;
191 useCaches = defaultUseCaches;
195 * Establishes the actual connection to the URL associated with this
196 * connection object
198 * @exception IOException if an error occurs
200 public abstract void connect() throws IOException;
203 * Returns the URL object associated with this connection
205 * @return The URL for this connection.
207 public URL getURL()
209 return url;
213 * Returns the value of the content-length header field or -1 if the value
214 * is not known or not present.
216 * @return The content-length field
218 public int getContentLength()
220 return getHeaderFieldInt("content-length", -1);
224 * Returns the the content-type of the data pointed to by the URL. This
225 * method first tries looking for a content-type header. If that is not
226 * present, it attempts to use the file name to determine the content's
227 * MIME type. If that is unsuccessful, the method returns null. The caller
228 * may then still attempt to determine the MIME type by a call to
229 * guessContentTypeFromStream()
231 * @return The content MIME type
233 public String getContentType()
235 return getHeaderField("content-type");
239 * Returns the value of the content-encoding field or null if it is not
240 * known or not present.
242 * @return The content-encoding field
244 public String getContentEncoding()
246 return getHeaderField("content-encoding");
250 * Returns the value of the expires header or 0 if not known or present.
251 * If populated, the return value is number of seconds since midnight
252 * on 1/1/1970 GMT.
254 * @return The expiration time.
256 public long getExpiration()
258 return getHeaderFieldDate("expires", 0L);
262 * Returns the date of the document pointed to by the URL as reported in
263 * the date field of the header or 0 if the value is not present or not
264 * known. If populated, the return value is number of seconds since
265 * midnight on 1/1/1970 GMT.
267 * @return The document date
269 public long getDate()
271 return getHeaderFieldDate("date", 0L);
275 * Returns the value of the last-modified header field or 0 if not known known
276 * or not present. If populated, the return value is the number of seconds
277 * since midnight on 1/1/1970.
279 * @return The last modified time
281 public long getLastModified()
283 return getHeaderFieldDate("last-modified", 0L);
287 * Return a String representing the header value at the specified index.
288 * This allows the caller to walk the list of header fields. The analogous
289 * getHeaderFieldKey(int) method allows access to the corresponding key
290 * for this header field
292 * @param index The index into the header field list to retrieve the value for
294 * @return The header value or null if index is past the end of the headers
296 public String getHeaderField(int index)
298 // Subclasses for specific protocols override this.
299 return null;
303 * Returns a String representing the value of the header field having
304 * the named key. Returns null if the header field does not exist.
306 * @param name The key of the header field
308 * @return The value of the header field as a String
310 public String getHeaderField(String name)
312 // Subclasses for specific protocols override this.
313 return null;
317 * Returns a map of all sent header fields
319 * @return all header fields
321 * @since 1.4
323 public Map getHeaderFields()
325 // Subclasses for specific protocols override this.
326 return Collections.EMPTY_MAP;
330 * Returns the value of the named header field as an int. If the field
331 * is not present or cannot be parsed as an integer, the default value
332 * will be returned.
334 * @param name The header field key to lookup
335 * @param defaultValue The defaule value if the header field is not found
336 * or can't be parsed.
338 * @return The value of the header field or the default value if the field
339 * is missing or malformed
341 public int getHeaderFieldInt(String name, int defaultValue)
343 String value = getHeaderField(name);
345 if (value == null)
346 return defaultValue;
350 return Integer.parseInt(value);
352 catch (NumberFormatException e)
354 return defaultValue;
359 * Returns the value of the named header field as a date. This date will
360 * be the number of seconds since midnight 1/1/1970 GMT or the default
361 * value if the field is not present or cannot be converted to a date.
363 * @param name The name of the header field
364 * @param defaultValue The default date if the header field is not found
365 * or can't be converted.
367 * @return Returns the date value of the header filed or the default value
368 * if the field is missing or malformed
370 public long getHeaderFieldDate(String name, long defaultValue)
372 if (! dateformats_initialized)
373 initializeDateFormats();
375 if (position == null)
376 position = new ParsePosition(0);
378 long result = defaultValue;
379 String str = getHeaderField(name);
381 if (str != null)
383 for (int i = 0; i < dateFormats.length; i++)
385 SimpleDateFormat df = dateFormats[i];
386 position.setIndex(0);
387 position.setErrorIndex(0);
388 Date date = df.parse(str, position);
389 if (date != null)
390 return date.getTime();
394 return result;
398 * Returns a String representing the header key at the specified index.
399 * This allows the caller to walk the list of header fields. The analogous
400 * getHeaderField(int) method allows access to the corresponding value for
401 * this tag.
403 * @param index The index into the header field list to retrieve the key for.
405 * @return The header field key or null if index is past the end
406 * of the headers.
408 public String getHeaderFieldKey(int index)
410 // Subclasses for specific protocols override this.
411 return null;
415 * This method returns the content of the document pointed to by the
416 * URL as an Object. The type of object depends on the MIME type of
417 * the object and particular content hander loaded. Most text type
418 * content handlers will return a subclass of
419 * <code>InputStream</code>. Images usually return a class that
420 * implements <code>ImageProducer</code>. There is not guarantee
421 * what type of object will be returned, however.
423 * <p>This class first determines the MIME type of the content, then
424 * creates a ContentHandler object to process the input. If the
425 * <code>ContentHandlerFactory</code> is set, then that object is
426 * called to load a content handler, otherwise a class called
427 * gnu.java.net.content.&lt;content_type&gt; is tried. If this
428 * handler does not exist, the method will simple return the
429 * <code>InputStream</code> returned by
430 * <code>getInputStream()</code>. Note that the default
431 * implementation of <code>getInputStream()</code> throws a
432 * <code>UnknownServiceException</code> so subclasses are encouraged
433 * to override this method.</p>
435 * @return the content
437 * @exception IOException If an error with the connection occurs.
438 * @exception UnknownServiceException If the protocol does not support the
439 * content type at all.
441 public Object getContent() throws IOException
443 if (!connected)
444 connect();
446 // FIXME: Doc indicates that other criteria should be applied as
447 // heuristics to determine the true content type, e.g. see
448 // guessContentTypeFromName() and guessContentTypeFromStream methods
449 // as well as FileNameMap class & fileNameMap field & get/set methods.
450 String type = getContentType();
451 ContentHandler ch = getContentHandler(type);
453 if (ch != null)
454 return ch.getContent(this);
456 return getInputStream();
460 * Retrieves the content of this URLConnection
462 * @param classes The allowed classes for the content
464 * @return the content
466 * @exception IOException If an error occurs
467 * @exception UnknownServiceException If the protocol does not support the
468 * content type
470 public Object getContent(Class[] classes) throws IOException
472 // FIXME: implement this
473 return getContent();
477 * This method returns a <code>Permission</code> object representing the
478 * permissions required to access this URL. This method returns
479 * <code>java.security.AllPermission</code> by default. Subclasses should
480 * override it to return a more specific permission. For example, an
481 * HTTP URL should return an instance of <code>SocketPermission</code>
482 * for the appropriate host and port.
483 * <p>
484 * Note that because of items such as HTTP redirects, the permission
485 * object returned might be different before and after connecting.
487 * @return A Permission object
489 * @exception IOException If the computation of the permission requires
490 * network or file I/O and an exception occurs while computing it
492 public Permission getPermission() throws IOException
494 // Subclasses may override this.
495 return new AllPermission();
499 * Returns an InputStream for this connection. As this default
500 * implementation returns null, subclasses should override this method
502 * @return An InputStream for this connection
504 * @exception IOException If an error occurs
505 * @exception UnknownServiceException If the protocol does not support input
507 public InputStream getInputStream() throws IOException
509 // Subclasses for specific protocols override this.
510 throw new UnknownServiceException("Protocol " + url.getProtocol()
511 + " does not support input.");
515 * Returns an OutputStream for this connection. As this default
516 * implementation returns null, subclasses should override this method
518 * @return An OutputStream for this connection
520 * @exception IOException If an error occurs
521 * @exception UnknownServiceException If the protocol does not support output
523 public OutputStream getOutputStream() throws IOException
525 // Subclasses for specific protocols override this.
526 throw new UnknownServiceException("Protocol " + url.getProtocol()
527 + " does not support output.");
531 * The methods prints the value of this object as a String by calling the
532 * toString() method of its associated URL. Overrides Object.toString()
534 * @return A String representation of this object
536 public String toString()
538 return this.getClass().getName() + ":" + url.toString();
542 * Sets the value of a flag indicating whether or not input is going
543 * to be done for this connection. This default to true unless the
544 * doOutput flag is set to false, in which case this defaults to false.
546 * @param input <code>true</code> if input is to be done,
547 * <code>false</code> otherwise
549 * @exception IllegalStateException If already connected
551 public void setDoInput(boolean input)
553 if (connected)
554 throw new IllegalStateException("Already connected");
556 doInput = input;
560 * Returns the value of a flag indicating whether or not input is going
561 * to be done for this connection. This default to true unless the
562 * doOutput flag is set to false, in which case this defaults to false.
564 * @return true if input is to be done, false otherwise
566 public boolean getDoInput()
568 return doInput;
572 * Sets a boolean flag indicating whether or not output will be done
573 * on this connection. The default value is false, so this method can
574 * be used to override the default
576 * @param output ture if output is to be done, false otherwise
578 * @exception IllegalStateException If already connected
580 public void setDoOutput(boolean output)
582 if (connected)
583 throw new IllegalStateException("Already connected");
585 doOutput = output;
589 * Returns a boolean flag indicating whether or not output will be done
590 * on this connection. This defaults to false.
592 * @return true if output is to be done, false otherwise
594 public boolean getDoOutput()
596 return doOutput;
600 * Sets a boolean flag indicating whether or not user interaction is
601 * allowed for this connection. (For example, in order to prompt for
602 * username and password info.
604 * @param allow true if user interaction should be allowed, false otherwise.
606 * @exception IllegalStateException If already connected
608 public void setAllowUserInteraction(boolean allow)
610 allowUserInteraction = allow;
614 * Returns a boolean flag indicating whether or not user interaction is
615 * allowed for this connection. (For example, in order to prompt for
616 * username and password info.
618 * @return true if user interaction is allowed, false otherwise
620 public boolean getAllowUserInteraction()
622 return allowUserInteraction;
626 * Sets the default flag for whether or not interaction with a user
627 * is allowed. This will be used for all connections unless overridden
629 * @param allow true to allow user interaction, false otherwise
631 public static void setDefaultAllowUserInteraction(boolean allow)
633 defaultAllowUserInteraction = allow;
637 * Returns the default flag for whether or not interaction with a user
638 * is allowed. This will be used for all connections unless overridden
640 * @return true if user interaction is allowed, false otherwise
642 public static boolean getDefaultAllowUserInteraction()
644 return defaultAllowUserInteraction;
648 * Sets a boolean flag indicating whether or not caching will be used
649 * (if possible) to store data downloaded via the connection.
651 * @param usecaches The new value
653 * @exception IllegalStateException If already connected
655 public void setUseCaches(boolean usecaches)
657 if (connected)
658 throw new IllegalStateException("Already connected");
660 useCaches = usecaches;
664 * Returns a boolean flag indicating whether or not caching will be used
665 * (if possible) to store data downloaded via the connection.
667 * @return true if caching should be used if possible, false otherwise
669 public boolean getUseCaches()
671 return useCaches;
675 * Sets the ifModified since instance variable. If this value is non
676 * zero and the underlying protocol supports it, the actual document will
677 * not be fetched unless it has been modified since this time. The value
678 * passed should be 0 if this feature is to be disabled or the time expressed
679 * as the number of seconds since midnight 1/1/1970 GMT otherwise.
681 * @param ifmodifiedsince The new value in milliseconds
682 * since January 1, 1970 GMT
684 * @exception IllegalStateException If already connected
686 public void setIfModifiedSince(long ifmodifiedsince)
688 if (connected)
689 throw new IllegalStateException("Already connected");
691 ifModifiedSince = ifmodifiedsince;
695 * Returns the ifModified since instance variable. If this value is non
696 * zero and the underlying protocol supports it, the actual document will
697 * not be fetched unless it has been modified since this time. The value
698 * returned will be 0 if this feature is disabled or the time expressed
699 * as the number of seconds since midnight 1/1/1970 GMT otherwise
701 * @return The ifModifiedSince value
703 public long getIfModifiedSince()
705 return ifModifiedSince;
709 * Returns the default value used to determine whether or not caching
710 * of documents will be done when possible.
712 * @return true if caches will be used, false otherwise
714 public boolean getDefaultUseCaches()
716 return defaultUseCaches;
720 * Sets the default value used to determine whether or not caching
721 * of documents will be done when possible.
723 * @param use true to use caches if possible by default, false otherwise
725 public void setDefaultUseCaches(boolean use)
727 defaultUseCaches = use;
731 * Sets the value of the named request property
733 * @param key The name of the property
734 * @param value The value of the property
736 * @exception IllegalStateException If already connected
737 * @exception NullPointerException If key is null
739 * @see URLConnection#getRequestProperty(String key)
740 * @see URLConnection#addRequestProperty(String key, String value)
742 * @since 1.4
744 public void setRequestProperty(String key, String value)
746 if (connected)
747 throw new IllegalStateException("Already connected");
749 if (key == null)
750 throw new NullPointerException("key is null");
752 // Do nothing unless overridden by subclasses that support setting
753 // header fields in the request.
757 * Adds a new request property by a key/value pair.
758 * This method does not overwrite existing properties with the same key.
760 * @param key Key of the property to add
761 * @param value Value of the Property to add
763 * @exception IllegalStateException If already connected
764 * @exception NullPointerException If key is null
766 * @see URLConnection#getRequestProperty(String key)
767 * @see URLConnection#setRequestProperty(String key, String value)
769 * @since 1.4
771 public void addRequestProperty(String key, String value)
773 if (connected)
774 throw new IllegalStateException("Already connected");
776 if (key == null)
777 throw new NullPointerException("key is null");
779 // Do nothing unless overridden by subclasses that support adding
780 // header fields in the request.
784 * Returns the value of the named request property.
786 * @param key The name of the property
788 * @return Value of the property
790 * @exception IllegalStateException If already connected
792 * @see URLConnection#setRequestProperty(String key, String value)
793 * @see URLConnection#addRequestProperty(String key, String value)
795 public String getRequestProperty(String key)
797 if (connected)
798 throw new IllegalStateException("Already connected");
800 // Overridden by subclasses that support reading header fields from the
801 // request.
802 return null;
806 * Returns an unmodifiable Map containing the request properties.
808 * @return The map of properties
810 * @exception IllegalStateException If already connected
812 * @since 1.4
814 public Map getRequestProperties()
816 if (connected)
817 throw new IllegalStateException("Already connected");
819 // Overridden by subclasses that support reading header fields from the
820 // request.
821 return Collections.EMPTY_MAP;
825 * Sets the default value of a request property. This will be used
826 * for all connections unless the value of the property is manually
827 * overridden.
829 * @param key The request property name the default is being set for
830 * @param value The value to set the default to
832 * @deprecated 1.3 The method setRequestProperty should be used instead.
833 * This method does nothing now.
835 * @see URLConnection#setRequestProperty(String key, String value)
837 public static void setDefaultRequestProperty(String key, String value)
839 // This method does nothing since JDK 1.3.
843 * Returns the default value of a request property. This will be used
844 * for all connections unless the value of the property is manually
845 * overridden.
847 * @param key The request property to return the default value of
849 * @return The value of the default property or null if not available
851 * @deprecated 1.3 The method getRequestProperty should be used instead.
852 * This method does nothing now.
854 * @see URLConnection#getRequestProperty(String key)
856 public static String getDefaultRequestProperty(String key)
858 // This method does nothing since JDK 1.3.
859 return null;
863 * Sets the ContentHandlerFactory for an application. This can be called
864 * once and only once. If it is called again, then an Error is thrown.
865 * Unlike for other set factory methods, this one does not do a security
866 * check prior to setting the factory.
868 * @param factory The ContentHandlerFactory for this application
870 * @exception Error If the factory has already been defined
871 * @exception SecurityException If a security manager exists and its
872 * checkSetFactory method doesn't allow the operation
874 public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory)
876 if (URLConnection.factory != null)
877 throw new Error("ContentHandlerFactory already set");
879 // Throw an exception if an extant security mgr precludes
880 // setting the factory.
881 SecurityManager s = System.getSecurityManager();
882 if (s != null)
883 s.checkSetFactory();
885 URLConnection.factory = factory;
889 * Returns the MIME type of a file based on the name of the file. This
890 * works by searching for the file's extension in a list of file extensions
891 * and returning the MIME type associated with it. If no type is found,
892 * then a MIME type of "application/octet-stream" will be returned.
894 * @param filename The filename to determine the MIME type for
896 * @return The MIME type String
898 * @specnote public since JDK 1.4
900 public static String guessContentTypeFromName(String filename)
902 int dot = filename.lastIndexOf(".");
904 if (dot != -1)
906 if (dot == filename.length())
907 return "application/octet-stream";
908 else
909 filename = filename.substring(dot + 1);
912 String type = MimeTypes.getMimeTypeFromExtension(filename);
914 if (type == null)
915 return"application/octet-stream";
917 return type;
921 * Returns the MIME type of a stream based on the first few characters
922 * at the beginning of the stream. This routine can be used to determine
923 * the MIME type if a server is believed to be returning an incorrect
924 * MIME type. This method returns "application/octet-stream" if it
925 * cannot determine the MIME type.
926 * <p>
927 * NOTE: Overriding MIME types sent from the server can be obnoxious
928 * to user's. See Internet Exploder 4 if you don't believe me.
930 * @param is The InputStream to determine the MIME type from
932 * @return The MIME type
934 * @exception IOException If an error occurs
936 public static String guessContentTypeFromStream(InputStream is)
937 throws IOException
939 is.mark(1024);
940 // FIXME: Implement this. Use system mimetype informations (like "file").
941 is.reset();
942 return null;
946 * This method returns the <code>FileNameMap</code> object being used
947 * to decode MIME types by file extension.
949 * @return The <code>FileNameMap</code>.
951 * @since 1.2
953 public static FileNameMap getFileNameMap()
955 return fileNameMap;
959 * This method sets the <code>FileNameMap</code> object being used
960 * to decode MIME types by file extension.
962 * @param map The <code>FileNameMap</code>.
964 * @exception SecurityException If a security manager exists and its
965 * checkSetFactory method doesn't allow the operation
967 * @since 1.2
969 public static void setFileNameMap(FileNameMap map)
971 // Throw an exception if an extant security manager precludes
972 // setting the factory.
973 SecurityManager s = System.getSecurityManager();
974 if (s != null)
975 s.checkSetFactory();
977 fileNameMap = map;
980 private ContentHandler getContentHandler(String contentType)
982 // No content type so just handle it as the default.
983 if (contentType == null || contentType.equals(""))
984 return null;
986 ContentHandler handler = null;
988 // See if a handler has been cached for this content type.
989 // For efficiency, if a content type has been searched for but not
990 // found, it will be in the hash table but as the contentType String
991 // instead of a ContentHandler.
993 Object cachedHandler;
994 if ((cachedHandler = handlers.get(contentType)) != null)
996 if (cachedHandler instanceof ContentHandler)
997 return (ContentHandler)cachedHandler;
998 else
999 return null;
1003 // If a non-default factory has been set, use it.
1004 if (factory != null)
1005 handler = factory.createContentHandler(contentType);
1007 // Now try default factory. Using this factory to instantiate built-in
1008 // content handlers is preferable
1009 if (handler == null)
1010 handler = defaultFactory.createContentHandler(contentType);
1012 // User-set factory has not returned a handler. Use the default search
1013 // algorithm.
1014 if (handler == null)
1016 // Get the list of packages to check and append our default handler
1017 // to it, along with the JDK specified default as a last resort.
1018 // Except in very unusual environments the JDK specified one shouldn't
1019 // ever be needed (or available).
1020 String propVal = System.getProperty("java.content.handler.pkgs");
1021 propVal = (propVal == null) ? "" : (propVal + "|");
1022 propVal = propVal + "gnu.java.net.content|sun.net.www.content";
1024 // Replace the '/' character in the content type with '.' and
1025 // all other non-alphabetic, non-numeric characters with '_'.
1026 char[] cArray = contentType.toCharArray();
1027 for (int i = 0; i < cArray.length; i++)
1029 if (cArray[i] == '/')
1030 cArray[i] = '.';
1031 else if (! ((cArray[i] >= 'A' && cArray[i] <= 'Z') ||
1032 (cArray[i] >= 'a' && cArray[i] <= 'z') ||
1033 (cArray[i] >= '0' && cArray[i] <= '9')))
1034 cArray[i] = '_';
1036 String contentClass = new String(cArray);
1038 // See if a class of this content type exists in any of the packages.
1039 StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
1042 String facName = pkgPrefix.nextToken() + "." + contentClass;
1045 handler =
1046 (ContentHandler) Class.forName(facName).newInstance();
1048 catch (Exception e)
1050 // Can't instantiate; handler still null, go on to next element.
1052 } while ((handler == null ||
1053 ! (handler instanceof ContentHandler)) &&
1054 pkgPrefix.hasMoreTokens());
1057 // Update the hashtable with the new content handler.
1058 if (handler instanceof ContentHandler)
1060 handlers.put(contentType, handler);
1061 return handler;
1064 // For efficiency on subsequent searches, put a dummy entry in the hash
1065 // table for content types that don't have a non-default ContentHandler.
1066 handlers.put(contentType, contentType);
1067 return null;
1070 // We don't put these in a static initializer, because it creates problems
1071 // with initializer co-dependency: SimpleDateFormat's constructors eventually
1072 // depend on URLConnection (via the java.text.*Symbols classes).
1073 private static synchronized void initializeDateFormats()
1075 if (dateformats_initialized)
1076 return;
1078 Locale locale = new Locale("En", "Us", "Unix");
1079 dateFormats = new SimpleDateFormat[3];
1080 dateFormats[0] =
1081 new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale);
1082 dateFormats[1] =
1083 new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale);
1084 dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
1085 dateformats_initialized = true;