org.sakaiproject.calendar.impl.readers.IcalendarReader.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.calendar.impl.readers.IcalendarReader.java

Source

/**********************************************************************************
 * $URL$
 * $Id$
  ***********************************************************************************
 *
 * Copyright (c) 2007, 2008 The Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************/

package org.sakaiproject.calendar.impl.readers;

import java.io.InputStream;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.DateProperty;
import net.fortuna.ical4j.util.CompatibilityHints;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.calendar.impl.GenericCalendarImporter;
import org.sakaiproject.exception.ImportException;
import org.sakaiproject.time.api.TimeBreakdown;
import org.sakaiproject.util.ResourceLoader;

/**
 * This class parses an import file from iCalendar.
 */
public class IcalendarReader extends Reader {
    private ResourceLoader rb = new ResourceLoader("calendar");
    private static Log M_log = LogFactory.getLog(IcalendarReader.class);
    private Map<String, String> defaultHeaderMap = getDefaultColumnMap();

    private static final String CONTACT_SECTION_HEADER = "Contacts";
    private static final String TODO_SECTION_HEADER = "Todos";
    private static final String EVENT_SECTION_HEADER = "Events";

    /**
     * Default constructor 
     */
    public IcalendarReader() {
        super();
    }

    /* (non-Javadoc)
     * @see org.sakaiproject.tool.calendar.ImportReader#importStreamFromDelimitedFile(java.io.InputStream, org.sakaiproject.tool.calendar.ImportReader.ReaderImportRowHandler)
     */
    public void importStreamFromDelimitedFile(InputStream stream, ReaderImportRowHandler handler)
            throws ImportException//, IOException, ParserException
    {

        try {

            ColumnHeader columnDescriptionArray[] = null;
            String descriptionColumns[] = { "Summary", "Description", "Start Date", "Start Time", "Duration",
                    "Location" };

            int lineNumber = 1;
            String durationformat = "";
            String requireValues = "";

            // column map stuff
            trimLeadingTrailingQuotes(descriptionColumns);
            columnDescriptionArray = buildColumnDescriptionArray(descriptionColumns);

            // enable "relaxed parsing"; read file using LF instead of CRLF
            CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
            CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);
            CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);
            CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);

            CalendarBuilder builder = new CalendarBuilder();
            net.fortuna.ical4j.model.Calendar calendar = builder.build(stream);

            for (Iterator i = calendar.getComponents("VEVENT").iterator(); i.hasNext();) {
                Component component = (Component) i.next();

                // Find event duration
                DateProperty dtstartdate;
                DateProperty dtenddate;
                if (component instanceof VEvent) {
                    VEvent vEvent = (VEvent) component;
                    dtstartdate = vEvent.getStartDate();
                    dtenddate = vEvent.getEndDate(true);
                } else {
                    dtstartdate = (DateProperty) component.getProperty("DTSTART");
                    dtenddate = (DateProperty) component.getProperty("DTEND");
                }

                if (component.getProperty("SUMMARY") == null) {
                    M_log.warn("IcalendarReader: SUMMARY is required; event not imported");
                    continue;
                }
                String summary = component.getProperty("SUMMARY").getValue();

                if (component.getProperty("RRULE") != null) {
                    M_log.warn("IcalendarReader: Re-occuring events not supported: " + summary);
                    continue;
                } else if (dtstartdate == null || dtenddate == null) {
                    M_log.warn("IcalendarReader: DTSTART/DTEND required: " + summary);
                    continue;
                }

                int durationsecs = (int) ((dtenddate.getDate().getTime() - dtstartdate.getDate().getTime()) / 1000);
                int durationminutes = (durationsecs / 60) % 60;
                int durationhours = (durationsecs / (60 * 60)) % 24;

                // Put duration in proper format (hh:mm or mm) if less than 1 hour
                if (durationminutes < 10) {
                    durationformat = "0" + durationminutes;
                } else {
                    durationformat = "" + durationminutes;
                }

                if (durationhours != 0) {
                    durationformat = durationhours + ":" + durationformat;
                }

                String description = "";
                if (component.getProperty("DESCRIPTION") != null)
                    description = component.getProperty("DESCRIPTION").getValue();

                String location = "";
                if (component.getProperty("LOCATION") != null)
                    location = component.getProperty("LOCATION").getValue();

                String columns[] = { component.getProperty("SUMMARY").getValue(), description,
                        DateFormat.getDateInstance(DateFormat.SHORT, rb.getLocale()).format(dtstartdate.getDate()),
                        DateFormat.getTimeInstance(DateFormat.SHORT, rb.getLocale()).format(dtstartdate.getDate()),
                        durationformat, location };

                // Remove trailing/leading quotes from all columns.
                //trimLeadingTrailingQuotes(columns);

                handler.handleRow(processLine(columnDescriptionArray, lineNumber, columns));

                lineNumber++;

            } // end for

        } catch (Exception e) {
            M_log.warn(".importSteamFromDelimitedFile(): ", e);
        }
    } // end importStreamFromDelimitedFile

    /* (non-Javadoc)
     * @see org.sakaiproject.tool.calendar.schedimportreaders.Reader#filterEvents(java.util.List, java.lang.String[])
     */
    public List filterEvents(List events, String[] customFieldNames) throws ImportException {
        Iterator it = events.iterator();
        int lineNumber = 1;

        //
        // Convert the date/time fields as they appear in the Outlook import to
        // be a synthesized start/end timerange.
        //
        while (it.hasNext()) {
            Map eventProperties = (Map) it.next();

            Date startTime = (Date) eventProperties
                    .get(defaultHeaderMap.get(GenericCalendarImporter.START_TIME_DEFAULT_COLUMN_HEADER));
            TimeBreakdown startTimeBreakdown = null;

            if (startTime != null) {
                // if the source time zone were known, this would be
                // a good place to set it: startCal.setTimeZone()
                GregorianCalendar startCal = new GregorianCalendar();
                startCal.setTimeInMillis(startTime.getTime());
                startTimeBreakdown = getTimeService().newTimeBreakdown(0, 0, 0, startCal.get(Calendar.HOUR_OF_DAY),
                        startCal.get(Calendar.MINUTE), startCal.get(Calendar.SECOND), 0);
            } else {
                Integer line = Integer.valueOf(lineNumber);
                String msg = (String) rb.getFormattedMessage("err_no_stime_on", new Object[] { line });
                throw new ImportException(msg);
            }

            Integer durationInMinutes = (Integer) eventProperties
                    .get(defaultHeaderMap.get(GenericCalendarImporter.DURATION_DEFAULT_COLUMN_HEADER));

            if (durationInMinutes == null) {
                Integer line = Integer.valueOf(lineNumber);
                String msg = (String) rb.getFormattedMessage("err_no_dtime_on", new Object[] { line });
                throw new ImportException(msg);
            }

            Date endTime = new Date(startTime.getTime() + (durationInMinutes.longValue() * 60 * 1000));

            TimeBreakdown endTimeBreakdown = null;

            if (endTime != null) {
                // if the source time zone were known, this would be
                // a good place to set it: endCal.setTimeZone()
                GregorianCalendar endCal = new GregorianCalendar();
                endCal.setTimeInMillis(endTime.getTime());
                endTimeBreakdown = getTimeService().newTimeBreakdown(0, 0, 0, endCal.get(Calendar.HOUR_OF_DAY),
                        endCal.get(Calendar.MINUTE), endCal.get(Calendar.SECOND), 0);
            }

            Date startDate = (Date) eventProperties
                    .get(defaultHeaderMap.get(GenericCalendarImporter.DATE_DEFAULT_COLUMN_HEADER));

            // if the source time zone were known, this would be
            // a good place to set it: startCal.setTimeZone()
            GregorianCalendar startCal = new GregorianCalendar();
            if (startDate != null)
                startCal.setTimeInMillis(startDate.getTime());

            startTimeBreakdown.setYear(startCal.get(Calendar.YEAR));
            startTimeBreakdown.setMonth(startCal.get(Calendar.MONTH) + 1);
            startTimeBreakdown.setDay(startCal.get(Calendar.DAY_OF_MONTH));

            endTimeBreakdown.setYear(startCal.get(Calendar.YEAR));
            endTimeBreakdown.setMonth(startCal.get(Calendar.MONTH) + 1);
            endTimeBreakdown.setDay(startCal.get(Calendar.DAY_OF_MONTH));

            eventProperties.put(GenericCalendarImporter.ACTUAL_TIMERANGE,
                    getTimeService().newTimeRange(getTimeService().newTimeLocal(startTimeBreakdown),
                            getTimeService().newTimeLocal(endTimeBreakdown), true, false));

            lineNumber++;
        }

        return events;
    }

    /* (non-Javadoc)
     * @see org.sakaiproject.tool.calendar.schedimportreaders.Reader#getDefaultColumnMap()
     */
    public Map<String, String> getDefaultColumnMap() {
        Map<String, String> columnHeaderMap = new HashMap<String, String>();

        columnHeaderMap.put(GenericCalendarImporter.TITLE_DEFAULT_COLUMN_HEADER,
                GenericCalendarImporter.TITLE_PROPERTY_NAME);
        columnHeaderMap.put(GenericCalendarImporter.DESCRIPTION_DEFAULT_COLUMN_HEADER,
                GenericCalendarImporter.DESCRIPTION_PROPERTY_NAME);
        columnHeaderMap.put(GenericCalendarImporter.DATE_DEFAULT_COLUMN_HEADER,
                GenericCalendarImporter.DATE_PROPERTY_NAME);
        columnHeaderMap.put(GenericCalendarImporter.START_TIME_DEFAULT_COLUMN_HEADER,
                GenericCalendarImporter.START_TIME_PROPERTY_NAME);
        columnHeaderMap.put(GenericCalendarImporter.DURATION_DEFAULT_COLUMN_HEADER,
                GenericCalendarImporter.DURATION_PROPERTY_NAME);
        //columnHeaderMap.put(ITEM_HEADER, GenericCalendarImporter.ITEM_TYPE_PROPERTY_NAME);
        columnHeaderMap.put(GenericCalendarImporter.LOCATION_DEFAULT_COLUMN_HEADER,
                GenericCalendarImporter.LOCATION_PROPERTY_NAME);

        return columnHeaderMap;
    }
}