URL.java 29.9 KB
Newer Older
1
/* URL.java -- Uniform Resource Locator Class
2
   Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005
3
   Free Software Foundation, Inc.
4 5 6 7 8 9 10

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */
Tom Tromey committed
38 39 40

package java.net;

41
import gnu.java.net.URLParseError;
42

43
import java.io.IOException;
44
import java.io.InputStream;
45 46
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
47
import java.io.Serializable;
48 49
import java.security.AccessController;
import java.security.PrivilegedAction;
50
import java.util.HashMap;
Tom Tromey committed
51 52
import java.util.StringTokenizer;

53

54
/*
Tom Tromey committed
55 56 57 58 59
 * Written using on-line Java Platform 1.2 API Specification, as well
 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
 * Status:  Believed complete and correct.
 */

60 61 62
/**
  * This final class represents an Internet Uniform Resource Locator (URL).
  * For details on the syntax of URL's and what they can be used for,
63
  * refer to RFC 1738, available from <a
64 65
  * href="http://ds.internic.net/rfcs/rfc1738.txt">
  * http://ds.internic.net/rfcs/rfc1738.txt</a>
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  * <p>
  * There are a great many protocols supported by URL's such as "http",
  * "ftp", and "file".  This object can handle any arbitrary URL for which
  * a URLStreamHandler object can be written.  Default protocol handlers
  * are provided for the "http" and "ftp" protocols.  Additional protocols
  * handler implementations may be provided in the future.  In any case,
  * an application or applet can install its own protocol handlers that
  * can be "chained" with other protocol hanlders in the system to extend
  * the base functionality provided with this class. (Note, however, that
  * unsigned applets cannot access properties by default or install their
  * own protocol handlers).
  * <p>
  * This chaining is done via the system property java.protocol.handler.pkgs
  * If this property is set, it is assumed to be a "|" separated list of
  * package names in which to attempt locating protocol handlers.  The
81
  * protocol handler is searched for by appending the string
82 83 84 85
  * ".&lt;protocol&gt;.Handler" to each packed in the list until a hander is
  * found. If a protocol handler is not found in this list of packages, or if
  * the property does not exist, then the default protocol handler of
  * "gnu.java.net.&lt;protocol&gt;.Handler" is tried.  If this is
86 87 88 89 90 91 92 93
  * unsuccessful, a MalformedURLException is thrown.
  * <p>
  * All of the constructor methods of URL attempt to load a protocol
  * handler and so any needed protocol handlers must be installed when
  * the URL is constructed.
  * <p>
  * Here is an example of how URL searches for protocol handlers.  Assume
  * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the
94
  * URL is "news://comp.lang.java.programmer".  URL would looking the
95 96 97 98 99 100 101 102 103 104 105
  * following places for protocol handlers:
  * <p><pre>
  * com.foo.news.Handler
  * com.bar.news.Handler
  * gnu.java.net.news.Handler
  * </pre><p>
  * If the protocol handler is not found in any of those locations, a
  * MalformedURLException would be thrown.
  * <p>
  * Please note that a protocol handler must be a subclass of
  * URLStreamHandler.
106 107 108 109 110 111 112 113
  * <p>
  * Normally, this class caches protocol handlers.  Once it finds a handler
  * for a particular protocol, it never tries to look up a new handler
  * again.  However, if the system property
  * gnu.java.net.nocache_protocol_handlers is set, then this
  * caching behavior is disabled.  This property is specific to this
  * implementation.  Sun's JDK may or may not do protocol caching, but it
  * almost certainly does not examine this property.
114 115 116 117 118
  * <p>
  * Please also note that an application can install its own factory for
  * loading protocol handlers (see setURLStreamHandlerFactory).  If this is
  * done, then the above information is superseded and the behavior of this
  * class in loading protocol handlers is dependent on that factory.
119
  *
120 121
  * @author Aaron M. Renn (arenn@urbanophile.com)
  * @author Warren Levy (warrenl@cygnus.com)
122 123 124
  *
  * @see URLStreamHandler
  */
