OSDN Git Service

37ddc0f6cd4d4e5578f4ee4c29022b30cbaefbc5
[pf3gnuchains/gcc-fork.git] / libjava / java / net / URL.java
1 /* URL.java -- Uniform Resource Locator Class
2    Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39 package java.net;
40
41 import gnu.java.net.URLParseError;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.ObjectInputStream;
45 import java.io.ObjectOutputStream;
46 import java.io.Serializable;
47 import java.util.HashMap;
48 import java.util.StringTokenizer;
49
50
51 /*
52  * Written using on-line Java Platform 1.2 API Specification, as well
53  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
54  * Status:  Believed complete and correct.
55  */
56
57 /**
58   * This final class represents an Internet Uniform Resource Locator (URL).
59   * For details on the syntax of URL's and what they can be used for,
60   * refer to RFC 1738, available from <a
61   * href="http://ds.internic.net/rfcs/rfc1738.txt">
62   * http://ds.internic.net/rfcs/rfc1738.txt</a>
63   * <p>
64   * There are a great many protocols supported by URL's such as "http",
65   * "ftp", and "file".  This object can handle any arbitrary URL for which
66   * a URLStreamHandler object can be written.  Default protocol handlers
67   * are provided for the "http" and "ftp" protocols.  Additional protocols
68   * handler implementations may be provided in the future.  In any case,
69   * an application or applet can install its own protocol handlers that
70   * can be "chained" with other protocol hanlders in the system to extend
71   * the base functionality provided with this class. (Note, however, that
72   * unsigned applets cannot access properties by default or install their
73   * own protocol handlers).
74   * <p>
75   * This chaining is done via the system property java.protocol.handler.pkgs
76   * If this property is set, it is assumed to be a "|" separated list of
77   * package names in which to attempt locating protocol handlers.  The
78   * protocol handler is searched for by appending the string
79   * ".&lt;protocol&gt;.Handler" to each packed in the list until a hander is
80   * found. If a protocol handler is not found in this list of packages, or if
81   * the property does not exist, then the default protocol handler of
82   * "gnu.java.net.&lt;protocol&gt;.Handler" is tried.  If this is
83   * unsuccessful, a MalformedURLException is thrown.
84   * <p>
85   * All of the constructor methods of URL attempt to load a protocol
86   * handler and so any needed protocol handlers must be installed when
87   * the URL is constructed.
88   * <p>
89   * Here is an example of how URL searches for protocol handlers.  Assume
90   * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the
91   * URL is "news://comp.lang.java.programmer".  URL would looking the
92   * following places for protocol handlers:
93   * <p><pre>
94   * com.foo.news.Handler
95   * com.bar.news.Handler
96   * gnu.java.net.news.Handler
97   * </pre><p>
98   * If the protocol handler is not found in any of those locations, a
99   * MalformedURLException would be thrown.
100   * <p>
101   * Please note that a protocol handler must be a subclass of
102   * URLStreamHandler.
103   * <p>
104   * Normally, this class caches protocol handlers.  Once it finds a handler
105   * for a particular protocol, it never tries to look up a new handler
106   * again.  However, if the system property
107   * gnu.java.net.nocache_protocol_handlers is set, then this
108   * caching behavior is disabled.  This property is specific to this
109   * implementation.  Sun's JDK may or may not do protocol caching, but it
110   * almost certainly does not examine this property.
111   * <p>
112   * Please also note that an application can install its own factory for
113   * loading protocol handlers (see setURLStreamHandlerFactory).  If this is
114   * done, then the above information is superseded and the behavior of this
115   * class in loading protocol handlers is dependent on that factory.
116   *
117   * @author Aaron M. Renn <arenn@urbanophile.com>
118   * @author Warren Levy <warrenl@cygnus.com>
119   *
120   * @see URLStreamHandler
121   */
122 public final class URL implements Serializable
123 {
124   private static final String DEFAULT_SEARCH_PATH =
125     "gnu.java.net.protocol|sun.net.www.protocol";
126
127   /**
128    * The name of the protocol for this URL.
129    * The protocol is always stored in lower case.
130    */
131   private String protocol;
132
133   /**
134    * The "authority" portion of the URL.
135    */
136   private String authority;
137
138   /**
139    * The hostname or IP address of this protocol.
140    * This includes a possible user. For example <code>joe@some.host.net</code>.
141    */
142   private String host;
143
144   /**
145    * The user information necessary to establish the connection.
146    */
147   private String userInfo;
148
149   /**
150    * The port number of this protocol or -1 if the port number used is
151    * the default for this protocol.
152    */
153   private int port = -1; // Initialize for constructor using context.
154
155   /**
156    * The "file" portion of the URL. It is defined as <code>path[?query]</code>.
157    */
158   private String file;
159
160   /**
161    * The anchor portion of the URL.
162    */
163   private String ref;
164
165   /**
166    * This is the hashCode for this URL
167    */
168   private int hashCode;
169
170   /**
171    * The protocol handler in use for this URL
172    */
173   transient URLStreamHandler ph;
174
175   /**
176    * If an application installs its own protocol handler factory, this is
177    * where we keep track of it.
178    */
179   private static URLStreamHandlerFactory factory;
180   private static final long serialVersionUID = -7627629688361524110L;
181
182   /**
183    * This a table where we cache protocol handlers to avoid the overhead
184    * of looking them up each time.
185    */
186   private static HashMap ph_cache = new HashMap();
187
188   /**
189    * Whether or not to cache protocol handlers.
190    */
191   private static boolean cache_handlers;
192
193   static
194     {
195       String s = System.getProperty("gnu.java.net.nocache_protocol_handlers");
196
197       if (s == null)
198         cache_handlers = true;
199       else
200         cache_handlers = false;
201     }
202
203   /**
204    * Constructs a URL and loads a protocol handler for the values passed as
205    * arguments.
206    *
207    * @param protocol The protocol for this URL ("http", "ftp", etc)
208    * @param host The hostname or IP address to connect to
209    * @param port The port number to use, or -1 to use the protocol's
210    * default port
211    * @param file The "file" portion of the URL.
212    *
213    * @exception MalformedURLException If a protocol handler cannot be loaded or
214    * a parse error occurs.
215    */
216   public URL(String protocol, String host, int port, String file)
217     throws MalformedURLException
218   {
219     this(protocol, host, port, file, null);
220   }
221
222   /**
223    * Constructs a URL and loads a protocol handler for the values passed in
224    * as arugments.  Uses the default port for the protocol.
225    *
226    * @param protocol The protocol for this URL ("http", "ftp", etc)
227    * @param host The hostname or IP address for this URL
228    * @param file The "file" portion of this URL.
229    *
230    * @exception MalformedURLException If a protocol handler cannot be loaded or
231    * a parse error occurs.
232    */
233   public URL(String protocol, String host, String file)
234     throws MalformedURLException
235   {
236     this(protocol, host, -1, file, null);
237   }
238
239   /**
240    * This method initializes a new instance of <code>URL</code> with the
241    * specified protocol, host, port, and file.  Additionally, this method
242    * allows the caller to specify a protocol handler to use instead of
243    * the default.  If this handler is specified, the caller must have
244    * the "specifyStreamHandler" permission (see <code>NetPermission</code>)
245    * or a <code>SecurityException</code> will be thrown.
246    *
247    * @param protocol The protocol for this URL ("http", "ftp", etc)
248    * @param host The hostname or IP address to connect to
249    * @param port The port number to use, or -1 to use the protocol's default
250    * port
251    * @param file The "file" portion of the URL.
252    * @param ph The protocol handler to use with this URL.
253    *
254    * @exception MalformedURLException If no protocol handler can be loaded
255    * for the specified protocol.
256    * @exception SecurityException If the <code>SecurityManager</code> exists
257    * and does not allow the caller to specify its own protocol handler.
258    *
259    * @since 1.2
260    */
261   public URL(String protocol, String host, int port, String file,
262              URLStreamHandler ph) throws MalformedURLException
263   {
264     if (protocol == null)
265       throw new MalformedURLException("null protocol");
266     protocol = protocol.toLowerCase();
267     this.protocol = protocol;
268
269     if (ph != null)
270       {
271         SecurityManager s = System.getSecurityManager();
272         if (s != null)
273           s.checkPermission(new NetPermission("specifyStreamHandler"));
274
275         this.ph = ph;
276       }
277     else
278       this.ph = getURLStreamHandler(protocol);
279
280     if (this.ph == null)
281       throw new MalformedURLException("Protocol handler not found: "
282                                       + protocol);
283
284     this.host = host;
285     this.port = port;
286     this.authority = (host != null) ? host : "";
287     if (port >= 0)
288         this.authority += ":" + port;
289
290     int hashAt = file.indexOf('#');
291     if (hashAt < 0)
292       {
293         this.file = file;
294         this.ref = null;
295       }
296     else
297       {
298         this.file = file.substring(0, hashAt);
299         this.ref = file.substring(hashAt + 1);
300       }
301     hashCode = hashCode(); // Used for serialization.
302   }
303
304   /**
305    * Initializes a URL from a complete string specification such as
306    * "http://www.urbanophile.com/arenn/".  First the protocol name is parsed
307    * out of the string.  Then a handler is located for that protocol and
308    * the parseURL() method of that protocol handler is used to parse the
309    * remaining fields.
310    *
311    * @param spec The complete String representation of a URL
312    *
313    * @exception MalformedURLException If a protocol handler cannot be found
314    * or the URL cannot be parsed
315    */
316   public URL(String spec) throws MalformedURLException
317   {
318     this((URL) null, spec, (URLStreamHandler) null);
319   }
320
321   /**
322    * This method parses a String representation of a URL within the
323    * context of an existing URL.  Principally this means that any
324    * fields not present the URL are inheritied from the context URL.
325    * This allows relative URL's to be easily constructed.  If the
326    * context argument is null, then a complete URL must be specified
327    * in the URL string.  If the protocol parsed out of the URL is
328    * different from the context URL's protocol, then then URL String
329    * is also expected to be a complete URL.
330    *
331    * @param context The context on which to parse the specification
332    * @param spec The string to parse an URL
333    *
334    * @exception MalformedURLException If a protocol handler cannot be found
335    * for the URL cannot be parsed
336    */
337   public URL(URL context, String spec) throws MalformedURLException
338   {
339     this(context, spec, (URLStreamHandler) null);
340   }
341
342   /**
343    * Creates an URL from given arguments
344    * This method parses a String representation of a URL within the
345    * context of an existing URL.  Principally this means that any fields
346    * not present the URL are inheritied from the context URL.  This allows
347    * relative URL's to be easily constructed.  If the context argument is
348    * null, then a complete URL must be specified in the URL string.
349    * If the protocol parsed out of the URL is different
350    * from the context URL's protocol, then then URL String is also
351    * expected to be a complete URL.
352    * <p>
353    * Additionally, this method allows the caller to specify a protocol handler
354    * to use instead of  the default.  If this handler is specified, the caller
355    * must have the "specifyStreamHandler" permission
356    * (see <code>NetPermission</code>) or a <code>SecurityException</code>
357    * will be thrown.
358    *
359    * @param context The context in which to parse the specification
360    * @param spec The string to parse as an URL
361    * @param ph The stream handler for the URL
362    *
363    * @exception MalformedURLException If a protocol handler cannot be found
364    * or the URL cannot be parsed
365    * @exception SecurityException If the <code>SecurityManager</code> exists
366    * and does not allow the caller to specify its own protocol handler.
367    *
368    * @since 1.2
369    */
370   public URL(URL context, String spec, URLStreamHandler ph)
371     throws MalformedURLException
372   {
373     /* A protocol is defined by the doc as the substring before a ':'
374      * as long as the ':' occurs before any '/'.
375      *
376      * If context is null, then spec must be an absolute URL.
377      *
378      * The relative URL need not specify all the components of a URL.
379      * If the protocol, host name, or port number is missing, the value
380      * is inherited from the context.  A bare file component is appended
381      * to the context's file.  The optional anchor is not inherited.
382      */
383
384     // If this is an absolute URL, then ignore context completely.
385     // An absolute URL must have chars prior to "://" but cannot have a colon
386     // right after the "://".  The second colon is for an optional port value
387     // and implies that the host from the context is used if available.
388     int colon;
389     if ((colon = spec.indexOf("://", 1)) > 0
390         && ! spec.regionMatches(colon, "://:", 0, 4))
391       context = null;
392
393     int slash;
394     if ((colon = spec.indexOf(':')) > 0
395         && (colon < (slash = spec.indexOf('/')) || slash < 0))
396       {
397         // Protocol specified in spec string.
398         protocol = spec.substring(0, colon).toLowerCase();
399         if (context != null && context.protocol.equals(protocol))
400           {
401             // The 1.2 doc specifically says these are copied to the new URL.
402             host = context.host;
403             port = context.port;
404             file = context.file;
405             userInfo = context.userInfo;
406             if (file == null || file.length() == 0)
407               file = "/";
408             authority = context.authority;
409           }
410       }
411     else if (context != null)
412       {
413         // Protocol NOT specified in spec string.
414         // Use context fields (except ref) as a foundation for relative URLs.
415         colon = -1;
416         protocol = context.protocol;
417         host = context.host;
418         port = context.port;
419         file = context.file;
420         userInfo = context.userInfo;
421         if (file == null || file.length() == 0)
422           file = "/";
423         authority = context.authority;
424       }
425     else // Protocol NOT specified in spec. and no context available.
426
427
428       throw new MalformedURLException("Absolute URL required with null context");
429
430     if (ph != null)
431       {
432         SecurityManager s = System.getSecurityManager();
433         if (s != null)
434           s.checkPermission(new NetPermission("specifyStreamHandler"));
435
436         this.ph = ph;
437       }
438     else
439       this.ph = getURLStreamHandler(protocol);
440
441     if (this.ph == null)
442       throw new MalformedURLException("Protocol handler not found: "
443                                       + protocol);
444
445     // JDK 1.2 doc for parseURL specifically states that any '#' ref
446     // is to be excluded by passing the 'limit' as the indexOf the '#'
447     // if one exists, otherwise pass the end of the string.
448     int hashAt = spec.indexOf('#', colon + 1);
449
450     try
451       {
452         this.ph.parseURL(this, spec, colon + 1,
453                          hashAt < 0 ? spec.length() : hashAt);
454       }
455     catch (URLParseError e)
456       {
457         throw new MalformedURLException(e.getMessage());
458       }
459
460     if (hashAt >= 0)
461       ref = spec.substring(hashAt + 1);
462
463     hashCode = hashCode(); // Used for serialization.
464   }
465
466   /**
467    * Test another URL for equality with this one.  This will be true only if
468    * the argument is non-null and all of the fields in the URL's match
469    * exactly (ie, protocol, host, port, file, and ref).  Overrides
470    * Object.equals(), implemented by calling the equals method of the handler.
471    *
472    * @param obj The URL to compare with
473    *
474    * @return true if the URL is equal, false otherwise
475    */
476   public boolean equals(Object obj)
477   {
478     if (! (obj instanceof URL))
479       return false;
480
481     return ph.equals(this, (URL) obj);
482   }
483
484   /**
485    * Returns the contents of this URL as an object by first opening a
486    * connection, then calling the getContent() method against the connection
487    *
488    * @return A content object for this URL
489    * @exception IOException If opening the connection or getting the
490    * content fails.
491    *
492    * @since 1.3
493    */
494   public Object getContent() throws IOException
495   {
496     return openConnection().getContent();
497   }
498
499   /**
500    * Gets the contents of this URL
501    *
502    * @param classes The allow classes for the content object.
503    *
504    * @return a context object for this URL.
505    *
506    * @exception IOException If an error occurs
507    */
508   public Object getContent(Class[] classes) throws IOException
509   {
510     // FIXME: implement this
511     return getContent();
512   }
513
514   /**
515    * Returns the file portion of the URL.
516    * Defined as <code>path[?query]</code>.
517    * Returns the empty string if there is no file portion.
518    *
519    * @return The filename specified in this URL, or an empty string if empty.
520    */
521   public String getFile()
522   {
523     return file == null ? "" : file;
524   }
525
526   /**
527    * Returns the path of the URL. This is the part of the file before any '?'
528    * character.
529    *
530    * @return The path specified in this URL, or null if empty.
531    *
532    * @since 1.3
533    */
534   public String getPath()
535   {
536     if (file == null)
537       return null;
538     int quest = file.indexOf('?');
539     return quest < 0 ? getFile() : file.substring(0, quest);
540   }
541
542   /**
543    * Returns the authority of the URL
544    *
545    * @return The authority specified in this URL.
546    *
547    * @since 1.3
548    */
549   public String getAuthority()
550   {
551     return authority;
552   }
553
554   /**
555    * Returns the host of the URL
556    *
557    * @return The host specified in this URL.
558    */
559   public String getHost()
560   {
561     int at = (host == null) ? -1 : host.indexOf('@');
562     return at < 0 ? host : host.substring(at + 1, host.length());
563   }
564
565   /**
566    * Returns the port number of this URL or -1 if the default port number is
567    * being used.
568    *
569    * @return The port number
570    *
571    * @see #getDefaultPort()
572    */
573   public int getPort()
574   {
575     return port;
576   }
577
578   /**
579    * Returns the default port of the URL. If the StreamHandler for the URL
580    * protocol does not define a default port it returns -1.
581    *
582    * @return The default port of the current protocol.
583    */
584   public int getDefaultPort()
585   {
586     return ph.getDefaultPort();
587   }
588
589   /**
590    * Returns the protocol of the URL
591    *
592    * @return The specified protocol.
593    */
594   public String getProtocol()
595   {
596     return protocol;
597   }
598
599   /**
600    * Returns the ref (sometimes called the "# reference" or "anchor") portion
601    * of the URL.
602    *
603    * @return The ref
604    */
605   public String getRef()
606   {
607     return ref;
608   }
609
610   /**
611    * Returns the user information of the URL. This is the part of the host
612    * name before the '@'.
613    *
614    * @return the user at a particular host or null when no user defined.
615    */
616   public String getUserInfo()
617   {
618     if (userInfo != null)
619       return userInfo;
620     int at = (host == null) ? -1 : host.indexOf('@');
621     return at < 0 ? null : host.substring(0, at);
622   }
623
624   /**
625    * Returns the query of the URL. This is the part of the file before the
626    * '?'.
627    *
628    * @return the query part of the file, or null when there is no query part.
629    */
630   public String getQuery()
631   {
632     int quest = (file == null) ? -1 : file.indexOf('?');
633     return quest < 0 ? null : file.substring(quest + 1, file.length());
634   }
635
636   /**
637    * Returns a hashcode computed by the URLStreamHandler of this URL
638    *
639    * @return The hashcode for this URL.
640    */
641   public int hashCode()
642   {
643     if (hashCode != 0)
644       return hashCode; // Use cached value if available.
645     else
646       return ph.hashCode(this);
647   }
648
649   /**
650    * Returns a URLConnection object that represents a connection to the remote
651    * object referred to by the URL. The URLConnection is created by calling the
652    * openConnection() method of the protocol handler
653    *
654    * @return A URLConnection for this URL
655    *
656    * @exception IOException If an error occurs
657    */
658   public URLConnection openConnection() throws IOException
659   {
660     return ph.openConnection(this);
661   }
662
663   /**
664    * Opens a connection to this URL and returns an InputStream for reading
665    * from that connection
666    *
667    * @return An <code>InputStream</code> for this URL.
668    *
669    * @exception IOException If an error occurs
670    */
671   public InputStream openStream() throws IOException
672   {
673     return openConnection().getInputStream();
674   }
675
676   /**
677    * Tests whether or not another URL refers to the same "file" as this one.
678    * This will be true if and only if the passed object is not null, is a
679    * URL, and matches all fields but the ref (ie, protocol, host, port,
680    * and file);
681    *
682    * @param url The URL object to test with
683    *
684    * @return true if URL matches this URL's file, false otherwise
685    */
686   public boolean sameFile(URL url)
687   {
688     return ph.sameFile(this, url);
689   }
690
691   /**
692    * Sets the specified fields of the URL. This is not a public method so
693    * that only URLStreamHandlers can modify URL fields. This might be called
694    * by the <code>parseURL()</code> method in that class. URLs are otherwise
695    * constant.
696    *
697    * @param protocol The protocol name for this URL
698    * @param host The hostname or IP address for this URL
699    * @param port The port number of this URL
700    * @param file The "file" portion of this URL.
701    * @param ref The anchor portion of this URL.
702    */
703   protected void set(String protocol, String host, int port, String file,
704                      String ref)
705   {
706     // TBD: Theoretically, a poorly written StreamHandler could pass an
707     // invalid protocol.  It will cause the handler to be set to null
708     // thus overriding a valid handler.  Callers of this method should
709     // be aware of this.
710     protocol = protocol.toLowerCase ();
711     if (! this.protocol.equals (protocol))
712       {
713         this.ph = getURLStreamHandler(protocol);
714         this.protocol = protocol;
715       }
716     this.authority = "";
717     this.port = port;
718     this.host = host;
719     this.file = file;
720     this.ref = ref;
721
722     if (host != null)
723       this.authority += host;
724     if (port >= 0)
725       this.authority += ":" + port;
726
727     hashCode = hashCode(); // Used for serialization.
728   }
729
730   /**
731    * Sets the specified fields of the URL. This is not a public method so
732    * that only URLStreamHandlers can modify URL fields. URLs are otherwise
733    * constant.
734    *
735    * @param protocol The protocol name for this URL.
736    * @param host The hostname or IP address for this URL.
737    * @param port The port number of this URL.
738    * @param authority The authority of this URL.
739    * @param userInfo The user and password (if needed) of this URL.
740    * @param path The "path" portion of this URL.
741    * @param query The query of this URL.
742    * @param ref The anchor portion of this URL.
743    *
744    * @since 1.3
745    */
746   protected void set(String protocol, String host, int port, String authority,
747                      String userInfo, String path, String query, String ref)
748   {
749     // TBD: Theoretically, a poorly written StreamHandler could pass an
750     // invalid protocol.  It will cause the handler to be set to null
751     // thus overriding a valid handler.  Callers of this method should
752     // be aware of this.
753     protocol = protocol.toLowerCase ();
754     if (! this.protocol.equals (protocol))
755       {
756         this.ph = getURLStreamHandler(protocol);
757         this.protocol = protocol;
758       }
759     this.host = host;
760     this.userInfo = userInfo;
761     this.port = port;
762     this.authority = authority;
763     if (query == null)
764       this.file = path;
765     else
766       this.file = path + "?" + query;
767     this.ref = ref;
768     hashCode = hashCode(); // Used for serialization.
769   }
770
771   /**
772    * Sets the URLStreamHandlerFactory for this class.  This factory is
773    * responsible for returning the appropriate protocol handler for
774    * a given URL.
775    *
776    * @param fac The URLStreamHandlerFactory class to use
777    *
778    * @exception Error If the factory is alread set.
779    * @exception SecurityException If a security manager exists and its
780    * checkSetFactory method doesn't allow the operation
781    */
782   public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac)
783   {
784     if (factory != null)
785       throw new Error("URLStreamHandlerFactory already set");
786
787     // Throw an exception if an extant security mgr precludes
788     // setting the factory.
789     SecurityManager s = System.getSecurityManager();
790     if (s != null)
791       s.checkSetFactory();
792     factory = fac;
793   }
794
795   /**
796    * Returns a String representing this URL.  The String returned is
797    * created by calling the protocol handler's toExternalForm() method.
798    *
799    * @return A string for this URL
800    */
801   public String toExternalForm()
802   {
803     // Identical to toString().
804     return ph.toExternalForm(this);
805   }
806
807   /**
808    * Returns a String representing this URL.  Identical to toExternalForm().
809    * The value returned is created by the protocol handler's
810    * toExternalForm method.  Overrides Object.toString()
811    *
812    * @return A string for this URL
813    */
814   public String toString()
815   {
816     // Identical to toExternalForm().
817     return ph.toExternalForm(this);
818   }
819
820   /**
821    * This internal method is used in two different constructors to load
822    * a protocol handler for this URL.
823    *
824    * @param protocol The protocol to load a handler for
825    *
826    * @return A URLStreamHandler for this protocol, or null when not found.
827    */
828   private static synchronized URLStreamHandler getURLStreamHandler(String protocol)
829   {
830     URLStreamHandler ph = null;
831
832     // First, see if a protocol handler is in our cache.
833     if (cache_handlers)
834       {
835         if ((ph = (URLStreamHandler) ph_cache.get(protocol)) != null)
836           return ph;
837       }
838
839     // If a non-default factory has been set, use it to find the protocol.
840     if (factory != null)
841       {
842         ph = factory.createURLStreamHandler(protocol);
843       }
844     else if (protocol.equals("core"))
845       {
846         ph = new gnu.java.net.protocol.core.Handler();
847       }
848     else if (protocol.equals("file"))
849       {
850         // This is an interesting case.  It's tempting to think that we
851         // could call Class.forName ("gnu.java.net.protocol.file.Handler") to
852         // get the appropriate class.  Unfortunately, if we do that the
853         // program will never terminate, because getURLStreamHandler is
854         // eventually called by Class.forName.
855         //
856         // Treating "file" as a special case is the minimum that will
857         // fix this problem.  If other protocols are required in a
858         // statically linked application they will need to be handled in
859         // the same way as "file".
860         ph = new gnu.java.net.protocol.file.Handler();
861       }
862
863     // Non-default factory may have returned null or a factory wasn't set.
864     // Use the default search algorithm to find a handler for this protocol.
865     if (ph == null)
866       {
867         // Get the list of packages to check and append our default handler
868         // to it, along with the JDK specified default as a last resort.
869         // Except in very unusual environments the JDK specified one shouldn't
870         // ever be needed (or available).
871         String ph_search_path =
872           System.getProperty("java.protocol.handler.pkgs");
873
874         // Tack our default package on at the ends.
875         if (ph_search_path != null)
876           ph_search_path += "|" + DEFAULT_SEARCH_PATH;
877         else
878           ph_search_path = DEFAULT_SEARCH_PATH;
879
880         // Finally loop through our search path looking for a match.
881         StringTokenizer pkgPrefix = new StringTokenizer(ph_search_path, "|");
882
883         do
884           {
885             String clsName =
886               (pkgPrefix.nextToken() + "." + protocol + ".Handler");
887
888             try
889               {
890                 Object obj = Class.forName(clsName).newInstance();
891
892                 if (! (obj instanceof URLStreamHandler))
893                   continue;
894                 else
895                   ph = (URLStreamHandler) obj;
896               }
897             catch (Exception e)
898               {
899                 // Can't instantiate; handler still null,
900                 // go on to next element.
901               }
902           }
903          while ((! (ph instanceof URLStreamHandler))
904                 && pkgPrefix.hasMoreTokens());
905       }
906
907     // Update the hashtable with the new protocol handler.
908     if (ph != null && cache_handlers)
909       if (ph instanceof URLStreamHandler)
910         ph_cache.put(protocol, ph);
911       else
912         ph = null;
913
914     return ph;
915   }
916
917   private void readObject(ObjectInputStream ois)
918     throws IOException, ClassNotFoundException
919   {
920     ois.defaultReadObject();
921     this.ph = getURLStreamHandler(protocol);
922     if (this.ph == null)
923       throw new IOException("Handler for protocol " + protocol + " not found");
924   }
925
926   private void writeObject(ObjectOutputStream oos) throws IOException
927   {
928     oos.defaultWriteObject();
929   }
930 }