OSDN Git Service

Jumbo patch:
[pf3gnuchains/gcc-fork.git] / libjava / java / net / URL.java
1 // URL.java - A Uniform Resource Locator.
2
3 /* Copyright (C) 1999  Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 package java.net;
12
13 import java.io.*;
14 import java.util.Hashtable;
15 import java.util.StringTokenizer;
16
17 /**
18  * @author Warren Levy <warrenl@cygnus.com>
19  * @date March 4, 1999.
20  */
21
22 /**
23  * Written using on-line Java Platform 1.2 API Specification, as well
24  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
25  * Status:  Believed complete and correct.
26  */
27
28 public final class URL implements Serializable
29 {
30   private String protocol;
31   private String host;
32   private int port = -1;        // Initialize for constructor using context.
33   private String file;
34   private String ref;
35   private URLStreamHandler handler;
36   private static Hashtable handlers = new Hashtable();
37   private static URLStreamHandlerFactory factory;
38
39   public URL(String protocol, String host, int port, String file)
40     throws MalformedURLException
41   {
42     this(protocol, host, port, file, null);
43   }
44
45   public URL(String protocol, String host, String file)
46     throws MalformedURLException
47   {
48     this(protocol, host, -1, file, null);
49   }
50
51   // JDK1.2
52   public URL(String protocol, String host, int port, String file,
53     URLStreamHandler handler) throws MalformedURLException
54   {
55     if (protocol == null)
56       throw new MalformedURLException("null protocol");
57     this.protocol = protocol;
58
59     if (handler != null)
60       {
61         // TODO12: Need SecurityManager.checkPermission and
62         // TODO12: java.net.NetPermission from JDK 1.2 to be implemented.
63         // Throw an exception if an extant security mgr precludes
64         // specifying a StreamHandler.
65         //
66         // SecurityManager s = System.getSecurityManager();
67         // if (s != null)
68         //   s.checkPermission(NetPermission("specifyStreamHandler"));
69
70         this.handler = handler;
71       }
72     else
73       this.handler = setURLStreamHandler(protocol);
74
75     if (this.handler == null)
76       throw new MalformedURLException("Handler for protocol not found");
77
78     this.host = host;
79
80     this.port = port;
81
82     int hashAt = file.indexOf('#');
83     if (hashAt < 0)
84       {
85         this.file = file;
86         this.ref = null;
87       }
88     else
89       {
90         this.file = file.substring(0, hashAt);
91         this.ref = file.substring(hashAt + 1);
92       }
93   }
94
95   public URL(String spec) throws MalformedURLException
96   {
97     this((URL) null, spec, (URLStreamHandler) null);
98   }
99
100   public URL(URL context, String spec) throws MalformedURLException
101   {
102     this(context, spec, (URLStreamHandler) null);
103   }
104
105   // JDK1.2
106   public URL(URL context, String spec, URLStreamHandler handler)
107     throws MalformedURLException
108   {
109     /* A protocol is defined by the doc as the substring before a ':'
110      * as long as the ':' occurs before any '/'.
111      *
112      * If context is null, then spec must be an absolute URL.
113      *
114      * The relative URL need not specify all the components of a URL.
115      * If the protocol, host name, or port number is missing, the value
116      * is inherited from the context.  A bare file component is appended
117      * to the context's file.  The optional anchor is not inherited. 
118      */
119
120     // If this is an absolute URL, then ignore context completely.
121     // An absolute URL must have chars prior to "://" but cannot have a colon
122     // right after the "://".  The second colon is for an optional port value
123     // and implies that the host from the context is used if available.
124     int colon;
125     if ((colon = spec.indexOf("://", 1)) > 0 &&
126         ! spec.regionMatches(colon, "://:", 0, 4))
127       context = null;
128
129     int slash;
130     if ((colon = spec.indexOf(':')) > 0 &&
131         (colon < (slash = spec.indexOf('/')) || slash < 0))
132       {
133         // Protocol specified in spec string.
134         protocol = spec.substring(0, colon);
135         if (context != null && context.protocol.equals(protocol))
136           {
137             // The 1.2 doc specifically says these are copied to the new URL.
138             host = context.host;
139             port = context.port;
140             file = context.file;
141           }
142       }
143     else if (context != null)
144       {
145         // Protocol NOT specified in spec string.
146         // Use context fields (except ref) as a foundation for relative URLs.
147         colon = -1;
148         protocol = context.protocol;
149         host = context.host;
150         port = context.port;
151         file = context.file;
152       }
153     else        // Protocol NOT specified in spec. and no context available.
154       throw new
155           MalformedURLException("Absolute URL required with null context");
156
157     if (handler != null)
158       {
159         // TODO12: Need SecurityManager.checkPermission and
160         // TODO12: java.net.NetPermission from JDK 1.2 to be implemented.
161         // Throw an exception if an extant security mgr precludes
162         // specifying a StreamHandler.
163         //
164         // SecurityManager s = System.getSecurityManager();
165         // if (s != null)
166         //   s.checkPermission(NetPermission("specifyStreamHandler"));
167
168         this.handler = handler;
169       }
170     else
171       this.handler = setURLStreamHandler(protocol);
172
173     if (this.handler == null)
174       throw new MalformedURLException("Handler for protocol not found");
175
176     // JDK 1.2 doc for parseURL specifically states that any '#' ref
177     // is to be excluded by passing the 'limit' as the indexOf the '#'
178     // if one exists, otherwise pass the end of the string.
179     int hashAt = spec.indexOf('#', colon + 1);
180     this.handler.parseURL(this, spec, colon + 1,
181                           hashAt < 0 ? spec.length() : hashAt);
182     if (hashAt >= 0)
183       ref = spec.substring(hashAt + 1);
184   }
185
186   public boolean equals(Object obj)
187   {
188     if (obj == null || ! (obj instanceof URL))
189       return false;
190
191     URL uObj = (URL) obj;
192     
193     // This comparison is very conservative.  It assumes that any
194     // field can be null.
195     return (port == uObj.port
196             && ((protocol == null && uObj.protocol == null)
197                 || (protocol != null && protocol.equals(uObj.protocol)))
198             && ((host == null && uObj.host == null)
199                 || (host != null && host.equals(uObj.host)))
200             && ((file == null && uObj.file == null)
201                 || (file != null && file.equals(uObj.file)))
202             && ((ref == null && uObj.ref == null)
203                 || (ref != null && ref.equals(uObj.ref))));
204   }
205
206   public final Object getContent() throws IOException
207   {
208     return openConnection().getContent();
209   }
210
211   public String getFile()
212   {
213     return file;
214   }
215
216   public String getHost()
217   {
218     return host;
219   }
220
221   public int getPort()
222   {
223     return port;
224   }
225
226   public String getProtocol()
227   {
228     return protocol;
229   }
230
231   public String getRef()
232   {
233     return ref;
234   }
235
236   public int hashCode()
237   {
238     // JCL book says this is computed using (only) the hashcodes of the 
239     // protocol, host and file fields.  Empirical evidence indicates this
240     // is probably XOR in JDK 1.1.  In JDK 1.2 it seems to be a sum including
241     // the port.
242     //
243     // JDK 1.2 online doc infers that host could be null because it
244     // explicitly states that file cannot be null but is silent on host.
245     // A simple example with protocol "http" (hashcode 3213448), host null,
246     // file "/" (hashcode 47) produced a hashcode (3213494) which appeared
247     // to be the sum of the two hashcodes plus the port.  Another example
248     // using "/index.html" for file bore this out; as well as "#" for file
249     // (which was reduced to "" with a hashcode of zero).  A "" host also
250     // causes the port number and the two hashcodes to be summed.
251
252     return (protocol.hashCode() + ((host == null) ? 0 : host.hashCode()) +
253         port + file.hashCode());
254   }
255
256   public URLConnection openConnection() throws IOException
257   {
258     return handler.openConnection(this);
259   }
260
261   public final InputStream openStream() throws IOException
262   {
263     return openConnection().getInputStream();
264   }
265
266   public boolean sameFile(URL other)
267   {
268     // This comparison is very conservative.  It assumes that any
269     // field can be null.
270     return (other != null 
271             && port == other.port
272             && ((protocol == null && other.protocol == null)
273                 || (protocol != null && protocol.equals(other.protocol)))
274             && ((host == null && other.host == null)
275                 || (host != null && host.equals(other.host)))
276             && ((file == null && other.file == null)
277                 || (file != null && file.equals(other.file))));
278   }
279
280   protected void set(String protocol, String host, int port, String file,
281                      String ref)
282   {
283     // TBD: Theoretically, a poorly written StreamHandler could pass an
284     // invalid protocol.  It will cause the handler to be set to null
285     // thus overriding a valid handler.  Callers of this method should
286     // be aware of this.
287     this.handler = setURLStreamHandler(protocol);
288     this.protocol = protocol;
289     this.port = port;
290     this.host = host;
291     this.file = file;
292     this.ref = ref;
293   }
294
295   public static synchronized void
296         setURLStreamHandlerFactory(URLStreamHandlerFactory fac)
297   {
298     if (factory != null)
299       throw new Error("URLStreamHandlerFactory already set");
300
301     // Throw an exception if an extant security mgr precludes
302     // setting the factory.
303     SecurityManager s = System.getSecurityManager();
304     if (s != null)
305       s.checkSetFactory();
306     factory = fac;
307   }
308
309   public String toExternalForm()
310   {
311     // Identical to toString().
312     return handler.toExternalForm(this);
313   }
314
315   public String toString()
316   {
317     // Identical to toExternalForm().
318     return handler.toExternalForm(this);
319   }
320
321   private URLStreamHandler setURLStreamHandler(String protocol)
322   {
323     URLStreamHandler handler;
324
325     // See if a handler has been cached for this protocol.
326     if ((handler = (URLStreamHandler) handlers.get(protocol)) != null)
327       return handler;
328
329     // If a non-default factory has been set, use it to find the protocol.
330     if (factory != null)
331       handler = factory.createURLStreamHandler(protocol);
332     else if (protocol.equals ("file"))
333       {
334         // This is an interesting case.  It's tempting to think that we
335         // could call Class.forName ("gnu.gcj.protocol.file.Handler") to
336         // get the appropriate class.  Unfortunately, if we do that the
337         // program will never terminate, because setURLStreamHandler is
338         // eventually called by Class.forName.
339         //
340         // Treating "file" as a special case is the minimum that will
341         // fix this problem.  If other protocols are required in a
342         // statically linked application they will need to be handled in
343         // the same way as "file".
344         handler = new gnu.gcj.protocol.file.Handler ();
345       }
346
347     // Non-default factory may have returned null or a factory wasn't set.
348     // Use the default search algorithm to find a handler for this protocol.
349     if (handler == null)
350       {
351         // Get the list of packages to check and append our default handler
352         // to it, along with the JDK specified default as a last resort.
353         // Except in very unusual environments the JDK specified one shouldn't
354         // ever be needed (or available).
355         String propVal = System.getProperty("java.protocol.handler.pkgs");
356         propVal = (propVal == null) ? "" : (propVal + "|");
357         propVal = propVal + "gnu.gcj.protocol|sun.net.www.protocol";
358
359         StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
360         do
361           {
362             String facName = pkgPrefix.nextToken() + "." + protocol +
363                                 ".Handler";
364             try
365               {
366                 handler =
367                   (URLStreamHandler) Class.forName(facName).newInstance();
368               }
369             catch (Exception e)
370               {
371                 // Can't instantiate; handler still null, go on to next element.
372               }
373           } while ((handler == null ||
374                     ! (handler instanceof URLStreamHandler)) &&
375                    pkgPrefix.hasMoreTokens());
376       }
377
378     // Update the hashtable with the new protocol handler.
379     if (handler != null)
380       if (handler instanceof URLStreamHandler)
381         handlers.put(protocol, handler);
382       else
383         handler = null;
384
385     return handler;
386   }
387 }