Index: src/org/netbeans/modules/masterfs/MasterFileSystem.java =================================================================== RCS file: /cvs/openide/masterfs/src/org/netbeans/modules/masterfs/MasterFileSystem.java,v retrieving revision 1.17 diff -u -r1.17 MasterFileSystem.java --- src/org/netbeans/modules/masterfs/MasterFileSystem.java 7 Oct 2004 12:50:41 -0000 1.17 +++ src/org/netbeans/modules/masterfs/MasterFileSystem.java 2 Feb 2005 17:47:47 -0000 @@ -14,6 +14,7 @@ package org.netbeans.modules.masterfs; +import org.netbeans.modules.masterfs.providers.AnnotationProvider; import org.openide.filesystems.*; import org.openide.util.NbBundle; import org.openide.util.actions.SystemAction; @@ -213,6 +214,11 @@ } public SystemAction[] getActions(java.util.Set foSet) { + SystemAction[] some = status.getActions (foSet); + if (some != null) { + return some; + } + SyncSection.getDefault().enterSection(); try { //check if all fileobjects come from the same filesystem @@ -312,7 +318,81 @@ } - private static final class StatusImpl implements FileSystem.HtmlStatus { + private static final class StatusImpl implements FileSystem.HtmlStatus, + org.openide.util.LookupListener, org.openide.filesystems.FileStatusListener { + /** result with providers */ + private org.openide.util.Lookup.Result annotationProviders; + private Collection previousProviders; + { + annotationProviders = org.openide.util.Lookup.getDefault ().lookup ( + new org.openide.util.Lookup.Template (AnnotationProvider.class) + ); + annotationProviders.addLookupListener (this); + resultChanged (null); + } + + public void resultChanged (org.openide.util.LookupEvent ev) { + java.util.Collection now = annotationProviders.allInstances (); + java.util.Collection add; + + if (previousProviders != null) { + add = new HashSet (now); + add.removeAll (previousProviders); + + previousProviders.removeAll (now); + java.util.Iterator it = previousProviders.iterator (); + while (it.hasNext ()) { + AnnotationProvider ap = (AnnotationProvider)it.next (); + ap.removeFileStatusListener (this); + } + + } else { + add = now; + } + + + + java.util.Iterator it = add.iterator (); + while (it.hasNext ()) { + AnnotationProvider ap = (AnnotationProvider)it.next (); + try { + ap.addFileStatusListener (this); + } catch (java.util.TooManyListenersException ex) { + org.openide.ErrorManager.getDefault ().notify (ex); + } + } + + previousProviders = now; + } + + public SystemAction[] getActions(java.util.Set foSet) { + + javax.swing.Action[] retVal = null; + java.util.Iterator it = annotationProviders.allInstances ().iterator (); + while (retVal == null && it.hasNext ()) { + AnnotationProvider ap = (AnnotationProvider)it.next (); + retVal = ap.actions (foSet); + } + if (retVal != null) { + // right now we handle just SystemAction, it can be changed if necessary + SystemAction[] ret = new SystemAction[retVal.length]; + for (int i = 0; i < retVal.length; i++) { + if (retVal[i] instanceof SystemAction) { + ret[i] = (SystemAction)retVal[i]; + } + } + return ret; + } + return null; + } + + public void annotationChanged (org.openide.filesystems.FileStatusEvent ev) { + if (ev.getSource () != MasterFileSystem.getDefault ()) { + throw new IllegalStateException ("The source must be master fs and not : " + ev.getSource ()); // NOI18N + } + MasterFileSystem.getDefault ().fireFileStatusChanged (ev); + } + private FileSystem getDelegateFileSystem (Set files) { FileSystem retVal = null; Iterator it = files.iterator(); @@ -325,7 +405,19 @@ } public Image annotateIcon(Image icon, int iconType, Set files) { - Image retVal = icon; + Image retVal = null; + + Iterator it = annotationProviders.allInstances ().iterator (); + while (retVal == null && it.hasNext ()) { + AnnotationProvider ap = (AnnotationProvider)it.next (); + retVal = ap.annotateIcon (icon, iconType, files); + } + if (retVal != null) { + return retVal; + } + + + retVal = icon; FileSystem fs = getDelegateFileSystem(files); if (fs != null) { Set transformedSet = new LazySet (files); @@ -336,7 +428,17 @@ } public String annotateName(String name, Set files) { - String retVal = name; + String retVal = null; + Iterator it = annotationProviders.allInstances ().iterator (); + while (retVal == null && it.hasNext ()) { + AnnotationProvider ap = (AnnotationProvider)it.next (); + retVal = ap.annotateName (name, files); + } + if (retVal != null) { + return retVal; + } + retVal = name; + Set transformedSet = new LazySet (files); FileSystem fs = getDelegateFileSystem(files); if (fs != null) { @@ -346,7 +448,17 @@ } public String annotateNameHtml(String name, Set files) { - String retVal = name; + String retVal = null; + Iterator it = annotationProviders.allInstances ().iterator (); + while (retVal == null && it.hasNext ()) { + AnnotationProvider ap = (AnnotationProvider)it.next (); + retVal = ap.annotateNameHtml (name, files); + } + if (retVal != null) { + return retVal; + } + retVal = name; + FileSystem fs = getDelegateFileSystem(files); if (fs != null) { if (fs != null && fs.getStatus() instanceof FileSystem.HtmlStatus) { Index: src/org/netbeans/modules/masterfs/providers/AnnotationProvider.java =================================================================== RCS file: src/org/netbeans/modules/masterfs/providers/AnnotationProvider.java diff -N src/org/netbeans/modules/masterfs/providers/AnnotationProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/netbeans/modules/masterfs/providers/AnnotationProvider.java 2 Feb 2005 17:47:47 -0000 @@ -0,0 +1,121 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2005 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.masterfs.providers; + +import java.io.IOException; + +/** Can provide status and actions for FileObjects. Register it + * in META-INF/services/org.netbeans.modules.masterfs.providers.AnnotationProvider + * file. + * + * @author Jaroslav Tulach + */ +public abstract class AnnotationProvider extends Object { + /** listeners */ + private org.openide.filesystems.FileStatusListener listener; + /** lock for modification of listeners */ + private static Object LOCK = new Object (); + + + /** Annotate the name of a file cluster. + * @param name the name suggested by default + * @param files an immutable set of {@link FileObject}s belonging to this filesystem + * @return the annotated name or null if this provider does not know how to annotate these files + */ + public abstract String annotateName (String name, java.util.Set files); + + /** Annotate the icon of a file cluster. + *

Please do not modify the original; create a derivative icon image, + * using a weak-reference cache if necessary. + * @param icon the icon suggested by default + * @param iconType an icon type from {@link java.beans.BeanInfo} + * @param files an immutable set of {@link FileObject}s belonging to this filesystem + * @return the annotated icon or null if some other provider shall anotate the icon + */ + public abstract java.awt.Image annotateIcon (java.awt.Image icon, int iconType, java.util.Set files); + + /** Annotate a name such that the returned value contains HTML markup. + * The return value less the html content should typically be the same + * as the return value from annotateName(). This is used, + * for example, by VCS filesystems to deëphasize the status information + * included in the file name by using a light grey font color. + *

+ * For consistency with Node.getHtmlDisplayName(), + * filesystems that proxy other filesystems (and so must implement + * this interface to supply HTML annotations) should return null if + * the filesystem they proxy does not provide an implementation of + * HTMLStatus. + * + * @see org.openide.awt.HtmlRenderer + * @see DataNode.getHtmlDisplayName() + * @see org.openide.nodes.Node#getHtmlDisplayName + **/ + public abstract String annotateNameHtml (String name, java.util.Set files); + + /** Provides actions that should be added to given set of files. + * @return null or array of actions for these files. + */ + public abstract javax.swing.Action[] actions (java.util.Set files); + + // + // Listener support + // + + + /** Registers FileStatusListener to receive events. + * The implementation registers the listener only when getStatus () is + * overriden to return a special value. + * + * @param listener The listener to register. + */ + public final void addFileStatusListener ( + org.openide.filesystems.FileStatusListener listener + ) throws java.util.TooManyListenersException { + synchronized (LOCK) { + if (this.listener != null) { + throw new java.util.TooManyListenersException (); + } + this.listener = listener; + } + } + + /** Removes FileStatusListener from the list of listeners. + *@param listener The listener to remove. + */ + public final void removeFileStatusListener ( + org.openide.filesystems.FileStatusListener listener + ) { + synchronized (LOCK) { + if (this.listener == listener) { + this.listener = null; + } + } + } + + /** Notifies all registered listeners about change of status of some files. + * + * @param event The event to be fired + */ + protected final void fireFileStatusChanged(org.openide.filesystems.FileStatusEvent event) { + org.openide.filesystems.FileStatusListener l; + synchronized (LOCK) { + l = this.listener; + } + if (l != null) { + l.annotationChanged (event); + } + } +} + + Index: test/unit/src/org/netbeans/modules/masterfs/providers/AnnotationProviderTest.java =================================================================== RCS file: test/unit/src/org/netbeans/modules/masterfs/providers/AnnotationProviderTest.java diff -N test/unit/src/org/netbeans/modules/masterfs/providers/AnnotationProviderTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/netbeans/modules/masterfs/providers/AnnotationProviderTest.java 2 Feb 2005 17:47:47 -0000 @@ -0,0 +1,288 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2005 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.masterfs.providers; + +import junit.framework.*; +import java.io.IOException; + +/** Check the behaviour of masterfs's annation provider. + * + * @author Jaroslav Tulach + */ +public class AnnotationProviderTest extends org.netbeans.junit.NbTestCase { + private AnnoProv a1, a2, a3, a4; + + private org.openide.filesystems.FileSystem fs; + private org.openide.filesystems.FileObject fo; + + public AnnotationProviderTest (String testName) { + super (testName); + } + + static { + System.setProperty ("org.openide.util.Lookup", "org.netbeans.modules.masterfs.providers.AnnotationProviderTest$Lkp"); // NOI18N + } + + // TODO add test methods here. The name must begin with 'test'. For example: + // public void testHello() {} + + protected void setUp () throws java.lang.Exception { + assertEquals (Lkp.class, org.openide.util.Lookup.getDefault ().getClass ()); + + // we need some masterfs fileobject + fs = org.netbeans.modules.masterfs.MasterFileSystem.settingsFactory (null); + fo = fs.getRoot (); + assertEquals (org.netbeans.modules.masterfs.MasterFileSystem.class, fo.getFileSystem ().getClass ()); + + + a1 = new AnnoProv (); + a2 = new AnnoProv (); + a3 = new AnnoProv (); + a4 = new AnnoProv (); + + Lkp.ic.add (a1); + Lkp.ic.add (a2); + Lkp.ic.add (a3); + Lkp.ic.add (a4); + } + + protected void tearDown () throws java.lang.Exception { + Lkp.ic.remove (a1); + Lkp.ic.remove (a2); + Lkp.ic.remove (a3); + Lkp.ic.remove (a4); + } + + public static junit.framework.Test suite () { + junit.framework.TestSuite suite = new junit.framework.TestSuite(AnnotationProviderTest.class); + + return suite; + } + + public void testAnnotateName () { + + a2.returnName = "MyName"; + + String r = fs.getStatus ().annotateName ("Kuk", java.util.Collections.singleton (fo)); + + assertEquals ("My name returned", a2.returnName, r); + assertEquals ("Kuk", a1.queriedName); + assertEquals ("Kuk", a2.queriedName); + assertNull ("Not queried at all", a3.queriedName); + assertNull ("Not queried at all", a4.queriedName); + + assertTrue ("fo is the file", a1.filesName.contains (fo)); + assertTrue ("fo is the file", a2.filesName.contains (fo)); + + assertNull (a1.filesActions); + assertNull (a1.filesHtml); + assertNull (a1.filesIcon); + + assertNull (a2.filesActions); + assertNull (a2.filesHtml); + assertNull (a2.filesIcon); + } + + public void testAnnotateIcon () { + + int t = java.awt.image.BufferedImage.TYPE_BYTE_GRAY; + java.awt.image.BufferedImage my = new java.awt.image.BufferedImage (10, 10, t); + java.awt.image.BufferedImage his = new java.awt.image.BufferedImage (20, 20, t); + a2.returnIcon = his; + + java.awt.Image r = fs.getStatus ().annotateIcon (my, 0, java.util.Collections.singleton (fo)); + + assertEquals ("My name returned", his, r); + assertSame (my, a1.queriedIcon); + assertSame (my, a2.queriedIcon); + assertNull ("Not queried at all", a3.queriedIcon); + assertNull ("Not queried at all", a4.queriedIcon); + + + assertNull (a1.filesActions); + assertNull (a1.filesHtml); + assertNull (a1.filesName); + + assertNull (a2.filesActions); + assertNull (a2.filesHtml); + assertNull (a2.filesName); + } + + public void testAnnotateNameHtml () { + + a2.returnHtml = "MyName"; + + Object o = fs.getStatus (); + assertTrue ("Must be HtmlStatus", o instanceof org.openide.filesystems.FileSystem.HtmlStatus); + org.openide.filesystems.FileSystem.HtmlStatus status = (org.openide.filesystems.FileSystem.HtmlStatus)o; + + + + String r = status.annotateNameHtml ("Kuk", java.util.Collections.singleton (fo)); + + assertEquals ("My name returned", a2.returnHtml, r); + assertEquals ("Kuk", a1.queriedHtml); + assertEquals ("Kuk", a2.queriedHtml); + assertNull ("Not queried at all", a3.queriedHtml); + assertNull ("Not queried at all", a4.queriedHtml); + + assertTrue ("fo is the file", a1.filesHtml.contains (fo)); + assertTrue ("fo is the file", a2.filesHtml.contains (fo)); + + assertNull (a1.filesActions); + assertNull (a1.filesName); + assertNull (a1.filesIcon); + + assertNull (a2.filesActions); + assertNull (a2.filesName); + assertNull (a2.filesIcon); + } + + public void testActionsWorksOnSystemActionsCorrectly () { + + a2.returnActions = new javax.swing.Action[] { + org.openide.actions.OpenAction.get (org.openide.actions.OpenAction.class) + }; + + java.util.Set s = java.util.Collections.singleton (fo); + javax.swing.Action[] actions = fs.getActions (s); + + assertEquals ("Actions has the same size", a2.returnActions.length, actions.length); + assertEquals ("And that is one", 1, actions.length); + assertEquals ("First elem is same", a2.returnActions[0], actions[0]); + assertEquals ("a1 queried", s, a1.filesActions); + assertEquals ("a2 queried", s, a2.filesActions); + assertNull ("Not queried at all", a3.filesActions); + assertNull ("Not queried at all", a4.filesActions); + + assertNull (a1.filesHtml); + assertNull (a1.filesName); + assertNull (a1.filesIcon); + + assertNull (a2.filesHtml); + assertNull (a2.filesName); + assertNull (a2.filesIcon); + } + + public void testListeningCapabilities () { + class L implements org.openide.filesystems.FileStatusListener { + public org.openide.filesystems.FileStatusEvent ev; + + public void annotationChanged (org.openide.filesystems.FileStatusEvent ev) { + assertNull (this.ev); + this.ev = ev; + } + } + L l = new L (); + + try { + fs.addFileStatusListener (l); + + a2.returnName = "xyz"; + String r = fs.getStatus().annotateName ("my name", java.util.Collections.singleton (fo)); + assertEquals (r, a2.returnName); + + org.openide.filesystems.FileStatusEvent ev; + ev = new org.openide.filesystems.FileStatusEvent (fs, false, true); + a3.fireFileStatusChanged (ev); + + assertEquals ("Our listener was called", ev, l.ev); + l.ev = null; + Lkp.ic.remove (a3); + + a3.fireFileStatusChanged (ev); + assertNull ("Now our listener was not called", l.ev); + + } finally { + fs.removeFileStatusListener (l); + } + } + + private class AnnotationProviderImpl extends AnnotationProvider { + + public java.lang.String annotateName (java.lang.String name, java.util.Set files) { + return null; + } + + public java.awt.Image annotateIcon (java.awt.Image icon, int iconType, java.util.Set files) { + return null; + } + + public java.lang.String annotateNameHtml (java.lang.String name, java.util.Set files) { + return null; + } + + public javax.swing.Action[] actions (java.util.Set files) { + return null; + } + } + + + public static final class Lkp extends org.openide.util.lookup.AbstractLookup { + static org.openide.util.lookup.InstanceContent ic; + + public Lkp () { + this (new org.openide.util.lookup.InstanceContent ()); + } + + private Lkp (org.openide.util.lookup.InstanceContent ic) { + super (ic); + this.ic = ic; + } + } // end of Lkp + + static final class AnnoProv extends AnnotationProvider { + public String returnHtml; + public String returnName; + public java.awt.Image returnIcon; + public javax.swing.Action[] returnActions; + + public String queriedHtml; + public String queriedName; + public java.awt.Image queriedIcon; + + public java.util.Set filesHtml; + public java.util.Set filesName; + public java.util.Set filesIcon; + public java.util.Set filesActions; + + public String annotateNameHtml (String name, java.util.Set files) { + assertNull (queriedHtml); + queriedHtml = name; + filesHtml = files; + return returnHtml; + } + + public String annotateName (String name, java.util.Set files) { + assertNull (queriedName); + queriedName = name; + filesName = files; + return returnName; + } + + public java.awt.Image annotateIcon (java.awt.Image icon, int iconType, java.util.Set files) { + assertNull (queriedIcon); + queriedIcon = icon; + filesIcon = files; + return returnIcon; + } + + public javax.swing.Action[] actions (java.util.Set files) { + assertNull (filesActions); + filesActions = files; + return returnActions; + } + + } +}