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.translators;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

import org.netbeans.modules.tasklist.client.SuggestionPriority;
import org.netbeans.modules.tasklist.core.Task;
import org.netbeans.modules.tasklist.core.TaskList;
import org.netbeans.modules.tasklist.core.translators.AbstractTranslator;
import org.netbeans.modules.tasklist.core.translators.UnknownFormatException;
import org.netbeans.modules.tasklist.usertasks.UserTask;
import org.netbeans.modules.tasklist.usertasks.UserTaskList;
import org.openide.ErrorManager;
import org.openide.util.NbBundle;

/**
 * This class provides import/export capabilities for the iCalendar calendar
 * format (used by for example KDE's Konqueror calendar/todoitem tool)
 * as specified in RFC 2445 with the following exceptions:
 * @todo Write the exceptions to the RFC here!!
 *
 * @todo Store the alarm-part of the associated time as an VALARM field (but
 *       I guess I must hardcode some of the fields (the alarm action etc);)
 *
 * @todo Trond: I have left traces after a class named AssociatedTime in this
 *       file. I might need some of it again when we decide we want to
 *       event support.
 *
 * @author Tor Norbye
 * @author Trond Norbye
 */
public class iCalSupport extends AbstractTranslator {
    public String getDefaultExtension() {
        return "ics";
    }
    
    public String getImportName() {
        return NbBundle.getMessage(iCalSupport.class, "iCalImp"); // NOI18N
    }
    
    public String getExportName() {
        return NbBundle.getMessage(iCalSupport.class, "iCalExp"); // NOI18N
    }
    
    public boolean supportsImport() {
        return true;
    }
    
    public boolean supportsExport() {
        return true;
    }
    
    // Extends AbstractTranslator
    
    protected String getExportDialogTitle() {
        return NbBundle.getMessage(iCalSupport.class, "ExportICAL"); // NOI18N
    }
    protected String getImportDialogTitle() {
        return NbBundle.getMessage(iCalSupport.class, "ImportICAL"); // NOI18N
    }

    // Format which includes the timezone at the end. This is the format
    // used by the tasklist's own written files for example.
    private static final String DATEFORMATZ = "yyyyMMdd'T'HHmmss'Z'"; // NOI18N
    // Format used when the timezone is specified separetly, e.g. with TZ:PST
    private static final String DATEFORMAT = "yyyyMMdd'T'HHmmss"; // NOI18N
    
    
    /**
     * Do the actual export of the list into the stream
     *  @param lst The tasklist to store
     *  @param out The output stream object to use
     *  @param interactive Whether or not the user should be kept informed
        @param dir May be null, or a directory where the writer is
                 going to write the tasklist. Exporters may for example
                 write additional files in this directory.
     *  @return true if the list was successfully written
     */
    public boolean writeList(TaskList lst, OutputStream out , boolean interactive, File dir) throws IOException {
        UserTaskList list = (UserTaskList)lst;

        // http://www.ietf.org/rfc/rfc2445.txt 4.1.4:
        // There is not a property parameter to declare the character set used
        // in a property value. The default character set for an iCalendar
        // object is UTF-8 as defined in [RFC 2279].
        Writer writer = new OutputStreamWriter(out, "UTF-8");  

        // Write header
        writer.write("BEGIN:VCALENDAR\r\n" +
        "PRODID:-//NetBeans tasklist//NONSGML 1.0//EN\r\n" +
        "VERSION:2.0\r\n"); // NOI18N
        
        //writer.write("TZ:GMT\r\n"); // NOI18N
        
        SimpleDateFormat formatter = new SimpleDateFormat(DATEFORMATZ);
        // Dates in UTC
        formatter.setTimeZone(new SimpleTimeZone(0, "GMT")); // NOI18N
        
        // Write out todo items
        Iterator it = list.getTasks().iterator();
        while (it.hasNext()) {
            // Note: The previous try/catch block was superfluous (?) since
            // no exceptions will we thrown inside this block (unless
            // the listiterator contains something else than UserTask ;-)
            UserTask item = (UserTask)it.next();
            writeTask(writer, item, formatter);
        }

        // Store all non-vtodo's
        if (otherItems != null) {
            // This might not be an elegant way to do this, but instead of
            // having to restore everything, I have stored all other items
            // in a (folded) string..
            writer.write("\r\n" + otherItems);
        }

        writer.write("\r\nEND:VCALENDAR\r\n");
        writer.flush();
        return true;
    }
    
