/* Copyright 2013 BarD Software s.r.o This file is part of GanttProject, an opensource project management tool. GanttProject is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GanttProject is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GanttProject. If not, see <http://www.gnu.org/licenses/>. */ package biz.ganttproject.impex.ical; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.List; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import net.fortuna.ical4j.data.CalendarBuilder; import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.data.UnfoldingReader; import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.Date; import net.fortuna.ical4j.model.Property; import net.fortuna.ical4j.model.Recur; import net.fortuna.ical4j.model.component.VEvent; import net.fortuna.ical4j.model.property.RRule; import net.fortuna.ical4j.util.CompatibilityHints; import net.sourceforge.ganttproject.GPLogger; import net.sourceforge.ganttproject.calendar.CalendarEditorPanel; import net.sourceforge.ganttproject.importer.ImporterBase; import net.sourceforge.ganttproject.wizard.AbstractWizard; import net.sourceforge.ganttproject.wizard.WizardPage; import biz.ganttproject.core.calendar.CalendarEvent; import biz.ganttproject.core.calendar.GPCalendarCalc; import biz.ganttproject.core.time.TimeDuration; import biz.ganttproject.core.time.impl.GPTimeUnitStack; import com.google.common.collect.Lists; /** * Implements an import wizard plugin responsible for importing ICS files. * This plugin adds file chooser page (2nd in the wizard) and calendar editor page (3rd in the wizard) * * @author dbarashev */ public class IcsFileImporter extends ImporterBase { private final CalendarEditorPage myEditorPage; public IcsFileImporter() { super("impex.ics"); myEditorPage = new CalendarEditorPage(); } @Override public String getFileNamePattern() { return "ics"; } @Override public void run() { getUiFacade().getUndoManager().undoableEdit("Import", new Runnable() { @Override public void run() { List<CalendarEvent> events = myEditorPage.getEvents(); if (events != null) { getProject().getActiveCalendar().setPublicHolidays(events); } } }); } @Override public WizardPage getCustomPage() { return myEditorPage; } @Override public boolean isReady() { return super.isReady() && myEditorPage.getEvents() != null; } @Override public void setFile(File file) { super.setFile(file); myEditorPage.setFile(file); if (file != null && file.exists() && file.canRead()) { myEditorPage.setEvents(readEvents(file)); } } /** * Calendar editor page which wraps a {@link CalendarEditorPanel} instance */ static class CalendarEditorPage implements WizardPage { private File myFile; private JPanel myPanel = new JPanel(); private List<CalendarEvent> myEvents; private void setFile(File f) { myFile = f; } void setEvents(List<CalendarEvent> events) { myEvents = events; } List<CalendarEvent> getEvents() { return myEvents; } public String getTitle() { return "Edit calendar"; } public JComponent getComponent() { return myPanel; } public void setActive(AbstractWizard wizard) { if (wizard != null) { myPanel.removeAll(); if (myFile != null && myFile.exists() && myFile.canRead()) { if (myEvents != null) { myPanel.add(new CalendarEditorPanel(wizard.getUIFacade(), myEvents, null).createComponent()); return; } } myPanel.add(new JLabel(String.format("File %s is not readable", myFile.getAbsolutePath()))); } } } /** * Reads calendar events from file * @return a list of events if file was parsed successfully or null otherwise */ private static List<CalendarEvent> readEvents(File f) { try { CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true); CalendarBuilder builder = new CalendarBuilder(); List<CalendarEvent> gpEvents = Lists.newArrayList(); Calendar c = builder.build(new UnfoldingReader(new FileReader(f))); for (Component comp : (List<Component>)c.getComponents()) { if (comp instanceof VEvent) { VEvent event = (VEvent) comp; if (event.getStartDate() == null) { GPLogger.log("No start date found, ignoring. Event="+event); continue; } Date eventStartDate = event.getStartDate().getDate(); if (event.getEndDate() == null) { GPLogger.log("No end date found, using start date instead. Event="+event); } Date eventEndDate = event.getEndDate() == null ? eventStartDate : event.getEndDate().getDate(); TimeDuration oneDay = GPTimeUnitStack.createLength(GPTimeUnitStack.DAY, 1); if (eventEndDate != null) { java.util.Date startDate = GPTimeUnitStack.DAY.adjustLeft(eventStartDate); java.util.Date endDate = GPTimeUnitStack.DAY.adjustLeft(eventEndDate); RRule recurrenceRule = (RRule) event.getProperty(Property.RRULE); boolean recursYearly = false; if (recurrenceRule != null) { recursYearly = Recur.YEARLY.equals(recurrenceRule.getRecur().getFrequency()) && 1 == recurrenceRule.getRecur().getInterval(); } while (startDate.compareTo(endDate) <= 0) { gpEvents.add(CalendarEvent.newEvent(startDate, recursYearly, CalendarEvent.Type.HOLIDAY, event.getSummary().getValue(), null)); startDate = GPCalendarCalc.PLAIN.shiftDate(startDate, oneDay); } } } } return gpEvents; } catch (IOException | ParserException e) { GPLogger.log(e); return null; } } }