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

package org.netbeans.core;

import java.util.*;
import java.beans.*;

import org.openide.ErrorManager;
import org.openide.cookies.InstanceCookie;
import org.openide.filesystems.*;
import org.openide.loaders.*;
import org.openide.util.RequestProcessor;
import org.openide.util.Task;
import org.openide.util.TaskListener;
import org.netbeans.core.projects.*;

/**
 * This class implements compile/runtime library path needed for the installed
 * modules to work well with the IDE. The folder watches for changes in the path
 * and mounts/unmounts FileSystems to/from repository.
 *
 * @author Jaroslav Tulach
 */
public class AutomountSupport extends FolderInstance 
implements RepositoryListener, Runnable {
    /** processor for working with automount */
    private static RequestProcessor RP = new RequestProcessor ("Automount"); // NOI18N
    
    /** thread variable to signal that we caused the update of filesystems */
    private static ThreadLocal VAR = new ThreadLocal ();

    /** A task to signal when processing has finished or began */
    private static DblTask defaultTask;

    /** store delay */
    private static final int DELAY = 1000;

    /** set of filesystems that were discovered by the folder recognizer and
     * added by it. The rest of filesystems that are not in the list is 
     * either added by Repository.addFileSystem by somebody else and thus 
     * is waiting for being flushed or was removed 
     */
    private static Set createdByMe = new org.openide.util.WeakSet ();
    
    /** set of filesystems that was recently added and should not be removed */
    private static Set sticky = new org.openide.util.WeakSet ();
//    private static Set sticky = new HashSet ();

    /** set of filesystems that was recently removed and should not be added */
    private static Set shineAway = new org.openide.util.WeakSet ();
//    private static Set shineAway = new HashSet ();

    /** cookies, which are known to throw errors */
    private static Set errCookies = new org.openide.util.WeakSet ();
    
    /** is the default instance */
    private boolean defaultInstance;
    
    /** New recognizer of the folder */
    private AutomountSupport(DataObject.Container fld, boolean defaultInstance) {
        super(fld);
        this.defaultInstance = defaultInstance;
    }
    
    /** Constructs an array of filesystems to 
     */
    protected Object createInstance(InstanceCookie[] cookies) 
    throws java.io.IOException, ClassNotFoundException {
        ArrayList arr = new ArrayList ();
        for (int i = 0; i < cookies.length; i++) {
            Object obj;
            try {
                obj = cookies[i].instanceCreate ();
            } catch (java.io.IOException ex) {
                if (errCookies.add(cookies[i])) {
                    ErrorManager.getDefault ().notify(ErrorManager.INFORMATIONAL, ex);
                }
                continue;
            }
            errCookies.remove(cookies[i]);
            if (obj instanceof FileSystem) {
                arr.add (obj);
                continue;
            }
            
            if (obj instanceof FileSystem[]) {
                arr.addAll (Arrays.asList ((FileSystem[])obj));
                continue;
            }
        }
        
        Object retValue;
        if (arr.size () == 1) {
            // return the filesystem
            retValue = arr.get (0);
        } else {
            // return array of filesystems
            retValue = arr.toArray (new FileSystem[0]);
        }
        

        if (defaultInstance) {
            // update for the topmost instance
            updateFileSystems (arr);
        }

        return retValue;
    }
    
    protected Object instanceForCookie (DataObject obj, InstanceCookie cookie)
    throws java.io.IOException, ClassNotFoundException {
        if (errCookies.contains(cookie)) {
            obj.delete();
            return null;
        }
        try {
            return super.instanceForCookie(obj, cookie);
        } catch (java.io.IOException ioe) {
            ErrorManager.getDefault ().notify(ErrorManager.INFORMATIONAL, ioe);
            
            obj.delete();
            return null;
        }
    }
    
    /** Accepts also folders - by creating the AutomountSupport for them.
     */
    protected InstanceCookie acceptContainer (DataObject.Container container) {
        return new AutomountSupport (container, false);
    }
        

    /**
     * Accepts only FileSystem instances.
     */
    protected InstanceCookie acceptCookie(InstanceCookie cookie) throws java.io.IOException, ClassNotFoundException {
        if (cookie instanceof InstanceCookie.Of) {
            InstanceCookie.Of of = (InstanceCookie.Of)cookie;
            if (of.instanceOf (FileSystem.class) || of.instanceOf (FileSystem[].class)) {
                return cookie;
            } else {
                return null;
            }
        }

        // else the regular check for filesystems from the instance
        Class c = cookie.instanceClass();
        if (c.isArray ()) {
            c = c.getComponentType ();
        }
        
	//System.err.println("AcceptCookie on " + c);
        if (FileSystem.class.isAssignableFrom(c))
            return cookie;
        else
            return null;
    }
    
    /** Processing of filesystems will be done in a separate thread.
     */
    protected Task postCreationTask (Runnable run) {
        return RP.post (run);
    }

    /** Returns a list of objects created in this instance.
     */
    private List getList () {
        Object fsOrFss = null;
        try {
            fsOrFss = instanceCreate ();
        } catch (java.io.IOException ex) {
            ErrorManager.getDefault ().notify (ex);
        } catch (ClassNotFoundException ex) {
            ErrorManager.getDefault ().notify (ex);
        }
        
        if (fsOrFss == null) {
            return Collections.EMPTY_LIST;
        }

        if (fsOrFss instanceof FileSystem) {
            return Collections.nCopies (1, fsOrFss);
        } else {
            return Arrays.asList ((FileSystem[])fsOrFss);
        }
    }

    /** Initialize the support.
     * @return the task one can use to wait for the process to finish
     */
    public static synchronized Task initialize () {
//        if (defaultInstance != null) {
//            return defaultInstance;
//        }
        if (defaultTask != null) {
            return defaultTask;
        }

        DataFolder folder = NbPlaces.getDefault().findSessionFolder ("Mount"); // NOI18N
        AutomountSupport auto = new AutomountSupport (folder, true);
        
        RequestProcessor.Task storeTask = RP.create (auto);
        // give the store task minimal priority to work as
        // less as possible
        storeTask.setPriority (Thread.MIN_PRIORITY);
 
        defaultTask = new DblTask (auto, storeTask);

        //
        // because the listener auto.fileSystemAdded and auto.fileSystemRemoved
        // delegates to defaultTask the task should be initilized before we
        // attach the listener to the repository
        //
        Repository rep = Repository.getDefault ();
        rep.addRepositoryListener (auto);

        synchronized (auto) {
            sticky.addAll (Arrays.asList (rep.toArray ()));
            if (sticky.size () > 1) {
                // there is more than just default filesystem => then check 
                // if everything is stored
                defaultTask.store ();
            }
        }
        
        //
        // the start of processing should be done at the end, when all tasks
        // are initialized (storeTask, defaultTask)
        // 
        auto.recreate ();


        return defaultTask;
    }
    
    /** Checks whether everything is saved.
     */
    public void run () {
        // and save 
        try { 
            FileSystem fs = Repository.getDefault ().getDefaultFileSystem();
            fs.runAtomicAction (new FileSystem.AtomicAction () {
                public void run () {
                    DataFolder df = NbPlaces.getDefault().findSessionFolder ("Mount"); // NOI18N
                    List list = getList ();

                    /* iterate all subfolders recursively and check whether everything is saved as it should be*/
                    Enumeration en = df.getPrimaryFile().getFolders(true);
                    while (en.hasMoreElements()) {
                        try {
                            DataFolder dfChild = (DataFolder)DataFolder.find((FileObject)en.nextElement());
                            checkSaved(list,dfChild, dfChild.getChildren(), false);
                        } catch (DataObjectNotFoundException e) {
                            continue;
                        }
                    }

                    /* and finally check mount folder */
                    checkSaved (list, df, df.getChildren (), true);
                }
            });
        } catch (java.io.IOException ex) {
            // cannot be thrown because it is not thrown from the inside code either
            throw new IllegalStateException ();
        }
           
    }

    /** FileSystem added. RepositoryListener.
     */
    public void fileSystemAdded (final RepositoryEvent ev) {
        defaultTask.fs (ev);
    }

    /** FileSystem removed. RepositoryListener.
     */
    public void fileSystemRemoved (final RepositoryEvent ev) {
        defaultTask.fs (ev);
    }

    /** FileSystem reorder. RepositoryListener.
     */
    public void fileSystemPoolReordered (RepositoryReorderedEvent ev) {
        if (VAR.get () == null) {
            if (isLog ()) {
                log ("Reorder in repository"); // NOI18N
                synchronized (Repository.getDefault ()) {
                    FileSystem[] arr = Repository.getDefault ().toArray ();
                    for (int i = 0; i < arr.length; i++) {
                        log ("   " + i + "th = " + arr[i]);
                    }
                }
            }
            defaultTask.store ();
        } else {
            log ("Repository reordered by AU"); // NOI18N
        }
    }
    
    /** Finds out whether a filesystem is contained in a repository.
     * @param fs filesystem 
     * @return true if so false otherwise
     */
    private static boolean containsFS (FileSystem fs) {
        FileSystem[] arr = Repository.getDefault ().toArray ();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == fs) {
                return true;
            }
        }
        return false;
    }
    
    /** compute filename in the same manner as InstanceDataObject.create
     * [PENDING] in next version this should be replaced by public support
     * likely from FileUtil
     * @see issue #17186
     */
    private static String escape(String name) throws java.io.IOException {
        try {
            java.lang.reflect.Method escape = 
                InstanceDataObject.class.getDeclaredMethod(
                    "escapeAndCut", new Class[] {String.class}); //NOI18N
            escape.setAccessible(true);
            return (String) escape.invoke(null, new String[] {name});
        } catch (Exception ex) {
            throw (java.io.IOException) ErrorManager.getDefault().
                annotate(new java.io.IOException("Escape support failed"), ex); // NOI18N
        }
    }
    
    /**
     * Computes a new name of file for use in folder df. If the reguested file
     * already exists in the given folder try to append a number.
     * This method should be deleted after #21083 is implemented.
     */
    public static String computeNewName(String requiredName, DataFolder df) throws java.io.IOException {
        if ((requiredName == null) || (requiredName.length() == 0)) {
            return null;
        }
        
        boolean isUsed = true;
        String srcName = requiredName;
        int i = 1;
        while (isUsed) {
            isUsed = false;
            String escaped = escape(srcName);
            String uniqueName = FileUtil.findFreeFileName(
                df.getPrimaryFile(), escaped, "settings" // NOI18N
            );
            if (!escaped.equals(uniqueName)) {
                isUsed = true;
                srcName = requiredName + "_" + i;
                i++;
            }
        }
        return srcName;
    }

    /** Stores filesystems that are not stored now.
     * @param now list of FileSystems read from disk
     * @param arr array of dat objects in the Mount folder
     */
    private void checkSaved (List now, DataFolder df, DataObject[] arr, boolean isSaveEnabled) {
        Repository rep = Repository.getDefault ();
        synchronized (rep) {
            List asList = Arrays.asList (rep.toArray ());
            List exists = new ArrayList (asList);
            exists.removeAll(createdByMe);            
            exists.removeAll (now);
            
            ArrayList order = new ArrayList (asList.size () * 2);
            order.addAll (Arrays.asList (arr));

            sticky.removeAll(now);

            // remove all data objects in the main level, that should not be there
            HashSet doNotDeleteFileSystems = new HashSet (asList);
            // just work with those that are created by me
            doNotDeleteFileSystems.retainAll (createdByMe);

            // if the order is different we will need diff
            List onlyOrder = new ArrayList (asList);
            onlyOrder.retainAll (now);
            boolean diff = onlyOrder.size () == now.size () && !onlyOrder.equals (now);
            if (diff) {
                // improve the order of data objects
                class Cmp extends HashMap implements Comparator {
                    public int compare (Object o1, Object o2) {
                        return index (o1) - index (o2);
                    }
                    
                    private int index (Object o1) {
                        return ((Integer)get (o1)).intValue ();
                    }
                }
                
                Cmp cmp = new Cmp ();
                int i = onlyOrder.size ();
                Iterator it = order.iterator ();
                while (it.hasNext ()) {
                    DataObject obj = (DataObject)it.next ();
                    i++;
                    
                    InstanceCookie ic = (InstanceCookie)obj.getCookie (InstanceCookie.class);
                    if (ic != null) {
                        try {
                            Object o = ic.instanceCreate ();
                            int indx = onlyOrder.indexOf (o);
                            if (indx >= 0) {
                                cmp.put (obj, new Integer (indx));
                                continue;
                            }
                        } catch (ClassNotFoundException ex) {
                            ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex);
                        } catch (java.io.IOException ex) {
                            ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex);
                        }
                    }
                    cmp.put (obj, new Integer (i));
                }
                
                Collections.sort (order, cmp);
            }
            
            Iterator it = exists.iterator ();
            if (isSaveEnabled) {
                while (it.hasNext ()) {
                    FileSystem fs = (FileSystem)it.next ();
                    if (fs.isDefault ()) {
                        continue;
                    }
                    try {
                        InstanceDataObject dobj;
                        String name = computeNewName(fs.getDisplayName(), df);
                        try {
                            dobj = InstanceDataObject.create (df, name, fs, null);
                            createdByMe.add(fs);
                        } catch(java.io.NotSerializableException nse) {
                            // this can happen when filesystem fs refuses to store itself by
                            // returning null from writeReplace
                            continue;
                        }
                    
                        if (dobj.instanceCreate () != fs) {
                            StringBuffer sb = new StringBuffer (255);
                            sb.append ("This bug is caused by wrong implementation of InstanceDataObject, see "); // NOI18N
                            sb.append ("http://www.netbeans.org/issues/show_bug.cgi?id=14557"); // NOI18N
                            sb.append ("\nSTORING: "); // NOI18N
                            sb.append (fs);
                            sb.append ("\nINSTNCE: "); // NOI18N
                            sb.append (dobj.instanceCreate ()); // NOI18N

                            throw new IllegalStateException (sb.toString ());

                        }
                    
                        // if we wrote the FS to disk, we do not want to delete it
                        doNotDeleteFileSystems.add (fs);
                    
                        if (isLog ()) {
                            log (" written to disk: " + fs + " into: " + dobj); // NOI18N
                        }
                    
                        order.add (dobj);
                        sticky.remove (fs);
                    
                        diff = true;
                    } catch (java.io.IOException ex) {
                        ErrorManager.getDefault ().notify (ex);
                    } catch (ClassNotFoundException ex) {
                        ErrorManager.getDefault ().notify (ex);
                    }
                }
            }

            for (int i = 0; i < arr.length; i++) {
                InstanceCookie ic = (InstanceCookie)arr[i].getCookie (InstanceCookie.class);
                if (ic != null) {
                    try {
                        Object obj = ic.instanceCreate ();
                        if ((obj instanceof FileSystem) && !doNotDeleteFileSystems.contains (obj)) {
                            arr[i].delete ();
                            createdByMe.retainAll (Arrays.asList(rep.toArray ()));                            
                            if (isLog ()) {
                                log ("  deleted from disk: " + obj + " from: " + arr[i]); // NOI18N
                            }
                            order.remove (arr[i]);
                            diff = true;
                        }
                    } catch (ClassNotFoundException ex) {
                        ErrorManager.getDefault ().notify (ex);
                    } catch (java.io.IOException ex) {
                        ErrorManager.getDefault ().notify (ex);
                    }
                }
            }

            // update the order of filesystems
            if (diff) {
                try {
                    df.setOrder ((DataObject[])order.toArray (new DataObject[0]));
                    if (isFinished ()) {
                        log ("I should not be finished");
                        waitFinished ();
                        log ("And I am not no longer: " + isFinished ());
                    }
                    
                    if (isLog ()) {
                        log ("Changed order on the disk"); // NOI18N
                        Iterator x = order.iterator ();
                        int i = 0; 
                        while (x.hasNext ()) {
                            DataObject obj = (DataObject)x.next ();
                            InstanceCookie ic = (InstanceCookie)obj.getCookie (InstanceCookie.class);
                            if (ic != null) {
                                log ("  " + i++ + " is: " + ic.instanceCreate ()); // NOI18N
                            }
                        }
                    }
                } catch (java.io.IOException ex) {
                    ErrorManager.getDefault ().notify (ex);
                } catch (ClassNotFoundException ex) {
                    ErrorManager.getDefault ().notify (ex);
                }
            }
            