Tom Tromey committed
125 126
public final class URL implements Serializable
{
127
  private static final String DEFAULT_SEARCH_PATH =
128
    "gnu.java.net.protocol|gnu.inet";
129

130 131 132
  // Cached System ClassLoader
  private static ClassLoader systemClassLoader;

133 134 135 136
  /**
   * The name of the protocol for this URL.
   * The protocol is always stored in lower case.
   */
Tom Tromey committed
137
  private String protocol;
138 139 140 141

  /**
   * The "authority" portion of the URL.
   */
142
  private String authority;
143 144 145 146 147

  /**
   * The hostname or IP address of this protocol.
   * This includes a possible user. For example <code>joe@some.host.net</code>.
   */
Tom Tromey committed
148
  private String host;
149 150

  /**
151 152 153 154 155
   * The user information necessary to establish the connection.
   */
  private String userInfo;

  /**
156 157 158
   * The port number of this protocol or -1 if the port number used is
   * the default for this protocol.
   */
159
  private int port = -1; // Initialize for constructor using context.
160 161 162 163

  /**
   * The "file" portion of the URL. It is defined as <code>path[?query]</code>.
   */
Tom Tromey committed
164
  private String file;
165 166 167 168

  /**
   * The anchor portion of the URL.
   */
Tom Tromey committed
169
  private String ref;
170 171 172 173

  /**
   * This is the hashCode for this URL
   */
174
  private int hashCode;
175 176 177 178

  /**
   * The protocol handler in use for this URL
   */
179
  transient URLStreamHandler ph;
180 181

  /**
182 183 184 185 186 187 188
   * If an application installs its own protocol handler factory, this is
   * where we keep track of it.
   */
  private static URLStreamHandlerFactory factory;
  private static final long serialVersionUID = -7627629688361524110L;

  /**
189 190 191
   * This a table where we cache protocol handlers to avoid the overhead
   * of looking them up each time.
   */
192
  private static HashMap ph_cache = new HashMap();
193 194

