1 /* URLStreamHandler.java -- Abstract superclass for all protocol handlers
2 Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
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
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. */
40 import gnu
.java
.lang
.CPStringBuilder
;
43 import java
.io
.IOException
;
47 * Written using on-line Java Platform 1.2 API Specification, as well
48 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
49 * Status: Believed complete and correct.
53 * This class is the superclass of all URL protocol handlers. The URL
54 * class loads the appropriate protocol handler to establish a connection
55 * to a (possibly) remote service (eg, "http", "ftp") and to do protocol
56 * specific parsing of URL's. Refer to the URL class documentation for
57 * details on how that class locates and loads protocol handlers.
59 * A protocol handler implementation should override the openConnection()
60 * method, and optionally override the parseURL() and toExternalForm()
61 * methods if necessary. (The default implementations will parse/write all
62 * URL's in the same form as http URL's). A protocol specific subclass
63 * of URLConnection will most likely need to be created as well.
65 * Note that the instance methods in this class are called as if they
66 * were static methods. That is, a URL object to act on is passed with
67 * every call rather than the caller assuming the URL is stored in an
68 * instance variable of the "this" object.
70 * The methods in this class are protected and accessible only to subclasses.
71 * URLStreamConnection objects are intended for use by the URL class only,
72 * not by other classes (unless those classes are implementing protocols).
74 * @author Aaron M. Renn (arenn@urbanophile.com)
75 * @author Warren Levy (warrenl@cygnus.com)
79 public abstract class URLStreamHandler
82 * Creates a URLStreamHander
84 public URLStreamHandler()
89 * Returns a URLConnection for the passed in URL. Note that this should
90 * not actually create the connection to the (possibly) remote host, but
91 * rather simply return a URLConnection object. The connect() method of
92 * URL connection is used to establish the actual connection, possibly
93 * after the caller sets up various connection options.
95 * @param url The URL to get a connection object for
97 * @return A URLConnection object for the given URL
99 * @exception IOException If an error occurs
101 protected abstract URLConnection
openConnection(URL url
)
105 * This method parses the string passed in as a URL and set's the
106 * instance data fields in the URL object passed in to the various values
107 * parsed out of the string. The start parameter is the position to start
108 * scanning the string. This is usually the position after the ":" which
109 * terminates the protocol name. The end parameter is the position to
110 * stop scanning. This will be either the end of the String, or the
111 * position of the "#" character, which separates the "file" portion of
112 * the URL from the "anchor" portion.
114 * This method assumes URL's are formatted like http protocol URL's, so
115 * subclasses that implement protocols with URL's the follow a different
116 * syntax should override this method. The lone exception is that if
117 * the protocol name set in the URL is "file", this method will accept
118 * an empty hostname (i.e., "file:///"), which is legal for that protocol
120 * @param url The URL object in which to store the results
121 * @param spec The String-ized URL to parse
122 * @param start The position in the string to start scanning from
123 * @param end The position in the string to stop scanning
125 protected void parseURL(URL url
, String spec
, int start
, int end
)
127 String host
= url
.getHost();
128 int port
= url
.getPort();
129 String file
= url
.getFile();
130 String ref
= url
.getRef();
131 String userInfo
= url
.getUserInfo();
132 String authority
= url
.getAuthority();
135 // On Windows we need to change \ to / for file URLs
136 char separator
= File
.separatorChar
;
137 if (url
.getProtocol().equals("file") && separator
!= '/')
139 file
= file
.replace(separator
, '/');
140 spec
= spec
.replace(separator
, '/');
143 if (spec
.regionMatches(start
, "//", 0, 2))
151 int slash
= spec
.indexOf('/', start
);
157 authority
= host
= spec
.substring(start
, hostEnd
);
159 // We first need a genuine host name (with userinfo).
160 // So we check for '@': if it's present check the port in the
161 // section after '@' in the other case check it in the full string.
162 // P.S.: We don't care having '@' at the beginning of the string.
163 if ((at_host
= host
.indexOf('@')) >= 0)
165 genuineHost
= host
.substring(at_host
);
166 userInfo
= host
.substring(0, at_host
);
171 // Look for optional port number. It is valid for the non-port
172 // part of the host name to be null (e.g. a URL "http://:80").
173 // TBD: JDK 1.2 in this case sets host to null rather than "";
174 // this is undocumented and likely an unintended side effect in 1.2
175 // so we'll be simple here and stick with "". Note that
176 // "http://" or "http:///" produce a "" host in JDK 1.2.
177 if ((colon
= genuineHost
.indexOf(':')) >= 0)
181 port
= Integer
.parseInt(genuineHost
.substring(colon
+ 1));
183 catch (NumberFormatException e
)
185 // Ignore invalid port values; port is already set to u's
189 // Now we must cut the port number in the original string.
191 host
= host
.substring(0, at_host
+ colon
);
193 host
= host
.substring(0, colon
);
198 else if (host
== null)
201 if (file
== null || file
.length() == 0
202 || (start
< end
&& spec
.charAt(start
) == '/'))
204 // No file context available; just spec for file.
205 // Or this is an absolute path name; ignore any file context.
206 file
= spec
.substring(start
, end
);
209 else if (start
< end
)
211 // Context is available, but only override it if there is a new file.
212 int lastSlash
= file
.lastIndexOf('/');
214 file
= spec
.substring(start
, end
);
216 file
= (file
.substring(0, lastSlash
)
217 + '/' + spec
.substring(start
, end
));
219 // For URLs constructed relative to a context, we
220 // need to canonicalise the file path.
221 file
= canonicalizeFilename(file
);
228 // Normally there should be no '#' in the file part,
230 int hash
= file
.indexOf('#');
233 ref
= file
.substring(hash
+ 1, file
.length());
234 file
= file
.substring(0, hash
);
238 // We care about the query tag only if there is no reference at all.
241 int queryTag
= file
.indexOf('?');
244 query
= file
.substring(queryTag
+ 1);
245 file
= file
.substring(0, queryTag
);
249 // XXX - Classpath used to call PlatformHelper.toCanonicalForm() on
250 // the file part. It seems like overhead, but supposedly there is some
251 // benefit in windows based systems (it also lowercased the string).
252 setURL(url
, url
.getProtocol(), host
, port
, authority
, userInfo
, file
, query
, ref
);
256 * Canonicalize a filename.
258 private static String
canonicalizeFilename(String file
)
260 // XXX - GNU Classpath has an implementation that might be more appropriate
261 // for Windows based systems (gnu.java.io.PlatformHelper.toCanonicalForm)
264 // Replace "/./" with "/". This probably isn't very efficient in
265 // the general case, but it's probably not bad most of the time.
266 while ((index
= file
.indexOf("/./")) >= 0)
267 file
= file
.substring(0, index
) + file
.substring(index
+ 2);
269 // Process "/../" correctly. This probably isn't very efficient in
270 // the general case, but it's probably not bad most of the time.
271 while ((index
= file
.indexOf("/../")) >= 0)
273 // Strip of the previous directory - if it exists.
274 int previous
= file
.lastIndexOf('/', index
- 1);
276 file
= file
.substring(0, previous
) + file
.substring(index
+ 3);
284 * Compares two URLs, excluding the fragment component
286 * @param url1 The first url
287 * @param url2 The second url to compare with the first
289 * @return True if both URLs point to the same file, false otherwise.
291 * @specnote Now protected
293 protected boolean sameFile(URL url1
, URL url2
)
298 // This comparison is very conservative. It assumes that any
299 // field can be null.
300 if (url1
== null || url2
== null)
302 int p1
= url1
.getPort();
304 p1
= url1
.ph
.getDefaultPort();
305 int p2
= url2
.getPort();
307 p2
= url2
.ph
.getDefaultPort();
312 s1
= url1
.getProtocol();
313 s2
= url2
.getProtocol();
314 if (s1
!= s2
&& (s1
== null || ! s1
.equals(s2
)))
318 if (s1
!= s2
&& (s1
== null || ! s1
.equals(s2
)))
320 s1
= canonicalizeFilename(url1
.getFile());
321 s2
= canonicalizeFilename(url2
.getFile());
322 if (s1
!= s2
&& (s1
== null || ! s1
.equals(s2
)))
328 * This methods sets the instance variables representing the various fields
329 * of the URL to the values passed in.
331 * @param u The URL to modify
332 * @param protocol The protocol to set
333 * @param host The host name to et
334 * @param port The port number to set
335 * @param file The filename to set
336 * @param ref The reference
338 * @exception SecurityException If the protocol handler of the URL is
339 * different from this one
341 * @deprecated 1.2 Please use
342 * #setURL(URL,String,String,int,String,String,String,String);
344 protected void setURL(URL u
, String protocol
, String host
, int port
,
345 String file
, String ref
)
347 u
.set(protocol
, host
, port
, file
, ref
);
351 * Sets the fields of the URL argument to the indicated values
353 * @param u The URL to modify
354 * @param protocol The protocol to set
355 * @param host The host name to set
356 * @param port The port number to set
357 * @param authority The authority to set
358 * @param userInfo The user information to set
359 * @param path The path/filename to set
360 * @param query The query part to set
361 * @param ref The reference
363 * @exception SecurityException If the protocol handler of the URL is
364 * different from this one
366 protected void setURL(URL u
, String protocol
, String host
, int port
,
367 String authority
, String userInfo
, String path
,
368 String query
, String ref
)
370 u
.set(protocol
, host
, port
, authority
, userInfo
, path
, query
, ref
);
374 * This is the default method for computing whether two URLs are
375 * equivalent. This method assumes that neither URL is null.
377 * @param url1 An URL object
378 * @param url2 Another URL object
380 * @return True if both given URLs are equal, false otherwise.
382 protected boolean equals(URL url1
, URL url2
)
384 // This comparison is very conservative. It assumes that any
385 // field can be null.
386 int port1
= url1
.getPort();
388 port1
= url1
.getDefaultPort();
389 int port2
= url2
.getPort();
391 port2
= url2
.getDefaultPort();
392 // Note that we don't bother checking the 'authority'; it is
394 return (port1
== port2
395 && ((url1
.getProtocol() == null && url2
.getProtocol() == null)
396 || (url1
.getProtocol() != null
397 && url1
.getProtocol().equals(url2
.getProtocol())))
398 && ((url1
.getUserInfo() == null && url2
.getUserInfo() == null)
399 || (url1
.getUserInfo() != null
400 && url1
.getUserInfo().equals(url2
.getUserInfo())))
401 && ((url1
.getHost() == null && url2
.getHost() == null)
402 || (url1
.getHost() != null && url1
.getHost().equals(url2
.getHost())))
403 && ((url1
.getPath() == null && url2
.getPath() == null)
404 || (url1
.getPath() != null && url1
.getPath().equals(url2
.getPath())))
405 && ((url1
.getQuery() == null && url2
.getQuery() == null)
406 || (url1
.getQuery() != null
407 && url1
.getQuery().equals(url2
.getQuery())))
408 && ((url1
.getRef() == null && url2
.getRef() == null)
409 || (url1
.getRef() != null && url1
.getRef().equals(url2
.getRef()))));
413 * Compares the host components of two URLs.
415 * @param url1 The first URL.
416 * @param url2 The second URL.
418 * @return True if both URLs contain the same host.
420 protected boolean hostsEqual(URL url1
, URL url2
)
422 InetAddress addr1
= getHostAddress(url1
);
423 InetAddress addr2
= getHostAddress(url2
);
425 if (addr1
!= null && addr2
!= null)
426 return addr1
.equals(addr2
);
428 String host1
= url1
.getHost();
429 String host2
= url2
.getHost();
431 if (host1
!= null && host2
!= null)
432 return host1
.equalsIgnoreCase(host2
);
434 return host1
== null && host2
== null;
438 * Get the IP address of our host. An empty host field or a DNS failure will
439 * result in a null return.
441 * @param url The URL to return the host address for.
443 * @return The address of the hostname in url.
445 protected InetAddress
getHostAddress(URL url
)
447 String hostname
= url
.getHost();
449 if (hostname
.equals(""))
454 return InetAddress
.getByName(hostname
);
456 catch (UnknownHostException e
)
463 * Returns the default port for a URL parsed by this handler. This method is
464 * meant to be overidden by handlers with default port numbers.
466 * @return The default port number.
468 protected int getDefaultPort()
474 * Provides the default hash calculation. May be overidden by handlers for
475 * other protocols that have different requirements for hashCode calculation.
477 * @param url The URL to calc the hashcode for.
479 * @return The hashcode for the given URL.
481 protected int hashCode(URL url
)
483 return url
.getProtocol().hashCode()
484 + ((url
.getHost() == null) ?
0 : url
.getHost().hashCode())
485 + url
.getFile().hashCode() + url
.getPort();
489 * This method converts a URL object into a String. This method creates
490 * Strings in the mold of http URL's, so protocol handlers which use URL's
491 * that have a different syntax should override this method
493 * @param url The URL object to convert
495 * @return A string representation of the url
497 protected String
toExternalForm(URL url
)
504 protocol
= url
.getProtocol();
505 authority
= url
.getAuthority();
506 if (authority
== null)
509 file
= url
.getFile();
512 // Guess a reasonable size for the string buffer so we have to resize
514 int size
= protocol
.length() + authority
.length() + file
.length() + 24;
515 CPStringBuilder sb
= new CPStringBuilder(size
);
517 if (protocol
.length() > 0)
523 // If we have superfluous leading slashes (that means, at least 2)
524 // we always add the authority component ("//" + host) to
525 // avoid ambiguity. Otherwise we would generate an URL like
528 // host: <empty> - file: //home/foo
529 // but URL spec says it is:
530 // host: home - file: /foo
531 if (authority
.length() != 0 || file
.startsWith("//") )
532 sb
.append("//").append(authority
).append(file
);
537 sb
.append('#').append(ref
);
539 return sb
.toString();