View Javadoc

1   package fr.in2p3.jsaga.impl.url;
2   
3   import fr.in2p3.jsaga.adaptor.data.read.FileAttributes;
4   import fr.in2p3.jsaga.helpers.URLEncoder;
5   import fr.in2p3.jsaga.impl.AbstractSagaObjectImpl;
6   import org.ogf.saga.SagaObject;
7   import org.ogf.saga.error.BadParameterException;
8   import org.ogf.saga.error.NoSuccessException;
9   import org.ogf.saga.session.Session;
10  import org.ogf.saga.url.URL;
11  
12  import java.net.URI;
13  import java.net.URISyntaxException;
14  
15  /* ***************************************************
16  * *** Centre de Calcul de l'IN2P3 - Lyon (France) ***
17  * ***             http://cc.in2p3.fr/             ***
18  * ***************************************************
19  * File:   URLImpl
20  * Author: Sylvain Reynaud (sreynaud@in2p3.fr)
21  * Date:   20 oct. 2008
22  * ***************************************************
23  * Description:                                      */
24  /**
25   *
26   */
27  public class URLImpl extends AbstractSagaObjectImpl implements URL {
28      private URI u;
29      private FileAttributes m_cache;
30      private boolean m_mustRemoveSlash;
31  
32      /** MAY encode the URL */
33      URLImpl(String url, boolean encode) throws BadParameterException {
34          if (encode) {
35              if (url.startsWith("file://")) {
36                  url = URLEncoder.encodePathOnly(url);
37              } else {
38                  url = URLEncoder.encode(url);
39              }
40          }
41          try {
42              while (url.startsWith("//")) {
43                  url = url.substring(1); // prevent URI to consider root dir as a host
44              }
45              u = new URI(url);
46              this.fixFileURI();  //sreynaud
47          } catch(URISyntaxException e) {
48              throw new BadParameterException("syntax error in url", e);
49          }
50      }
51  
52      /** Encode the relative path */
53      URLImpl(String relativePath) throws BadParameterException {
54          int colonPos = relativePath.indexOf(':');
55          int slashPos = relativePath.indexOf('/');
56          m_mustRemoveSlash = colonPos > -1 && (slashPos == -1 || colonPos < slashPos);
57          if (m_mustRemoveSlash) {
58              relativePath = URLEncoder.encodePathOnly("/"+relativePath);
59          } else {
60              relativePath = URLEncoder.encodePathOnly(relativePath);
61          }
62          try {
63              u = new URI(relativePath);
64          } catch (URISyntaxException e) {
65              throw new BadParameterException("syntax error in url", e);
66          }
67      }
68  
69      /** Encode the relative path + set the cache */
70      URLImpl(FileAttributes cache) throws BadParameterException {
71          this(cache.getRelativePath());
72          m_cache = cache;
73      }
74  
75      /** Encode the relative path */
76      URLImpl(URL base, String relativePath) {
77          this(   base,
78                  URLEncoder.encodePathOnly(relativePath),
79                  null,
80                  null
81          );
82      }
83  
84      /** Encode the URL */
85      URLImpl(URL base, URL relativeUrl) {
86          this(   base,
87                  URLEncoder.encodePathOnly(relativeUrl.getPath()),
88                  relativeUrl.getQuery()!=null ? relativeUrl.getQuery() : base.getQuery(),
89                  relativeUrl.getFragment()!=null ? relativeUrl.getFragment() : base.getFragment()
90          );
91      }
92  
93      /** DO NOT encode the URL */
94      private URLImpl(URL base, String relativePath, String query, String fragment) {
95          //workaround: Windows absolute paths must start with one and only one '/'
96          if (isWindowsAbsolutePath(base.getScheme(), relativePath)) {
97              relativePath = "/"+relativePath;
98          }
99  
100         // remove redondant '/'
101         if (relativePath.startsWith("//")) {
102             int i;for(i=0; i<relativePath.length() && relativePath.charAt(i)=='/'; i++);
103             if(i>1)relativePath="/"+relativePath.substring(i);
104         }
105 
106         //workaround: force to be interpreted as a relative path (even if path contains character ':')
107         if (! relativePath.startsWith("/")) {
108             relativePath = "./"+relativePath;
109         }
110 
111         // resolve URI
112         URI baseUri = ((URLImpl) base).u;
113         String relativeUri = relativePath
114                 + concatIfNotNull('?', new String[]{query, baseUri.getQuery()})
115                 + concatIfNotNull('#', new String[]{fragment, baseUri.getFragment()});
116         u = baseUri.resolve(relativeUri);
117     }
118 
119     /** DO NOT encode the URL */
120     private URLImpl(URI u) {
121         this.u = u;
122     }
123 
124     /** clone */
125     public SagaObject clone() throws CloneNotSupportedException {
126         URLImpl clone = (URLImpl) super.clone();
127         clone.u = u;
128         clone.m_cache = m_cache;
129         clone.m_mustRemoveSlash = m_mustRemoveSlash;
130         return clone;
131     }
132 
133     /** Encode the URL */
134     public void setString(String url) throws BadParameterException {
135         if (url == null) {
136             url = "";
137         }
138         String encodedUrl = (url.startsWith("file://"))
139                 ? URLEncoder.encodePathOnly(url)
140                 : URLEncoder.encode(url);
141         try {
142             u = new URI(encodedUrl);
143             this.fixFileURI();  //sreynaud
144         } catch(URISyntaxException e) {
145             throw new BadParameterException("syntax error in url", e);
146         }
147     }
148     public void setString() throws BadParameterException {
149         this.setString(null);
150     }
151 
152     /** Decode the URL */
153     public String getString() {
154         return URLEncoder.decode(u, m_mustRemoveSlash);
155     }
156     /** DO NOT decode the URL */
157     public String getEscaped() {
158         return u.toString();
159     }
160     /** DO NOT decode the URL */
161     public String toString() {
162         return u.toString();
163     }
164 
165     public String getFragment() {
166         return u.getFragment();
167     }
168 
169     public void setFragment(String fragment) throws BadParameterException {
170         try {
171             u = new URI(u.getScheme(), u.getUserInfo(), u.getHost(),
172                     u.getPort(), u.getPath(), u.getQuery(), fragment);
173         } catch(URISyntaxException e) {
174             throw new BadParameterException("syntax error in fragment", e);
175         }
176     }
177     public void setFragment() throws BadParameterException {
178         this.setFragment(null);
179     }
180 
181     public String getHost() {
182         if (u.getHost() == null) {
183             return this.getSchemeSpecificPart().getHost();   //sreynaud
184         }
185         return u.getHost();
186     }
187 
188     public void setHost(String host) throws BadParameterException {
189         try {
190             u = new URI(u.getScheme(), u.getUserInfo(), host,
191                     u.getPort(), u.getPath(), u.getQuery(), u.getFragment());
192         } catch(URISyntaxException e) {
193             throw new BadParameterException("syntax error in host", e);
194         }
195     }
196     public void setHost() throws BadParameterException {
197         this.setHost(null);
198     }
199 
200     public String getPath() {
201         if (u.getPath() == null) {
202             return this.getSchemeSpecificPart().getPath();  //sreynaud
203         } else if (".".equals(u.getAuthority())) {
204             return "."+u.getPath();                         //sreynaud
205         } else if (u.getPath().startsWith("/./") || m_mustRemoveSlash) {
206             return u.getPath().substring(1);                //sreynaud
207         } else if (u.getPath().startsWith("//")) {
208             return trimPath(u.getPath());                   //sreynaud
209         }
210         return u.getPath();
211     }
212     private static String trimPath(String path) {
213         if (path.startsWith("//")) {
214             return trimPath(path.substring(1));
215         } else {
216             return path;
217         }
218     }
219 
220     public void setPath(String path) throws BadParameterException {
221         if (path == null) {
222             path = "";
223         }
224         try {
225             if (path.startsWith("./")) {
226                 //sreynaud: set relative path
227                 u = new URI(u.getScheme(), u.getAuthority(),
228                         path.substring(2), u.getQuery(), u.getFragment());
229             } else {
230                 //sreynaud: fix absolute path
231                 int i;for(i=0; i<path.length() && path.charAt(i)=='/'; i++);
232                 if(i>1)path="/"+path.substring(i);
233                 if (path == "" && u.getRawAuthority() == null) 
234                 	throw new BadParameterException("Path cannot by empty if authority is empty");
235                 u = new URI(u.getScheme(), u.getUserInfo(), u.getHost(),
236                         u.getPort(), path, u.getQuery(), u.getFragment());
237             }
238         } catch(URISyntaxException e) {
239             throw new BadParameterException("syntax error in path", e);
240         }
241     }
242     public void setPath() throws BadParameterException {
243         this.setPath(null);
244     }
245 
246     public int getPort() {
247         if (u.getPort() == -1) {
248             return this.getSchemeSpecificPart().getPort();  //sreynaud
249         }
250         return u.getPort();
251     }
252 
253     public void setPort(int port) throws BadParameterException {
254         try {
255             u = new URI(u.getScheme(), u.getUserInfo(), u.getHost(),
256                     port, u.getPath(), u.getQuery(), u.getFragment());
257         } catch(URISyntaxException e) {
258             throw new BadParameterException("syntax error in port", e);     // ???
259         }
260     }
261     public void setPort() throws BadParameterException {
262         this.setPort(-1);
263     }
264 
265     public String getQuery() {
266         if (u.getQuery() == null) {
267             return this.getSchemeSpecificPart().getQuery(); //sreynaud
268         }
269         return u.getQuery();
270     }
271 
272     public void setQuery(String query) throws BadParameterException {
273         try {
274             u = new URI(u.getScheme(), u.getUserInfo(), u.getHost(),
275                     u.getPort(), u.getPath(), query, u.getFragment());
276         } catch(URISyntaxException e) {
277             throw new BadParameterException("syntax error in query", e);
278         }
279     }
280     public void setQuery() throws BadParameterException {
281         this.setQuery(null);
282     }
283 
284     public String getScheme() {
285         return u.getScheme();
286     }
287 
288     public void setScheme(String scheme) throws BadParameterException {
289         try {
290             u = new URI(scheme, u.getUserInfo(), u.getHost(),
291                     u.getPort(), u.getPath(), u.getQuery(), u.getFragment());
292         } catch(URISyntaxException e) {
293             throw new BadParameterException("syntax error in scheme", e);
294         }
295     }
296     public void setScheme() throws BadParameterException {
297         this.setScheme(null);
298     }
299 
300     public String getUserInfo() {
301         if (u.getUserInfo() == null) {
302             return this.getSchemeSpecificPart().getUserInfo();  //sreynaud
303         }
304         return u.getUserInfo();
305     }
306 
307     public void setUserInfo(String userInfo) throws BadParameterException {
308         try {
309             u = new URI(u.getScheme(), userInfo, u.getHost(),
310                     u.getPort(), u.getPath(), u.getQuery(), u.getFragment());
311         } catch(URISyntaxException e) {
312             throw new BadParameterException("syntax error in query", e);
313         }
314     }
315     public void setUserInfo() throws BadParameterException {
316         this.setUserInfo(null);
317     }
318 
319     public URL translate(String scheme) throws BadParameterException, NoSuccessException {
320         try {
321             URI url = new URI(scheme, u.getUserInfo(), u.getHost(),
322                     u.getPort(), u.getPath(), u.getQuery(), u.getFragment());
323             // Not quite correct: the SAGA specs say that NoSuccessException should be
324             // thrown when the scheme is not supported. How to check this
325             // here ???
326             return new URLImpl(url);
327         } catch(URISyntaxException e) {
328             throw new BadParameterException("syntax error in scheme", e);
329         }
330     }
331     public URL translate(Session session, String scheme) throws BadParameterException, NoSuccessException {
332         return this.translate(scheme);
333     }
334 
335     public URL resolve(URL url) throws NoSuccessException {
336         URLImpl urlimpl = (URLImpl) url;
337         URI uri = u.resolve(urlimpl.u);
338         if (uri == urlimpl.u) {
339             return url;
340         }
341         return new URLImpl(uri);
342     }
343 
344     public boolean isAbsolute() {
345         return u.isAbsolute();
346     }
347 
348     public URL normalize() {
349         URI uri = u.normalize();
350         if (uri == u) {
351             return this;
352         }
353         return new URLImpl(uri);
354     }
355 
356     ////////////////////////////////////////// java methods ///////////////////////////////////////////
357 
358     public int hashCode() {
359         return u.hashCode();
360     }
361 
362     public boolean equals(Object o) {
363         if (o == null) {
364             return false;
365         }
366         if (! (o instanceof URLImpl)) {
367             return false;
368         }
369         URLImpl other = (URLImpl) o;
370         return u.equals(other.u);
371     }
372 
373     ////////////////////////////////////////// cache methods //////////////////////////////////////////
374 
375     public void setCache(FileAttributes cache) {
376         m_cache = cache;
377     }
378 
379     public FileAttributes getCache() {
380         return m_cache;
381     }
382 
383     public boolean hasCache() {
384         return (m_cache != null);
385     }
386 
387     ///////////////////////////////////////// private methods /////////////////////////////////////////
388 
389     private boolean isWindowsAbsolutePath(String scheme, String relativePath) {
390         return ("file".equals(scheme) || "zip".equals(scheme))
391                 && System.getProperty("os.name").startsWith("Windows")
392                 && relativePath.length()>=2 && Character.isLetter(relativePath.charAt(0)) && relativePath.charAt(1)==':'
393                 && (relativePath.length()==2 || (relativePath.length()>2 && relativePath.charAt(2)=='/'));
394     }
395 
396     private void fixFileURI() throws URISyntaxException {
397         boolean isRelative = (u.getHost()==null && u.getAuthority()!=null && !u.getAuthority().equals("."));
398         boolean isWindows = (u.getHost()!=null && u.getHost().length()==1 && u.getAuthority()!=null && u.getAuthority().endsWith(":"));
399         if (isRelative || isWindows) {
400             u = new URI(u.getScheme(), u.getUserInfo(),
401                     "",                                 // fix number of '/' after scheme
402                     u.getPort(),
403                     "/"+u.getAuthority()+u.getPath(),   // fix path
404                     u.getQuery(), u.getFragment());
405         }
406     }
407 
408     private URI getSchemeSpecificPart() {
409         try {
410             return new URI(u.getRawSchemeSpecificPart());
411         } catch (URISyntaxException e) {
412             return u;
413         }
414     }
415 
416     private static String concatIfNotNull(char prefix, String[] suffix) {
417         for (int i=0; suffix!=null && i<suffix.length; i++) {
418             if (suffix[i] != null) {
419                 return prefix + suffix[i];
420             }
421         }
422         return "";
423     }
424 }