OSDN Git Service

2003-05-09 Michael Koch <konqueror@gmx.de>
[pf3gnuchains/gcc-fork.git] / libjava / java / net / URLConnection.java
1 /* URLConnection.java -- Abstract superclass for reading from URL's
2    Copyright (C) 1998, 2002 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10  
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.
15
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
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.
25
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. */
37
38
39 package java.net;
40
41 import java.io.InputStream;
42 import java.io.IOException;
43 import java.io.OutputStream;
44 import java.security.Permission;
45 import java.security.AllPermission;
46 import java.text.ParsePosition;
47 import java.text.SimpleDateFormat;
48 import java.util.Date;
49 import java.util.Locale;
50 import java.util.Hashtable;
51 import java.util.Map;
52 import java.util.StringTokenizer;
53 import gnu.gcj.io.MimeTypes;
54
55 /**
56  * Written using on-line Java Platform 1.2 API Specification, as well
57  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
58  * Status:  One guessContentTypeFrom... methods not implemented.
59  *    getContent method assumes content type from response; see comment there.
60  */
61
62 /**
63  * This class models a connection that retrieves the information pointed
64  * to by a URL object.  This is typically a connection to a remote node
65  * on the network, but could be a simple disk read.
66  * <p>
67  * A URLConnection object is normally created by calling the openConnection()
68  * method of a URL object.  This method is somewhat misnamed because it does
69  * not actually open the connection.  Instead, it return an unconnected
70  * instance of this object.  The caller then has the opportunity to set
71  * various connection options prior to calling the actual connect() method.
72  * <p>
73  * After the connection has been opened, there are a number of methods in
74  * this class that access various attributes of the data, typically
75  * represented by headers sent in advance of the actual data itself.
76  * <p>
77  * Also of note are the getInputStream and getContent() methods which allow
78  * the caller to retrieve the actual data from the connection.  Note that
79  * for some types of connections, writing is also allowed.  The setDoOutput()
80  * method must be called prior to connecing in order to enable this, then
81  * the getOutputStream method called after the connection in order to
82  * obtain a stream to write the output to.
83  * <p>
84  * The getContent() method is of particular note.  This method returns an
85  * Object that encapsulates the data returned.  There is no way do determine
86  * the type of object that will be returned in advance.  This is determined
87  * by the actual content handlers as described in the description of that
88  * method.
89  *
90  * @author Aaron M. Renn <arenn@urbanophile.com>
91  * @author Warren Levy <warrenl@cygnus.com>
92  */
93 public abstract class URLConnection
94 {
95   /**
96    * This is an object that maps filenames to MIME types.  The interface
97    * to do this is implemented by this class, so just create an empty
98    * instance and store it here.
99    */
100   private static FileNameMap fileNameMap;
101  
102   /**
103    * This is the ContentHandlerFactory set by the caller, if any
104    */
105   private static ContentHandlerFactory factory;
106   
107   /**
108    * This is the default value that will be used to determine whether or
109    * not user interaction should be allowed.
110    */
111   private static boolean defaultAllowUserInteraction = false;
112   
113   /**
114    * This is the default flag indicating whether or not to use caches to
115    * store the data returned from a server
116    */
117   private static boolean defaultUseCaches = true;
118
119   /**
120    * This variable determines whether or not interaction is allowed with
121    * the user.  For example, to prompt for a username and password.
122    */
123   protected boolean allowUserInteraction;
124
125   /**
126    * Indicates whether or not a connection has been established to the
127    * destination specified in the URL
128    */
129   protected boolean connected = false;
130   
131   /**
132    * Indicates whether or not input can be read from this URL
133    */
134   protected boolean doInput = true;
135   
136   /**
137    * Indicates whether or not output can be sent to this URL
138    */
139   protected boolean doOutput = false;
140   
141   /**
142    * If this flag is set, the protocol is allowed to cache data whenever
143    * it can (caching is not guaranteed). If it is not set, the protocol
144    * must a get a fresh copy of the data.
145    * <p>
146    * This field is set by the setUseCaches method and returned by the
147    * getUseCaches method.
148    *
149    * Its default value is that determined by the last invocation of
150    * setDefaultUseCaches
151    */
152   protected boolean useCaches;
153
154   /**
155    * If this value is non-zero, then the connection will only attempt to
156    * fetch the document pointed to by the URL if the document has been
157    * modified more recently than the date set in this variable.  That date
158    * should be specified as the number of seconds since 1/1/1970 GMT.
159    */
160   protected long ifModifiedSince = 0L;
161
162   /**
163    * This is the URL associated with this connection
164    */
165   protected URL url;
166
167   private static ContentHandler contentHandler;
168   private static Hashtable handlers = new Hashtable();
169   private static Locale locale; 
170   private static SimpleDateFormat dateFormat1, dateFormat2, dateFormat3;
171   private static boolean dateformats_initialized = false;
172
173   /**
174    * Creates a URL connection to a given URL. A real connection is not made.
175    * Use #connect to do this.
176    *
177    * @param url The Object to create the URL connection to
178    *
179    * @see URLConnection#connect()
180    */
181   protected URLConnection(URL url)
182   {
183     // Set up all our instance variables
184     this.url = url;
185     allowUserInteraction = defaultAllowUserInteraction;
186     useCaches = defaultUseCaches;
187   }
188
189   /**
190    * Establishes the actual connection to the URL associated with this
191    * connection object
192    */
193   public abstract void connect() throws IOException;
194
195   /**
196    * Returns the URL object associated with this connection
197    *
198    * @return The URL for this connection.
199    */
200   public URL getURL()
201   {
202     return url;
203   }
204
205   /**
206    * Returns the value of the content-length header field or -1 if the value
207    * is not known or not present.
208    *
209    * @return The content-length field
210    */
211   public int getContentLength()
212   {
213     return getHeaderFieldInt("content-length", -1);
214   }
215
216   /**
217    * Returns the the content-type of the data pointed to by the URL.  This
218    * method first tries looking for a content-type header.  If that is not
219    * present, it attempts to use the file name to determine the content's
220    * MIME type.  If that is unsuccessful, the method returns null.  The caller
221    * may then still attempt to determine the MIME type by a call to
222    * guessContentTypeFromStream()
223    *
224    * @return The content MIME type
225    */
226   public String getContentType()
227   {
228     return getHeaderField("content-type");
229   }
230
231   /**
232    * Returns the value of the content-encoding field or null if it is not
233    * known or not present.
234    * 
235    * @return The content-encoding field
236    */
237   public String getContentEncoding()
238   {
239     return getHeaderField("content-encoding");
240   }
241
242   /**
243    * Returns the value of the expires header or 0 if not known or present.
244    * If populated, the return value is number of seconds since midnight
245    * on 1/1/1970 GMT.
246    *
247    * @return The expiration time.
248    */
249   public long getExpiration()
250   {
251     return getHeaderFieldDate("expires", 0L);
252   }
253
254   /**
255    * Returns the date of the document pointed to by the URL as reported in
256    * the date field of the header or 0 if the value is not present or not
257    * known. If populated, the return value is number of seconds since
258    * midnight on 1/1/1970 GMT.
259    *
260    * @return The document date
261    */
262   public long getDate()
263   {
264     return getHeaderFieldDate("date", 0L);
265   }
266
267   /**
268    * Returns the value of the last-modified header field or 0 if not known known
269    * or not present.  If populated, the return value is the number of seconds
270    * since midnight on 1/1/1970.
271    *
272    * @return The last modified time
273    */
274   public long getLastModified()
275   {
276     return getHeaderFieldDate("last-modified", 0L);
277   }
278
279   /**
280    * Return a String representing the header value at the specified index.
281    * This allows the caller to walk the list of header fields.  The analogous
282    * getHeaderFieldKey(int) method allows access to the corresponding key
283    * for this header field
284    *
285    * @param index The index into the header field list to retrieve the value for
286    *
287    * @return The header value or null if index is past the end of the headers
288    */
289   public String getHeaderField(int index)
290   {
291     // Subclasses for specific protocols override this.
292     return null;
293   }
294
295   /**
296    * Returns a String representing the value of the header field having
297    * the named key.  Returns null if the header field does not exist.
298    *
299    * @param The key of the header field
300    *
301    * @return The value of the header field as a String
302    */
303   public String getHeaderField(String name)
304   {
305     // Subclasses for specific protocols override this.
306     return null;
307   }
308
309   /**
310    * Returns a map of all sent header fields
311    * 
312    * @since 1.4
313    */
314   public Map getHeaderFields()
315   {
316     // Subclasses for specific protocols override this.
317     return null;
318   }
319
320   /**
321    * Returns the value of the named header field as an int.  If the field
322    * is not present or cannot be parsed as an integer, the default value
323    * will be returned.
324    *
325    * @param name The name of the header field
326    * @param val The default value
327    *
328    * @return The value of the header field or the default value if the field
329    * is missing or malformed
330    */
331   public int getHeaderFieldInt(String name, int val)
332   {
333     String str = getHeaderField(name);
334     try
335       {
336         if (str != null)
337           val = Integer.parseInt(str);
338       }
339     catch (NumberFormatException e)
340       {
341         ; // Do nothing; val is the default.
342       }
343     return val;
344   }
345
346   /**
347    * Returns the value of the named header field as a date.  This date will
348    * be the number of seconds since midnight 1/1/1970 GMT or the default
349    * value if the field is not present or cannot be converted to a date.
350    *
351    * @param name The name of the header field
352    * @param val The dafault date
353    *
354    * @return Returns the date value of the header filed or the default value
355    * if the field is missing or malformed
356    */
357   public long getHeaderFieldDate(String name, long val)
358   {
359     if (! dateformats_initialized)
360       initializeDateFormats();
361     String str = getHeaderField(name);
362     if (str != null)
363       {
364         Date date;
365         if ((date = dateFormat1.parse(str, new ParsePosition(0))) != null)
366           val = date.getTime();
367         else if ((date = dateFormat2.parse(str, new ParsePosition(0))) != null)
368           val = date.getTime();
369         else if ((date = dateFormat3.parse(str, new ParsePosition(0))) != null)
370           val = date.getTime();
371       }
372     return val;
373   }
374
375   /**
376    * Returns a String representing the header key at the specified index.
377    * This allows the caller to walk the list of header fields.  The analogous
378    * getHeaderField(int) method allows access to the corresponding value for
379    * this tag.
380    *
381    * @param index The index into the header field list to retrieve the key for. 
382    *
383    * @return The header field key or null if index is past the end
384    * of the headers.
385    */
386   public String getHeaderFieldKey(int index)
387   {
388     // Subclasses for specific protocols override this.
389     return null;
390   }
391
392   /**
393    * This method returns the content of the document pointed to by the URL
394    * as an Object.  The type of object depends on the MIME type of the
395    * object and particular content hander loaded.  Most text type content
396    * handlers will return a subclass of InputStream.  Images usually return
397    * a class that implements ImageProducer.  There is not guarantee what
398    * type of object will be returned, however.
399    * <p>
400    * This class first determines the MIME type of the content, then creates
401    * a ContentHandler object to process the input.  If the ContentHandlerFactory
402    * is set, then that object is called to load a content handler, otherwise
403    * a class called gnu.java.net.content.<content_type> is tried.
404    * The default class will also be used if the content handler factory returns
405    * a null content handler.
406    *
407    * @exception IOException If an error occurs
408    * @exception UnknownServiceException If the protocol does not support the
409    * content type
410    */
411   public Object getContent() throws IOException
412   {
413     // FIXME: Doc indicates that other criteria should be applied as
414     // heuristics to determine the true content type, e.g. see 
415     // guessContentTypeFromName() and guessContentTypeFromStream methods
416     // as well as FileNameMap class & fileNameMap field & get/set methods.
417     String cType = getContentType();
418     contentHandler = setContentHandler(cType);
419     if (contentHandler == null)
420       return getInputStream();
421
422     return contentHandler.getContent(this);
423   }
424
425   /**
426    * Retrieves the content of this URLConnection
427    *
428    * @exception IOException If an error occurs
429    * @exception UnknownServiceException If the protocol does not support the
430    * content type
431    */
432   public Object getContent(Class[] classes) throws IOException
433   {
434     // FIXME: implement this
435     return getContent ();
436   }
437
438   /**
439    * This method returns a <code>Permission</code> object representing the
440    * permissions required to access this URL.  This method returns
441    * <code>java.security.AllPermission</code> by default.  Subclasses should
442    * override it to return a more specific permission.  For example, an
443    * HTTP URL should return an instance of <code>SocketPermission</code>
444    * for the appropriate host and port.
445    * <p>
446    * Note that because of items such as HTTP redirects, the permission
447    * object returned might be different before and after connecting.
448    *
449    * @return A Permission object
450    *
451    * @exception IOException If the computation of the permission requires
452    * network or file I/O and an exception occurs while computing it
453    */
454   public Permission getPermission() throws IOException
455   {
456     // Subclasses may override this.
457     return new java.security.AllPermission();
458   }
459
460   /**
461    * Returns an InputStream for this connection.  As this default
462    * implementation returns null, subclasses should override this method
463    *
464    * @return An InputStream for this connection
465    *
466    * @exception IOException If an error occurs
467    * @exception UnknownServiceException If the protocol does not support input
468    */
469   public InputStream getInputStream() throws IOException
470   {
471     // Subclasses for specific protocols override this.
472     throw new UnknownServiceException("Protocol " + url.getProtocol() +
473                         " does not support input.");
474   }
475
476   /**
477    * Returns an OutputStream for this connection.  As this default
478    * implementation returns null, subclasses should override this method
479    *
480    * @return An OutputStream for this connection
481    *
482    * @exception IOException If an error occurs
483    * @exception UnknownServiceException If the protocol does not support output
484    */
485   public OutputStream getOutputStream() throws IOException
486   {
487     // Subclasses for specific protocols override this.
488     throw new UnknownServiceException("Protocol " + url.getProtocol() +
489                         " does not support output.");
490   }
491
492   /**
493    * The methods prints the value of this object as a String by calling the
494    * toString() method of its associated URL.  Overrides Object.toString()
495    * 
496    * @return A String representation of this object
497    */
498   public String toString()
499   {
500     return this.getClass().getName() + ":" + url.toString();
501   }
502
503   /**
504    * Returns the value of a flag indicating whether or not input is going
505    * to be done for this connection.  This default to true unless the
506    * doOutput flag is set to false, in which case this defaults to false.
507    * 
508    * @param doinput The new value of the doInput field
509    *
510    * @exception IllegalStateException If already connected
511    */
512   public void setDoInput(boolean input)
513   {
514     if (connected)
515       throw new IllegalStateException ("Already connected");
516
517     doInput = input;
518   }
519
520   /**
521    * Returns the value of a flag indicating whether or not input is going
522    * to be done for this connection.  This default to true unless the
523    * doOutput flag is set to false, in which case this defaults to false.
524    *
525    * @return true if input is to be done, false otherwise
526    */
527   public boolean getDoInput()
528   {
529     return doInput;
530   }
531
532   /**
533    * Returns a boolean flag indicating whether or not output will be done
534    * on this connection.  The default value is false, so this method can
535    * be used to override the default
536    *
537    * @param output ture if output is to be done, false otherwise
538    *
539    * @exception IllegalStateException If already connected
540    */
541   public void setDoOutput(boolean output)
542   {
543     if (connected)
544       throw new IllegalStateException ("Already connected");
545
546     doOutput = output;
547   }
548
549   /**
550    * Returns a boolean flag indicating whether or not output will be done
551    * on this connection.  This defaults to false.
552    *
553    * @return true if output is to be done, false otherwise
554    */
555   public boolean getDoOutput()
556   {
557     return doOutput;
558   }
559
560   /**
561    * Sets a boolean flag indicating whether or not user interaction is
562    * allowed for this connection.  (For example, in order to prompt for
563    * username and password info.
564    *
565    * @param allow true if user interaction should be allowed, false otherwise.
566    *
567    * @exception IllegalStateException If already connected
568    */
569   public void setAllowUserInteraction(boolean allow)
570   {
571     allowUserInteraction = allow;
572   }
573
574   /**
575    * Returns a boolean flag indicating whether or not user interaction is
576    * allowed for this connection.  (For example, in order to prompt for
577    * username and password info.
578    *
579    * @return true if user interaction is allowed, false otherwise
580    */
581   public boolean getAllowUserInteraction()
582   {
583     return allowUserInteraction;
584   }
585
586   /**
587    * Sets the default flag for whether or not interaction with a user
588    * is allowed.  This will be used for all connections unless overridden
589    *
590    * @param allow true to allow user interaction, false otherwise
591    */
592   public static void setDefaultAllowUserInteraction(boolean allow)
593   {
594     defaultAllowUserInteraction = allow;
595   }
596
597   /**
598    * Returns the default flag for whether or not interaction with a user
599    * is allowed.  This will be used for all connections unless overridden
600    *
601    * @return true if user interaction is allowed, false otherwise
602    */
603   public static boolean getDefaultAllowUserInteraction()
604   {
605     return defaultAllowUserInteraction;
606   }
607
608   /**
609    * Sets a boolean flag indicating whether or not caching will be used
610    * (if possible) to store data downloaded via the connection.
611    *
612    * @param usecaches The new value
613    *
614    * @exception IllegalStateException If already connected
615    */
616   public void setUseCaches(boolean usecaches)
617   {
618     if (connected)
619       throw new IllegalStateException ("Already connected");
620
621     useCaches = usecaches;
622   }
623
624   /**
625    * Returns a boolean flag indicating whether or not caching will be used
626    * (if possible) to store data downloaded via the connection.
627    *
628    * @return true if caching should be used if possible, false otherwise
629    */
630   public boolean getUseCaches()
631   {
632     return useCaches;
633   }
634
635   /**
636    * Sets the ifModified since instance variable.  If this value is non
637    * zero and the underlying protocol supports it, the actual document will
638    * not be fetched unless it has been modified since this time.  The value
639    * passed should  be 0 if this feature is to be disabled or the time expressed
640    * as the number of seconds since midnight 1/1/1970 GMT otherwise.
641    *
642    * @param ifmodifiedsince The new value in milliseconds
643    * since January 1, 1970 GMT
644    *
645    * @exception IllegalStateException If already connected
646    */
647   public void setIfModifiedSince(long ifmodifiedsince)
648   {
649     if (connected)
650       throw new IllegalStateException ("Already connected");
651
652     ifModifiedSince = ifmodifiedsince;
653   }
654
655   /**
656    * Returns the ifModified since instance variable.  If this value is non
657    * zero and the underlying protocol supports it, the actual document will
658    * not be fetched unless it has been modified since this time.  The value
659    * returned will be 0 if this feature is disabled or the time expressed
660    * as the number of seconds since midnight 1/1/1970 GMT otherwise
661    *
662    * @return The ifModifiedSince value
663    */
664   public long getIfModifiedSince()
665   {
666     return ifModifiedSince;
667   }
668
669   /**
670    * Returns the default value of the useCaches field
671    */
672   public boolean getDefaultUseCaches()
673   {
674     return defaultUseCaches;
675   }
676
677   /**
678    * Sets the default value used to determine whether or not caching
679    * of documents will be done when possible.
680    *
681    * @param use true to use caches if possible by default, false otherwise
682    */
683   public void setDefaultUseCaches(boolean defaultusecaches)
684   {
685     defaultUseCaches = defaultusecaches;
686   }
687
688   /**
689    * Returns the default value used to determine whether or not caching
690    * of documents will be done when possible.
691    *
692    * @param key Key of the property to set
693    * @param value Value of the Property to set
694    *
695    * @exception IllegalStateException If already connected
696    * @exception NullPointerException If key is null
697    *
698    * @see URLConnection#getRequestProperty(String key)
699    * @see URLConnection#addRequestProperty(String key, String value)
700    */
701   public void setRequestProperty(String key, String value)
702   {
703     if (connected)
704       throw new IllegalStateException ("Already connected");
705
706     // Do nothing unless overridden by subclasses that support setting
707     // header fields in the request.
708   }
709
710   /**
711    * Sets the value of the named request property
712    *
713    * @param key Key of the property to add
714    * @param value Value of the Property to add
715    *
716    * @exception IllegalStateException If already connected
717    * @exception NullPointerException If key is null
718    * 
719    * @see URLConnection#getRequestProperty(String key)
720    * @see URLConnection#setRequestProperty(String key, String value)
721    * 
722    * @since 1.4
723    */
724   public void addRequestProperty(String key, String value)
725   {
726     if (connected)
727       throw new IllegalStateException ("Already connected");
728
729     if (getRequestProperty (key) == null)
730       {
731         setRequestProperty (key, value);
732       }
733   }
734
735   /**
736    * Returns the value of the named request property.
737    *
738    * @param key The name of the property
739    *
740    * @return Value of the property
741    *
742    * @exception IllegalStateException If already connected
743    *
744    * @see URLConnection#setRequestProperty(String key, String value)
745    * @see URLConnection#addRequestProperty(String key, String value)
746    */
747   public String getRequestProperty(String key)
748   {
749     if (connected)
750       throw new IllegalStateException ("Already connected");
751
752     // Overridden by subclasses that support reading header fields from the
753     // request.
754     return null;
755   }
756
757   /**
758    * Returns an unmodifiable Map containing the request properties.
759    *
760    * @return The map of properties
761    *
762    * @exception IllegalStateException If already connected
763    *
764    * @since 1.4
765    */
766   public Map getRequestProperties()
767   {
768     // Overridden by subclasses that support reading header fields from the
769     // request.
770     return null;
771   }
772
773   /**
774    * Sets the default value of a request property.  This will be used
775    * for all connections unless the value of the property is manually
776    * overridden.
777    *
778    * @param key The request property name the default is being set for
779    * @param value The value to set the default to
780    *
781    * @deprecated 1.3 The method setRequestProperty should be used instead
782    *
783    * @see URLConnectionr#setRequestProperty(String key, String value)
784    */
785   public static void setDefaultRequestProperty(String key, String value)
786   {
787     // Do nothing unless overridden by subclasses that support setting
788     // default request properties.
789   }
790
791   /**
792    * Returns the default value of a request property.  This will be used
793    * for all connections unless the value of the property is manually
794    * overridden.
795    *
796    * @param key The request property to return the default value of
797    *
798    * @return The value of the default property or null if not available
799    * 
800    * @deprecated 1.3 The method getRequestProperty should be used instead
801    *
802    * @see URLConnection#getRequestProperty(String key)
803    */
804   public static String getDefaultRequestProperty(String key)
805   {
806     // Overridden by subclasses that support default request properties.
807     return null;
808   }
809
810   /**
811    * Set's the ContentHandlerFactory for an application.  This can be called
812    * once and only once.  If it is called again, then an Error is thrown.
813    * Unlike for other set factory methods, this one does not do a security
814    * check prior to setting the factory.
815    *
816    * @param factory The ContentHandlerFactory for this application
817    *
818    * @exception Error If the factory has already been defined
819    * @exception SecurityException If a security manager exists and its
820    * checkSetFactory method doesn't allow the operation
821    */
822   public static synchronized void setContentHandlerFactory
823                                     (ContentHandlerFactory fac)
824   {
825     if (factory != null)
826       throw new Error("ContentHandlerFactory already set");
827
828     // Throw an exception if an extant security mgr precludes
829     // setting the factory.
830     SecurityManager s = System.getSecurityManager();
831     if (s != null)
832       s.checkSetFactory();
833
834     factory = fac;
835   }
836
837   /**
838    * Returns the MIME type of a file based on the name of the file.  This
839    * works by searching for the file's extension in a list of file extensions
840    * and returning the MIME type associated with it.  If no type is found,
841    * then a MIME type of "application/octet-stream" will be returned.
842    *
843    * @param filename The filename to determine the MIME type for
844    *
845    * @return The MIME type String
846    *
847    * @specnote public since JDK 1.4
848    */
849   public static String guessContentTypeFromName(String filename)
850   {
851     int dot = filename.lastIndexOf (".");
852     
853     if (dot != -1)
854       {
855         if (dot == filename.length())
856           return ("application/octet-stream");
857         else
858           filename = filename.substring (dot + 1);
859       }
860     
861     String type = MimeTypes.getMimeTypeFromExtension (filename);
862     
863     if (type == null)
864       return("application/octet-stream");
865
866     return type;
867   }
868
869   /**
870    * Returns the MIME type of a stream based on the first few characters
871    * at the beginning of the stream.  This routine can be used to determine
872    * the MIME type if a server is believed to be returning an incorrect
873    * MIME type.  This method returns "application/octet-stream" if it 
874    * cannot determine the MIME type.
875    * <p>
876    * NOTE: Overriding MIME types sent from the server can be obnoxious
877    * to user's.  See Internet Exploder 4 if you don't believe me.
878    *
879    * @param is The InputStream to determine the MIME type from
880    *
881    * @return The MIME type
882    *
883    * @exception IOException If an error occurs
884    */
885   public static String guessContentTypeFromStream(InputStream is)
886     throws IOException
887   {
888     is.mark(1024);
889     // FIXME: Implement this. Use system mimetype informations (like "file").
890     is.reset();
891     return null;
892   }
893
894   /**
895    * This method returns the <code>FileNameMap</code> object being used
896    * to decode MIME types by file extension.
897    *
898    * @return The <code>FileNameMap</code>.
899    *
900    * @since 1.2
901    */
902   public static FileNameMap getFileNameMap()
903   {
904     return fileNameMap;
905   }
906
907   /**
908    * This method set the <code>FileNameMap</code> object being used
909    * to decode MIME types by file extension.
910    *
911    * @param map The <code>FileNameMap</code>.
912    *
913    * @exception SecurityException If a security manager exists and its
914    * checkSetFactory method doesn't allow the operation
915    * 
916    * @since 1.2
917    */
918   public static void setFileNameMap(FileNameMap map)
919   {
920     // Throw an exception if an extant security mgr precludes
921     // setting the factory.
922     SecurityManager s = System.getSecurityManager();
923     if (s != null)
924       s.checkSetFactory();
925
926     fileNameMap = map;
927   }
928
929   private ContentHandler setContentHandler(String contentType)
930   {
931     ContentHandler handler;
932
933     // No content type so just handle it as the default.
934     if (contentType == null || contentType == "")
935       return null;
936
937     // See if a handler has been cached for this content type.
938     // For efficiency, if a content type has been searched for but not
939     // found, it will be in the hash table but as the contentType String
940     // instead of a ContentHandler.
941     if ((handler = (ContentHandler) handlers.get(contentType)) != null)
942       if (handler instanceof ContentHandler)
943         return handler;
944       else
945         return null;
946
947     // If a non-default factory has been set, use it to find the content type.
948     if (factory != null)
949       handler = factory.createContentHandler(contentType);
950
951     // Non-default factory may have returned null or a factory wasn't set.
952     // Use the default search algorithm to find a handler for this content type.
953     if (handler == null)
954       {
955         // Get the list of packages to check and append our default handler
956         // to it, along with the JDK specified default as a last resort.
957         // Except in very unusual environments the JDK specified one shouldn't
958         // ever be needed (or available).
959         String propVal = System.getProperty("java.content.handler.pkgs");
960         propVal = (propVal == null) ? "" : (propVal + "|");
961         propVal = propVal + "gnu.gcj.content|sun.net.www.content";
962
963         // Replace the '/' character in the content type with '.' and
964         // all other non-alphabetic, non-numeric characters with '_'.
965         StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
966         char[] cArray = contentType.toCharArray();
967         for (int i = 0; i < cArray.length; i++)
968           {
969             if (cArray[i] == '/')
970               cArray[i] = '.';
971             else if (! ((cArray[i] >= 'A' && cArray[i] <= 'Z') || 
972                         (cArray[i] >= 'a' && cArray[i] <= 'z') ||
973                         (cArray[i] >= '0' && cArray[i] <= '9')))
974               cArray[i] = '_';
975           }
976         String contentClass = new String(cArray);
977
978         // See if a class of this content type exists in any of the packages.
979         do
980           {
981             String facName = pkgPrefix.nextToken() + "." + contentClass;
982             try
983               {
984                 handler =
985                   (ContentHandler) Class.forName(facName).newInstance();
986               }
987             catch (Exception e)
988               {
989                 // Can't instantiate; handler still null, go on to next element.
990               }
991           } while ((handler == null ||
992                     ! (handler instanceof ContentHandler)) &&
993                    pkgPrefix.hasMoreTokens());
994       }
995
996     // Update the hashtable with the new content handler.
997     if (handler != null && handler instanceof ContentHandler)
998       {
999         handlers.put(contentType, handler);
1000         return handler;
1001       }
1002
1003     // For efficiency on subsequent searches, put a dummy entry in the hash
1004     // table for content types that don't have a non-default ContentHandler.
1005     handlers.put(contentType, contentType);
1006     return null;
1007   }
1008   
1009   // We don't put these in a static initializer, because it creates problems
1010   // with initializer co-dependency: SimpleDateFormat's constructors eventually 
1011   // depend on URLConnection (via the java.text.*Symbols classes).
1012   private synchronized void initializeDateFormats()
1013   {
1014     if (dateformats_initialized)
1015       return;
1016     locale = new Locale("En", "Us", "Unix");
1017     dateFormat1 = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", 
1018                                        locale);
1019     dateFormat2 = new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", 
1020                                        locale);
1021     dateFormat3 = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
1022     dateformats_initialized = true;
1023   }
1024 } // class URLConnection
1025