0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/calendar/tags/sakai_2-4-1/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/GenericCalendarImporter.java $
0003: * $Id: GenericCalendarImporter.java 13515 2006-08-11 16:19:13Z bkirschn@umich.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.calendar.impl;
0021:
0022: import java.io.InputStream;
0023: import java.text.DateFormat;
0024: import java.text.ParseException;
0025: import java.text.SimpleDateFormat;
0026: import java.util.ArrayList;
0027: import java.util.Collection;
0028: import java.util.Date;
0029: import java.util.HashMap;
0030: import java.util.Iterator;
0031: import java.util.List;
0032: import java.util.Map;
0033: import java.util.Stack;
0034: import java.util.Vector;
0035:
0036: import org.apache.commons.logging.Log;
0037: import org.apache.commons.logging.LogFactory;
0038: import org.sakaiproject.calendar.api.Calendar;
0039: import org.sakaiproject.calendar.api.CalendarEvent;
0040: import org.sakaiproject.calendar.api.CalendarEventEdit;
0041: import org.sakaiproject.calendar.api.CalendarImporterService;
0042: import org.sakaiproject.calendar.api.CalendarService;
0043: import org.sakaiproject.calendar.api.RecurrenceRule;
0044: import org.sakaiproject.calendar.impl.readers.CSVReader;
0045: import org.sakaiproject.calendar.impl.readers.MeetingMakerReader;
0046: import org.sakaiproject.calendar.impl.readers.OutlookReader;
0047: import org.sakaiproject.calendar.impl.readers.Reader;
0048: import org.sakaiproject.entity.api.Reference;
0049: import org.sakaiproject.entity.api.ResourceProperties;
0050: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0051: import org.sakaiproject.exception.ImportException;
0052: import org.sakaiproject.exception.PermissionException;
0053: import org.sakaiproject.time.api.Time;
0054: import org.sakaiproject.time.api.TimeRange;
0055: import org.sakaiproject.time.api.TimeService;
0056: import org.sakaiproject.util.FormattedText;
0057: import org.sakaiproject.util.ResourceLoader;
0058: import org.w3c.dom.Document;
0059: import org.w3c.dom.Element;
0060:
0061: /**
0062: * This class provides common importing functionality after a lower-level reader has taken care of the peculiarities of a given import format.
0063: */
0064: public class GenericCalendarImporter implements CalendarImporterService {
0065: /** Our logger. */
0066: private static Log M_log = LogFactory
0067: .getLog(GenericCalendarImporter.class);
0068:
0069: public static final String LOCATION_PROPERTY_NAME = "Location";
0070:
0071: public static final String LOCATION_DEFAULT_COLUMN_HEADER = "Location";
0072:
0073: public static final String ITEM_TYPE_PROPERTY_NAME = "ItemType";
0074:
0075: public static final String ITEM_TYPE_DEFAULT_COLUMN_HEADER = "Type";
0076:
0077: public static final String FREQUENCY_PROPERTY_NAME = "Frequency";
0078:
0079: public static final String FREQUENCY_DEFAULT_COLUMN_HEADER = "Frequency";
0080:
0081: public static final String END_TIME_PROPERTY_NAME = "EndTime";
0082:
0083: public static final String END_TIME_DEFAULT_COLUMN_HEADER = "Ends";
0084:
0085: public static final String DURATION_PROPERTY_NAME = "Duration";
0086:
0087: public static final String DURATION_DEFAULT_COLUMN_HEADER = "Duration";
0088:
0089: public static final String START_TIME_PROPERTY_NAME = "StartTime";
0090:
0091: public static final String START_TIME_DEFAULT_COLUMN_HEADER = "Start";
0092:
0093: public static final String DATE_PROPERTY_NAME = "Date";
0094:
0095: public static final String DATE_DEFAULT_COLUMN_HEADER = "Date";
0096:
0097: public static final String DESCRIPTION_PROPERTY_NAME = "Description";
0098:
0099: public static final String DESCRIPTION_DEFAULT_COLUMN_HEADER = "Description";
0100:
0101: public static final String TITLE_PROPERTY_NAME = "Title";
0102:
0103: public static final String TITLE_DEFAULT_COLUMN_HEADER = "Title";
0104:
0105: public static final String INTERVAL_PROPERTY_NAME = "Interval";
0106:
0107: public static final String INTERVAL_DEFAULT_COLUMN_HEADER = "Interval";
0108:
0109: public static final String ENDS_PROPERTY_NAME = "Ends";
0110:
0111: public static final String ENDS_DEFAULT_COLUMN_HEADER = "Ends";
0112:
0113: public static final String REPEAT_PROPERTY_NAME = "Repeat";
0114:
0115: public static final String REPEAT_DEFAULT_COLUMN_HEADER = "Repeat";
0116:
0117: // Injected Property Names - These properties are synthesized during the
0118: // translation process.
0119: public static final String ACTUAL_TIMERANGE = "ActualStartTime";
0120:
0121: // Map of readers for various formats. Keyed by import type.
0122: private final Map readerMap = new HashMap();
0123:
0124: public static final DateFormat TIME_FORMATTER = new SimpleDateFormat(
0125: "hh:mm a");
0126:
0127: public static final DateFormat TIME_FORMATTER_WITH_SECONDS = new SimpleDateFormat(
0128: "hh:mm:ss a");
0129:
0130: static final DateFormat time24HourFormatter = new SimpleDateFormat(
0131: "HH:mm");
0132:
0133: static final DateFormat time24HourFormatterWithSeconds = new SimpleDateFormat(
0134: "HH:mm:ss");
0135:
0136: private ResourceLoader rb = new ResourceLoader("calendarimpl");
0137:
0138: // These are injected at runtime by Spring.
0139: private CalendarService calendarService = null;
0140:
0141: private TimeService timeService = null;
0142:
0143: /*
0144: * This class is used as a "prototype" event that may be added to a real calendar. We emulate enough of a calendar event to hold all the information necessary to create a real event.
0145: */
0146: public class PrototypeEvent implements CalendarEventEdit {
0147: private RecurrenceRule recurrenceRule;
0148:
0149: private Map fields;
0150:
0151: private String location;
0152:
0153: private String type;
0154:
0155: private String description;
0156:
0157: private String displayName;
0158:
0159: private TimeRange timeRange;
0160:
0161: private int lineNumber;
0162:
0163: /**
0164: * Default constructor
0165: */
0166: public PrototypeEvent() {
0167: fields = new HashMap();
0168: }
0169:
0170: /*
0171: * (non-Javadoc)
0172: *
0173: * @see org.sakaiproject.calendar.api.CalendarEvent#getRange()
0174: */
0175: public TimeRange getRange() {
0176: return this .timeRange;
0177: }
0178:
0179: /*
0180: * (non-Javadoc)
0181: *
0182: * @see org.sakaiproject.calendar.api.CalendarEvent#getDisplayName()
0183: */
0184: public String getDisplayName() {
0185: return this .displayName;
0186: }
0187:
0188: /*
0189: * (non-Javadoc)
0190: *
0191: * @see org.sakaiproject.calendar.api.CalendarEvent#getDescription()
0192: */
0193: public String getDescription() {
0194: return FormattedText
0195: .convertFormattedTextToPlaintext(description);
0196: }
0197:
0198: /*
0199: * (non-Javadoc)
0200: *
0201: * @see org.sakaiproject.calendar.api.CalendarEvent#getType()
0202: */
0203: public String getType() {
0204: return this .type;
0205: }
0206:
0207: /*
0208: * (non-Javadoc)
0209: *
0210: * @see org.sakaiproject.calendar.api.CalendarEvent#getLocation()
0211: */
0212: public String getLocation() {
0213: return this .location;
0214: }
0215:
0216: /*
0217: * (non-Javadoc)
0218: *
0219: * @see org.sakaiproject.calendar.api.CalendarEvent#getField(java.lang.String)
0220: */
0221: public String getField(String fieldName) {
0222: return (String) this .fields.get(fieldName);
0223: }
0224:
0225: /*
0226: * (non-Javadoc)
0227: *
0228: * @see org.sakaiproject.calendar.api.CalendarEvent#getCalendarReference()
0229: */
0230: public String getCalendarReference() {
0231: // Stub routine only
0232: return null;
0233: }
0234:
0235: /*
0236: * (non-Javadoc)
0237: *
0238: * @see org.sakaiproject.calendar.api.CalendarEvent#getRecurrenceRule()
0239: */
0240: public RecurrenceRule getRecurrenceRule() {
0241: // Stub routine only
0242: return this .recurrenceRule;
0243: }
0244:
0245: /*
0246: * (non-Javadoc)
0247: *
0248: * @see org.sakaiproject.service.legacy.entity.Resource#getUrl()
0249: */
0250: public String getUrl() {
0251: // Stub routine only
0252: return null;
0253: }
0254:
0255: /*
0256: * (non-Javadoc)
0257: *
0258: * @see org.sakaiproject.service.legacy.entity.Resource#getReference()
0259: */
0260: public String getReference() {
0261: // Stub routine only
0262: return null;
0263: }
0264:
0265: /**
0266: * @inheritDoc
0267: */
0268: public String getReference(String rootProperty) {
0269: return getReference();
0270: }
0271:
0272: /**
0273: * @inheritDoc
0274: */
0275: public String getUrl(String rootProperty) {
0276: return getUrl();
0277: }
0278:
0279: /*
0280: * (non-Javadoc)
0281: *
0282: * @see org.sakaiproject.service.legacy.entity.Resource#getId()
0283: */
0284: public String getId() {
0285: // Stub routine only
0286: return null;
0287: }
0288:
0289: /*
0290: * (non-Javadoc)
0291: *
0292: * @see org.sakaiproject.service.legacy.entity.Resource#getProperties()
0293: */
0294: public ResourceProperties getProperties() {
0295: // Stub routine only
0296: return null;
0297: }
0298:
0299: /*
0300: * (non-Javadoc)
0301: *
0302: * @see org.sakaiproject.service.legacy.entity.Resource#toXml(org.w3c.dom.Document, java.util.Stack)
0303: */
0304: public Element toXml(Document arg0, Stack arg1) {
0305: // Stub routine only
0306: return null;
0307: }
0308:
0309: /*
0310: * (non-Javadoc)
0311: *
0312: * @see java.lang.Comparable#compareTo(java.lang.Object)
0313: */
0314: public int compareTo(Object o) {
0315: // Stub routine only
0316: return 0;
0317: }
0318:
0319: /*
0320: * (non-Javadoc)
0321: *
0322: * @see org.sakaiproject.service.legacy.entity.AttachmentContainer#getAttachments()
0323: */
0324: public List getAttachments() {
0325: // Stub routine only
0326: return null;
0327: }
0328:
0329: /*
0330: * (non-Javadoc)
0331: *
0332: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setRange(org.sakaiproject.service.legacy.time.TimeRange)
0333: */
0334: public void setRange(TimeRange timeRange) {
0335: this .timeRange = timeRange;
0336: }
0337:
0338: /*
0339: * (non-Javadoc)
0340: *
0341: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setDisplayName(java.lang.String)
0342: */
0343: public void setDisplayName(String displayName) {
0344: this .displayName = displayName;
0345: }
0346:
0347: /*
0348: * (non-Javadoc)
0349: *
0350: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setDescription(java.lang.String)
0351: */
0352: public void setDescription(String description) {
0353: this .description = FormattedText
0354: .convertPlaintextToFormattedText(description);
0355: }
0356:
0357: /*
0358: * (non-Javadoc)
0359: *
0360: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setType(java.lang.String)
0361: */
0362: public void setType(String type) {
0363: this .type = type;
0364: }
0365:
0366: /*
0367: * (non-Javadoc)
0368: *
0369: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setLocation(java.lang.String)
0370: */
0371: public void setLocation(String location) {
0372: this .location = location;
0373: }
0374:
0375: /**
0376: * Returns true if current user is thhe event's owner/creator
0377: * @return boolean true or false
0378: */
0379: public boolean isUserOwner() {
0380: // Stub routine only
0381: return true;
0382:
0383: }
0384:
0385: /**
0386: * Gets the event creator (userid), if any (cover for PROP_CREATOR).
0387: * @return The event's creator property.
0388: */
0389: public String getCreator() {
0390: // Stub routine only
0391: return null;
0392:
0393: } // getCreator
0394:
0395: /**
0396: * Set the event creator (cover for PROP_CREATOR) to current user
0397: */
0398: public void setCreator() {
0399: // Stub routine only
0400:
0401: } // setCreator
0402:
0403: /**
0404: * Gets the event modifier (userid), if any (cover for PROP_MODIFIED_BY).
0405: * @return The event's modified-by property.
0406: */
0407: public String getModifiedBy() {
0408: // Stub routine only
0409: return null;
0410:
0411: } // getModifiedBy
0412:
0413: /**
0414: * Set the event modifier (cover for PROP_MODIFIED_BY) to current user
0415: */
0416: public void setModifiedBy() {
0417: // Stub routine only
0418:
0419: } // setModifiedBy
0420:
0421: /*
0422: * (non-Javadoc)
0423: *
0424: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setField(java.lang.String, java.lang.String)
0425: */
0426: public void setField(String key, String value) {
0427: this .fields.put(key, value);
0428: }
0429:
0430: /*
0431: * (non-Javadoc)
0432: *
0433: * @see org.sakaiproject.calendar.api.CalendarEventEdit#setRecurrenceRule(org.sakaiproject.calendar.api.RecurrenceRule)
0434: */
0435: public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
0436: this .recurrenceRule = recurrenceRule;
0437: }
0438:
0439: /*
0440: * (non-Javadoc)
0441: *
0442: * @see org.sakaiproject.service.legacy.entity.Edit#isActiveEdit()
0443: */
0444: public boolean isActiveEdit() {
0445: // Stub routine only
0446: return false;
0447: }
0448:
0449: /*
0450: * (non-Javadoc)
0451: *
0452: * @see org.sakaiproject.service.legacy.entity.Edit#getPropertiesEdit()
0453: */
0454: public ResourcePropertiesEdit getPropertiesEdit() {
0455: // Stub routine only
0456: return null;
0457: }
0458:
0459: /*
0460: * (non-Javadoc)
0461: *
0462: * @see org.sakaiproject.service.legacy.entity.AttachmentContainerEdit#addAttachment(org.sakaiproject.service.legacy.entity.Reference)
0463: */
0464: public void addAttachment(Reference arg0) {
0465: // Stub routine only
0466: }
0467:
0468: /*
0469: * (non-Javadoc)
0470: *
0471: * @see org.sakaiproject.service.legacy.entity.AttachmentContainerEdit#removeAttachment(org.sakaiproject.service.legacy.entity.Reference)
0472: */
0473: public void removeAttachment(Reference arg0) {
0474: // Stub routine only
0475: }
0476:
0477: /*
0478: * (non-Javadoc)
0479: *
0480: * @see org.sakaiproject.service.legacy.entity.AttachmentContainerEdit#replaceAttachments(org.sakaiproject.service.legacy.entity.ReferenceVector)
0481: */
0482: public void replaceAttachments(List arg0) {
0483: // Stub routine only
0484: }
0485:
0486: /*
0487: * (non-Javadoc)
0488: *
0489: * @see org.sakaiproject.service.legacy.entity.AttachmentContainerEdit#clearAttachments()
0490: */
0491: public void clearAttachments() {
0492: // Stub routine only
0493: }
0494:
0495: /**
0496: * Get the start date formatted for display.
0497: */
0498: public String getDisplayStartDate() {
0499: return this .timeRange.firstTime().toStringLocalDate();
0500: }
0501:
0502: /**
0503: * Get the start time formatted for display.
0504: */
0505: public String getDisplayStartTime() {
0506: return this .timeRange.firstTime().toStringLocalTime();
0507: }
0508:
0509: /**
0510: * Get the end time of the event formatted for display. This handles the fact that events that end at a given time actually end about a minute earlier.
0511: */
0512: public String getDisplayEndTime() {
0513: // We store event time ranges as slightly less than the end time.
0514: // Make a new time range that is inclusive, just to show the users.
0515:
0516: Time endTime = getTimeService().newTime(
0517: this .getRange().lastTime().getTime() + (60 * 1000));
0518:
0519: return endTime.toStringLocalTime();
0520: }
0521:
0522: /**
0523: * Get the line number on which this event occurs.
0524: */
0525: public int getLineNumber() {
0526: return lineNumber;
0527: }
0528:
0529: /**
0530: * Set the line number on which this event occurs.
0531: *
0532: * @param i
0533: */
0534: public void setLineNumber(int i) {
0535: lineNumber = i;
0536: }
0537:
0538: /**
0539: * {@inheritDoc}
0540: */
0541: public void setDescriptionFormatted(String description) {
0542: this .description = description;
0543: }
0544:
0545: /**
0546: * {@inheritDoc}
0547: */
0548: public String getDescriptionFormatted() {
0549: return description;
0550: }
0551:
0552: /*
0553: * (non-Javadoc)
0554: *
0555: * @see org.sakaiproject.calendar.api.CalendarEvent#getGroups()
0556: */
0557: public Collection getGroups() {
0558: // TODO Auto-generated method stub
0559: return new Vector();
0560: }
0561:
0562: public Collection getGroupObjects() {
0563: return new Vector();
0564: }
0565:
0566: /*
0567: * (non-Javadoc)
0568: *
0569: * @see org.sakaiproject.calendar.api.CalendarEvent#getAccess()
0570: */
0571: public EventAccess getAccess() {
0572: return CalendarEvent.EventAccess.SITE;
0573: }
0574:
0575: public String getGroupRangeForDisplay(Calendar calendar) {
0576: return null;
0577: }
0578:
0579: public void clearGroupAccess() throws PermissionException {
0580: }
0581:
0582: public void setGroupAccess(Collection groups, boolean own)
0583: throws PermissionException {
0584: }
0585:
0586: }
0587:
0588: /**
0589: * Constructor to set up a few of the formatters.
0590: */
0591: public GenericCalendarImporter() {
0592: super ();
0593: TIME_FORMATTER.setLenient(false);
0594: time24HourFormatter.setLenient(false);
0595: time24HourFormatterWithSeconds.setLenient(false);
0596: }
0597:
0598: /*
0599: * (non-Javadoc)
0600: *
0601: * @see org.sakaiproject.service.legacy.calendar#doImport(java.lang.String, java.io.InputStream, java.util.Map, java.lang.String[])
0602: */
0603: public List doImport(String importType, InputStream importStream,
0604: Map columnMapping, String[] customFieldPropertyNames)
0605: throws ImportException {
0606: final List rowList = new ArrayList();
0607: final Reader scheduleImport;
0608:
0609: try {
0610: scheduleImport = (Reader) ((Class) readerMap
0611: .get(importType)).newInstance();
0612:
0613: // Set the timeservice in the reader.
0614: scheduleImport.setTimeService(getTimeService());
0615: }
0616:
0617: catch (InstantiationException e1) {
0618: String msg = (String) rb.getFormattedMessage("err_import",
0619: new Object[] { importType });
0620: throw new ImportException(msg);
0621: } catch (IllegalAccessException e1) {
0622: String msg = (String) rb.getFormattedMessage("err_import",
0623: new Object[] { importType });
0624: throw new ImportException(msg);
0625: }
0626:
0627: if (scheduleImport == null) {
0628: throw new ImportException(rb
0629: .getString("err_import_unknown"));
0630: }
0631:
0632: // If no column mapping has been specified, use the default.
0633: if (columnMapping != null) {
0634: scheduleImport
0635: .setColumnHeaderToAtributeMapping(columnMapping);
0636: }
0637:
0638: // Read in the file.
0639: scheduleImport.importStreamFromDelimitedFile(importStream,
0640: new Reader.ReaderImportRowHandler() {
0641: // This is the callback that is called for each row.
0642: public void handleRow(Iterator columnIterator)
0643: throws ImportException {
0644: final Map eventProperties = new HashMap();
0645:
0646: // Add all the properties to the map
0647: while (columnIterator.hasNext()) {
0648: Reader.ReaderImportCell column = (Reader.ReaderImportCell) columnIterator
0649: .next();
0650:
0651: String value = column.getCellValue().trim();
0652: Object mapCellValue = null;
0653:
0654: // First handle any empy columns.
0655: if (value.length() == 0) {
0656: mapCellValue = null;
0657: } else {
0658: if (FREQUENCY_PROPERTY_NAME
0659: .equals(column
0660: .getPropertyName())) {
0661: mapCellValue = column
0662: .getCellValue();
0663: } else if (END_TIME_PROPERTY_NAME
0664: .equals(column
0665: .getPropertyName())
0666: || START_TIME_PROPERTY_NAME
0667: .equals(column
0668: .getPropertyName())) {
0669: boolean success = false;
0670:
0671: try {
0672: mapCellValue = TIME_FORMATTER
0673: .parse(value);
0674: success = true;
0675: }
0676:
0677: catch (ParseException e) {
0678: // Try another format
0679: }
0680:
0681: if (!success) {
0682: try {
0683: mapCellValue = TIME_FORMATTER_WITH_SECONDS
0684: .parse(value);
0685: success = true;
0686: }
0687:
0688: catch (ParseException e) {
0689: // Try another format
0690: }
0691: }
0692:
0693: if (!success) {
0694: try {
0695: mapCellValue = time24HourFormatter
0696: .parse(value);
0697: success = true;
0698: }
0699:
0700: catch (ParseException e) {
0701: // Try another format
0702: }
0703: }
0704:
0705: if (!success) {
0706: try {
0707: mapCellValue = time24HourFormatterWithSeconds
0708: .parse(value);
0709: success = true;
0710: }
0711:
0712: catch (ParseException e) {
0713: // Give up, we've run out of possible formats.
0714: String msg = (String) rb
0715: .getFormattedMessage(
0716: "err_time",
0717: new Object[] {
0718: new Integer(
0719: column
0720: .getLineNumber()),
0721: column
0722: .getColumnHeader() });
0723: throw new ImportException(
0724: msg);
0725: }
0726: }
0727: } else if (DURATION_PROPERTY_NAME
0728: .equals(column
0729: .getPropertyName())) {
0730: String timeFormatErrorString = (String) rb
0731: .getFormattedMessage(
0732: "err_time",
0733: new Object[] {
0734: new Integer(
0735: column
0736: .getLineNumber()),
0737: column
0738: .getColumnHeader() });
0739:
0740: String parts[] = value.split(":");
0741:
0742: if (parts.length == 1) {
0743: // Convert to minutes to get into one property field.
0744: try {
0745: mapCellValue = new Integer(
0746: Integer
0747: .parseInt(parts[0]));
0748: } catch (NumberFormatException ex) {
0749: throw new ImportException(
0750: timeFormatErrorString);
0751: }
0752: } else if (parts.length == 2) {
0753: // Convert to hours:minutes to get into one property field.
0754: try {
0755: mapCellValue = new Integer(
0756: Integer
0757: .parseInt(parts[0])
0758: * 60
0759: + Integer
0760: .parseInt(parts[1]));
0761: } catch (NumberFormatException ex) {
0762: throw new ImportException(
0763: timeFormatErrorString);
0764: }
0765: } else {
0766: // Not a legal format of mm or hh:mm
0767: throw new ImportException(
0768: timeFormatErrorString);
0769: }
0770: } else if (DATE_PROPERTY_NAME
0771: .equals(column
0772: .getPropertyName())
0773: || ENDS_PROPERTY_NAME
0774: .equals(column
0775: .getPropertyName())) {
0776: DateFormat df = DateFormat
0777: .getDateInstance(
0778: DateFormat.SHORT,
0779: rb.getLocale());
0780: df.setLenient(false);
0781: try {
0782: mapCellValue = df.parse(value);
0783: } catch (ParseException e) {
0784: String msg = (String) rb
0785: .getFormattedMessage(
0786: "err_date",
0787: new Object[] {
0788: new Integer(
0789: column
0790: .getLineNumber()),
0791: column
0792: .getColumnHeader() });
0793: throw new ImportException(msg);
0794: }
0795: } else if (INTERVAL_PROPERTY_NAME
0796: .equals(column
0797: .getPropertyName())
0798: || REPEAT_PROPERTY_NAME
0799: .equals(column
0800: .getPropertyName())) {
0801: try {
0802: mapCellValue = new Integer(
0803: column.getCellValue());
0804: } catch (NumberFormatException ex) {
0805: String msg = (String) rb
0806: .getFormattedMessage(
0807: "err_interval",
0808: new Object[] {
0809: new Integer(
0810: column
0811: .getLineNumber()),
0812: column
0813: .getColumnHeader() });
0814: throw new ImportException(msg);
0815: }
0816: } else {
0817: // Just a string...
0818: mapCellValue = column
0819: .getCellValue();
0820: }
0821: }
0822:
0823: // Store in the map for later reference.
0824: eventProperties.put(column
0825: .getPropertyName(), mapCellValue);
0826: }
0827:
0828: // Add the map of properties for this row to the list of rows.
0829: rowList.add(eventProperties);
0830: }
0831: });
0832:
0833: return getPrototypeEvents(scheduleImport.filterEvents(rowList,
0834: customFieldPropertyNames), customFieldPropertyNames);
0835: }
0836:
0837: /**
0838: * Interprets the list of maps created by doImport()
0839: *
0840: * @param map
0841: */
0842: protected List getPrototypeEvents(List rowList,
0843: String[] customFieldPropertyNames) throws ImportException {
0844: Iterator it = rowList.iterator();
0845: List eventList = new ArrayList();
0846: int lineNumber = 1;
0847:
0848: while (it.hasNext()) {
0849: Map eventProperties = (Map) it.next();
0850: RecurrenceRule recurrenceRule = null;
0851: PrototypeEvent prototypeEvent = new PrototypeEvent();
0852:
0853: prototypeEvent
0854: .setDescription((String) eventProperties
0855: .get(GenericCalendarImporter.DESCRIPTION_PROPERTY_NAME));
0856: prototypeEvent.setDisplayName((String) eventProperties
0857: .get(GenericCalendarImporter.TITLE_PROPERTY_NAME));
0858: prototypeEvent
0859: .setLocation((String) eventProperties
0860: .get(GenericCalendarImporter.LOCATION_PROPERTY_NAME));
0861: prototypeEvent
0862: .setType((String) eventProperties
0863: .get(GenericCalendarImporter.ITEM_TYPE_PROPERTY_NAME));
0864:
0865: if (prototypeEvent.getType() == null
0866: || prototypeEvent.getType().length() == 0) {
0867: prototypeEvent.setType("Activity");
0868: }
0869:
0870: // The time range has been calculated in the reader, based on
0871: // whatever time fields are available in the particular import format.
0872: // This range has been placed in the ACTUAL_TIMERANGE property.
0873:
0874: TimeRange timeRange = (TimeRange) eventProperties
0875: .get(GenericCalendarImporter.ACTUAL_TIMERANGE);
0876:
0877: if (timeRange == null) {
0878: String msg = (String) rb.getFormattedMessage(
0879: "err_notime", new Object[] { new Integer(
0880: lineNumber) });
0881: throw new ImportException(msg);
0882: }
0883:
0884: // The start/end times were calculated during the import process.
0885: prototypeEvent.setRange(timeRange);
0886:
0887: // Do custom fields, if any.
0888: if (customFieldPropertyNames != null) {
0889: for (int i = 0; i < customFieldPropertyNames.length; i++) {
0890: prototypeEvent.setField(
0891: customFieldPropertyNames[i],
0892: (String) eventProperties
0893: .get(customFieldPropertyNames[i]));
0894: }
0895: }
0896:
0897: // See if this is a recurring event
0898: String frequencyString = (String) eventProperties
0899: .get(GenericCalendarImporter.FREQUENCY_PROPERTY_NAME);
0900:
0901: if (frequencyString != null) {
0902: Integer interval = (Integer) eventProperties
0903: .get(GenericCalendarImporter.INTERVAL_PROPERTY_NAME);
0904: Integer count = (Integer) eventProperties
0905: .get(GenericCalendarImporter.REPEAT_PROPERTY_NAME);
0906: Date until = (Date) eventProperties
0907: .get(GenericCalendarImporter.ENDS_PROPERTY_NAME);
0908:
0909: if (count != null && until != null) {
0910: String msg = (String) rb.getFormattedMessage(
0911: "err_datebad", new Object[] { new Integer(
0912: lineNumber) });
0913: throw new ImportException(msg);
0914: }
0915:
0916: if (interval == null && count == null && until == null) {
0917: recurrenceRule = getCalendarService()
0918: .newRecurrence(frequencyString);
0919: } else if (until == null && interval != null
0920: && count != null) {
0921: recurrenceRule = getCalendarService()
0922: .newRecurrence(frequencyString,
0923: interval.intValue(),
0924: count.intValue());
0925: } else if (until == null && interval != null
0926: && count == null) {
0927: recurrenceRule = getCalendarService()
0928: .newRecurrence(frequencyString,
0929: interval.intValue());
0930: } else if (until != null && interval != null
0931: && count == null) {
0932: Time untilTime = getTimeService().newTime(
0933: until.getTime());
0934:
0935: recurrenceRule = getCalendarService()
0936: .newRecurrence(frequencyString,
0937: interval.intValue(), untilTime);
0938: }
0939:
0940: // See if we were able to successfully create a recurrence rule.
0941: if (recurrenceRule == null) {
0942: String msg = (String) rb.getFormattedMessage(
0943: "err_freqbad", new Object[] { new Integer(
0944: lineNumber) });
0945: throw new ImportException(msg);
0946: }
0947:
0948: prototypeEvent.setRecurrenceRule(recurrenceRule);
0949: }
0950: prototypeEvent.setLineNumber(lineNumber);
0951: eventList.add(prototypeEvent);
0952: lineNumber++;
0953: }
0954:
0955: return eventList;
0956: }
0957:
0958: /*
0959: * (non-Javadoc)
0960: *
0961: * @see org.sakaiproject.tool.calendar.schedimport.importers.Importer#getDefaultColumnMap(java.lang.String)
0962: */
0963: public Map getDefaultColumnMap(String importType)
0964: throws ImportException {
0965: try {
0966: Reader scheduleImport = (Reader) ((Class) readerMap
0967: .get(importType)).newInstance();
0968:
0969: if (scheduleImport != null) {
0970: return scheduleImport.getDefaultColumnMap();
0971: }
0972: }
0973:
0974: catch (InstantiationException e1) {
0975: String msg = (String) rb.getFormattedMessage("err_import",
0976: new Object[] { importType });
0977: throw new ImportException(msg);
0978: } catch (IllegalAccessException e1) {
0979: String msg = (String) rb.getFormattedMessage("err_import",
0980: new Object[] { importType });
0981: throw new ImportException(msg);
0982: }
0983:
0984: // No map exists if we get here.
0985: return null;
0986: }
0987:
0988: /**
0989: * Getter for injected service
0990: */
0991: public CalendarService getCalendarService() {
0992: return calendarService;
0993: }
0994:
0995: /**
0996: * Getter for injected service
0997: */
0998: public TimeService getTimeService() {
0999: return timeService;
1000: }
1001:
1002: /**
1003: * Setter for injected service
1004: *
1005: * @param service
1006: */
1007: public void setCalendarService(CalendarService service) {
1008: calendarService = service;
1009: }
1010:
1011: /**
1012: * Setter for injected service
1013: *
1014: * @param service
1015: */
1016: public void setTimeService(TimeService service) {
1017: timeService = service;
1018: }
1019:
1020: /**********************************************************************************************************************************************************************************************************************************************************
1021: * Init and Destroy
1022: *********************************************************************************************************************************************************************************************************************************************************/
1023:
1024: /**
1025: * Final initialization, once all dependencies are set.
1026: */
1027: public void init() {
1028: try {
1029: // Add our readers. This might be done from a
1030: // config file in future versions.
1031: readerMap.put(OUTLOOK_IMPORT, OutlookReader.class);
1032: readerMap
1033: .put(MEETINGMAKER_IMPORT, MeetingMakerReader.class);
1034: readerMap.put(CSV_IMPORT, CSVReader.class);
1035: } catch (Throwable t) {
1036: M_log.warn("init(): ", t);
1037: }
1038: }
1039:
1040: /**
1041: * Returns to uninitialized state.
1042: */
1043: public void destroy() {
1044: M_log.info("destroy()");
1045: }
1046: }
|