    /**
     * Write out the given todo item to the given writer.
     * @param writer The writer object to use
     * @param task The task/todo item to use
     * @param sdf A "SimpleDateFormat-formatter" used to convert a date to string
     * @todo Finish all the unused fields
     */
    private void writeTask(Writer writer, UserTask task, SimpleDateFormat sdf) {
        try {
            // Catch errors locally so that we don't botch the whole
            // list if you run into an I/O error
            writer.write("\r\nBEGIN:VTODO\r\n"); // NOI18N
            
            // UID (Unique Identifier)  (see RFC 822 and RFC 2445)
            writer.write("UID:"); // NOI18N
            writer.write(task.getUID());
            writer.write("\r\n"); // NOI18N
            
            // Created date
            long created = task.getCreatedDate();
            String datestring = sdf.format(new Date(created));
            writer.write("CREATED:"); // NOI18N
            writer.write(datestring);
            writer.write("\r\n"); // NOI18N
            
            // dtstart -- not yet implemented
            
            // due -- not yet implemented
            
            // organizer -- not yet implemented
            
            // summary: (Description)
            String desc = task.getSummary();
            if (desc != null && desc.length() > 0) {
                writeEscaped(writer, "SUMMARY", null, desc); // NOI18N
                writer.write("\r\n"); // NOI18N
            }
            
            // description (details)
            String details = task.getDetails();
            if (details != null && details.length() > 0) {
                writeEscaped(writer, "DESCRIPTION", null, details); // NOI18N
                writer.write("\r\n"); // NOI18N
            }
            
            // Priority
            if (task.getPriority() != SuggestionPriority.MEDIUM) {
                writer.write("PRIORITY:"); // NOI18N
                writer.write(Integer.toString(task.getPriority().intValue()));
                writer.write("\r\n");
            }
            
            // Class -- not implemented (always PRIVATE, right?) Also allowed:
            // PRIVATE, CONFIDENTIAL
            /* XXX Don't bother with this yet... waste of diskspace
               and parsing time -- only needed when we either export
               to XCS, or directly interoperate. There's too much
               missing yet to add partial support
            // For now, hardcode to private such that others don't get access
            writer.write("CLASS:PRIVATE\r\n"); // NOI18N
             */
            
            // attendee -- not implemented
            
            // Others not implemented:
            // dtstart, geo, location, organizer, percent, recurid, seq, status,
            // due, duration (both cannot occur)
            
            // Optional ones not implemented:
            // attach, attendee, categories, comment, contact, exdate, exrule,
            // rstatus, related, resources, rdate, rrule, x-prop (actually,
            // xprop is special, we will have those)
            
            
            writer.write("PERCENT-COMPLETE:"); // NOI18N
            writer.write(Integer.toString(task.getPercentComplete()));
            writer.write("\r\n"); // NOI18N
            
            boolean computed = task.isProgressComputed();
            if (computed) {
                writeEscaped(writer, "X-NETBEANS-PROGRESS-COMPUTED",  // NOI18N
                             null, "yes");
                writer.write("\r\n"); // NOI18N
            }
            
            writer.write("X-NETBEANS-EFFORT:"); // NOI18N
            writer.write(Integer.toString(task.getEffort()));
            writer.write("\r\n"); // NOI18N
            
            computed = task.isEffortComputed();
            if (computed) {
                writeEscaped(writer, "X-NETBEANS-EFFORT-COMPUTED",  // NOI18N
                             null, "yes");
                writer.write("\r\n"); // NOI18N
            }
            
            writer.write("X-NETBEANS-SPENT-TIME:"); // NOI18N
            writer.write(Integer.toString(task.getSpentTime()));
            writer.write("\r\n"); // NOI18N
            
            computed = task.isSpentTimeComputed();
            if (computed) {
                writeEscaped(writer, "X-NETBEANS-SPENT-TIME-COMPUTED",  // NOI18N
                             null, "yes");
                writer.write("\r\n"); // NOI18N
            }
            
            // Category (XXX standard allows MULTIPLE categories, I must handle
            // that when I parse back)
            String category = task.getCategory();
            if (category != null && category.length() > 0) {
                // TODO Write out multiple CATEGORIES lines instead
                // of a combined comma separated list which is what we're
                // doing here
                writeEscaped(writer, "CATEGORIES", null, category); // NOI18N
                writer.write("\r\n"); // NOI18N
            }
            
            // Last modified
            // Last Edited Date, if different than created
            long edited = task.getLastEditedDate();
            
            if (edited != created) {
                // They differ
                datestring = sdf.format(new Date(edited));
                writer.write("LAST-MODIFIED:"); // NOI18N
                writer.write(datestring);
                writer.write("\r\n"); // NOI18N
            }
            
            
            // Filename
            String filename = task.getFilename();
            if (filename != null && filename.length() > 0) {
                writeEscaped(writer, "X-NETBEANS-FILENAME",  // NOI18N
                             null, filename);
                writer.write("\r\n"); // NOI18N
            }
            
            // Line number
            int lineno = task.getLineNumber();
            if (lineno != 0) {
                writer.write("X-NETBEANS-LINE:"); // NOI18N
                writer.write(Integer.toString(lineno));
                writer.write("\r\n"); // NOI18N
            }
            
            // URL -- not yet implemented
            // 
            
            // Parent item
            // attribute reltype for related-to defaults to "PARENT" so we
            // don't need to specify it
            if (task.getParent() != null) {
                String parentuid = ((UserTask)task.getParent()).getUID();
                writer.write("RELATED-TO:"); // NOI18N
                // XXX does it need to be escaped?
                // Certainly my uids don't need to be, but other tools
                // may be generating UIDs with characters that need to
                // be escaped. Or does the spec forbid that?
                writer.write(parentuid);
                writer.write("\r\n"); // NOI18N
            }
  
            Date d = task.getDueDate();
            if (d != null) {
                writer.write("X-NETBEANS-DUETIME:"); // NOI18N
                writer.write(Long.toString(d.getTime()));
                writer.write("\r\n"); // NOI18N

                if (task.isDueAlarmSent()) {
                    writer.write("X-NETBEANS-DUE-SIGNALED:true\r\n"); // NOI18N                    
                }                
            }
//            AssociatedTime associatedTime = task.getAssociatedTime();
//            if (associatedTime != null) {
//                Date d = associatedTime.getStartTime();
//                if (d != null) {
//                    writer.write("X-NETBEANS-STARTTIME:"); // NOI18N
//                    writer.write(Long.toString(d.getTime()));
//                    writer.write("\r\n"); // NOI18N
//                }
//                d = associatedTime.getEndTime();
//                if (d != null) {
//                    writer.write("X-NETBEANS-ENDTIME:"); // NOI18N
//                    writer.write(Long.toString(d.getTime()));
//                    writer.write("\r\n"); // NOI18N
//                }
//                d = associatedTime.getDueDate();
//                if (d != null) {
//                    writer.write("X-NETBEANS-DUETIME:"); // NOI18N
//                    writer.write(Long.toString(d.getTime()));
//                    writer.write("\r\n"); // NOI18N
//                }
//                
//                if (associatedTime.isRecurrent()) {
//                    writer.write("X-NETBEANS-DUERECURRENT-INTERVAL:"); // NOI18N
//                    writer.write(Integer.toString(associatedTime.getInterval()));
//                    writer.write("\r\nX-NETBEANS-DUERECURRENT-MEASUREMENT:"); // NOI18N
//                    switch (associatedTime.getMeasurement()) {
//                        case AssociatedTime.DAY :
//                            writer.write("DAY\r\n"); // NOI18N
//                            break;
//                        case AssociatedTime.WEEK :
//                            writer.write("WEEK\r\n"); // NOI18N
//                            break;
//                        case AssociatedTime.MONTH :
//                            writer.write("MONTH\r\n"); // NOI18N
//                            break;
//                        case AssociatedTime.YEAR :
//                            writer.write("YEAR\r\n"); // NOI18N
//                            break;
//                        default :
//                            System.err.println("EINVAL"); //NOI18N
//                    }
//                }
//            }
            
            // Write out unsupported tags on this VTODO
            if (taskHashMap != null) {
                StringBuffer sb = (StringBuffer)taskHashMap.get(task);
                if (sb != null) {
                    // The string is stored in folded format!
                    writer.write(sb.toString());
                }
            }
            
            writer.write("END:VTODO\r\n"); // NOI18N
            
            // Recurse over subtasks
            // XXX do the other tags here...
            Iterator it = task.subtasksIterator();
            while (it.hasNext()) {
                UserTask subtask = (UserTask)it.next();
                writeTask(writer, subtask, sdf);
            }
        } catch (IOException e) {
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
        }
    }
    
