alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.tasklist.usertasks;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.netbeans.modules.tasklist.core.Task;
import org.netbeans.modules.tasklist.core.TaskAnnotation;
import org.netbeans.modules.tasklist.core.TaskList;
import org.netbeans.modules.tasklist.core.translators.AbstractTranslator;
import org.netbeans.modules.tasklist.core.translators.FormatTranslator;
import org.netbeans.modules.tasklist.core.translators.UnknownFormatException;
import org.netbeans.modules.tasklist.usertasks.translators.HTMLTranslator;
import org.netbeans.modules.tasklist.usertasks.translators.UserTaskListXMLTranslator;
import org.netbeans.modules.tasklist.usertasks.translators.iCalSupport;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.NotifyDescriptor.Message;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;


/**
 * This class represents the tasklist itself
 * @todo The tasks that had a due time while the IDE was shut down will not
 *       store it's "alarm-sent-property" unless the tasklist is touched after
 *       the file is beeing parsed... I'll fix this as soon as possible...
 *
 * @author Tor Norbye
 * @author Trond Norbye
 */
public class UserTaskList extends TaskList implements Timeout {    
    // List category
    final static String USER_CATEGORY = "usertasks"; // NOI18N    
    
    private static UserTaskList tasklist = null;
    
    private static final java.util.Timer TIMER = new java.util.Timer(true);

    /**
     * Currently working on this task 
     */
    private static UserTask started = null;
    
    /**
     * The running task was suspended. It means that after the user
     * touched the mouse it will be running again.
     */
    private static boolean suspended = false;
    
    /**
     * Time as returned by System.currentMillis() when the task was started
     */
    private static long startedAt;
    
    private static int initialDuration;
    
    /**
     * Old value of the duration attribute of the current task
     */
    private static int lastDuration;
    
    /**
     * Returns the default task list
     *
     * @return default task list
     */
    public static UserTaskList getDefault() {
        if (tasklist == null) {
            try {
                tasklist = new UserTaskList();
                tasklist.readDocument();
            } catch (IOException ioe) {
                DialogDisplayer.getDefault().notify(new Message(
                    ioe, NotifyDescriptor.ERROR_MESSAGE));
            }
        }
        return tasklist;
    }
    
