KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > loaders > DataShadow


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.loaders;
21
22
23 import java.awt.Image JavaDoc;
24 import java.awt.datatransfer.Transferable JavaDoc;
25 import java.beans.*;
26 import java.io.*;
27 import java.lang.ref.*;
28 import java.lang.reflect.InvocationTargetException JavaDoc;
29 import java.net.*;
30 import java.text.MessageFormat JavaDoc;
31 import java.util.*;
32 import java.util.logging.Level JavaDoc;
33 import java.util.logging.Logger JavaDoc;
34 import org.openide.filesystems.*;
35 import org.openide.nodes.*;
36 import org.openide.nodes.Node.*;
37 import org.openide.util.*;
38 import org.openide.util.datatransfer.ExTransferable;
39 import org.openide.xml.XMLUtil;
40
41 /** Default implementation of a shortcut to another data object.
42 * Since 1.13 it extends MultiDataObject.
43 * @author Jan Jancura, Jaroslav Tulach
44 */

45 public class DataShadow extends MultiDataObject implements DataObject.Container {
46     /** generated Serialized Version UID */
47     static final long serialVersionUID = 6305590675982925167L;
48
49     /** original data object */
50     private DataObject original;
51     /** Listener attached to original DataObject. */
52     private OrigL origL = null;
53     /** List of nodes created for the DataShadow. */
54     private LinkedList<ShadowNode> nodes = new LinkedList<ShadowNode> ();
55
56     /** Extension name. */
57     static final String JavaDoc SHADOW_EXTENSION = "shadow"; // NOI18N
58

59     /** Map of all <FileObject, Set<DataShadow>>. Where the file object
60      Is the original file */

61     private static Map<FileObject, Set<Reference<DataShadow>>> allDataShadows;
62     
63     private static Mutex MUTEX = new Mutex ();
64
65     /** Getter for the Set that contains all DataShadows. */
66     private static synchronized Map<FileObject, Set<Reference<DataShadow>>> getDataShadowsSet() {
67         if (allDataShadows == null) {
68             allDataShadows = new HashMap<FileObject, Set<Reference<DataShadow>>>();
69         }
70         return allDataShadows;
71     }
72     
73     private static synchronized void enqueueDataShadow(DataShadow ds) {
74         Map<FileObject, Set<Reference<DataShadow>>> m = getDataShadowsSet ();
75         
76         FileObject prim = ds.original.getPrimaryFile ();
77         Reference<DataShadow> ref = new DSWeakReference<DataShadow>(ds);
78         Set<Reference<DataShadow>> s = m.get (prim);
79         if (s == null) {
80             s = Collections.singleton (ref);
81             getDataShadowsSet ().put (prim, s);
82         } else {
83             if (! (s instanceof HashSet)) {
84                 s = new HashSet<Reference<DataShadow>> (s);
85                 getDataShadowsSet ().put (prim, s);
86             }
87             s.add (ref);
88         }
89     }
90
91     /** @return all active DataShadows or null */
92     private static synchronized List<DataShadow> getAllDataShadows() {
93         if (allDataShadows == null || allDataShadows.isEmpty()) {
94             return null;
95         }
96         
97         List<DataShadow> ret = new ArrayList<DataShadow>(allDataShadows.size());
98         Iterator<Set<Reference<DataShadow>>> it = allDataShadows.values ().iterator();
99         while (it.hasNext()) {
100             Set<Reference<DataShadow>> ref = it.next();
101             Iterator<Reference<DataShadow>> refs = ref.iterator ();
102             while (refs.hasNext ()) {
103                 Reference<DataShadow> r = refs.next ();
104                 DataShadow shadow = r.get ();
105                 if (shadow != null) {
106                     ret.add (shadow);
107                 }
108             }
109         }
110         
111         return ret;
112     }
113     
114     /** Checks whether a change of the given dataObject
115      * does not hurt validity of a DataShadow
116      */

117     static void checkValidity(EventObject ev) {
118         DataObject src = null;
119         if (ev instanceof OperationEvent) {
120             src = ((OperationEvent)ev).getObject();
121         }
122
123         Set<Reference<DataShadow>> shadows = null;
124         synchronized (DataShadow.class) {
125             if (allDataShadows == null || allDataShadows.isEmpty ()) return;
126             
127             if (src != null) {
128                 shadows = allDataShadows.get (src.getPrimaryFile ());
129                 if (shadows == null) {
130                     // we know the source of the event and there are no
131
// shadows with such original
132
return;
133                 }
134                 // to prevent modifications
135
shadows = new HashSet<Reference<DataShadow>> (shadows);
136             }
137         }
138         
139         DataObject changed = null;
140         OperationEvent.Copy c;
141         if (
142             ev instanceof OperationEvent.Rename
143             ||
144             ev instanceof OperationEvent.Move
145         ) {
146             changed = ((OperationEvent)ev).getObject();
147         }
148         
149         if (shadows != null) {
150             //
151
// optimized for speed, we have found the shadow(s) that
152
// belong to this FileObject
153
//
154
for (Reference<DataShadow> r: shadows) {
155                 DataShadow shadow = r.get ();
156                 if (shadow != null) {
157                     shadow.refresh (shadow.original == changed);
158                 }
159             }
160             return;
161         }
162         
163         List<DataShadow> all = getAllDataShadows();
164         if (all == null) {
165             return;
166         }
167         
168         
169         int size = all.size();
170         for (int i = 0; i < size; i++) {
171             DataShadow obj = all.get(i);
172             // if original was renamed or moved update
173
// the file with the link
174
obj.refresh (obj.original == changed);
175         }
176     }
177     
178     /** Constructs new data shadow for given primary file and referenced original.
179     * Method to allow subclasses of data shadow.
180     *
181     * @param fo the primary file
182     * @param original original data object
183     * @param loader the loader that created the object
184     */

185     protected DataShadow (
186         FileObject fo, DataObject original, MultiFileLoader loader
187     ) throws DataObjectExistsException {
188         super (fo, loader);
189         init(original);
190     }
191
192     /** Constructs new data shadow for given primary file and referenced original.
193     * Method to allow subclasses of data shadow.
194     *
195     * @param fo the primary file
196     * @param original original data object
197     * @param loader the loader that created the object
198     * @deprecated Since 1.13 do not use this constructor, it is for backward compatibility only
199     */

200     @Deprecated JavaDoc
201     protected DataShadow (
202         FileObject fo, DataObject original, DataLoader loader
203     ) throws DataObjectExistsException {
204         super (fo, loader);
205         init(original);
206     }
207     
208     /** Perform initialization after construction.
209     * @param original original data object
210     */

211     private void init(DataObject original) {
212         if (original == null)
213             throw new IllegalArgumentException JavaDoc();
214         setOriginal (original);
215         enqueueDataShadow(this);
216     }
217     
218     /** Constructs new data shadow for given primary file and referenced original.
219     * @param fo the primary file
220     * @param original original data object
221     */

222     private DataShadow (FileObject fo, DataObject original) throws DataObjectExistsException {
223         this (fo, original, (MultiFileLoader)DataLoaderPool.getShadowLoader ());
224     }
225
226     /** Method that creates new data shadow in a folder. The name chosen is based
227     * on the name of the original object.
228     *
229     * @param folder target folder to create data in
230     * @param original original object that should be represented by the shadow
231     */

232     public static DataShadow create (DataFolder folder, DataObject original)
233     throws IOException {
234         return create (folder, null, original, SHADOW_EXTENSION);
235     }
236
237     /** Method that creates new data shadow in a folder. The default extension
238     * is used.
239     *
240     * @param folder target folder to create data in
241     * @param name name to give to the shadow
242     * @param original object that should be represented by the shadow
243     */

244     public static DataShadow create (
245         DataFolder folder,
246         final String JavaDoc name,
247         final DataObject original
248     ) throws IOException {
249         return create (folder, name, original, SHADOW_EXTENSION);
250     }
251     
252     /** Method that creates new data shadow in a folder. All modifications are
253     * done atomically using {@link FileSystem#runAtomicAction}.
254     *
255     * @param folder target folder to create data in
256     * @param name name to give to the shadow
257     * @param original original object that should be represented by the shadow
258     */

259     public static DataShadow create (
260         DataFolder folder,
261         final String JavaDoc name,
262         final DataObject original,
263         final String JavaDoc ext
264     ) throws IOException {
265         final FileObject fo = folder.getPrimaryFile ();
266         final DataShadow[] arr = new DataShadow[1];
267
268         DataObjectPool.getPOOL().runAtomicAction(fo, new FileSystem.AtomicAction() {
269             public void run() throws IOException {
270                 FileObject file = writeOriginal(name, ext, fo, original);
271                 final DataObject obj = DataObject.find(file);
272                 if (obj instanceof DataShadow) {
273                     arr[0] = (DataShadow)obj;
274                 } else {
275                     // wrong instance => shadow was not found
276
throw new DataObjectNotFoundException(obj.getPrimaryFile()) {
277                         public String JavaDoc getMessage() {
278                             return super.getMessage() + ": " + obj.getClass().getName(); // NOI18N
279
}
280                     };
281                 }
282             }
283         });
284
285         return arr[0];
286     }
287     
288     /** Writes the original DataObject into file of given name and extension.
289      * Both parameters {@link name} and {@link ext} are ignored when the data file
290      * is passed in as a {@link trg} parameter, in that case name and link can be <code>null</code>.
291      * @param name name of the file to write original DataObject in
292      * @param ext extension of the file to write original DataObject in
293      * @param trg folder where FileObject of given name and ext will be created or
294      * file which content is replaced
295      * @param obj DataObject which link is stored into
296      * @return the file with link
297      * @exception IOException on I/O error
298      */

299     private static FileObject writeOriginal (
300         final String JavaDoc name, final String JavaDoc ext, final FileObject trg, final DataObject obj
301     ) throws IOException {
302         try {
303             return MUTEX.writeAccess (new Mutex.ExceptionAction<FileObject> () {
304                 public FileObject run () throws IOException {
305                     FileObject fo;
306                     if (trg.isData ()) {
307                         fo = trg;
308                     } else {
309                          String JavaDoc n;
310                          if (name == null) {
311                              // #45810 - if obj is disk root then fix the filename
312
String JavaDoc baseName = obj.getName().replace(':', '_').replace('/', '_'); // NOI18N
313
n = FileUtil.findFreeFileName (trg, baseName, ext);
314                          } else {
315                              n = name;
316                          }
317                          fo = trg.createData (n, ext);
318                     }
319                     writeShadowFile(fo, obj.getPrimaryFile().getURL());
320                     return fo;
321                 }
322             });
323         } catch (MutexException e) {
324             throw (IOException) e.getException ();
325         }
326     }
327     
328     /** Overwrites existing file object with new link and fs.
329      * @exception IOException on I/O error
330      */

331     static void writeOriginal (final FileObject shadow, final URL url) throws IOException {
332         try {
333             MUTEX.writeAccess (new Mutex.ExceptionAction<Void JavaDoc> () {
334                 public Void JavaDoc run () throws IOException {
335                     writeShadowFile(shadow, url);
336                     return null;
337                 }
338             });
339         } catch (MutexException e) {
340             throw (IOException) e.getException ();
341         }
342     }
343
344     /**
345      * Writes content of shadow file.
346      * @param fo shadow file
347      * @param url URL to original
348      */

349     private static void writeShadowFile(FileObject fo, URL url) throws IOException {
350         FileLock lock = fo.lock();
351         Writer os = new OutputStreamWriter(fo.getOutputStream(lock), "UTF-8");
352         try {
353             os.write(url.toExternalForm()); // NOI18N
354
} finally {
355             os.close();
356             lock.releaseLock();
357         }
358     }
359     
360     /**
361      * Tries to load the original file from a shadow.
362      * Looks for file contents as well as the originalFile/originalFileSystem attributes.
363      * @param fileObject a data shadow
364      * @return the original <code>DataObject</code> referenced by the shadow
365      * @throws IOException error during load or broken link
366      */

367     protected static DataObject deserialize(FileObject fileObject) throws IOException {
368         String JavaDoc[] fileAndFileSystem = readOriginalFileAndFileSystem(fileObject);
369         assert fileAndFileSystem[0] != null;
370         FileObject target;
371         URI u;
372         try {
373             u = new URI(fileAndFileSystem[0]);
374         } catch (URISyntaxException e) {
375             u = null;
376         }
377         if (u != null && u.isAbsolute()) {
378             target = URLMapper.findFileObject(u.toURL());
379         } else {
380             FileSystem fs;
381             if ("SystemFileSystem".equals(fileAndFileSystem[1])) { // NOI18N
382
fs = Repository.getDefault().getDefaultFileSystem();
383             } else {
384                 // Even if it is specified, we no longer have mounts, so we can no longer find it.
385
fs = fileObject.getFileSystem();
386             }
387             target = fs.findResource(fileAndFileSystem[0]);
388         }
389         if (target != null) {
390             return DataObject.find(target);
391         } else {
392             throw new FileNotFoundException(fileAndFileSystem[0] + ':' + fileAndFileSystem[1]);
393         }
394     }
395     static URL readURL(FileObject fileObject) throws IOException {
396         String JavaDoc[] fileAndFileSystem = readOriginalFileAndFileSystem(fileObject);
397         assert fileAndFileSystem[0] != null;
398         URI u;
399         try {
400             u = new URI(fileAndFileSystem[0]);
401         } catch (URISyntaxException e) {
402             u = null;
403         }
404         if (u != null && u.isAbsolute()) {
405             return u.toURL();
406         } else {
407             FileSystem fs;
408             if ("SystemFileSystem".equals(fileAndFileSystem[1])) { // NOI18N
409
fs = Repository.getDefault().getDefaultFileSystem();
410             } else {
411                 fs = fileObject.getFileSystem();
412             }
413             return new URL(fs.getRoot().getURL(), fileAndFileSystem[0]);
414         }
415     }
416     private static String JavaDoc[] readOriginalFileAndFileSystem(final FileObject f) throws IOException {
417         if ( f.getSize() == 0 ) {
418             Object JavaDoc fileName = f.getAttribute("originalFile"); // NOI18N
419
if ( fileName instanceof String JavaDoc ) {
420                 return new String JavaDoc[] {(String JavaDoc) fileName, (String JavaDoc) f.getAttribute("originalFileSystem")}; // NOI18N
421
} else if (fileName instanceof URL) {
422                 return new String JavaDoc[] {((URL) fileName).toExternalForm(), null};
423             } else {
424                 throw new FileNotFoundException(f.getPath());
425             }
426         } else {
427             try {
428                 return MUTEX.readAccess(new Mutex.ExceptionAction<String JavaDoc[]>() {
429                     public String JavaDoc[] run() throws IOException {
430                         BufferedReader ois = new BufferedReader(new InputStreamReader(f.getInputStream(), "UTF-8")); // NOI18N
431
try {
432                             String JavaDoc s = ois.readLine();
433                             String JavaDoc fs = ois.readLine();
434                             return new String JavaDoc[] {s, fs};
435                         } finally {
436                             ois.close();
437                         }
438                     }
439                 });
440             } catch (MutexException e) {
441                 throw (IOException) e.getException();
442             }
443         }
444     }
445     
446     private FileObject checkOriginal (DataObject orig) throws IOException {
447         if (orig == null)
448             return null;
449         return deserialize(getPrimaryFile()).getPrimaryFile();
450     }
451
452     /** Return the original shadowed object.
453     * @return the data object
454     */

455     public DataObject getOriginal () {
456         waitUpdatesProcessed();
457         return original;
458     }
459     
460     /** Implementation of Container interface.
461      * @return array of one element, the original
462      */

463     public DataObject[] getChildren () {
464         return new DataObject[] { original };
465     }
466
467     /* Creates node delegate.
468     */

469     protected Node createNodeDelegate () {
470         return new ShadowNode (this);
471     }
472
473     /* Getter for delete action.
474     * @return true if the object can be deleted
475     */

476     public boolean isDeleteAllowed () {
477         return getPrimaryFile().canWrite();
478     }
479
480     /* Getter for copy action.
481     * @return true if the object can be copied
482     */

483     public boolean isCopyAllowed () {
484         return true;
485     }
486
487     /* Getter for move action.
488     * @return true if the object can be moved
489     */

490     public boolean isMoveAllowed () {
491         return getPrimaryFile().canWrite();
492     }
493
494     /* Getter for rename action.
495     * @return true if the object can be renamed
496     */

497     public boolean isRenameAllowed () {
498         return getPrimaryFile().canWrite();
499     }
500
501     /* Help context for this object.
502     * @return help context
503     */

504     public HelpCtx getHelpCtx () {
505         return original.getHelpCtx ();
506     }
507
508     /* Creates shadow for this object in specified folder. The current
509     * implementation creates reference data shadow and pastes it into
510     * specified folder.
511     *
512     * @param f the folder to create shortcut in
513     * @return the shadow
514     */

515     protected DataShadow handleCreateShadow (DataFolder f) throws IOException {
516         if (original instanceof DataFolder) {
517             DataFolder.testNesting(((DataFolder)original), f);
518         }
519         return original.handleCreateShadow (f);
520     }
521
522     /* Scans the orginal bundle */
523     @Override JavaDoc
524     public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> c) {
525         if (c.isInstance(this)) {
526             return c.cast(this);
527         }
528         return original.getCookie (this, c);
529     }
530
531     /* Try to refresh link to original file */
532     public void refresh() {
533         refresh(false);
534     }
535     
536     private void refresh(boolean moved) {
537         try {
538             /* Link isn't broken */
539             if (moved)
540                 tryUpdate();
541             FileObject obj = checkOriginal(original);
542             if (obj != null) {
543                 if (obj != this.original.getPrimaryFile ()) {
544                     this.setOriginal (DataObject.find (obj));
545                 }
546                 return;
547             }
548         } catch (IOException e) {
549         }
550         try {
551             /* Link is broken */
552             this.setValid(false);
553         } catch (java.beans.PropertyVetoException JavaDoc e) {
554         }
555     }
556     
557     private void tryUpdate() throws IOException {
558         URL url = readURL(getPrimaryFile ());
559         if (url.equals(original.getPrimaryFile().getURL())) {
560             return;
561         }
562         writeOriginal (null, null, getPrimaryFile (), original);
563     }
564     
565     private void setOriginal (DataObject o) {
566         if (origL == null) {
567             origL = new OrigL (this);
568         }
569
570         // set new original
571
if (original != null) {
572             original.removePropertyChangeListener (origL);
573         }
574
575         DataObject oldOriginal = original;
576         
577         o.addPropertyChangeListener (origL);
578         original = o;
579
580         // update nodes
581
ShadowNode n [] = null;
582         synchronized (nodes) {
583             n = (ShadowNode [])nodes.toArray (new ShadowNode [nodes.size ()]);
584         }
585         
586         try {
587             for (int i = 0; i < n.length; i++) {
588                 n[i].originalChanged ();
589             }
590         }
591         catch (IllegalStateException JavaDoc e) {
592             System.out.println("Please reopen the bug #18998 if you see this message."); // NOI18N
593
System.out.println("Old:"+oldOriginal + // NOI18N
594
((oldOriginal == null) ? "" : (" / " + oldOriginal.isValid() + " / " + System.identityHashCode(oldOriginal)))); // NOI18N
595
System.out.println("New:"+original + // NOI18N
596
((original == null) ? "" : (" / " + original.isValid() + " / " + System.identityHashCode(original)))); // NOI18N
597
throw e;
598         }
599     }
600     
601     private static RequestProcessor RP = new RequestProcessor("DataShadow validity check");
602     private static Reference<Task> lastTask = new WeakReference<Task>(null);
603
604     private static void updateShadowOriginal(DataShadow shadow) {
605         class Updator implements Runnable JavaDoc {
606             DataShadow sh;
607             FileObject primary;
608             
609             public void run () {
610                 DataObject newOrig;
611
612                 try {
613                     newOrig = DataObject.find (primary);
614                 } catch (DataObjectNotFoundException e) {
615                     newOrig = null;
616                 }
617
618                 if (newOrig != null) {
619                     sh.setOriginal (newOrig);
620                 } else {
621                     checkValidity (new OperationEvent (sh.original));
622                 }
623                 
624                 primary = null;
625                 sh = null;
626             }
627         }
628         Updator u = new Updator();
629         u.sh = shadow;
630         u.primary = u.sh.original.getPrimaryFile ();
631         ERR.fine("updateShadowOriginal: " + u.sh + " primary " + u.primary); // NOI18N
632
lastTask = new WeakReference<Task>(RP.post(u, 100, Thread.MIN_PRIORITY + 1));
633     }
634     
635     /** For use in tests that need to be sure that all updators have finished.
636      */