  /**
195
   * Whether or not to cache protocol handlers.
196
   */
197
  private static boolean cache_handlers;
Tom Tromey committed
198

199 200
  static
    {
201 202
      String s = System.getProperty("gnu.java.net.nocache_protocol_handlers");

203
      if (s == null)
204
	cache_handlers = true;
205
      else
206
	cache_handlers = false;
207
    }
208

209
  /**
210 211
   * Constructs a URL and loads a protocol handler for the values passed as
   * arguments.
212
   *
213 214 215 216 217
   * @param protocol The protocol for this URL ("http", "ftp", etc)
   * @param host The hostname or IP address to connect to
   * @param port The port number to use, or -1 to use the protocol's
   * default port
   * @param file The "file" portion of the URL.
218
   *
219 220
   * @exception MalformedURLException If a protocol handler cannot be loaded or
   * a parse error occurs.
221
   */
Tom Tromey committed
222 223 224 225 226 227
  public URL(String protocol, String host, int port, String file)
    throws MalformedURLException
  {
    this(protocol, host, port, file, null);
  }

228
  /**
229 230
   * Constructs a URL and loads a protocol handler for the values passed in
   * as arugments.  Uses the default port for the protocol.
231
   *
232 233 234
   * @param protocol The protocol for this URL ("http", "ftp", etc)
   * @param host The hostname or IP address for this URL
   * @param file The "file" portion of this URL.
235
   *
236 237
   * @exception MalformedURLException If a protocol handler cannot be loaded or
   * a parse error occurs.
238
   */
Tom Tromey committed
239 240 241 242 243 244
  public URL(String protocol, String host, String file)
    throws MalformedURLException
  {
    this(protocol, host, -1, file, null);
  }

245
  /**
246 247
   * This method initializes a new instance of <code>URL</code> with the
   * specified protocol, host, port, and file.  Additionally, this method
248
   * allows the caller to specify a protocol handler to use instead of
249 250 251
   * the default.  If this handler is specified, the caller must have
   * the "specifyStreamHandler" permission (see <code>NetPermission</code>)
   * or a <code>SecurityException</code> will be thrown.
252
   *
253 254 255 256 257
   * @param protocol The protocol for this URL ("http", "ftp", etc)
   * @param host The hostname or IP address to connect to
   * @param port The port number to use, or -1 to use the protocol's default
   * port
   * @param file The "file" portion of the URL.
258
   * @param ph The protocol handler to use with this URL.
259
   *
260 261 262 263
   * @exception MalformedURLException If no protocol handler can be loaded
   * for the specified protocol.
   * @exception SecurityException If the <code>SecurityManager</code> exists
   * and does not allow the caller to specify its own protocol handler.
264 265 266
   *
   * @since 1.2
   */
267 268
  public URL(String protocol, String host, int port, String file,
             URLStreamHandler ph) throws MalformedURLException
Tom Tromey committed
269 270 271
  {
    if (protocol == null)
      throw new MalformedURLException("null protocol");
Anthony Green committed
272 273
    protocol = protocol.toLowerCase();
    this.protocol = protocol;
Tom Tromey committed
274

275
    if (ph != null)
Tom Tromey committed
276
      {
277
	SecurityManager s = System.getSecurityManager();
278
	if (s != null)
279
	  s.checkPermission(new NetPermission("specifyStreamHandler"));
Tom Tromey committed
280

281
	this.ph = ph;
Tom Tromey committed
282 283
      }
    else
284
      this.ph = getURLStreamHandler(protocol);
Tom Tromey committed
285

286
    if (this.ph == null)
287 288
      throw new MalformedURLException("Protocol handler not found: "
                                      + protocol);
Tom Tromey committed
289 290 291

    this.host = host;
    this.port = port;
292
    this.authority = (host != null) ? host : "";
Michael Koch committed
293
    if (port >= 0 && host != null)
294
	this.authority += ":" + port;
Tom Tromey committed
295 296 297 298 299 300 301 302 303 304 305 306

    int hashAt = file.indexOf('#');
    if (hashAt < 0)
      {
	this.file = file;
	this.ref = null;
      }
    else
      {
	this.file = file.substring(0, hashAt);
	this.ref = file.substring(hashAt + 1);
      }
307
    hashCode = hashCode(); // Used for serialization.
Tom Tromey committed
308 309
  }

310
  /**
311 312 313 314 315
   * Initializes a URL from a complete string specification such as
   * "http://www.urbanophile.com/arenn/".  First the protocol name is parsed
   * out of the string.  Then a handler is located for that protocol and
   * the parseURL() method of that protocol handler is used to parse the
   * remaining fields.
316
   *
317 318 319 320
   * @param spec The complete String representation of a URL
   *
   * @exception MalformedURLException If a protocol handler cannot be found
   * or the URL cannot be parsed
321
   */
Tom Tromey committed
322 323
  public URL(String spec) throws MalformedURLException
  {
324
    this((URL) null, spec != null ? spec : "", (URLStreamHandler) null);
Tom Tromey committed
325 326
  }

327
  /**
328 329 330 331 332 333 334 335 336
   * This method parses a String representation of a URL within the
   * context of an existing URL.  Principally this means that any
   * fields not present the URL are inheritied from the context URL.
   * This allows relative URL's to be easily constructed.  If the
   * context argument is null, then a complete URL must be specified
   * in the URL string.  If the protocol parsed out of the URL is
   * different from the context URL's protocol, then then URL String
   * is also expected to be a complete URL.
   *
337 338 339
   * @param context The context on which to parse the specification
   * @param spec The string to parse an URL
   *
340
   * @exception MalformedURLException If a protocol handler cannot be found
341
   * for the URL cannot be parsed
342
   */
Tom Tromey committed
343 344 345 346 347
  public URL(URL context, String spec) throws MalformedURLException
  {
    this(context, spec, (URLStreamHandler) null);
  }

348 349
  /**
   * Creates an URL from given arguments
350 351 352 353 354
   * This method parses a String representation of a URL within the
   * context of an existing URL.  Principally this means that any fields
   * not present the URL are inheritied from the context URL.  This allows
   * relative URL's to be easily constructed.  If the context argument is
   * null, then a complete URL must be specified in the URL string.
355
   * If the protocol parsed out of the URL is different
356 357 358 359 360 361 362 363
   * from the context URL's protocol, then then URL String is also
   * expected to be a complete URL.
   * <p>
   * Additionally, this method allows the caller to specify a protocol handler
   * to use instead of  the default.  If this handler is specified, the caller
   * must have the "specifyStreamHandler" permission
   * (see <code>NetPermission</code>) or a <code>SecurityException</code>
   * will be thrown.
364 365 366
   *
   * @param context The context in which to parse the specification
   * @param spec The string to parse as an URL
367
   * @param ph The stream handler for the URL
368
   *
369 370 371 372 373
   * @exception MalformedURLException If a protocol handler cannot be found
   * or the URL cannot be parsed
   * @exception SecurityException If the <code>SecurityManager</code> exists
   * and does not allow the caller to specify its own protocol handler.
   *
374 375
   * @since 1.2
   */
376
  public URL(URL context, String spec, URLStreamHandler ph)
Tom Tromey committed
377 378 379 380 381 382 383 384 385 386
    throws MalformedURLException
  {
    /* A protocol is defined by the doc as the substring before a ':'
     * as long as the ':' occurs before any '/'.
     *
     * If context is null, then spec must be an absolute URL.
     *
     * The relative URL need not specify all the components of a URL.
     * If the protocol, host name, or port number is missing, the value
     * is inherited from the context.  A bare file component is appended
387
     * to the context's file.  The optional anchor is not inherited.
Tom Tromey committed
388 389
     */

Warren Levy committed
390 391 392 393
    // If this is an absolute URL, then ignore context completely.
    // An absolute URL must have chars prior to "://" but cannot have a colon
    // right after the "://".  The second colon is for an optional port value
    // and implies that the host from the context is used if available.
Tom Tromey committed
394
    int colon;
395
    int slash = spec.indexOf('/');
396
    if ((colon = spec.indexOf("://", 1)) > 0
397
	&& ((colon < slash || slash < 0))
398
        && ! spec.regionMatches(colon, "://:", 0, 4))
Warren Levy committed
399 400
      context = null;

401
    if ((colon = spec.indexOf(':')) > 0
402
        && (colon < slash || slash < 0))
Tom Tromey committed
403 404
      {
	// Protocol specified in spec string.
405
	protocol = spec.substring(0, colon).toLowerCase();
Warren Levy committed
406
	if (context != null && context.protocol.equals(protocol))
Tom Tromey committed
407 408 409 410 411
	  {
	    // The 1.2 doc specifically says these are copied to the new URL.
	    host = context.host;
	    port = context.port;
	    file = context.file;
412
            userInfo = context.userInfo;
413 414
	    if (file == null || file.length() == 0)
	      file = "/";
415
	    authority = context.authority;
Tom Tromey committed
416 417 418 419 420 421 422 423 424 425 426
	  }
      }
    else if (context != null)
      {
	// Protocol NOT specified in spec string.
	// Use context fields (except ref) as a foundation for relative URLs.
	colon = -1;
	protocol = context.protocol;
	host = context.host;
	port = context.port;
	file = context.file;
427
        userInfo = context.userInfo;
428 429
	if (file == null || file.length() == 0)
	  file = "/";
430
	authority = context.authority;
Tom Tromey committed
431
      }
432 433
    else // Protocol NOT specified in spec. and no context available.
      throw new MalformedURLException("Absolute URL required with null context");
Tom Tromey committed
434

Michael Koch committed
435 436
    protocol = protocol.trim();

437
    if (ph != null)
Tom Tromey committed
438
      {
439
	SecurityManager s = System.getSecurityManager();
440
	if (s != null)
441
	  s.checkPermission(new NetPermission("specifyStreamHandler"));
Tom Tromey committed
442

443
	this.ph = ph;
Tom Tromey committed
444 445
      }
    else
446
      this.ph = getURLStreamHandler(protocol);
Tom Tromey committed
447

448
    if (this.ph == null)
449
      throw new MalformedURLException("Protocol handler not found: "
450
                                      + protocol);
Tom Tromey committed
451 452 453 454 455

    // JDK 1.2 doc for parseURL specifically states that any '#' ref
    // is to be excluded by passing the 'limit' as the indexOf the '#'
    // if one exists, otherwise pass the end of the string.
    int hashAt = spec.indexOf('#', colon + 1);
456 457 458 459

    try
      {
	this.ph.parseURL(this, spec, colon + 1,
460
	                 hashAt < 0 ? spec.length() : hashAt);
461 462 463 464 465
      }
    catch (URLParseError e)
      {
	throw new MalformedURLException(e.getMessage());
      }
466

Tom Tromey committed
467 468
    if (hashAt >= 0)
      ref = spec.substring(hashAt + 1);
469

470
    hashCode = hashCode(); // Used for serialization.
Tom Tromey committed
471 472
  }

473
  /**
474
   * Test another URL for equality with this one.  This will be true only if
475
   * the argument is non-null and all of the fields in the URL's match
476 477 478
   * exactly (ie, protocol, host, port, file, and ref).  Overrides
   * Object.equals(), implemented by calling the equals method of the handler.
   *
479
   * @param obj The URL to compare with
480 481
   *
   * @return true if the URL is equal, false otherwise
482
   */
483
  public boolean equals(Object obj)
Tom Tromey committed
484
  {
485
    if (! (obj instanceof URL))
Tom Tromey committed
486 487
      return false;

488
    return ph.equals(this, (URL) obj);
Tom Tromey committed
489 490
  }

491
  /**
492 493
   * Returns the contents of this URL as an object by first opening a
   * connection, then calling the getContent() method against the connection
494
   *
495 496 497
   * @return A content object for this URL
   * @exception IOException If opening the connection or getting the
   * content fails.
498
   *
499 500
   * @since 1.3
   */
501
  public Object getContent() throws IOException
Tom Tromey committed
502 503 504 505
  {
    return openConnection().getContent();
  }

506 507 508
  /**
   * Gets the contents of this URL
   *
509 510 511 512
   * @param classes The allow classes for the content object.
   *
   * @return a context object for this URL.
   *
513 514
   * @exception IOException If an error occurs
   */
515
  public Object getContent(Class[] classes) throws IOException
516 517 518 519 520
  {
    // FIXME: implement this
    return getContent();
  }

521
  /**
522 523 524
   * Returns the file portion of the URL.
   * Defined as <code>path[?query]</code>.
   * Returns the empty string if there is no file portion.
525
   *
Anthony Green committed
526
   * @return The filename specified in this URL, or an empty string if empty.
527
   */
Tom Tromey committed
528 529
  public String getFile()
  {
530
    return file == null ? "" : file;
Tom Tromey committed
531 532
  }

533
  /**
534 535
   * Returns the path of the URL. This is the part of the file before any '?'
   * character.
536
   *
Anthony Green committed
537
   * @return The path specified in this URL, or null if empty.
538
   *
539 540
   * @since 1.3
   */
541 542
  public String getPath()
  {
543 544
    // The spec says we need to return an empty string, but some
    // applications depends on receiving null when the path is empty.
Anthony Green committed
545 546 547
    if (file == null)
      return null;
    int quest = file.indexOf('?');
548
    return quest < 0 ? getFile() : file.substring(0, quest);
549 550
  }

551 552
  /**
   * Returns the authority of the URL
553 554
   *
   * @return The authority specified in this URL.
555
   *
556 557 558 559 560 561 562 563 564
   * @since 1.3
   */
  public String getAuthority()
  {
    return authority;
  }