    /**
     * Write out a content line escaped according to the spec:
     * break at 75 chars, add escapes to certain characters, etc.
     *
     * @param writer the writer used to write the data
     * @param name the name of the tag to write (without the ':')
     * @param param the param of the field
     * @param value the value to write
     */
    private void writeEscaped(Writer writer, String name, String param, String value) throws IOException {
        int col = name.length();
        writer.write(name);
        
        if (param != null) {
            col += param.length() + 1; // NOI18N
            writer.write(" "); // NOI18N
            writer.write(param);
        }
        ++col;
        writer.write(":");
        
        int n = value.length();
        for (int i = 0; i < n; i++) {
            char c = value.charAt(i);
            switch (c) {
                case '\n':
                    writer.write("\\n"); // NOI18N
                    col++; // One extra char expansion
                    break;
                case ';':
                case ',':
                case '\\':
                    // Escape the character by preceding it by a "\"
                    writer.write('\\');
                    col++; // One extra char expansion
                    // NOTE FALL THROUGH!
                default:
                    writer.write(c);
                    break;
            }
            
            col++;
            if (col >= 75) {
                col = 1; // for the space on the next line
                writer.write("\r\n "); // NOI18N   note the space - important
            }
        }
    }
        
    private Reader reader = null;
    private int lineno = 0;
    private int prevChar = -1;
    