    static {
        TIMER.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                timer();
            }
        }, 0, 1000 * 15);
        ActivityListener.init();
    }
    
    /**
     * Executed once per minute
     */
    private static void timer() {
        if (started == null)
            return;
        
        long now = System.currentTimeMillis();
        if (lastDuration == started.getSpentTime()) {
            if ((System.currentTimeMillis() - 
                ActivityListener.getLastActivityMillis()) > 10 * 60 * 1000) {
                suspended = true;
            } else {
                if (suspended) {
                    startedAt = ActivityListener.getLastActivityMillis();
                    initialDuration = started.getSpentTime();
                    suspended = false;
                }
                int diff = (int) ((now - startedAt) / (60 * 1000));
                started.setSpentTime(initialDuration + diff);
                lastDuration = started.getSpentTime();
            }
        } else {
            lastDuration = started.getSpentTime();
        }
    }
    
    /**
     * Returns started task
     *
     * @return started task or null
     */
    public static UserTask getStarted() {
        return started;
    }
    
    /**
     * Starts another task
     *
     * @param task currently working on this task. May be null.
     */
    public static void start(UserTask task) {
        if (started != null) {
            timer();
        }
        
        started = task;
        
        if (started != null) {
            started.setSpentTimeComputed(false);
            startedAt = System.currentTimeMillis();
            lastDuration = task.getSpentTime();
            initialDuration = task.getSpentTime();
        }
    }
    
    /** File being shown in this tasklist */
    private FileObject file = null;
    
    /** String-path version of the file field */
    private String path = null;
    
    /** The current timeout */
    private long currentTimeout;
    
    private AbstractTranslator io = null;
    
    /**
     * During loading of a tasklist I may encounter items that have expired
     * while the IDE was shut down. Since the load-function turns off the 
     * effect of markChanged, I need to store this information in another
     * variable, and save the tasklist when the load is finished..
     */
    private boolean expiredTask;
    
    /** Timer which keeps track of outstanding save requests - that way
     * deleting multiple items for example will not cause multiple saves. */
    private Timer runTimer = null;


    // User can work on one task at time (simpliication) ~~~~~~~~~~~~~~~

    /**
     * Creates a new instance of TaskList
     */
    public UserTaskList() {
        expiredTask = false;
        currentTimeout = Long.MAX_VALUE;
    }
    
    /** 
     * Creates a new instance of TaskList
     *
     * @deprecated, use readFile instead.
     * @see readFile
     */
    public UserTaskList(FileObject file) {
        this();
        readFile(file);
    }
    
    /** Location of the tasklist */
    public FileObject getFile() {
        return file;
    }
    
    /**
     * Due to the fact that readDocument may return false (unknown file format
     * for example), this should not be done in an constructor (unless we
     * provide a good() function to test if the object state is good...).
     * @param file the file to read..
     * @return true if success, false otherwise..
     */
    public boolean readFile(FileObject file) {
        this.file = file;
        File f = FileUtil.toFile(file);
        this.path = f.getPath();
        boolean ret;
        
        try {
            ret = readDocument();
            // the readDocument function will order the next timeout!
        } catch (IOException ioe) {
            DialogDisplayer.getDefault().notify(new Message(
            ioe, NotifyDescriptor.ERROR_MESSAGE));
            ret = false;
        }
        
        return ret;
    }
    
    /**
     * Searches for categories through all tasks.
     *
     * @return all found categories
     */
    public String[] getCategories() {
        Iterator it = this.getTasks().iterator();
        Set cat = new java.util.HashSet();
        while (it.hasNext()) {
            UserTask ut = (UserTask) it.next();
            findCategories(ut, cat);
        }
        return (String[]) cat.toArray(new String[cat.size()]);
    }
    
    /**
     * Searches for categories
     *
     * @param task search for categories in this task and all of it's subtasks
     * recursively
     * @param cat container for found categories. String[]
     */
    private static void findCategories(UserTask task, Set cat) {
        if (task.getCategory().length() != 0)
            cat.add(task.getCategory());
        
        Iterator it = task.subtasksIterator();
        while (it.hasNext()) {
            findCategories((UserTask) it.next(), cat);
        }
    }
    
    /** Write todo items out to disk */
    public void save() {
        if (!needSave || dontSave) {
            return;
        }

        Iterator it = getTasks().iterator();
        while (it.hasNext()) {
            UserTask task = (UserTask) it.next();
            task.updateLineNumberRecursively();
        }

        // Write out todo items to disk
        scheduleWrite();
    }
    
    private boolean readDocument() throws IOException {
        String name = path;
        if (name == null) {
            name = Settings.getDefault().getExpandedFilename();
        }
        File fname = new File(name);
        if (!fname.exists()) {
            // Not an error - you may not have saved tasks yet
            
            // Perhaps we have just upgraded? Look in the old place (temporary
            // migration)
            name = System.getProperty("netbeans.user") +  // NOI18N
            File.separatorChar + "system" + File.separatorChar + // NOI18N
            "TaskList" + File.separatorChar + "tasklist.ics"; // NOI18N
            fname = new File(name);
            if (fname.exists()) {
                io = new iCalSupport();
                dontSave = true;
                boolean success;
                try {
                    success = io.read(this, null, fname, false);
                } catch (UnknownFormatException e) {
                    // NOTE the exception text should be localized!
                    DialogDisplayer.getDefault().notify(new Message(e.getMessage(),
                    NotifyDescriptor.ERROR_MESSAGE));
                    success = false;
                }
                dontSave = false;
                if (success) {
                    save();
                    fname.delete();
                }
                needSave = false;
                return success;
            }
            
            return false;
        }
        
        io = new iCalSupport();
        
        dontSave = true;
        boolean success;
        try {
            success = io.read(this, null, fname, false);
            orderNextTimeout();
            /*
            if (root != null) {
                showAnnotations((UserTask)root);
            }
            */
        } catch (UnknownFormatException e) {
            // NOTE the exception text should be localized!
            DialogDisplayer.getDefault().notify(new Message(e.getMessage(),
               NotifyDescriptor.ERROR_MESSAGE));
            success = false;
        }
        
        needSave = false;
        dontSave = false;        
        
        if (expiredTask) {
            // One (or more) tasks expired while the IDE was closed...
            // save the list as soon as possible...
            expiredTask = true;
            markChanged();
        }
        
        return success;
    }

    /** Show all annotations in this tasklist in the editor */
    void showAnnotations(Iterator tasks) {
        while (tasks.hasNext()) {
            UserTask task = (UserTask)tasks.next();
            if (task.getLine() != null) {
                task.updateAnnotation();
            }
            if (task.hasSubtasks()) {
                showAnnotations(task.getSubtasks().iterator());
            }
        }
    }

    /** Remove all annotations in this tasklist from the editor */
    void hideAnnotations(Iterator tasks) {
        while (tasks.hasNext()) {
            UserTask task = (UserTask)tasks.next();
            if (task.getAnnotation() != null) {
                TaskAnnotation anno = task.getAnnotation();
                anno.detach();
                task.setAnnotation(null);
            }
            if (task.hasSubtasks()) {
                hideAnnotations(task.getSubtasks().iterator());
            }
        }
    }
    
    // Look up a particular item by uid
    public UserTask findItem(Iterator tasks, String uid) {
        while (tasks.hasNext()) {
            UserTask task = (UserTask)tasks.next();
            if (task.getUID().equals(uid)) {
                return task;
            }
            if (task.hasSubtasks()) {
                UserTask f = findItem(task.subtasksIterator(), uid);
                if (f != null) {
                    return f;
                }
            }
        }
        return null;
    }

    /** Schedule a document save */
    private void scheduleWrite() {
        // Stop our current timer; the previous node has not
        // yet been scanned; too brief an interval
	if (runTimer != null) {
	    runTimer.stop();
	    runTimer = null;
	}
	runTimer = new Timer(300, // 0.3 second delay
		     new ActionListener() {
			 public void actionPerformed(ActionEvent evt) {
                             runTimer = null;
                             
                             // Write out todo items to disk
                             try {
                                 writeDocument();
                             } catch (IOException ioe) {
                                 DialogDisplayer.getDefault().notify(new Message(
                                          ioe, NotifyDescriptor.ERROR_MESSAGE));
                             }
                             needSave = false;
			 }
		     });
	runTimer.setRepeats(false);
	runTimer.setCoalesce(true);
	runTimer.start();
    }

    /** 
     * Write todolist to iCal.
     */
    private void writeDocument() throws IOException {
        if (io == null) {
            io = new iCalSupport();
        }
        
        String fname;
        if (path != null) {
            fname = path;
        } else {
            fname = Settings.getDefault().getExpandedFilename();
        }
        File f = new File(fname);
        File pf = f.getParentFile();
        String filename = f.getPath().substring(pf.getPath().length()+1);
        boolean saveBackup = Settings.getDefault().getBackups();
        
        // Make sure we have the right IO, since the user may have
        // renamed the save-file
        if (!(io instanceof iCalSupport)) {
            io = new iCalSupport();
        }
        
        io.write(this, null, filename, pf, false, saveBackup);

        // Remove permissions for others on the file when on Unix
        // varieties
        if (new File("/bin/chmod").exists()) {
            try {
                Runtime.getRuntime().exec(
                       new String[] {"/bin/chmod", "go-rwx",  // NOI18N
                                          fname });
            } catch (Exception e) {
                // Silently accept
                ErrorManager.getDefault().notify(
                                               ErrorManager.INFORMATIONAL, e);
            }
        }

        needSave = false;
    }
    
    void purgeCompletedItems() {
        if (getTasks().size() == 0 ) {
            return;
        }
        dontSave = true;
        Iterator it = getTasks().iterator();
        while (it.hasNext()) {
            UserTask task = (UserTask) it.next();
            recursivePurge(task);
        }
        dontSave = false;
        save();
    }
    
    /** Remove any completed items from the list.
     * Only works if started on the root node.
     * NOTE - if a task is marked done, it AND ALL ITS CHILDREN
     * are removed, even if the children haven't been marked done.
     */
    private void recursivePurge(UserTask node) {
        if (node.getSubtasks() != null) {
            List l = new ArrayList(node.getSubtasks());
            
            // I may have to repeat purges because once I delete an item from
            // the list, the iterator needs to be redone
            Iterator it = l.iterator();
            while (it.hasNext()) {
                UserTask task = (UserTask)it.next();
                if (task.isDone()) {
                    Task pt = task.getParent();
                    int index = pt.indexOf(task);
                    pt.removeSubtask(task);
                    // Ensure that we're not showing any markers for this item
                    fireRemoved(pt, task, index);
                } else if (task.hasSubtasks()) {
                    recursivePurge(task);
                }
            }
        }
    }

    /**
     * Order a timeout for the next due date
     */
    void orderNextTimeout() {
        long nextTimeout = Long.MAX_VALUE;
        long now = System.currentTimeMillis();
        
        UserTask ref = null;

        Iterator i = getTasks().iterator();
        while (i.hasNext()) {
            UserTask t = (UserTask)i.next();            
            long n = t.getDueTime();

            if (n != Long.MAX_VALUE && !t.isDueAlarmSent()) {
                if (n <= now) {
                    showExpiredTask(t);
                    continue;
                } else if (n < nextTimeout) {
                    nextTimeout = n;
                    ref = t;
                }
            }
        }

        if (nextTimeout != currentTimeout) {
            // cancel the previous ordered timeout, and add the new one
            if (currentTimeout != Long.MAX_VALUE) {
                TimeoutProvider.getInstance().cancel(this, null);
            }
            TimeoutProvider.getInstance().add(this, ref, nextTimeout);
            currentTimeout = nextTimeout;
        }
    }
    
    /** Return the translators capable of handling this tasklist.
     * @return Array of translators that can read/write the tasklist
     */
    public FormatTranslator[] getTranslators() {
        FormatTranslator[] translators = new FormatTranslator[] {
            new iCalSupport(),
            new UserTaskListXMLTranslator(),
            new HTMLTranslator("usertasks-tree-html.xsl"),
            new HTMLTranslator("usertasks-table-html.xsl")
        };
        return translators;
    }
    
    public String toString() {
        return "UserTaskList(" + path + ")"; // NOI18N
    }
    
    /**
     * Callback function for the TimeoutProvider to call when the timeout
     * expired. This function will block the TimeoutProviders thread, so
     * it should be used for a timeconsuming task (one should probably
     * reschedule oneself with the SwingUtilities.invokeLater() ???)
     * @param o the object provided as a user reference
     */
    public void timeoutExpired(Object o) {
        // Show the task...
        showExpiredTask((UserTask)o);

        // order the next timeout for this list
        orderNextTimeout();
    }

    /**
     * Present the user with a dialog that shows information of the task that
     * expired... 
     *
     * @todo Replace the UserTaskDuePanel with the EditTaskPanel????
     * @param task the task to show
     */
    private void showExpiredTask(UserTask task) {
        task.setDueAlarmSent(true);
        expiredTask = true;
        markChanged();
        
        final UserTask t = task;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                UserTaskDuePanel panel = new UserTaskDuePanel(t);
                
                String title = NbBundle.getMessage(NewTaskAction.class, "TaskDueLabel"); // NOI18N
                DialogDescriptor d = new DialogDescriptor(panel, title);                
                d.setModal(false);
                d.setMessageType(NotifyDescriptor.PLAIN_MESSAGE);
                d.setOptionType(NotifyDescriptor.OK_CANCEL_OPTION);
                java.awt.Dialog dlg = DialogDisplayer.getDefault().createDialog(d);
                dlg.pack();
                dlg.show();
            }
        });
    }

    /** Extract tasks recursivelly to given vector. */
    void addAllTasks(Vector v) {
        if (getTasks().size() == 0) {
            return;
        }
        addAllTasks(getTasks().iterator(), v);
    }

    void addAllTasks(Iterator tasks, Vector v) {
        while (tasks.hasNext()) {
            UserTask t = (UserTask)tasks.next();
            v.addElement(t);
            if (t.hasSubtasks()) {
                addAllTasks(t.getSubtasks().iterator(), v);
            }
        }
    }

    protected void fireAdded(Task task) {
        super.fireAdded(task);
        markChanged();
        orderNextTimeout();
    }

    protected void fireStructureChanged(Task task) {
        super.fireStructureChanged(task);
        markChanged();
    }

    protected void fireRemoved(Task pt, Task task, int index) {
        super.fireRemoved(pt, task, index);
        if (getStarted() == task)
            started = null;
        markChanged();
        orderNextTimeout();
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.