001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/calendar/tags/sakai_2-4-1/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/readers/MeetingMakerReader.java $
003: * $Id: MeetingMakerReader.java 11630 2006-07-06 14:55:43Z bkirschn@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.calendar.impl.readers;
021:
022: import java.io.BufferedReader;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.util.Date;
026: import java.util.Calendar;
027: import java.util.GregorianCalendar;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032:
033: import org.sakaiproject.calendar.impl.GenericCalendarImporter;
034: import org.sakaiproject.exception.ImportException;
035: import org.sakaiproject.time.api.TimeBreakdown;
036: import org.sakaiproject.util.ResourceLoader;
037:
038: /**
039: * This class parses an import file from MeetingMaker.
040: */
041: public class MeetingMakerReader extends Reader {
042: private ResourceLoader rb = new ResourceLoader("calendarimpl");
043:
044: private static final String CONTACT_SECTION_HEADER = "Contacts";
045: private static final String TODO_SECTION_HEADER = "Todos";
046: private static final String EVENT_SECTION_HEADER = "Events";
047:
048: public static final String TITLE_HEADER = "Title";
049: public static final String LOCATION_HEADER = "Location";
050: public static final String DATE_HEADER = "Date";
051: public static final String START_TIME_HEADER = "Start Time";
052: public static final String DURATION_HEADER = "Duration";
053: public static final String AGENDA_NOTES_HEADER = "Agenda/Notes";
054:
055: /**
056: * Default constructor
057: */
058: public MeetingMakerReader() {
059: super ();
060: }
061:
062: /* (non-Javadoc)
063: * @see org.sakaiproject.tool.calendar.ImportReader#importStreamFromDelimitedFile(java.io.InputStream, org.sakaiproject.tool.calendar.ImportReader.ReaderImportRowHandler)
064: */
065: public void importStreamFromDelimitedFile(InputStream stream,
066: ReaderImportRowHandler handler) throws ImportException {
067: boolean inEventSection = false;
068: boolean alreadySawEventSection = false;
069:
070: BufferedReader bufferedReader = getReader(stream);
071:
072: ColumnHeader columnDescriptionArray[] = null;
073:
074: int lineNumber = 1;
075:
076: boolean readDone = false;
077:
078: while (!readDone) {
079: try {
080: // Prepare the column map on the first line.
081: String lineBuffer = bufferedReader.readLine();
082:
083: // See if we have exhausted the input
084: if (lineBuffer == null) {
085: break;
086: }
087:
088: // See if we're in the "Event" section of the import file.
089: if (EVENT_SECTION_HEADER.equals(lineBuffer.trim())) {
090: inEventSection = true;
091: alreadySawEventSection = true;
092: lineNumber++;
093: continue;
094: } else if (TODO_SECTION_HEADER
095: .equals(lineBuffer.trim())) {
096: inEventSection = false;
097: lineNumber++;
098: continue;
099: } else if (CONTACT_SECTION_HEADER.equals(lineBuffer
100: .trim())) {
101: inEventSection = false;
102: lineNumber++;
103: continue;
104: } else if (lineBuffer.toString().startsWith(
105: "Time Zone:")) {
106: // Ignore the timezone line.
107: lineNumber++;
108: continue;
109: }
110:
111: // If we leave the event section and see another non-event section header,
112: // then stop reading the stream since there is only one event section.
113: if (alreadySawEventSection && !inEventSection) {
114: readDone = true;
115: continue;
116: }
117:
118: if (inEventSection) {
119: if (columnDescriptionArray == null) {
120: String[] columns = lineBuffer.split("\t");
121:
122: trimLeadingTrailingQuotes(columns);
123:
124: columnDescriptionArray = buildColumnDescriptionArray(columns);
125:
126: // Immediately start the next loop.
127: lineNumber++;
128: continue;
129: } else {
130: // Empty lines are preserved at some points, like in quoted string
131: // descriptions, but at this point, just skip over them.
132: if (lineBuffer.trim().length() == 0) {
133: lineNumber++;
134: continue;
135: }
136:
137: String[] columns = lineBuffer.split("\t");
138:
139: // If the last column starts with a double-quote, then keep
140: // concatentating lines until we see a line that ends
141: // with double-quotes.
142: String endingColumnValue = columns[columns.length - 1]
143: .trim();
144:
145: if (endingColumnValue.startsWith("\"")
146: && (!endingColumnValue.endsWith("\"") || endingColumnValue
147: .length() == 1)) {
148: String continuationLineBuffer = bufferedReader
149: .readLine();
150:
151: // See if we have exhausted the input
152: while (continuationLineBuffer != null) {
153: columns[columns.length - 1] = columns[columns.length - 1]
154: + "\n" + continuationLineBuffer;
155:
156: // Break out when we hit the end of the quoted string.
157: if (continuationLineBuffer.trim()
158: .endsWith("\"")) {
159: break;
160: }
161:
162: continuationLineBuffer = bufferedReader
163: .readLine();
164: }
165: }
166:
167: // Remove trailing/leading quotes from all columns.
168: trimLeadingTrailingQuotes(columns);
169:
170: handler.handleRow(processLine(
171: columnDescriptionArray, lineNumber,
172: columns));
173: }
174: }
175: } catch (IOException e) {
176: // We'll get an exception when we've exhauster
177: readDone = true;
178: }
179:
180: // If we get this far, increment the line counter.
181: lineNumber++;
182: }
183: }
184:
185: /* (non-Javadoc)
186: * @see org.sakaiproject.tool.calendar.schedimportreaders.Reader#filterEvents(java.util.List, java.lang.String[])
187: */
188: public List filterEvents(List events, String[] customFieldNames)
189: throws ImportException {
190: Iterator it = events.iterator();
191: int lineNumber = 1;
192:
193: //
194: // Convert the date/time fields as they appear in the Outlook import to
195: // be a synthesized start/end timerange.
196: //
197: while (it.hasNext()) {
198: Map eventProperties = (Map) it.next();
199:
200: Date startTime = (Date) eventProperties
201: .get(GenericCalendarImporter.START_TIME_PROPERTY_NAME);
202: TimeBreakdown startTimeBreakdown = null;
203:
204: if (startTime != null) {
205: // if the source time zone were known, this would be
206: // a good place to set it: startCal.setTimeZone()
207: GregorianCalendar startCal = new GregorianCalendar();
208: startCal.setTimeInMillis(startTime.getTime());
209: startTimeBreakdown = getTimeService().newTimeBreakdown(
210: 0, 0, 0, startCal.get(Calendar.HOUR_OF_DAY),
211: startCal.get(Calendar.MINUTE),
212: startCal.get(Calendar.SECOND), 0);
213: } else {
214: Integer line = new Integer(lineNumber);
215: String msg = (String) rb.getFormattedMessage(
216: "err_no_stime_on", new Object[] { line });
217: throw new ImportException(msg);
218: }
219:
220: Integer durationInMinutes = (Integer) eventProperties
221: .get(GenericCalendarImporter.DURATION_PROPERTY_NAME);
222:
223: if (durationInMinutes == null) {
224: Integer line = new Integer(lineNumber);
225: String msg = (String) rb.getFormattedMessage(
226: "err_no_dtime_on", new Object[] { line });
227: throw new ImportException(msg);
228: }
229:
230: Date endTime = new Date(startTime.getTime()
231: + (durationInMinutes.longValue() * 60 * 1000));
232:
233: TimeBreakdown endTimeBreakdown = null;
234:
235: if (endTime != null) {
236: // if the source time zone were known, this would be
237: // a good place to set it: endCal.setTimeZone()
238: GregorianCalendar endCal = new GregorianCalendar();
239: endCal.setTimeInMillis(endTime.getTime());
240: endTimeBreakdown = getTimeService().newTimeBreakdown(0,
241: 0, 0, endCal.get(Calendar.HOUR_OF_DAY),
242: endCal.get(Calendar.MINUTE),
243: endCal.get(Calendar.SECOND), 0);
244: }
245:
246: Date startDate = (Date) eventProperties
247: .get(GenericCalendarImporter.DATE_PROPERTY_NAME);
248:
249: // if the source time zone were known, this would be
250: // a good place to set it: startCal.setTimeZone()
251: GregorianCalendar startCal = new GregorianCalendar();
252: if (startDate != null)
253: startCal.setTimeInMillis(startDate.getTime());
254:
255: startTimeBreakdown.setYear(startCal.get(Calendar.YEAR));
256: startTimeBreakdown
257: .setMonth(startCal.get(Calendar.MONTH) + 1);
258: startTimeBreakdown.setDay(startCal
259: .get(Calendar.DAY_OF_MONTH));
260:
261: endTimeBreakdown.setYear(startCal.get(Calendar.YEAR));
262: endTimeBreakdown.setMonth(startCal.get(Calendar.MONTH) + 1);
263: endTimeBreakdown
264: .setDay(startCal.get(Calendar.DAY_OF_MONTH));
265:
266: eventProperties.put(
267: GenericCalendarImporter.ACTUAL_TIMERANGE,
268: getTimeService().newTimeRange(
269: getTimeService().newTimeLocal(
270: startTimeBreakdown),
271: getTimeService().newTimeLocal(
272: endTimeBreakdown), true, false));
273:
274: lineNumber++;
275: }
276:
277: return events;
278: }
279:
280: /* (non-Javadoc)
281: * @see org.sakaiproject.tool.calendar.schedimportreaders.Reader#getDefaultColumnMap()
282: */
283: public Map getDefaultColumnMap() {
284: Map columnHeaderMap = new HashMap();
285:
286: columnHeaderMap.put(TITLE_HEADER,
287: GenericCalendarImporter.TITLE_PROPERTY_NAME);
288: columnHeaderMap.put(AGENDA_NOTES_HEADER,
289: GenericCalendarImporter.DESCRIPTION_PROPERTY_NAME);
290: columnHeaderMap.put(DATE_HEADER,
291: GenericCalendarImporter.DATE_PROPERTY_NAME);
292: columnHeaderMap.put(START_TIME_HEADER,
293: GenericCalendarImporter.START_TIME_PROPERTY_NAME);
294: columnHeaderMap.put(DURATION_HEADER,
295: GenericCalendarImporter.DURATION_PROPERTY_NAME);
296: columnHeaderMap.put(LOCATION_HEADER,
297: GenericCalendarImporter.LOCATION_PROPERTY_NAME);
298:
299: return columnHeaderMap;
300: }
301: }
|