KickJava   Java API By Example, From Geeks To Geeks.

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


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.BufferedInputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.lang.ref.Reference JavaDoc;
27 import java.lang.ref.WeakReference JavaDoc;
28 import java.util.Date JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.logging.Logger JavaDoc;
33 import org.openide.util.Lookup;
34 import org.openide.util.LookupEvent;
35 import org.openide.util.LookupListener;
36 import org.openide.util.Union2;
37
38 /**
39  * This class is intended to enhance MIME resolving. This class offers
40  * only one method: findMIMEType(FileObject fo). If this method is called, then
41  * registered subclasses of MIMEResolver are asked one by one to resolve MIME type of this FileObject.
42  * Resolving is finished right after first resolver is able to resolve this FileObject or if all registered
43  * resolvers returns null (not recognized).
44  *
45  *Resolvers are registered if they have their record in IDE_HOME\system\Services
46  * in form *.instance e.g.: org-some-package-JavaResolver.instance
47  *
48  * @author rmatous
49  */

50 final class MIMESupport extends Object JavaDoc {
51     /* The following two fields represent a single-entry cache, which proved
52      * to be as effective as any other more complex caching due to typical
53      * access pattern from DataSystems.
54      */

55     private static final Reference JavaDoc<FileObject> EMPTY = new WeakReference JavaDoc<FileObject>(null);
56     private static Reference JavaDoc<FileObject> lastFo = EMPTY;
57     private static Reference JavaDoc<FileObject> lastCfo = EMPTY;
58     private static Object JavaDoc lock = new Object JavaDoc();
59     
60     /** for logging and test interaction */
61     private static Logger JavaDoc ERR = Logger.getLogger(MIMESupport.class.getName());
62
63     private MIMESupport() {
64     }
65
66     /** Asks all registered subclasses of MIMEResolver to resolve FileObject passed as parameter.
67      * @param fo is FileObject, whose MIME should be resolved
68      * @param def the default value to return or null
69      * @return MIME type or null if not resolved*/

70     static String JavaDoc findMIMEType(FileObject fo, String JavaDoc def) {
71         if (!fo.isValid() || fo.isFolder()) {
72             return null;
73         }
74
75         if ((def != null) && !CachedFileObject.isAnyResolver()) {
76             return def;
77         }
78
79         CachedFileObject cfo = null;
80
81         try {
82             synchronized (lock) {
83                 CachedFileObject lcfo = (CachedFileObject)lastCfo.get();
84                 if (lcfo == null || fo != lastFo.get() || timeOf(fo) != timeOf(lcfo)) {
85                     cfo = new CachedFileObject(fo);
86                 } else {
87                     cfo = lcfo;
88                 }
89
90                 lastCfo = EMPTY;
91             }
92
93             return cfo.getMIMEType(def);
94         } finally {
95             synchronized (lock) {
96                 lastFo = new WeakReference JavaDoc<FileObject>(fo);
97                 lastCfo = new WeakReference JavaDoc<FileObject>(cfo);
98             }
99         }
100     }
101     private static long timeOf(FileObject fo) {
102         if (fo == null) {
103             throw new NullPointerException JavaDoc();
104         }
105         Date JavaDoc d = fo.lastModified();
106         assert d != null : "Null lastModified from " + fo;
107         return d.getTime();
108     }
109     
110     /** Testing purposes.
111      */

112     static MIMEResolver[] getResolvers() {
113         return CachedFileObject.getResolvers();
114     }
115
116     private static class CachedFileObject extends FileObject implements FileChangeListener {
117         static Lookup.Result<MIMEResolver> result;
118         private static Union2<MIMEResolver[],Set JavaDoc<Thread JavaDoc>> resolvers; // call getResolvers instead
119
/** resolvers that were here before we cleaned them */
120         private static MIMEResolver[] previousResolvers;
121         
122         String JavaDoc mimeType;
123         java.util.Date JavaDoc lastModified;
124         CachedInputStream fixIt;
125
126         /*All calls delegated to this object.
127          Except few methods, that returns cached values*/

128         FileObject fileObj;
129
130         CachedFileObject(FileObject fo) {
131             fileObj = fo;
132             lastModified = fileObj.lastModified();
133             fileObj.addFileChangeListener(FileUtil.weakFileChangeListener(this, fileObj));
134         }
135
136         private static MIMEResolver[] getResolvers() {
137             Set JavaDoc<Thread JavaDoc> creators;
138             synchronized (CachedFileObject.class) {
139                 if (resolvers != null && resolvers.hasFirst()) {
140                     return resolvers.first();
141                 }
142                 if (resolvers != null) {
143                     creators = resolvers.second();
144                     if (creators.contains (Thread.currentThread())) {
145                         // prevent stack overflow
146
ERR.fine("Stack Overflow prevention. Returning previousResolvers: " + previousResolvers);
147                         MIMEResolver[] toRet = previousResolvers;
148                         if (toRet == null) {
149                             toRet = new MIMEResolver[0];
150                         }
151                         return toRet;
152                     }
153                 } else {
154                     creators = new HashSet JavaDoc<Thread JavaDoc>();
155                     resolvers = Union2.createSecond(creators);
156                 }
157
158                 if (result == null) {
159                     result = Lookup.getDefault().lookupResult(MIMEResolver.class);
160                     result.addLookupListener(
161                         new LookupListener() {
162                             public void resultChanged(LookupEvent evt) {
163                                 synchronized (CachedFileObject.class) {
164                                     ERR.fine("Clearing cache"); // NOI18N
165
Union2<MIMEResolver[],Set JavaDoc<Thread JavaDoc>> prev = resolvers;
166                                     if (prev != null && prev.hasFirst()) {
167                                         previousResolvers = prev.first();
168                                     }
169                                     resolvers = null;
170                                     lastFo = EMPTY;
171                                     lastCfo = EMPTY;
172                                 }
173                             }
174                         }
175                     );
176                 }
177
178                 // ok, let's compute the value
179
creators.add(Thread.currentThread());
180             }
181
182             ERR.fine("Computing resolvers"); // NOI18N
183

184             MIMEResolver[] toRet = result.allInstances().toArray(new MIMEResolver[0]);
185
186             ERR.fine("Resolvers computed"); // NOI18N
187

188             synchronized (CachedFileObject.class) {
189                 if (resolvers != null && resolvers.hasSecond() && resolvers.second() == creators) {
190                     // ok, we computed the value and nobody cleared it till now
191
resolvers = Union2.createFirst(toRet);
192                     previousResolvers = null;
193                     ERR.fine("Resolvers assigned"); // NOI18N
194
} else {
195                     ERR.fine("Somebody else computes resolvers: " + resolvers); // NOI18N
196
}
197
198
199                 return toRet;
200             }
201         }
202
203         public static boolean isAnyResolver() {
204             return getResolvers().length > 0;
205         }
206
207         public void freeCaches() {
208             fixIt = null;
209             mimeType = null;
210             lastModified = null;
211         }
212
213         public String JavaDoc getMIMEType() {
214             return getMIMEType(null);
215         }
216
217         public String JavaDoc getMIMEType(String JavaDoc def) {
218             if (mimeType == null) {
219                 mimeType = resolveMIME(def);
220             }
221
222             return mimeType;
223         }
224
225         private String JavaDoc resolveMIME(String JavaDoc def) {
226             String JavaDoc retVal = null;
227             MIMEResolver[] local = getResolvers();
228
229             try {
230                 for (int i = 0; i < local.length; i++) {
231                     retVal = local[i].findMIMEType(this);
232
233                     if (retVal != null) {
234                         return retVal;
235                     }
236                 }
237
238                 if (def != null) {
239                     return def;
240                 }
241
242                 return "content/unknown"; // NOI18N
243
} finally {
244                 if (fixIt != null) {
245                     fixIt.internalClose();
246                 }
247
248                 fixIt = null;
249             }
250         }
251
252         public java.util.Date JavaDoc lastModified() {
253             if (lastModified != null) {
254                 return lastModified;
255             }
256
257             return lastModified = fileObj.lastModified();
258         }
259
260         public InputStream JavaDoc getInputStream() throws java.io.FileNotFoundException JavaDoc {
261             if (fixIt == null) {
262                 InputStream JavaDoc is = fileObj.getInputStream();
263
264                 if (!(is instanceof BufferedInputStream JavaDoc)) {
265                     is = new BufferedInputStream JavaDoc(is);
266                 }
267
268                 fixIt = new CachedInputStream(is);
269             }
270
271             fixIt.cacheToStart();
272
273             return fixIt;
274         }
275
276         public void fileChanged(FileEvent fe) {
277             freeCaches();
278         }
279
280         public void fileDeleted(FileEvent fe) {
281             freeCaches();
282
283             //removeFromCache (fe.getFile ());
284
}
285
286         public void fileRenamed(FileRenameEvent fe) {
287             freeCaches();
288         }
289
290         /*All other methods only delegate to fileObj*/
291         public FileObject getParent() {
292             return fileObj.getParent();
293         }
294
295         @Deprecated JavaDoc // have to override for compat
296
public String JavaDoc getPackageNameExt(char separatorChar, char extSepChar) {
297             return fileObj.getPackageNameExt(separatorChar, extSepChar);
298         }
299
300         public FileObject copy(FileObject target, String JavaDoc name, String JavaDoc ext)
301         throws IOException JavaDoc {
302             return fileObj.copy(target, name, ext);
303         }
304
305         protected void fireFileDeletedEvent(Enumeration JavaDoc<FileChangeListener> en, FileEvent fe) {
306             fileObj.fireFileDeletedEvent(en, fe);
307         }
308
309         protected void fireFileFolderCreatedEvent(Enumeration JavaDoc<FileChangeListener> en, FileEvent fe) {
310             fileObj.fireFileFolderCreatedEvent(en, fe);
311         }
312
313         @Deprecated JavaDoc // have to override for compat
314
public void setImportant(boolean b) {
315             fileObj.setImportant(b);
316         }
317
318         public boolean isData() {
319             return fileObj.isData();
320         }
321
322         public Object JavaDoc getAttribute(String JavaDoc attrName) {
323             return fileObj.getAttribute(attrName);
324         }
325
326         public Enumeration JavaDoc<? extends FileObject> getFolders(boolean rec) {
327             return fileObj.getFolders(rec);
328         }
329
330         public void delete(FileLock lock) throws IOException JavaDoc {
331             fileObj.delete(lock);
332         }
333
334         public boolean isRoot() {
335             return fileObj.isRoot();
336         }
337
338         public Enumeration JavaDoc<? extends FileObject> getData(boolean rec) {
339             return fileObj.getData(rec);
340         }
341
342         public FileObject[] getChildren() {
343             return fileObj.getChildren();
344         }
345
346         public String JavaDoc getNameExt() {
347             return fileObj.getNameExt();
348         }
349
350         public boolean isValid() {
351             return fileObj.isValid();
352         }
353
354         @Deprecated JavaDoc // have to override for compat
355
public boolean isReadOnly() {
356             return fileObj.isReadOnly();
357         }
358
359         public String JavaDoc getExt() {
360             return fileObj.getExt();
361         }
362
363         public String JavaDoc getName() {
364             return fileObj.getName();
365         }
366
367         public void removeFileChangeListener(FileChangeListener fcl) {
368             fileObj.removeFileChangeListener(fcl);
369         }
370
371         protected void fireFileRenamedEvent(Enumeration JavaDoc<FileChangeListener> en, FileRenameEvent fe) {
372             fileObj.fireFileRenamedEvent(en, fe);
373         }
374
375         public void refresh(boolean expected) {
376             fileObj.refresh(expected);
377         }
378
379         protected void fireFileAttributeChangedEvent(Enumeration JavaDoc<FileChangeListener> en, FileAttributeEvent fe) {
380             fileObj.fireFileAttributeChangedEvent(en, fe);
381         }
382
383         public long getSize() {
384             return fileObj.getSize();
385         }
386
387         public Enumeration JavaDoc<String JavaDoc> getAttributes() {
388             return fileObj.getAttributes();
389         }
390
391         public void rename(FileLock lock, String JavaDoc name, String JavaDoc ext)
392         throws IOException JavaDoc {
393             fileObj.rename(lock, name, ext);
394         }
395
396         protected void fireFileChangedEvent(Enumeration JavaDoc<FileChangeListener> en, FileEvent fe) {
397             fileObj.fireFileChangedEvent(en, fe);
398         }
399
400         public FileObject getFileObject(String JavaDoc name, String JavaDoc ext) {
401             return fileObj.getFileObject(name, ext);
402         }
403
404         public void refresh() {
405             fileObj.refresh();
406         }
407
408         public FileObject createData(String JavaDoc name, String JavaDoc ext)
409         throws IOException JavaDoc {
410             return fileObj.createData(name, ext);
411         }
412
413         public void addFileChangeListener(FileChangeListener fcl) {
414             fileObj.addFileChangeListener(fcl);
415         }
416
417         protected void fireFileDataCreatedEvent(Enumeration JavaDoc<FileChangeListener> en, FileEvent fe) {
418             fileObj.fireFileDataCreatedEvent(en, fe);
419         }
420
421         public boolean isFolder() {
422             return fileObj.isFolder();
423         }
424
425         public FileObject createFolder(String JavaDoc name) throws IOException JavaDoc {
426             return fileObj.createFolder(name);
427         }
428
429         public Enumeration JavaDoc<? extends FileObject> getChildren(boolean rec) {
430             return fileObj.getChildren(rec);
431         }
432
433         public void setAttribute(String JavaDoc attrName, Object JavaDoc value)
434         throws IOException JavaDoc {
435             fileObj.setAttribute(attrName, value);
436         }
437
438         @Deprecated JavaDoc // have to override for compat
439
public String JavaDoc getPackageName(char separatorChar) {
440             return fileObj.getPackageName(separatorChar);
441         }
442
443         public FileSystem getFileSystem() throws FileStateInvalidException {
444             return fileObj.getFileSystem();
445         }
446
447         public OutputStream JavaDoc getOutputStream(FileLock lock)
448         throws java.io.IOException JavaDoc {
449             return fileObj.getOutputStream(lock);
450         }
451
452         public boolean existsExt(String JavaDoc ext) {
453             return fileObj.existsExt(ext);
454         }
455
456         public FileObject move(FileLock lock, FileObject target, String JavaDoc name, String JavaDoc ext)
457         throws IOException JavaDoc {
458             return fileObj.move(lock, target, name, ext);
459         }
460
461         public FileLock lock() throws IOException JavaDoc {
462             return fileObj.lock();
463         }
464
465         public void fileFolderCreated(FileEvent fe) {
466         }
467
468         public void fileDataCreated(FileEvent fe) {
469         }
470
471         public void fileAttributeChanged(FileAttributeEvent fe) {
472         }
473
474         /** MIMEResolvers should not cache this FileObject. But they can cache
475          * resolved patterns in Map with this FileObject as key.*/

476         public int hashCode() {
477             return fileObj.hashCode();
478         }
479
480         public boolean equals(java.lang.Object JavaDoc obj) {
481             if (obj instanceof CachedFileObject) {
482                 return ((CachedFileObject) obj).fileObj.equals(fileObj);
483             }
484
485             return super.equals(obj);
486         }
487     }
488
489     private static class CachedInputStream extends InputStream JavaDoc {
490         private InputStream JavaDoc inputStream;
491         private byte[] buffer = null;
492         private int len = 0;
493         private int pos = 0;
494         private boolean eof = false;
495
496         CachedInputStream(InputStream JavaDoc is) {
497             inputStream = is;
498         }
499
500         /** This stream can be closed only from MIMESupport. That`s why
501          * internalClose was added*/

502         public void close() throws java.io.IOException JavaDoc {
503         }
504
505         void internalClose() {
506             try {
507                 inputStream.close();
508             } catch (IOException JavaDoc ioe) {
509             }
510         }
511
512         protected void finalize() {
513             internalClose();
514         }
515
516         public int read() throws IOException JavaDoc {
517             if (eof) {
518                 return -1;
519             }
520
521             int c;
522             int n;
523
524             if (pos < len) {
525                 c = buffer[pos++];
526                 c = (c < 0) ? (c + 256) : c;
527
528                 return c;
529             }
530
531             int buflen = (len > 0) ? (len * 2) : 256;
532             byte[] buf = new byte[buflen];
533
534             if (len > 0) {
535                 System.arraycopy(buffer, 0, buf, 0, len);
536             }
537
538             n = inputStream.read(buf, len, buflen - len);
539
540             if (n <= 0) {
541                 eof = true;
542
543                 return -1;
544             }
545
546             buffer = buf;
547             len += n;
548
549             c = buffer[pos++];
550             c = (c < 0) ? (c + 256) : c;
551
552             return c;
553         }
554
555         void cacheToStart() {
556             pos = 0;
557             eof = false;
558         }
559
560         /** for debug purposes. Returns buffered content. */
561         public String JavaDoc toString() {
562             String JavaDoc retVal = super.toString() + '[' + inputStream.toString() + ']' + '\n'; //NOI18N
563
retVal += new String JavaDoc(buffer);
564
565             return retVal;
566         }
567     }
568 }
569
Popular Tags