  /**
   * Returns the host of the URL
565 566
   *
   * @return The host specified in this URL.
567
   */
Tom Tromey committed
568 569
  public String getHost()
  {
570 571
    int at = (host == null) ? -1 : host.indexOf('@');
    return at < 0 ? host : host.substring(at + 1, host.length());
Tom Tromey committed
572 573
  }

574
  /**
575 576 577 578 579 580
   * Returns the port number of this URL or -1 if the default port number is
   * being used.
   *
   * @return The port number
   *
   * @see #getDefaultPort()
581
   */
Tom Tromey committed
582 583 584 585 586
  public int getPort()
  {
    return port;
  }

587
  /**
588 589
   * Returns the default port of the URL. If the StreamHandler for the URL
   * protocol does not define a default port it returns -1.
590 591
   *
   * @return The default port of the current protocol.
592 593 594
   */
  public int getDefaultPort()
  {
595
    return ph.getDefaultPort();
596 597 598 599
  }

  /**
   * Returns the protocol of the URL
600 601
   *
   * @return The specified protocol.
602
   */
Tom Tromey committed
603 604 605 606 607
  public String getProtocol()
  {
    return protocol;
  }

608 609 610 611 612 613
  /**
   * Returns the ref (sometimes called the "# reference" or "anchor") portion
   * of the URL.
   *
   * @return The ref
   */
Tom Tromey committed
614 615 616 617 618
  public String getRef()
  {
    return ref;
  }

619
  /**
620 621 622 623
   * Returns the user information of the URL. This is the part of the host
   * name before the '@'.
   *
   * @return the user at a particular host or null when no user defined.
624
   */
625
  public String getUserInfo()
626
  {
627 628
    if (userInfo != null)
      return userInfo;
629
    int at = (host == null) ? -1 : host.indexOf('@');
630 631 632
    return at < 0 ? null : host.substring(0, at);
  }

633
  /**
634 635 636
   * Returns the query of the URL. This is the part of the file before the
   * '?'.
   *
637
   * @return the query part of the file, or null when there is no query part.
638
   */
639
  public String getQuery()
640
  {
641
    int quest = (file == null) ? -1 : file.indexOf('?');
642
    return quest < 0 ? null : file.substring(quest + 1, file.length());
643 644
  }

645 646
  /**
   * Returns a hashcode computed by the URLStreamHandler of this URL
647 648
   *
   * @return The hashcode for this URL.
649
   */
Tom Tromey committed
650 651
  public int hashCode()
  {
652
    if (hashCode != 0)
653
      return hashCode; // Use cached value if available.
654
    else
655
      return ph.hashCode(this);
Tom Tromey committed
656 657
  }

658 659
  /**
   * Returns a URLConnection object that represents a connection to the remote
660 661
   * object referred to by the URL. The URLConnection is created by calling the
   * openConnection() method of the protocol handler
662
   *
663
   * @return A URLConnection for this URL
664
   *
665 666
   * @exception IOException If an error occurs
   */
Tom Tromey committed
667 668
  public URLConnection openConnection() throws IOException
  {
669
    return ph.openConnection(this);
Tom Tromey committed
670 671
  }

672 673 674 675
  /**
   * Opens a connection to this URL and returns an InputStream for reading
   * from that connection
   *
676
   * @return An <code>InputStream</code> for this URL.
677
   *
678 679
   * @exception IOException If an error occurs
   */
680
  public InputStream openStream() throws IOException
Tom Tromey committed
681 682 683 684
  {
    return openConnection().getInputStream();
  }

685 686 687 688 689 690 691 692 693 694
  /**
   * Tests whether or not another URL refers to the same "file" as this one.
   * This will be true if and only if the passed object is not null, is a
   * URL, and matches all fields but the ref (ie, protocol, host, port,
   * and file);
   *
   * @param url The URL object to test with
   *
   * @return true if URL matches this URL's file, false otherwise
   */
695
  public boolean sameFile(URL url)
Tom Tromey committed
696
  {
697
    return ph.sameFile(this, url);
Tom Tromey committed
698 699
  }

700 701
  /**
   * Sets the specified fields of the URL. This is not a public method so
702 703
   * that only URLStreamHandlers can modify URL fields. This might be called
   * by the <code>parseURL()</code> method in that class. URLs are otherwise
704 705
   * constant. If the given protocol does not exist, it will keep the previously
   * set protocol.
706 707 708 709 710 711
   *
   * @param protocol The protocol name for this URL
   * @param host The hostname or IP address for this URL
   * @param port The port number of this URL
   * @param file The "file" portion of this URL.
   * @param ref The anchor portion of this URL.
712
   */
Tom Tromey committed
713
  protected void set(String protocol, String host, int port, String file,
714
                     String ref)
Tom Tromey committed
715
  {
716 717 718 719 720 721 722 723
    URLStreamHandler protocolHandler = null;
    protocol = protocol.toLowerCase();
    if (! this.protocol.equals(protocol))
      protocolHandler = getURLStreamHandler(protocol);
    
    // It is an hidden feature of the JDK. If the protocol does not exist,
    // we keep the previously initialized protocol.
    if (protocolHandler != null)
Anthony Green committed
724
      {
725
	this.ph = protocolHandler;
Anthony Green committed
726 727
	this.protocol = protocol;
      }
728
    this.authority = "";
Tom Tromey committed
729 730 731 732
    this.port = port;
    this.host = host;
    this.file = file;
    this.ref = ref;
733 734 735 736 737 738

    if (host != null)
      this.authority += host;
    if (port >= 0)
      this.authority += ":" + port;

739
    hashCode = hashCode(); // Used for serialization.
Tom Tromey committed
740 741
  }

742 743 744
  /**
   * Sets the specified fields of the URL. This is not a public method so
   * that only URLStreamHandlers can modify URL fields. URLs are otherwise
745 746
   * constant. If the given protocol does not exist, it will keep the previously
   * set protocol.
747
   *
748 749 750 751 752 753 754 755 756
   * @param protocol The protocol name for this URL.
   * @param host The hostname or IP address for this URL.
   * @param port The port number of this URL.
   * @param authority The authority of this URL.
   * @param userInfo The user and password (if needed) of this URL.
   * @param path The "path" portion of this URL.
   * @param query The query of this URL.
   * @param ref The anchor portion of this URL.
   *
757 758
   * @since 1.3
   */
759 760
  protected void set(String protocol, String host, int port, String authority,
                     String userInfo, String path, String query, String ref)
761
  {
762 763 764 765 766 767 768 769
    URLStreamHandler protocolHandler = null;
    protocol = protocol.toLowerCase();
    if (! this.protocol.equals(protocol))
      protocolHandler = getURLStreamHandler(protocol);
    
    // It is an hidden feature of the JDK. If the protocol does not exist,
    // we keep the previously initialized protocol.
    if (protocolHandler != null)
Anthony Green committed
770
      {
771
	this.ph = protocolHandler;
Anthony Green committed
772 773
	this.protocol = protocol;
      }
774 775
    this.host = host;
    this.userInfo = userInfo;
776
    this.port = port;
777
    this.authority = authority;
778
    if (query == null)
779
      this.file = path;
780
    else
781
      this.file = path + "?" + query;
782
    this.ref = ref;
783
    hashCode = hashCode(); // Used for serialization.
784 785
  }

786
  /**
787 788 789 790 791
   * Sets the URLStreamHandlerFactory for this class.  This factory is
   * responsible for returning the appropriate protocol handler for
   * a given URL.
   *
   * @param fac The URLStreamHandlerFactory class to use
792
   *
793
   * @exception Error If the factory is alread set.
794 795 796
   * @exception SecurityException If a security manager exists and its
   * checkSetFactory method doesn't allow the operation
   */
797
  public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac)