    private StringBuffer nsb = new StringBuffer(400); // Name
    private StringBuffer psb = new StringBuffer(400); // Param
    private StringBuffer vsb = new StringBuffer(400); // Value
    
    /** Return most recently parsed value nextContentLine */
    private String getValue() {
        return vsb.toString();
    }
    
    /** Return most recently parsed value nextContentLine */
    private String getName() {
        String name = nsb.toString();
        if (name.length() == 0) {
            return null;
        } else {
            return name;
        }
    }
    
    /**
     * Get the most recently parsed parameter
     */
    private String getParam() {
        String param = psb.toString();
        if (param.length() == 0) {
            return null;
        } else {
            return param;
        }
    }
    
    /** Read (doing all the ical unfolding) the next content line.
     * Side effects the reader object and the lineno.
     * @return The next content line, or null if there is some
     * I/O problem preventing us from continuing (e.g. EOF).
     */
    private void processContentLine() throws IOException {
        // ignore it - and locate the next field
        
        // Reuse string buffers for improved efficiency
        nsb.setLength(0);
        psb.setLength(0);
        vsb.setLength(0);
        
        if (prevChar != -1) {
            nsb.append((char)prevChar);
        }
        
        // Read in characters, doing substitutions as necessary
        boolean escape = (prevChar == '\\');
        prevChar = -1;
        StringBuffer sb = nsb; // Processing name
        boolean processingName = true; // may not need these flags anymore, use sb
        boolean processingValue = false;
        
        while (true) {
            int ci = reader.read();
            if (ci == -1) {
                // End of stream
                return;
            }
            char c = (char)ci;
            // See section 4.3.11 in rfc 2445
            if (escape) {
                escape = false;
                switch (c) {
                    case '\\':
                        sb.append('\\');
                        break;
                    case 'n':
                        sb.append('\n');
                        break;
                    case 'N':
                        sb.append('N');
                        break;
                    case ';':
                        sb.append(';');
                        break;
                    case ',':
                        sb.append(',');
                        break;
                    default:
                        // Error - illegal input. For now I guess
                        // we'll just pass the escape through...
                        sb.append('\\');
                        sb.append(c);
                }
            } else {
                switch (c) {
                    case '\\':
                        escape = true;
                        break;
                    case ' ':
                        if (processingName) {
                            processingName = false;
                            sb = psb;
                        } else if (processingValue) {
                            sb.append(c);
                        }
                        break;
                    case ';':
                        if (processingName) {
                            processingName = false;
                            sb = psb;
                        } else if (processingValue) {
                            sb.append(c);
                        }
                        break;
                    case ':':
                        if (processingValue) {
                            // Error in input - I've seen Korganizer do this;
                            // they're supposed to escape : but they didn't
                            sb.append(c);
                        } else {
                            sb = vsb;
                            processingValue = true;
                            processingName = false;
                        }
                        break;
                    case '\r':
                        // The spec calls for lines to be terminated with \r\n
                        // but internally we don't want \r's
                        break;
                    case '\n':
                        // New line
                        lineno++;
                        prevChar = reader.read();
                        while (prevChar == '\n') {
                            // Skip blank lines
                            prevChar = reader.read();
                            lineno++;
                        }
                        
                        // @TODO TROND: Si meg... dette stemmer vel ikke helt??
                        // jeg skal jo ogs? godta HTAB (ASCII 9!!!)
                        if (prevChar == ' ' || prevChar == '\t') {
                            // Aha! Line continuation -- we've just
                            // unfolded a line, keep processing
                            break;
                        } else { // includes case where prevChar==-1: EOF
                            // No, this is a new content line so
                            // consider ourselves done with this line
                            return;
                        }
                    default:
                        sb.append(c);
                }
            }
        }
    }
    