/*
            // add all filestems that are missing
            ArrayList list = new ArrayList (now);
            list.addAll (Arrays.asList (rep.toArray ()));
            updateFileSystems (list);
*/
        }
    }

    /*
     * Updates the filesystems.
     * @param current collection of FileSystem objects
     */
    private static void updateFileSystems (List future) {
        Repository rep = Repository.getDefault ();
        synchronized (rep) {
            List exists = Arrays.asList (rep.toArray ());

            // remove all existing without those future
            Collection toRemove = new LinkedList (exists);
            toRemove.removeAll (future);

            // also do not remove sticky filesystems
            toRemove.removeAll (sticky);
            
            log ("sticky: " + sticky);
            
            // just keep those filesystems that are created by me
            toRemove.retainAll (createdByMe);
            
//            log ("by me : " + createdByMe);

            if (isLog ()) {
                Iterator it = toRemove.iterator ();
                while (it.hasNext ()) {
                    FileSystem fs = (FileSystem)it.next ();
                    log ("  r: " + fs + " contains: " + sticky.contains (fs)); // NOI18N
                }
            }

            // add all future without those that exists
            Collection toAdd = new LinkedList (future);
            toAdd.removeAll (exists);
            
            // also do not add filesystems recently removed
            toAdd.removeAll (shineAway);
//            log ("shineAway: " + shineAway); // NOI18N

            if (isLog ()) {
                Iterator it = toAdd.iterator ();
                while (it.hasNext ()) {
                    FileSystem fs = (FileSystem)it.next ();
                    log ("  a: " + fs + " contains: " + sticky.contains (fs));  // NOI18N
                }
            }
            
            VAR.set (VAR);

            // remove & add changes
            cycleFileSystems (toRemove, false);
            cycleFileSystems (toAdd, true);
            // mount the new ones to the repository

            VAR.set (null);
            
            // now all that are recognized by me (future) can be also treated
            // as created by me
            
            createdByMe.addAll (future);
            
            // and as such no other filesystems are created by me any longer
            createdByMe.retainAll (Arrays.asList(rep.toArray ()));
            
            
            
            // update the order
            FileSystem[] arr = rep.toArray ();
            
            // in future keep only those that are in filesystems
            List future1 = new LinkedList (future);
            future1.retainAll (Arrays.asList (arr));
            
            int[] perm = new int[arr.length];
            int old = future1.size () + 1;

            
            boolean diff = false;
            for (int i = 0; i < arr.length; i++) {
                if (arr[i].isDefault ()) {
                    perm[i] = 0;
                    continue;
                }
                
                int indx = future1.indexOf (arr[i]);

                if (indx == -1) {
                    // filesystem added by somebody else
                    indx = old++;
                } else {
                    indx++;
                }

                perm[indx] = i;
                diff |= indx != i;
            }
            
            if (diff) {
                if (isLog ()) {
                    log ("Doing reorder"); // NOI18N
                    for (int i = 0; i < arr.length; i++) {
                        log ("  " + i + " <- " + perm[i] + " now: " + arr[i]);  // NOI18N
                    }
                
                    log ("On disk"); // NOI18N
                    Iterator x = future1.iterator ();
                    while (x.hasNext ()) {
                        log ("  " + x.next ()); // NOI18N
                    }
                    
                    log ("------"); // NOI18N
                }
                
                VAR.set (VAR);
                try {
                    rep.reorder (perm);
                } finally {
                    VAR.set (null);
                }
            } else {
                log ("No reoder"); // NOI18N
            }
/*            
            arr = rep.toArray ();
            if (future.size () != arr.length - 1) {
                log ("Not the same size: " + future.size () + " arr: " + arr.length);
            } else {
                for (int i = 0; i < arr.length - 1; i++) {
                    if (arr[i + 1] != future.get (i)) {
                        log ("  Help:" + i + " arr: " + arr[i + 1] + " current: " + future.get (i));
                    } else {
                        log ("  " + i + " arr: " + arr[i + 1]);
                    }
                }
            }
 */
        }
    }
    
    private static void cycleFileSystems (Collection fss, boolean add) {
        Repository repository = Repository.getDefault ();
        for (Iterator it = fss.iterator(); it.hasNext(); ) {
            FileSystem fs = (FileSystem)it.next();
            
            if (add) {
                // special handling for already mounted filesystems
                FileSystem old = repository.findFileSystem (fs.getSystemName ());
                if (old != null && old != fs && sticky.remove (old)) {
                    // if the sticky object was there (for example mounted before 
                    // the automount was initialized) => replace it with this one
                    repository.removeFileSystem (old);
                }
                    
                repository.addFileSystem(fs);
            } else {
                repository.removeFileSystem (fs);
            }
        }
    }
    
    /** error manager.
     */
    private static ErrorManager err;
    
    /** Checks whether we log.
     */
    private static boolean isLog () {
        if (err == null) {
            err = ErrorManager.getDefault ().getInstance ("org.netbeans.core.AutomountSupport"); // NOI18N
        }
        return err.isLoggable (ErrorManager.INFORMATIONAL);
    }
    
    /** Logs to error manager.
     */
    private static void log (String msg) {
        if (isLog ()) {
            err.log (msg);
        }
    }
    
    /** Double task. Delegates to two tasks.
     */
    private static final class DblTask extends Task 
    implements TaskListener {
        /** task that loads objects in */
        private Task defaultInstance;
        /** task that stores objects to disk */
        private RequestProcessor.Task storeTask;
        /** task that updates state of AutomountSupport from Repository */
        private Task updateTask = Task.EMPTY;
        /** true if store task has been called at least once */
        private boolean isStoring;
        
        
        public DblTask (Task defaultInstance, RequestProcessor.Task storeTask) {
            this.defaultInstance = defaultInstance;
            this.storeTask = storeTask;
            
            defaultInstance.addTaskListener (this);
            storeTask.addTaskListener (this);
        }
            
        /** Invokes the store task.
         */
        public void store () {
            synchronized (this) {
                storeTask.schedule (DELAY);
                isStoring = true;
            }
            notifyRunning ();
        }
        
        /** Test whether the task has finished running.
        * @return <code>true</code> if so
        */
        private synchronized boolean areBothFinished () {
            boolean t1 = !isStoring || storeTask.isFinished (); // either we are not storing at all or we finished storing
            if (!t1) return false;
            
            boolean t2 = defaultInstance.isFinished ();
            if (!t2) return false;
            
            boolean t3 = updateTask.isFinished (); 
            
            return t3;
        }

        /** Wait until the task is finished. 
        * Changed not to be <code>final</code> in version 1.5
        */
        public void waitFinished () {
            log ("Entered waitFinished");
            for (int i = 0; i < 2; i++) {
                do {
                    updateTask.waitFinished ();
                    defaultInstance.waitFinished ();

                    // get the value of isStoring that could be modified 
                    // by different thread in synchronized block
                    boolean storing;
                    synchronized (this) {
                        storing = isStoring;
                    }

                    if (storing) {
                        storeTask.waitFinished ();
                        // do the check once more, as this usually triggers
                        // the FolderInstance creation
                        continue;
                    }
                } while (!areBothFinished ());
            }
            log ("Leaving waitFinished");
        }
            
        /** Task finished.
         */
        public void taskFinished (Task t) {
            if (areBothFinished ()) {
                notifyFinished ();
            }
        }
        
        /** Processes a filesystem that was added or removed.
         */
        public synchronized void fs (RepositoryEvent ev) {
            boolean isNull = VAR.get () == null;
            if (!isNull) {
                // ok, no need to continue
                log ("fs: " + ev.getFileSystem () + " added: " + ev.isAdded () + " by AU support"); // NOI18N
                return;
            }
            
            class Run implements Runnable {
                public RepositoryEvent ev;
                
                public Run (RepositoryEvent ev) {
                    this.ev = ev;
                }
                
                public void run () {
                    Repository rep = Repository.getDefault ();
                    synchronized (rep) {
                        FileSystem fs = ev.getFileSystem ();
                        boolean c = containsFS (fs);
                        boolean added = ev.isAdded ();

                        log ("fs: " + fs + " added: " + added + " contains: " + c); // NOI18N
                        
                        if (added) {
                            if (c) {
                                sticky.add (fs);
                                shineAway.remove (fs);
                                defaultTask.store ();
                            }
                        } else {
                            if (!c) {
                                sticky.remove (fs);
                                shineAway.add (fs);
                                defaultTask.store ();
                            }
                        }
                    }
                    // removes the listener
                    removeTaskListener (DblTask.this);
                    ev = null;
                }
                
            }
            
            Task task = RP.post(new Run (ev));
            
            task.addTaskListener (DblTask.this);
            updateTask = task;
        }
    }
}
