KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > filesystems > URLMapper


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.filesystems;
21
22 import java.io.File JavaDoc;
23 import java.io.FileNotFoundException JavaDoc;
24 import java.io.UnsupportedEncodingException JavaDoc;
25 import java.net.MalformedURLException JavaDoc;
26 import java.net.URI JavaDoc;
27 import java.net.URISyntaxException JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.net.URLDecoder JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Arrays JavaDoc;
32 import java.util.Enumeration JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.LinkedHashSet JavaDoc;
35 import java.util.LinkedList JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Set JavaDoc;
38 import org.netbeans.modules.openide.filesystems.DefaultURLMapperProxy;
39 import org.openide.util.Lookup;
40 import org.openide.util.LookupEvent;
41 import org.openide.util.LookupListener;
42
43 /** Mapper from FileObject -> URL.
44  * Should be registered in default lookup. For details see {@link Lookup#getDefault()}.
45  * For all methods, if the passed-in file object is the root folder
46  * of some filesystem, then it is assumed that any valid file object
47  * in that filesystem may also have a URL constructed for it by means
48  * of appending the file object's resource path to the URL of the root
49  * folder. If this cannot work for all file objects on the filesystem,
50  * the root folder must not be assigned a URL of that type. nbfs: URLs
51  * of course always work correctly in this regard.
52  * @since 2.16
53  */