Tom Tromey committed
798 799 800 801 802 803 804 805 806 807 808 809
  {
    if (factory != null)
      throw new Error("URLStreamHandlerFactory already set");

    // Throw an exception if an extant security mgr precludes
    // setting the factory.
    SecurityManager s = System.getSecurityManager();
    if (s != null)
      s.checkSetFactory();
    factory = fac;
  }

810 811 812 813 814 815
  /**
   * Returns a String representing this URL.  The String returned is
   * created by calling the protocol handler's toExternalForm() method.
   *
   * @return A string for this URL
   */
Tom Tromey committed
816 817 818
  public String toExternalForm()
  {
    // Identical to toString().
819
    return ph.toExternalForm(this);
Tom Tromey committed
820 821
  }

822 823
  /**
   * Returns a String representing this URL.  Identical to toExternalForm().
824
   * The value returned is created by the protocol handler's
825 826 827 828
   * toExternalForm method.  Overrides Object.toString()
   *
   * @return A string for this URL
   */
Tom Tromey committed
829 830 831
  public String toString()
  {
    // Identical to toExternalForm().
832
    return ph.toExternalForm(this);
Tom Tromey committed
833 834
  }

835 836 837 838 839 840 841 842
  /**
   * This internal method is used in two different constructors to load
   * a protocol handler for this URL.
   *
   * @param protocol The protocol to load a handler for
   *
   * @return A URLStreamHandler for this protocol, or null when not found.
   */
