|
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;
}
|