54 public abstract class URLMapper {
55     /**
56      * URL which works inside this VM.
57      * Not guaranteed to work outside the VM (though it may).
58      */

59     public static final int INTERNAL = 0;
60
61     /**
62      * URL which works inside this machine.
63      * Not guaranteed to work from other machines (though it may).
64      * <div class="nonnormative">
65      * Typical protocols used: <code>file</code> for disk files (see {@link File#toURI});
66      * <code>jar</code> to wrap other URLs (e.g. <samp>jar:file:/some/thing.jar!/some/entry</samp>).
67      * </div>
68      */

69     public static final int EXTERNAL = 1;
70
71     /** URL which works from networked machines.*/
72     public static final int NETWORK = 2;
73
74     /** results with URLMapper instances*/
75     private static Lookup.Result<URLMapper> result;
76     private static final List JavaDoc<URLMapper> CACHE_JUST_COMPUTING = new ArrayList JavaDoc<URLMapper>();
77     private static final ThreadLocal JavaDoc<List JavaDoc<URLMapper>> threadCache = new ThreadLocal JavaDoc<List JavaDoc<URLMapper>>();
78
79     static {
80         DefaultURLMapperProxy.setDefault(new DefaultURLMapper());
81         result = Lookup.getDefault().lookupResult(URLMapper.class);
82         result.addLookupListener(
83             new LookupListener() {
84                 public void resultChanged(LookupEvent ev) {
85                     synchronized (URLMapper.class) {
86                         cache = null;
87                     }
88                 }
89             }
90         );
91     }
92
93     /** Basic impl. for JarFileSystem, LocalFileSystem, MultiFileSystem */
94     private static URLMapper defMapper;
95
96     /** Cache of all available URLMapper instances. */
97     private static List JavaDoc<URLMapper> cache;
98
99     /** Find a good URL for this file object which works according to type:
100      * <ul>
101      * <li>inside this VM
102      * <li>inside this machine
103      * <li>from networked machines
104      * </ul>
105      * @return a suitable URL, or null
106      */

107     public static URL JavaDoc findURL(FileObject fo, int type) {
108
109         /** secondly registered URLMappers are asked to resolve URL */
110         for (URLMapper mapper : getInstances()) {
111             URL JavaDoc retVal = mapper.getURL(fo, type);
112
113             if (retVal != null) {
114                 return retVal;
115             }
116         }
117
118         // if not resolved yet then internal URL with nbfs protocol is returned
119
// XXX this would be better handled by making DefaultURLMapper just return nbfs for INTERNAL when necessary!
120
if (type == INTERNAL) {
121             try {
122                 return FileURL.encodeFileObject(fo);
123             } catch (FileStateInvalidException iex) {
124                 // ignore
125
}
126         }
127
128         return null;
129     }
130
131     /** Get a good URL for this file object which works according to type:
132      * <ul>
133      * <li>inside this VM
134      * <li>inside this machine
135      * <li>from networked machines
136      * </ul>
137      * The implementation can't use neither {@link FileUtil#toFile} nor {@link FileUtil#toFileObject}
138      * otherwise StackOverflowError maybe thrown.
139      * @return a suitable URL, or null
140      */

141     public abstract URL JavaDoc getURL(FileObject fo, int type);
142
143     /** Find an array of FileObjects for this URL.
144      * Zero or more FOs may be returned.
145      *
146      * For each returned FO, it must be true that FO -> URL gives the
147      * exact URL which was passed in, but depends on appropriate type
148      * <code> findURL(FileObject fo, int type) </code>.
149      * @param url to wanted FileObjects
150      * @return a suitable array of FileObjects, or empty array if not successful
151      * @since 2.22
152      * @deprecated Use {@link #findFileObject} instead.
153      */

154     @Deprecated JavaDoc
155     public static FileObject[] findFileObjects(URL JavaDoc url) {
156         Set JavaDoc<FileObject> retSet = new LinkedHashSet JavaDoc<FileObject>();
157
158         for (URLMapper mapper: getInstances()) {
159             FileObject[] retVal = mapper.getFileObjects(url);
160
161             if (retVal != null) {
162                 retSet.addAll(Arrays.asList(retVal));
163             }
164         }
165
166         return retSet.toArray(new FileObject[retSet.size()]);
167     }
168
169     /** Find an appropriate instance of FileObject that addresses this URL
170      *
171      * @param url URL to be converted to file object
172      * @return file object corresponding to URL or null if no one was found
173      * @since 4.29
174      */

175     public static FileObject findFileObject(URL JavaDoc url) {
176         if (url == null) {
177             throw new NullPointerException JavaDoc("Cannot pass null URL to URLMapper.findFileObject"); // NOI18N
178
}
179
180         /** first basic implementation */
181         FileObject[] results = null;
182
183         Iterator JavaDoc<URLMapper> instances = getInstances().iterator();
184
185         while (instances.hasNext() && ((results == null) || (results.length == 0))) {
186             URLMapper mapper = instances.next();
187
188             results = mapper.getFileObjects(url);
189         }
190
191         return ((results != null) && (results.length > 0)) ? results[0] : null;
192     }
193
194     /**
195      * Get an array of FileObjects for this URL.
196      * There is no reason to return array
197      * with size greater than one because method {@link #findFileObject findFileObject}
198      * uses just the first element (subsequent elements will not be accepted anyway).
199      * The implementation cannot use either {@link FileUtil#toFile} nor {@link FileUtil#toFileObject}
200      * or StackOverflowError may be thrown.
201      * <p class="nonnormative">The only reason to return an array here
202      * is for backward compatibility.</p>
203      * @param url to wanted FileObjects
204      * @return an array of FileObjects with size no greater than one, or null
205      * @since 2.22
206      */

207     public abstract FileObject[] getFileObjects(URL JavaDoc url);
208
209     /** Returns all available instances of URLMapper.
210      * @return list of URLMapper instances
211      */

212     private static List JavaDoc<URLMapper> getInstances() {
213         synchronized (URLMapper.class) {
214             if (cache != null) {
215                 if ((cache != CACHE_JUST_COMPUTING) || (threadCache.get() == CACHE_JUST_COMPUTING)) {
216                     return cache;
217                 }
218             }
219
220             // Set cache to empty array here to prevent infinite loop.
221
// See issue #41358, #43359
222
cache = CACHE_JUST_COMPUTING;
223             threadCache.set(CACHE_JUST_COMPUTING);
224         }
225
226         ArrayList JavaDoc<URLMapper> res = null;
227
228         try {
229             res = new ArrayList JavaDoc<URLMapper>(result.allInstances());
230             {
231                 // XXX hack to put default last, since we cannot easily adjust META-INF/services/o.o.f.URLM order to our tastes
232
// (would need to ask *all other* impls to be earlier somewhere)
233
URLMapper def = null;
234                 Iterator JavaDoc<URLMapper> it = res.iterator();
235                 while (it.hasNext()) {
236                     URLMapper m = it.next();
237                     if (m instanceof DefaultURLMapperProxy) {
238                         def = m;
239                         it.remove();
240                         break;
241                     }
242                 }
243                 if (def != null) {
244                     res.add(def);
245                 }
246             }
247             return res;
248         } finally {
249             synchronized (URLMapper.class) {
250                 if (cache == CACHE_JUST_COMPUTING) {
251                     cache = res;
252                 }
253
254                 threadCache.set(null);
255             }
256         }
257     }
258
259     /*** Basic impl. for JarFileSystem, LocalFileSystem, MultiFileSystem */
260     private static class DefaultURLMapper extends URLMapper {
261         DefaultURLMapper() {
262         }
263
264         // implements URLMapper.getFileObjects(URL url)
265
public FileObject[] getFileObjects(URL JavaDoc url) {
266             String JavaDoc prot = url.getProtocol();
267
268             if (prot.equals("nbfs")) { //// NOI18N
269

270                 FileObject retVal = FileURL.decodeURL(url);
271
272                 return (retVal == null) ? null : new FileObject[] { retVal };
273             }
274
275             if (prot.equals("jar")) { //// NOI18N
276

277                 return getFileObjectsForJarProtocol(url);
278             }
279
280             if (prot.equals("file")) { //// NOI18N
281

282                 File JavaDoc f = toFile(url);
283
284                 if (f != null) {
285                     FileObject[] foRes = findFileObjectsInRepository(f);
286
287                     if ((foRes != null) && (foRes.length > 0)) {
288                         return foRes;
289                     }
290                 }
291             }
292
293             return null;
294         }
295
296         private FileObject[] findFileObjectsInRepository(File JavaDoc f) {
297             if (!f.equals(FileUtil.normalizeFile(f))) {
298                 throw new IllegalArgumentException JavaDoc(
299                     "Parameter file was not " + // NOI18N
300
"normalized. Was " + f + " instead of " + FileUtil.normalizeFile(f)
301                 ); // NOI18N
302
}
303
304             @SuppressWarnings JavaDoc("deprecation") // keep for backward compatibility w/ NB 3.x
305
Enumeration JavaDoc<? extends FileSystem> en = Repository.getDefault().getFileSystems();
306             LinkedList JavaDoc<FileObject> list = new LinkedList JavaDoc<FileObject>();
307             String JavaDoc fileName = f.getAbsolutePath();
308
309             while (en.hasMoreElements()) {
310                 FileSystem fs = en.nextElement();
311                 String JavaDoc rootName = null;
312                 FileObject fsRoot = fs.getRoot();
313                 File JavaDoc root = findFileInRepository(fsRoot);
314
315                 if (root == null) {
316                     Object JavaDoc rootPath = fsRoot.getAttribute("FileSystem.rootPath"); //NOI18N
317

318                     if ((rootPath != null) && (rootPath instanceof String JavaDoc)) {
319                         rootName = (String JavaDoc) rootPath;
320                     } else {
321                         continue;
322                     }
323                 }
324
325                 if (rootName == null) {
326                     rootName = root.getAbsolutePath();
327                 }
328
329                 /**root is parent of file*/
330                 if (fileName.indexOf(rootName) == 0) {
331                     String JavaDoc res = fileName.substring(rootName.length()).replace(File.separatorChar, '/');
332                     FileObject fo = fs.findResource(res);
333                     File JavaDoc file2Fo = (fo != null) ? findFileInRepository(fo) : null;
334
335                     if ((fo != null) && (file2Fo != null) && f.equals(file2Fo)) {
336                         if (fo.getClass().toString().indexOf("org.netbeans.modules.masterfs.MasterFileObject") != -1) { //NOI18N
337
list.addFirst(fo);
338                         } else {
339                             list.addLast(fo);
340                         }
341                     }
342                 }
343             }
344
345             FileObject[] results = new FileObject[list.size()];
346             list.toArray(results);
347
348             return results;
349         }
350
351         // implements URLMapper.getURL(FileObject fo, int type)
352
public URL JavaDoc getURL(FileObject fo, int type) {
353             if (fo == null) {
354                 return null;
355             }
356
357             if (type == NETWORK) {
358                 return null;
359             }
360
361             if (fo instanceof MultiFileObject && (type == INTERNAL)) {
362                 // Stick to nbfs protocol, otherwise URL calculations
363
// get messed up. See #39613.
364
return null;
365             }
366
367             File JavaDoc fFile = findFileInRepository(fo);
368
369             if (fFile != null) {
370                 try {
371                     return toURL(fFile, fo);
372                 } catch (MalformedURLException JavaDoc mfx) {
373                     assert false : mfx;
374
375                     return null;
376                 }
377             }
378
379             URL JavaDoc retURL = null;
380             FileSystem fs = null;
381
382             try {
383                 fs = fo.getFileSystem();
384             } catch (FileStateInvalidException fsex) {
385                 return null;
386             }
387
388             if (fs instanceof JarFileSystem) {
389                 JarFileSystem jfs = (JarFileSystem) fs;
390                 File JavaDoc f = jfs.getJarFile();
391
392                 if (f == null) {
393                     return null;
394                 }
395
396                 try {
397                     String JavaDoc toReplace = "__EXCLAMATION_REPLACEMENT__";//NOI18N
398
retURL = new URL JavaDoc(
399                             "jar:" + new File JavaDoc(f,toReplace + fo.getPath()).toURI().toString().replaceFirst("/"+toReplace,"!/") + // NOI18N
400
((fo.isFolder() && !fo.isRoot()) ? "/" : "")
401                         ); // NOI18N
402
} catch (MalformedURLException JavaDoc mfx) {
403                     mfx.printStackTrace();
404
405                     return null;
406                 }
407             } else if (fs instanceof XMLFileSystem) {
408                 URL JavaDoc retVal = null;
409
410                 try {
411                     retVal = ((XMLFileSystem) fs).getURL(fo.getPath());
412
413                     if (retVal == null) {
414                         return null;
415                     }
416
417                     if (type == INTERNAL) {
418                         return retVal;
419                     }
420
421                     boolean isInternal = retVal.getProtocol().startsWith("nbres"); //NOI18N
422

423                     if ((type == EXTERNAL) && !isInternal) {
424                         return retVal;
425                     }
426
427                     return null;
428                 } catch (FileNotFoundException JavaDoc fnx) {
429                     return null;
430                 }
431             }
432
433             return retURL;
434         }
435
436         private static URL JavaDoc toURL(File JavaDoc fFile, FileObject fo)
437         throws MalformedURLException JavaDoc {
438             URL JavaDoc retVal = null;
439
440             if (fo.isFolder() && !fo.isValid()) {
441                 String JavaDoc urlDef = fFile.toURI().toURL().toExternalForm();
442                 String JavaDoc pathSeparator = "/"; //NOI18N
443

444                 if (!urlDef.endsWith(pathSeparator)) {
445                     retVal = new URL JavaDoc(urlDef + pathSeparator);
446                 }
447             }
448
449             return (retVal == null) ? fFile.toURI().toURL() : retVal;
450         }
451
452         private static File JavaDoc findFileInRepository(FileObject fo) {
453             File JavaDoc f = (File JavaDoc) fo.getAttribute("java.io.File"); // NOI18N
454

455             return (f != null) ? FileUtil.normalizeFile(f) : null;
456         }
457
458         private static FileObject[] getFileObjectsForJarProtocol(URL JavaDoc url) {
459             FileObject retVal = null;
460             JarURLParser jarUrlParser = new JarURLParser(url);
461             File JavaDoc file = jarUrlParser.getJarFile();
462             String JavaDoc entryName = jarUrlParser.getEntryName();
463
464             if (file != null) {
465                 JarFileSystem fs = findJarFileSystem(file);
466
467                 if (fs != null) {
468                     if (entryName == null) {
469                         entryName = ""; // #39190: root of JAR
470
}
471
472                     retVal = fs.findResource(entryName);
473                 }
474             }
475
476             return (retVal == null) ? null : new FileObject[] { retVal };
477         }
478
479         private static JarFileSystem findJarFileSystem(File JavaDoc jarFile) {
480             JarFileSystem retVal = null;
481             @SuppressWarnings JavaDoc("deprecation") // keep for backward compatibility w/ NB 3.x
482
Enumeration JavaDoc<? extends FileSystem> en = Repository.getDefault().getFileSystems();
483
484             while (en.hasMoreElements()) {
485                 FileSystem fs = en.nextElement();
486
487                 if (fs instanceof JarFileSystem) {
488                     File JavaDoc fsJarFile = ((JarFileSystem) fs).getJarFile();
489
490                     if (fsJarFile.equals(jarFile)) {
491                         retVal = (JarFileSystem) fs;
492
493                         break;
494                     }
495                 }
496             }
497
498             return retVal;
499         }
500
501         private static File JavaDoc toFile(URL JavaDoc u) {
502             if (u == null) {
503                 throw new NullPointerException JavaDoc();
504             }
505
506             try {
507                 URI JavaDoc uri = new URI JavaDoc(u.toExternalForm());
508
509                 return FileUtil.normalizeFile(new File JavaDoc(uri));
510             } catch (URISyntaxException JavaDoc use) {
511                 // malformed URL
512
return null;
513             } catch (IllegalArgumentException JavaDoc iae) {
514                 // not a file: URL
515
return null;
516             }
517         }
518
519         private static class JarURLParser {
520             private File JavaDoc jarFile;
521             private String JavaDoc entryName;
522
523             JarURLParser(URL JavaDoc originalURL) {
524                 parse(originalURL);
525             }
526
527             /** copy & pasted from JarURLConnection.parse*/
528             void parse(URL JavaDoc originalURL) {
529                 String JavaDoc spec = originalURL.getFile();
530
531                 int separator = spec.indexOf('!');
532
533                 if (separator != -1) {
534                     try {
535                         jarFile = toFile(new URL JavaDoc(spec.substring(0, separator++)));
536                         entryName = null;
537                     } catch (MalformedURLException JavaDoc e) {
538                         return;
539                     }
540
541                     /* if ! is the last letter of the innerURL, entryName is null */
542                     if (++separator != spec.length()) {
543                         try {
544                             entryName = URLDecoder.decode(spec.substring(separator, spec.length()),"UTF-8");
545                         } catch (UnsupportedEncodingException JavaDoc ex) {
546                             return;
547                         }
548                     }
549                 }
550             }
551
552             File JavaDoc getJarFile() {
553                 return jarFile;
554             }
555
556             String JavaDoc getEntryName() {
557                 return entryName;
558             }
559         }
560     }
561 }
562
Popular Tags