843
  private static synchronized URLStreamHandler getURLStreamHandler(String protocol)
Tom Tromey committed
844
  {
845
    URLStreamHandler ph = null;
Tom Tromey committed
846

847 848 849
    // First, see if a protocol handler is in our cache.
    if (cache_handlers)
      {
850 851
	if ((ph = (URLStreamHandler) ph_cache.get(protocol)) != null)
	  return ph;
852
      }
Tom Tromey committed
853 854 855

    // If a non-default factory has been set, use it to find the protocol.
    if (factory != null)
856
      {
857
	ph = factory.createURLStreamHandler(protocol);
858
      }
859
    else if (protocol.equals("core"))
860
      {
861
 	ph = new gnu.java.net.protocol.core.Handler();
862
      }
863
    else if (protocol.equals("file"))
Tom Tromey committed
864 865
      {
	// This is an interesting case.  It's tempting to think that we
866
	// could call Class.forName ("gnu.java.net.protocol.file.Handler") to
Tom Tromey committed
867
	// get the appropriate class.  Unfortunately, if we do that the
868
	// program will never terminate, because getURLStreamHandler is
Tom Tromey committed
869 870 871 872 873 874
	// eventually called by Class.forName.
	//
	// Treating "file" as a special case is the minimum that will
	// fix this problem.  If other protocols are required in a
	// statically linked application they will need to be handled in
	// the same way as "file".
875
	ph = new gnu.java.net.protocol.file.Handler();
Tom Tromey committed
876
      }
Tom Tromey committed
877 878 879

    // Non-default factory may have returned null or a factory wasn't set.
    // Use the default search algorithm to find a handler for this protocol.
880
    if (ph == null)
Tom Tromey committed
881 882 883 884 885
      {
	// Get the list of packages to check and append our default handler
	// to it, along with the JDK specified default as a last resort.
	// Except in very unusual environments the JDK specified one shouldn't
	// ever be needed (or available).
886 887
	String ph_search_path =
	  System.getProperty("java.protocol.handler.pkgs");
888 889 890

	// Tack our default package on at the ends.
	if (ph_search_path != null)
891
	  ph_search_path += "|" + DEFAULT_SEARCH_PATH;
892
	else
893
	  ph_search_path = DEFAULT_SEARCH_PATH;
Tom Tromey committed
894

895
	// Finally loop through our search path looking for a match.
896 897
	StringTokenizer pkgPrefix = new StringTokenizer(ph_search_path, "|");

898 899
	// Cache the systemClassLoader
	if (systemClassLoader == null)
900
	  {
901 902 903 904 905 906 907
	    systemClassLoader = (ClassLoader) AccessController.doPrivileged
	      (new PrivilegedAction() {
		  public Object run() {
		    return ClassLoader.getSystemClassLoader();
		  }
		});
	  }
908

909 910
	do
	  {
911 912
	    try
	      {
913 914 915 916 917 918 919
		// Try to get a class from the system/application
		// classloader, initialize it, make an instance
		// and try to cast it to a URLStreamHandler.
		String clsName =
		  (pkgPrefix.nextToken() + "." + protocol + ".Handler");
		Class c = Class.forName(clsName, true, systemClassLoader);
		ph = (URLStreamHandler) c.newInstance();
920
	      }
921 922 923 924
            catch (ThreadDeath death)
              {
                throw death;
              }
925
	    catch (Throwable t) { /* ignored */ }
926
	  }
927
	 while (ph == null && pkgPrefix.hasMoreTokens());
Tom Tromey committed
928 929 930
      }

    // Update the hashtable with the new protocol handler.
931
    if (ph != null && cache_handlers)
932 933 934
      ph_cache.put(protocol, ph);
    else
      ph = null;
Tom Tromey committed
935

936
    return ph;
Tom Tromey committed
937
  }
938 939 940 941 942

  private void readObject(ObjectInputStream ois)
    throws IOException, ClassNotFoundException
  {
    ois.defaultReadObject();
943 944
    this.ph = getURLStreamHandler(protocol);
    if (this.ph == null)
945 946 947 948 949 950 951
      throw new IOException("Handler for protocol " + protocol + " not found");
  }

  private void writeObject(ObjectOutputStream oos) throws IOException
  {
    oos.defaultWriteObject();
  }
Tom Tromey committed
952
}