    /**
     * Stash away a bulk of data in the writer
     * @param writer where to store data
     * @param name the last name read from the stream
     * @param param the last param read from the stream
     * @param value the last value read from the stream
     * @throws IOException if anything goes wrong...
     */
    private void stashBulk(Writer writer, String name, String param, String value) throws IOException {
        int stack = 0;
        
        writeEscaped(writer, name, param, value);
        writer.write("\r\n"); // NOI18N
        
        boolean done = false;
        while (!done) {
            processContentLine();
            name = getName();
            if (name == null) {
                break;
            }
            value = getValue();
            param = getParam();
            
            if (name.equals("BEGIN")) { // NOI18N
                ++stack;
            } else if (name.equals("END")) { // NOI18N
                if (stack == 0) {
                    done = true;
                } else {
                    --stack;
                }
            }
            
            writeEscaped(writer, name, param, value);
            writer.write("\r\n"); // NOI18N
        }
    }
    
    /**
     * Read and parse a single VTODO entry.
     * @param list The list of usertasks
     * @param formatter A date formatter object used to parse dates.
     * @return the complete VTODO entry or null
     */
    private Task readVTODO(UserTaskList list, UserTask prev, SimpleDateFormat formatter) throws IOException {
        UserTask task = new UserTask("", list);
        task.setSilentUpdate(true, false);
        task.setLastEditedDate(System.currentTimeMillis());
        StringWriter writer = null;
        String related = null;
        
        while (true) {
            processContentLine();
            String name = getName();
            if (name == null) {
                // incomplete entry, throw it away!!! @@@
                // but what happens to the stream????
                return null;
            }
            String value = getValue();
            String param = getParam();
            
            if (name.equals("BEGIN")) { // NOI18N
                if (writer == null) {
                    writer = new StringWriter();
                }
                stashBulk(writer, name, param, value);
            }
            
            if (name.equals("END")) { // NOI18N
                break;  // @@@ Should I verify that this is the end of a VTODO???
            } else if (name.equals("CREATED")) { // NOI18N
                try {
                    Date created = formatter.parse(value);
                    task.setCreatedDate(created.getTime());
                } catch (ParseException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if (name.equals("UID")) { // NOI18N
                task.setUID(value);
            } else if (name.equals("LAST-MODIFIED")) { // NOI18N
                try {
                    Date edited = formatter.parse(value);
                    task.setLastEditedDate(edited.getTime());
                } catch (ParseException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if (name.equals("PERCENT-COMPLETE")) { // NOI18N
                try {
                    int complete = Integer.parseInt(value);
                    task.setPercentComplete(complete);
                } catch (NumberFormatException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if (name.equals("X-NETBEANS-PROGRESS-COMPUTED")) { // NOI18N
                if (value.equals("yes"))
                    task.setProgressComputed(true);
            } else if (name.equals("PRIORITY")) { // NOI18N
                try {
                    int prio = Integer.parseInt(value);
                    task.setPriority(SuggestionPriority.getPriority(prio));
                } catch (NumberFormatException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if (name.equals("X-NETBEANS-EFFORT-COMPUTED")) { // NOI18N
                if (value.equals("yes"))
                    task.setEffortComputed(true);
            } else if (name.equals("X-NETBEANS-EFFORT")) { // NOI18N
                try {
                    int e = Integer.parseInt(value);
                    task.setEffort(e);
                } catch (NumberFormatException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if (name.equals("X-NETBEANS-SPENT-TIME-COMPUTED")) { // NOI18N
                if (value.equals("yes"))
                    task.setSpentTimeComputed(true);
            } else if (name.equals("X-NETBEANS-SPENT-TIME")) { // NOI18N
                try {
                    int e = Integer.parseInt(value);
                    task.setSpentTime(e);
                } catch (NumberFormatException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if (name.equals("CATEGORIES")) { // NOI18N
                String cat = value;
                String oldcat = task.getCategory();
                if ((oldcat != null) && (oldcat.length() > 0)) {
                    // Multiple categories.
                    // Just append
                    cat = oldcat + "," + cat;
                }
                task.setCategory(cat);
            } else if ("DESCRIPTION".equals(name)) { // NOI18N
                task.setDetails(value);
            } else if ("SUMMARY".equals(name)) { // NOI18N
                task.setSummary(value);
            } else if ("X-NETBEANS-FILENAME".equals(name)) { // NOI18N
                task.setFilename(value);
            } else if ("X-NETBEANS-LINE".equals(name)) { // NOI18N
                int lineno = 0;
                try {
                    lineno = Integer.parseInt(value);
                } catch (NumberFormatException e) {
                    ErrorManager.getDefault().notify(e);
                }
                task.setLineNumber(lineno);
            } else if ("RELATED-TO".equals(name)) { // NOI18N
                related = value;
//            } else if ("X-NETBEANS-STARTTIME".equals(name)) { // NOI18N  
//                long start = Long.MAX_VALUE;
//                try {
//                    start = Long.parseLong(value);
//                } catch (NumberFormatException e) {
//                    ErrorManager.getDefault().notify(e);
//                }
//
//                if (start != Long.MAX_VALUE) {
//                    if (associatedTime == null) {
//                        associatedTime = new AssociatedTime();
//                    }
//
//                    associatedTime.setStartTime(new java.util.Date(start));
//                }
//            } else if ("X-NETBEANS-ENDTIME".equals(name)) { // NOI18N
//                long end = Long.MAX_VALUE;
//                try {
//                    end = Long.parseLong(value);
//                } catch (NumberFormatException e) {
//                    ErrorManager.getDefault().notify(e);
//                }
//                if (end != Long.MAX_VALUE) {
//                    if (associatedTime == null) {
//                        associatedTime = new AssociatedTime();
//                    }
//                    
//                    associatedTime.setEndTime(new java.util.Date(end));
//                }
            } else if ("X-NETBEANS-DUETIME".equals(name)) { // NOI18N
                Date d = null;
                try {
                    d = new Date(Long.parseLong(value));
                    task.setDueDate(d);
                } catch (NumberFormatException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if ("DUE".equals(name)) { // NOI18N
                try {
                    Date due = formatter.parse(value);
                    task.setDueDate(due);
                } catch (ParseException e) {
                    ErrorManager.getDefault().notify(e);
                }
            } else if ("X-NETBEANS-DUE-SIGNALED".equals(name)) {
                task.setDueAlarmSent(true);                
//            } else if ("X-NETBEANS-DUERECURRENT-INTERVAL".equals(name)) { // NOI18N
//                int interval = 0;
//                try {
//                    interval = Integer.parseInt(value);
//                } catch (NumberFormatException e) {
//                    ErrorManager.getDefault().notify(e);
//                }
//                
//                if (associatedTime == null) {
//                    associatedTime = new AssociatedTime();
//                }
//                
//                associatedTime.setInterval(interval);
//            } else if ("X-NETBEANS-DUERECURRENT-MEASUREMENT".equals(name)) { // NOI18N
//                int measurement = AssociatedTime.DAY;
//                
//                if ("DAY".equals(value)) { // NOI18N
//                    measurement = AssociatedTime.DAY;
//                } else if ("WEEK".equals(value)) { // NOI18N
//                    measurement = AssociatedTime.WEEK;
//                } else if ("MONTH".equals(value)) { // NOI18N
//                    measurement = AssociatedTime.MONTH;
//                } else if ("YEAR".equals(value)) { // NOI18N
//                    measurement = AssociatedTime.YEAR;   
//                } 
//
//                if (associatedTime == null) {
//                    associatedTime = new AssociatedTime();
//                }
//                associatedTime.setMeasurement(measurement);
            } else {
                // stash away the line!!!
                if (writer == null) {
                    writer = new StringWriter();
                }
                
                writeEscaped(writer, name, param, value);
                writer.write("\r\n"); // NOI18N
            }
        }
        
//        if (associatedTime != null) {
//            task.setAssociatedTime(associatedTime);
//        }
        
        if (writer != null) {
            if (taskHashMap == null) {
                taskHashMap = new HashMap();
            }
            taskHashMap.put(task, writer.getBuffer());
        }
        
        UserTask alreadyExists = list.findItem(list.getTasks().iterator(), task.getUID());
        if (alreadyExists != null) {
            // I should replace alreadyexists with task...
            Task parent = alreadyExists.getParent();
            parent.removeSubtask(alreadyExists);
            parent.addSubtask(task);
            
            Iterator li = alreadyExists.subtasksIterator();
            while (li.hasNext()) {
                Task c = (Task)li.next();
                alreadyExists.removeSubtask(c);
                task.addSubtask(c);
            }
        } else if (related != null) {
            // the parent setting !!
            UserTask parent;
            if (prev != null && prev.getUID().equals(related)) {
                parent = prev;
            } else {
                parent = list.findItem(list.getTasks().iterator(), related);
            }
            
            if (parent != null) {
                parent.addSubtask(task, true);
            }
        }
        
        return task;
    }
    
    /**
     * Read an iCalendar stream, and store all of the VTODOs inside the tasklist.
     * Keep all unrecognized lines in otherItems...
     * @param list where to store the list
     * @param reader the reader to use on the input stream
     * @param interactive ???
     * @throws IOException if a read error occurs
     * @throws UnknownFileFormatException if I somehow believes that this is no
     *         iCalendar format...
     * @return true if success
     */
    public boolean readList(TaskList list, InputStream reader, boolean interactive) throws IOException, UnknownFormatException 
    {
        this.reader = new InputStreamReader(reader, "UTF-8");
        UserTaskList ulist = (UserTaskList)list;
        UserTask prev = null;
        
        StringWriter writer = new StringWriter();
        
        SimpleDateFormat formatter = null;
        formatter = new SimpleDateFormat(DATEFORMATZ);
        formatter.setTimeZone(new SimpleTimeZone(0, "GMT")); // NOI18N
        
        do {
            processContentLine();
            String name = getName();
            if (name == null) {
                break;
            } else if (name.length() == 0 || name.equals("\r")) { // NOI18N
                continue; // skip empty lines....
            }
            
            String value = getValue();
            String param = getParam();
            
            if (name.equals("BEGIN")) { // NOI18N
                if (value == null) {
                    // SYNTAX ERROR!! XXX What to do??
                    return false;
                }
                
                if (value.equals("VTODO")) { // NOI18N
                    // Call a sub-function to process this line!!!
                    Task task = readVTODO(ulist, prev, formatter);
                    
                    if (task != null) {
                        if (task.getParent() == null) {
                            ulist.appendTask(task);
                        }
                        task.setSilentUpdate(false, false);
                        prev = (UserTask)task;
                    }
                } else if (value.equals("VCALENDAR")) { // NOI18N
                    // Just swallow
                } else {
                    // Stash away everything up to the corresponding END
                    stashBulk(writer, name, param, value);
                }
            } else if (name.equals("PRODID")) { // NOI18N
                // just swallow
            } else if (name.equals("VERSION")) { // NOI18N
                // just swallow
            } else if (name.equals("END")) { // NOI18N
                // Just swallow
            } else if (name.equals("CALSCALE")) { // NOI18N
                // Evolution (if not others) adds CALSCALE:GREGORIAN near
                // the top of the file.
                // Just swallow
                // ...or make sure that value=GREGORIAN and if not, warn user?
            } else if (name.equals("TZ")) { // NOI18N
                formatter = new SimpleDateFormat(DATEFORMAT);
                if (!value.equals("GMT")) {
                    // Use a date format without a timezone at the end
                    // of it, since they're probably not included now
                    // that the timezone has been reported once and for
                    // all. GnomeCal writes tasklists in this format.
                    TimeZone tz = TimeZone.getTimeZone(value);
                    if (tz != null) {
                        formatter.setTimeZone(tz);
                    } else {
                        ErrorManager.getDefault().log("Timezone \"" + value + "\" unknown. Times in imported task(s) may be incorrect by up to 24 hours");
                    } 
                }
            } else {
                if (lineno <= 1) {
                    // XXX Hmmm I should probably read the RFC and see
                    // what I could XXX expect.. For now, just treat
                    // it as an incorrect file format.
                    //
                    // See RFC 2446 chapter 5 - it specifies which
                    // entries must be handled and which can be
                    // ignored.
                    //
                    String msg = NbBundle.getMessage(iCalSupport.class, "ProbablyNotiCalFormat"); // NOI18N
                    throw new UnknownFormatException(msg);
                } else {
                    // Error on some other line: probably an
                    // unsupported tag (For example, I discovered that
                    // it claimed evolution-task files aren't in ics
                    // format because it came across the tag CALSCALE
                    // and bailed.)
                    if (name.startsWith("X-")) { // NOI18N
                        ErrorManager.getDefault().log(
                           ErrorManager.WARNING, "WARNING: " +
                           "Ignoring nonstandard entry (line " + lineno +
                           "): name=" + name + ", value=" + value + ", param=" +
                           param);
                    } else {
                        ErrorManager.getDefault().log(
                           ErrorManager.WARNING, "WARNING: " +
                           "Unsupported iCalendar file entry (line " + lineno +
                           "): name=" + name + ", value=" + value + ", param=" +
                           param);
                    }
                }
            }
        } while (true);
        
        otherItems = writer.getBuffer().toString();
        if (otherItems.length() == 0) {
            otherItems = null;
        }
        return true;
    }
    
    /**
     * The iCalendar supports other "tags" for a VTODO item than Tasklist. In
     * order to avoid loosing such information, these unknown tags are stored
     * inside the iCalSupport object.
     * The hashmap contains an usertask and a StringBuffer
     */
    private HashMap taskHashMap;
    
    /**
     * The iCalendar format supports other items than VTODO's. In order to
     * avoid loosing such information, these unknown tags are stored inside the
     * iCalSupport object.
     */
    private java.lang.String otherItems;
}
... 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.