OSDN Git Service

Somehow this didnt got commited due to full hard disk.
[pf3gnuchains/gcc-fork.git] / libjava / java / net / URLStreamHandler.java
1 /* URLStreamHandler.java -- Abstract superclass for all protocol handlers
2    Copyright (C) 1998, 1999, 2002, 2003 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.IOException;
42 import java.io.File;
43
44 /*
45  * Written using on-line Java Platform 1.2 API Specification, as well
46  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
47  * Status:  Believed complete and correct.
48  */
49
50 /**
51  * This class is the superclass of all URL protocol handlers.  The URL
52  * class loads the appropriate protocol handler to establish a connection
53  * to a (possibly) remote service (eg, "http", "ftp") and to do protocol
54  * specific parsing of URL's.  Refer to the URL class documentation for
55  * details on how that class locates and loads protocol handlers.
56  * <p>
57  * A protocol handler implementation should override the openConnection()
58  * method, and optionally override the parseURL() and toExternalForm()
59  * methods if necessary. (The default implementations will parse/write all
60  * URL's in the same form as http URL's).  A protocol  specific subclass 
61  * of URLConnection will most likely need to be created as well.
62  * <p>
63  * Note that the instance methods in this class are called as if they
64  * were static methods.  That is, a URL object to act on is passed with
65  * every call rather than the caller assuming the URL is stored in an
66  * instance variable of the "this" object.
67  * <p>
68  * The methods in this class are protected and accessible only to subclasses.
69  * URLStreamConnection objects are intended for use by the URL class only,
70  * not by other classes (unless those classes are implementing protocols).
71  *
72  * @author Aaron M. Renn (arenn@urbanophile.com)
73  * @author Warren Levy (warrenl@cygnus.com)
74  * 
75  * @see URL
76  */
77 public abstract class URLStreamHandler
78 {
79   /**
80    * Creates a URLStreamHander
81    */
82   public URLStreamHandler ()
83   {
84   }
85
86   /**
87    * Returns a URLConnection for the passed in URL.  Note that this should
88    * not actually create the connection to the (possibly) remote host, but
89    * rather simply return a URLConnection object.  The connect() method of
90    * URL connection is used to establish the actual connection, possibly
91    * after the caller sets up various connection options.
92    *
93    * @param url The URL to get a connection object for
94    *
95    * @return A URLConnection object for the given URL
96    *
97    * @exception IOException If an error occurs
98    */
99   protected abstract URLConnection openConnection(URL u)
100     throws IOException;
101
102   /**
103    * This method parses the string passed in as a URL and set's the
104    * instance data fields in the URL object passed in to the various values
105    * parsed out of the string.  The start parameter is the position to start
106    * scanning the string.  This is usually the position after the ":" which
107    * terminates the protocol name.  The end parameter is the position to
108    * stop scanning.  This will be either the end of the String, or the
109    * position of the "#" character, which separates the "file" portion of
110    * the URL from the "anchor" portion.
111    * <p>
112    * This method assumes URL's are formatted like http protocol URL's, so 
113    * subclasses that implement protocols with URL's the follow a different 
114    * syntax should override this method.  The lone exception is that if
115    * the protocol name set in the URL is "file", this method will accept
116    * an empty hostname (i.e., "file:///"), which is legal for that protocol
117    *
118    * @param url The URL object in which to store the results
119    * @param spec The String-ized URL to parse
120    * @param start The position in the string to start scanning from
121    * @param end The position in the string to stop scanning
122    */
123   protected void parseURL(URL url, String spec, int start, int end)
124   {
125     String host = url.getHost();
126     int port = url.getPort();
127     String file = url.getFile();
128     String ref = url.getRef();
129     
130     if (spec.regionMatches (start, "//", 0, 2))
131       {
132         int hostEnd;
133         int colon;
134
135         start += 2;
136         int slash = spec.indexOf('/', start);
137         if (slash >= 0) 
138           hostEnd = slash;
139         else
140           hostEnd = end;
141
142         host = spec.substring (start, hostEnd);
143         
144         // Look for optional port number.  It is valid for the non-port
145         // part of the host name to be null (e.g. a URL "http://:80").
146         // TBD: JDK 1.2 in this case sets host to null rather than "";
147         // this is undocumented and likely an unintended side effect in 1.2
148         // so we'll be simple here and stick with "". Note that
149         // "http://" or "http:///" produce a "" host in JDK 1.2.
150         if ((colon = host.indexOf(':')) >= 0)
151           {
152             try
153               {
154                 port = Integer.parseInt(host.substring(colon + 1));
155               }
156             catch (NumberFormatException e)
157               {
158                 ; // Ignore invalid port values; port is already set to u's
159                   // port.
160               }
161             host = host.substring(0, colon);
162           }
163         file = null;
164         start = hostEnd;
165       } 
166     else if (host == null) 
167       host = "";
168
169     if (file == null || file.length() == 0
170         || (start < end && spec.charAt(start) == '/')) 
171       {
172         // No file context available; just spec for file.
173         // Or this is an absolute path name; ignore any file context.
174         file = spec.substring(start, end);
175         ref = null;
176       } 
177     else if (start < end)
178       {
179         // Context is available, but only override it if there is a new file.
180         char sepChar = '/';
181         int lastSlash = file.lastIndexOf (sepChar);
182         if (lastSlash < 0 && File.separatorChar != sepChar
183             && url.getProtocol ().equals ("file"))
184           {
185             // On Windows, even '\' is allowed in a "file" URL.
186             sepChar = File.separatorChar;
187             lastSlash = file.lastIndexOf (sepChar);
188           }
189         
190         file = file.substring(0, lastSlash)
191                 + sepChar + spec.substring (start, end);
192
193         if (url.getProtocol ().equals ("file"))
194           {
195             // For "file" URLs constructed relative to a context, we
196             // need to canonicalise the file path.
197             try
198               {
199                 boolean endsWithSlash = file.charAt(file.length() - 1) == '/';
200                 file = new File (file).getCanonicalPath ();
201                 if (endsWithSlash
202                     && file.charAt(file.length() - 1) != '/')
203                   file += '/';
204               }
205             catch (IOException e)
206               {
207               }
208           }
209
210         ref = null;
211       }
212
213     if (ref == null)
214       {
215         // Normally there should be no '#' in the file part,
216         // but we are nice.
217         int hash = file.indexOf('#');
218         if (hash != -1)
219           {
220             ref = file.substring(hash + 1, file.length());
221             file = file.substring(0, hash);
222           }
223       }
224
225     // XXX - Classpath used to call PlatformHelper.toCanonicalForm() on
226     // the file part. It seems like overhead, but supposedly there is some
227     // benefit in windows based systems (it also lowercased the string).
228
229     setURL(url, url.getProtocol(), host, port, file, ref);
230   }
231   
232   private static String canonicalizeFilename(String file)
233   {
234     // XXX - GNU Classpath has an implementation that might be more appropriate
235     // for Windows based systems (gnu.java.io.PlatformHelper.toCanonicalForm)
236
237     int index;
238
239     // Replace "/./" with "/".  This probably isn't very efficient in
240     // the general case, but it's probably not bad most of the time.
241     while ((index = file.indexOf("/./")) >= 0)
242       file = file.substring(0, index) + file.substring(index + 2);
243
244     // Process "/../" correctly.  This probably isn't very efficient in
245     // the general case, but it's probably not bad most of the time.
246     while ((index = file.indexOf("/../")) >= 0)
247       {
248         // Strip of the previous directory - if it exists.
249         int previous = file.lastIndexOf('/', index - 1);
250         if (previous >= 0)
251           file = file.substring(0, previous) + file.substring(index + 3);
252         else
253           break;
254       }
255     return file; 
256   }
257
258   /**
259    * Compares two URLs, excluding the fragment component
260    *
261    * @param url1 The first url
262    * @param url2 The second url to compare with the first
263    * 
264    * @specnote Now protected
265    */
266   protected boolean sameFile(URL url1, URL url2)
267   {
268     if (url1 == url2)
269       return true;
270     // This comparison is very conservative.  It assumes that any
271     // field can be null.
272     if (url1 == null || url2 == null)
273       return false;
274     int p1 = url1.getPort ();
275     if (p1 == -1)
276       p1 = url1.ph.getDefaultPort ();
277     int p2 = url2.getPort ();
278     if (p2 == -1)
279       p2 = url2.ph.getDefaultPort ();
280     if (p1 != p2)
281       return false;
282     String s1, s2;
283     s1 = url1.getProtocol();
284     s2 = url2.getProtocol();
285     if (s1 != s2 && (s1 == null || ! s1.equals(s2)))
286       return false;
287     s1 = url1.getHost();
288     s2 = url2.getHost();
289     if (s1 != s2 && (s1 == null || ! s1.equals(s2)))
290       return false;
291     s1 = canonicalizeFilename(url1.getFile());
292     s2 = canonicalizeFilename(url2.getFile());
293     if (s1 != s2 && (s1 == null || ! s1.equals(s2)))
294       return false;
295     return true;
296   }
297
298   /**
299    * This methods sets the instance variables representing the various fields
300    * of the URL to the values passed in.
301    *
302    * @param u The URL to modify
303    * @param protocol The protocol to set
304    * @param host The host name to et
305    * @param port The port number to set
306    * @param file The filename to set
307    * @param ref The reference
308    *
309    * @exception SecurityException If the protocol handler of the URL is
310    * different from this one
311    *
312    * @deprecated 1.2 Please use
313    * #setURL(URL,String,String,int,String,String,String,String);
314    */
315   protected void setURL(URL u, String protocol, String host, int port,
316                         String file, String ref)
317   {
318     u.set(protocol, host, port, file, ref);
319   }
320
321   /**
322    * Sets the fields of the URL argument to the indicated values
323    *
324    * @param u The URL to modify
325    * @param protocol The protocol to set
326    * @param host The host name to set
327    * @param port The port number to set
328    * @param authority The authority to set
329    * @param userInfo The user information to set
330    * @param path The path/filename to set
331    * @param query The query part to set
332    * @param ref The reference
333    *
334    * @exception SecurityException If the protocol handler of the URL is
335    * different from this one
336    */
337   protected void setURL(URL u, String protocol, String host, int port,
338                         String authority, String userInfo, String path,
339                         String query, String ref)
340   {
341     u.set(protocol, host, port, authority, userInfo, path, query, ref);
342   }
343
344   /**
345    * Provides the default equals calculation. May be overidden by handlers for
346    * other protocols that have different requirements for equals(). This method
347    * requires that none of its arguments is null. This is guaranteed by the
348    * fact that it is only called by java.net.URL class.
349    *
350    * @param url1 An URL object
351    * @param url2 An URL object
352    */
353   protected boolean equals (URL url1, URL url2)
354   {
355     // This comparison is very conservative.  It assumes that any
356     // field can be null.
357     return (url1.getPort () == url2.getPort ()
358             && ((url1.getProtocol () == null && url2.getProtocol () == null)
359                 || (url1.getProtocol () != null
360                         && url1.getProtocol ().equals (url2.getProtocol ())))
361             && ((url1.getUserInfo () == null && url2.getUserInfo () == null)
362                 || (url1.getUserInfo () != null
363                         && url1.getUserInfo ().equals(url2.getUserInfo ())))
364             && ((url1.getAuthority () == null && url2.getAuthority () == null)
365                 || (url1.getAuthority () != null
366                         && url1.getAuthority ().equals(url2.getAuthority ())))
367             && ((url1.getHost () == null && url2.getHost () == null)
368                 || (url1.getHost () != null
369                         && url1.getHost ().equals(url2.getHost ())))
370             && ((url1.getPath () == null && url2.getPath () == null)
371                 || (url1.getPath () != null
372                         && url1.getPath ().equals (url2.getPath ())))
373             && ((url1.getQuery () == null && url2.getQuery () == null)
374                 || (url1.getQuery () != null
375                         && url1.getQuery ().equals(url2.getQuery ())))
376             && ((url1.getRef () == null && url2.getRef () == null)
377                 || (url1.getRef () != null
378                         && url1.getRef ().equals(url2.getRef ()))));
379   }
380
381   /**
382    * Compares the host components of two URLs.
383    *
384    * @exception UnknownHostException If an unknown host is found
385    */
386   protected boolean hostsEqual (URL url1, URL url2)
387   {
388     InetAddress addr1 = getHostAddress (url1);
389     InetAddress addr2 = getHostAddress (url2);
390
391     if (addr1 != null || addr2 != null)
392       return addr1.equals (addr2);
393
394     String host1 = url1.getHost();
395     String host2 = url2.getHost();
396     
397     if (host1 != null && host2 != null)
398       return host1.equalsIgnoreCase (host2);
399
400     return host1 == null && host2 == null;
401   }
402
403   /**
404    * Get the IP address of our host. An empty host field or a DNS failure will
405    * result in a null return.
406    */
407   protected InetAddress getHostAddress (URL url)
408   {
409     String hostname = url.getHost ();
410
411     if (hostname == "")
412       return null;
413     
414     try
415       {
416         return InetAddress.getByName (hostname);
417       }
418     catch (UnknownHostException e)
419       {
420         return null;
421       }
422   }
423
424   /**
425    * Returns the default port for a URL parsed by this handler. This method is
426    * meant to be overidden by handlers with default port numbers.
427    */
428   protected int getDefaultPort ()
429   {
430     return -1;
431   }
432
433   /**
434    * Provides the default hash calculation. May be overidden by handlers for
435    * other protocols that have different requirements for hashCode calculation.
436    */
437   protected int hashCode (URL url)
438   {
439     return url.getProtocol ().hashCode () +
440            ((url.getHost () == null) ? 0 : url.getHost ().hashCode ()) +
441            url.getFile ().hashCode() +
442            url.getPort ();
443   }
444
445   /**
446    * This method converts a URL object into a String.  This method creates
447    * Strings in the mold of http URL's, so protocol handlers which use URL's
448    * that have a different syntax should override this method
449    *
450    * @param url The URL object to convert
451    */
452   protected String toExternalForm(URL u)
453   {
454     String protocol, host, file, ref;
455     int port;
456
457     protocol = u.getProtocol();
458
459     // JDK 1.2 online doc infers that host could be null because it
460     // explicitly states that file cannot be null, but is silent on host.
461     host = u.getHost();
462     if (host == null)
463       host = "";
464
465     port = u.getPort();
466     file = u.getFile();
467     ref = u.getRef();
468
469     // Guess a reasonable size for the string buffer so we have to resize
470     // at most once.
471     int size = protocol.length() + host.length() + file.length() + 24;
472     StringBuffer sb = new StringBuffer(size);
473
474     if (protocol != null && protocol.length() > 0)
475       {
476         sb.append(protocol);
477         sb.append(":");
478       }
479
480     if (host.length() != 0)
481       sb.append("//").append(host);
482
483     // Append port if port was in URL spec.
484     if (port != -1)
485       sb.append(':').append(port);
486
487     sb.append(file);
488
489     if (ref != null)
490       sb.append('#').append(ref);
491
492     return sb.toString();
493   }
494 }