637     static final void waitUpdatesProcessed() {
638         if (!RP.isRequestProcessorThread()) {
639             Task t = lastTask.get();
640             if (t != null) {
641                 t.waitFinished();
642             }
643         }
644     }
645     
646     protected DataObject handleCopy (DataFolder f) throws IOException {
647         if (original instanceof DataFolder) {
648             DataFolder.testNesting(((DataFolder)original), f);
649         }
650         return super.handleCopy(f);
651     }
652     
653     protected FileObject handleMove (DataFolder f) throws IOException {
654         if (original instanceof DataFolder) {
655             DataFolder.testNesting(((DataFolder)original), f);
656         }
657         return super.handleMove(f);
658     }
659
660     private static class OrigL implements PropertyChangeListener {
661         Reference<DataShadow> shadow = null;
662         public OrigL (DataShadow shadow) {
663             this.shadow = new WeakReference<DataShadow> (shadow);
664         }
665         public void propertyChange (PropertyChangeEvent evt) {
666             final DataShadow shadow = this.shadow.get ();
667
668             if (shadow != null && DataObject.PROP_VALID.equals (evt.getPropertyName ())) {
669                 updateShadowOriginal(shadow);
670             }
671         }
672     }
673
674     /** Node for a shadow object. */
675     protected static class ShadowNode extends FilterNode {
676         /** message to create name of node */
677         private static MessageFormat JavaDoc format;
678         /** message to create short description of node */
679         private static MessageFormat JavaDoc descriptionFormat;
680         /** if true, the DataShadow name is used instead of original's name,
681          * affects DataShadows of filesystem roots only
682          */

683         private static final String JavaDoc ATTR_USEOWNNAME = "UseOwnName"; //NOI18N
684

685         /** shadow */
686         private DataShadow obj;
687
688         /** the sheet computed for this node or null */
689         private Sheet sheet;
690         
691         /** Create a shadowing node.
692          * @param shadow the shadow
693          */

694         public ShadowNode (DataShadow shadow) {
695             this (shadow, shadow.original.getNodeDelegate ());
696         }
697
698         /** Initializes it */
699         private ShadowNode (DataShadow shadow, Node node) {
700             super (node);
701             this.obj = shadow;
702             synchronized (this.obj.nodes) {
703                 this.obj.nodes.add (this);
704             }
705         }
706
707         /* Clones the node
708         */

709         public Node cloneNode () {
710             ShadowNode sn = new ShadowNode (obj);
711             return sn;
712         }
713
714         /* Renames the shadow data object.
715         * @param name new name for the object
716         * @exception IllegalArgumentException if the rename failed
717         */

718         public void setName (String JavaDoc name) {
719             try {
720                 if (!name.equals (obj.getName ())) {
721                     obj.rename (name);
722                     if (obj.original.getPrimaryFile ().isRoot ()) {
723                         obj.getPrimaryFile ().setAttribute (ATTR_USEOWNNAME, Boolean.TRUE);
724                     }
725                     fireDisplayNameChange (null, null);
726                     fireNameChange (null, null);
727                 }
728             } catch (IOException ex) {
729                 throw new IllegalArgumentException JavaDoc (ex.getMessage ());
730             }
731         }
732
733         /** The name of the shadow.
734         * @return the name
735         */

736         public String JavaDoc getName () {
737             return obj.getName ();
738         }
739
740         /* Creates name based on the original one.
741         */

742         public String JavaDoc getDisplayName () {
743             if (format == null) {
744                 format = new MessageFormat JavaDoc (NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowName"));
745             }
746             String JavaDoc n = format.format (createArguments ());
747             try {
748                 return obj.getPrimaryFile().getFileSystem().getStatus().annotateName(n, obj.files());
749             } catch (FileStateInvalidException fsie) {
750                 // ignore
751
return n;
752             }
753         }
754         public String JavaDoc getHtmlDisplayName() {
755             if (format == null) {
756                 format = new MessageFormat JavaDoc(NbBundle.getBundle(DataShadow.class).getString("FMT_shadowName"));
757             }
758             try {
759                 String JavaDoc n = XMLUtil.toElementContent(format.format(createArguments()));
760                 FileSystem.Status s = obj.getPrimaryFile().getFileSystem().getStatus();
761                 if (s instanceof FileSystem.HtmlStatus) {
762                     return ((FileSystem.HtmlStatus) s).annotateNameHtml(n, obj.files());
763                 }
764             } catch (IOException e) {
765                 // ignore, OK
766
}
767             return null;
768         }
769
770         /** Creates arguments for given shadow node */
771         private Object JavaDoc[] createArguments () {
772             String JavaDoc origDisp;
773             String JavaDoc shadowName = obj.getName ();
774             if (obj.original.isValid()) {
775                 origDisp = obj.original.getNodeDelegate().getDisplayName();
776             } else {
777                 // We will soon be a broken data shadow, in the meantime...
778
origDisp = ""; // NOI18N
779
}
780             Boolean JavaDoc useOwnName = (Boolean JavaDoc)obj.getPrimaryFile ().getAttribute (ATTR_USEOWNNAME);
781             if (obj.original.getPrimaryFile ().isRoot () &&
782                 (useOwnName == null || !useOwnName.booleanValue ())) {
783                 try {
784                     shadowName = obj.original.getPrimaryFile ().getFileSystem ().getDisplayName ();
785                 } catch (FileStateInvalidException e) {
786                     // ignore
787
}
788             }
789             return new Object JavaDoc[] {
790                        shadowName, // name of the shadow
791
super.getDisplayName (), // name of original
792
systemNameOrFileName (obj.getPrimaryFile ()), // full name of file for shadow
793
systemNameOrFileName (obj.original.getPrimaryFile ()), // full name of original file
794
origDisp, // display name of original
795
};
796         }
797
798         /** System name of file name
799         */

800         private static String JavaDoc systemNameOrFileName (FileObject fo) {
801             return FileUtil.getFileDisplayName(fo);
802         }
803
804         /* Creates description based on the original one.
805         */

806         public String JavaDoc getShortDescription () {
807             if (descriptionFormat == null) {
808                 descriptionFormat = new MessageFormat JavaDoc (
809                                         NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowHint")
810                                     );
811             }
812             return descriptionFormat.format (createArguments ());
813         }
814         
815         /* Show filesystem icon if it is a root.
816          */

817         public Image JavaDoc getIcon(int type) {
818             Image JavaDoc i = rootIcon(type);
819             if (i != null) {
820                 return i;
821             } else {
822                 return super.getIcon(type);
823             }
824         }
825         public Image JavaDoc getOpenedIcon(int type) {
826             Image JavaDoc i = rootIcon(type);
827             if (i != null) {
828                 return i;
829             } else {
830                 return super.getOpenedIcon(type);
831             }
832         }
833         private Image JavaDoc rootIcon(int type) {
834             FileObject orig = obj.original.getPrimaryFile();
835             if (orig.isRoot()) {
836                 try {
837                     FileSystem fs = orig.getFileSystem();
838                     try {
839                         Image JavaDoc i = Introspector.getBeanInfo(fs.getClass()).getIcon(type);
840                         return fs.getStatus().annotateIcon(i, type, obj.original.files());
841                     } catch (IntrospectionException ie) {
842                         Logger.getLogger(DataShadow.class.getName()).log(Level.WARNING, null, ie);
843                         // ignore
844
}
845                 } catch (FileStateInvalidException fsie) {
846                     // ignore
847
}
848             }
849             return null;
850         }
851
852         /* @return obj.isDeleteAllowed () */
853         public boolean canDestroy () {
854             return obj.isDeleteAllowed ();
855         }
856
857         /* Destroyes the node
858         */

859         public void destroy () throws IOException {
860             synchronized (obj.nodes) {
861                 obj.nodes.remove (this);
862             }
863             obj.delete ();
864             // super.destroy ();
865
}
866
867         /** @return true if shadow can be renamed
868         */

869         public final boolean canRename () {
870             return obj.isRenameAllowed ();
871         }
872
873         /* Returns true if this object allows copying.
874         * @returns true if so
875         */

876         public final boolean canCopy () {
877             return obj.isCopyAllowed ();
878         }
879
880         /* Returns true if this object allows cutting.
881         * @returns true if so
882         */

883         public final boolean canCut () {
884             return obj.isMoveAllowed ();
885         }
886
887         /* First of all the DataObject.getCookie method is
888         * called. If it produces non-null result, it is returned.
889         * Otherwise the value returned from super.getCookie
890         * method is returned.
891         *
892         * @return the cookie or null
893         */

894         @Override JavaDoc
895         public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> cl) {
896             T c = obj.getCookie(cl);
897             if (c != null) {
898                 return c;
899             } else {
900                 return super.getCookie (cl);
901             }
902         }
903
904         /** Returns modified properties of the original node.
905         * @return property sets
906         */

907         public PropertySet[] getPropertySets () {
908             Sheet s = sheet;
909             if (s == null) {
910                 s = sheet = cloneSheet ();
911             }
912             return s.toArray ();
913         }
914
915         /** Copy this node to the clipboard.
916         *
917         * @return {@link org.openide.util.datatransfer.ExTransferable.Single} with one copy flavor
918         * @throws IOException if it could not copy
919         * @see NodeTransfer
920         */

921         public Transferable JavaDoc clipboardCopy () throws IOException {
922             ExTransferable t = ExTransferable.create (super.clipboardCopy ());
923             t.put (LoaderTransfer.transferable (
924                 obj,
925                 LoaderTransfer.CLIPBOARD_COPY)
926             );
927             return t;
928         }
929
930         /** Cut this node to the clipboard.
931         *
932         * @return {@link org.openide.util.datatransfer.ExTransferable.Single} with one cut flavor
933         * @throws IOException if it could not cut
934         * @see NodeTransfer
935         */

936         public Transferable JavaDoc clipboardCut () throws IOException {
937             ExTransferable t = ExTransferable.create (super.clipboardCut ());
938             t.put (LoaderTransfer.transferable (
939                 obj,
940                 LoaderTransfer.CLIPBOARD_CUT)
941             );
942             return t;
943         }
944         /**
945         * This implementation only calls clipboardCopy supposing that
946         * copy to clipboard and copy by d'n'd are similar.
947         *
948         * @return transferable to represent this node during a drag
949         * @exception IOException when the
950         * cut cannot be performed
951         */

952         public Transferable JavaDoc drag () throws IOException {
953             return clipboardCopy ();
954         }
955
956         /** Creates a node listener that allows listening on the
957         * original node and propagating events to the proxy.
958         * <p>Intended for overriding by subclasses, as with {@link #createPropertyChangeListener}.
959         *
960         * @return a {@link org.openide.nodes.FilterNode.NodeAdapter} in the default implementation
961         */

962         protected org.openide.nodes.NodeListener createNodeListener () {
963             return new PropL (this);
964         }
965
966         /** Equal if the o is ShadowNode to the same shadow object.
967         */

968         public boolean equals (Object JavaDoc o) {
969             if (o instanceof ShadowNode) {
970                 ShadowNode sn = (ShadowNode)o;
971                 return sn.obj == obj;
972             }
973             return false;
974         }
975
976         /** Hashcode is computed by the represented shadow.
977         */

978         public int hashCode () {
979             return obj.hashCode ();
980         }
981
982
983         /** Clones the property sheet of original node.
984         */

985         private Sheet cloneSheet () {
986             PropertySet[] sets = this.getOriginal().getPropertySets ();
987
988             Sheet s = new Sheet ();
989             for (int i = 0; i < sets.length; i++) {
990                 Sheet.Set ss = new Sheet.Set ();
991                 ss.put (sets[i].getProperties ());
992                 ss.setName (sets[i].getName ());
993                 ss.setDisplayName (sets[i].getDisplayName ());
994                 ss.setShortDescription (sets[i].getShortDescription ());
995
996                 // modifies the set if it contains name of object property
997
modifySheetSet (ss);
998
999                 s.put (ss);
1000            }
1001
1002            return s;
1003        }
1004
1005        /** Modifies the sheet set to contain name of property and name of
1006        * original object.
1007        */

1008        private void modifySheetSet (Sheet.Set ss) {
1009            Property p = ss.remove (DataObject.PROP_NAME);
1010            if (p != null) {
1011                p = new PropertySupport.Name (this);
1012                ss.put (p);
1013
1014                p = new Name ();
1015                ss.put (p);
1016            }
1017        }
1018
1019        private void originalChanged () {
1020            DataObject ori = obj.original;
1021            if (ori.isValid()) {
1022                changeOriginal (ori.getNodeDelegate(), true);
1023            } else {
1024                updateShadowOriginal(obj);
1025            }
1026        }
1027
1028        /** Class that renames the original object and also updates
1029        * the link
1030        */

1031        private final class Name extends PropertySupport.ReadWrite<String JavaDoc> {
1032            public Name () {
1033                super (
1034                    "OriginalName", // NOI18N
1035
String JavaDoc.class,
1036                    DataObject.getString ("PROP_ShadowOriginalName"),
1037                    DataObject.getString ("HINT_ShadowOriginalName")
1038                );
1039            }
1040
1041            public String JavaDoc getValue () {
1042                return obj.original.getName();
1043            }
1044
1045            public void setValue (String JavaDoc val) throws IllegalAccessException JavaDoc,
1046                IllegalArgumentException JavaDoc, InvocationTargetException JavaDoc {
1047                if (!canWrite())
1048                    throw new IllegalAccessException JavaDoc();
1049
1050                try {
1051                    DataObject orig = obj.original;
1052                    orig.rename (val);
1053                    writeOriginal (null, null, obj.getPrimaryFile (), orig);
1054                } catch (IOException ex) {
1055                    throw new InvocationTargetException JavaDoc (ex);
1056                }
1057            }
1058
1059            public boolean canWrite () {
1060                return obj.original.isRenameAllowed();
1061            }
1062        }
1063        
1064        /** Property listener on data object that delegates all changes of
1065        * properties to this node.
1066        */

1067        private static class PropL extends FilterNode.NodeAdapter {
1068            public PropL (ShadowNode sn) {
1069                super (sn);
1070            }
1071
1072            protected void propertyChange (FilterNode fn, PropertyChangeEvent ev) {
1073              if (Node.PROP_PROPERTY_SETS.equals(ev.getPropertyName ())) {
1074                // clear the sheet
1075
ShadowNode sn = (ShadowNode)fn;
1076                sn.sheet = null;
1077              }
1078              
1079              super.propertyChange (fn, ev);
1080              }
1081        }
1082    }
1083    
1084    static final class DSWeakReference<T> extends WeakReference<T>
1085    implements Runnable JavaDoc {
1086        private int hash;
1087        private FileObject original;
1088        
1089        DSWeakReference(T o) {
1090            super(o, org.openide.util.Utilities.activeReferenceQueue());
1091            this.hash = o.hashCode();
1092            if (o instanceof DataShadow) {
1093                DataShadow s = (DataShadow)o;
1094                this.original = s.original.getPrimaryFile ();
1095            }
1096        }
1097        
1098        public int hashCode() {
1099            return hash;
1100        }
1101        
1102        public boolean equals(Object JavaDoc o) {
1103            T mine = get();
1104            if (mine == null) {
1105                return false;
1106            }
1107            
1108            if (o instanceof DSWeakReference) {
1109                DSWeakReference him = (DSWeakReference) o;
1110                return mine.equals(him.get());
1111            }
1112            
1113            return false;
1114        }
1115
1116        public void run() {
1117            if (original != null) {
1118                synchronized (getDataShadowsSet ()) {
1119                    getDataShadowsSet().remove(original);
1120                }
1121            } else {
1122                synchronized (BrokenDataShadow.getDataShadowsSet()) {
1123                    BrokenDataShadow.getDataShadowsSet().remove(this); // XXX this is wrong - key is String (URL of broken shadow)
1124
}
1125            }
1126        }
1127    }
1128}
1129
Popular Tags