001package biweekly.io; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import biweekly.ICalVersion; 007import biweekly.Warning; 008import biweekly.parameter.ICalParameters; 009import biweekly.property.ICalProperty; 010import biweekly.util.ICalDate; 011import biweekly.util.ListMultimap; 012 013/* 014 Copyright (c) 2013-2015, Michael Angstadt 015 All rights reserved. 016 017 Redistribution and use in source and binary forms, with or without 018 modification, are permitted provided that the following conditions are met: 019 020 1. Redistributions of source code must retain the above copyright notice, this 021 list of conditions and the following disclaimer. 022 2. Redistributions in binary form must reproduce the above copyright notice, 023 this list of conditions and the following disclaimer in the documentation 024 and/or other materials provided with the distribution. 025 026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 027 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 028 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 029 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 030 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 031 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 032 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 033 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 034 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 035 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 036 */ 037 038/** 039 * Stores information used during the parsing of an iCalendar object. 040 * @author Michael Angstadt 041 */ 042public class ParseContext { 043 private ICalVersion version; 044 private List<Warning> warnings = new ArrayList<Warning>(); 045 private ListMultimap<String, TimezonedDate> timezonedDates = new ListMultimap<String, TimezonedDate>(); 046 private List<TimezonedDate> floatingDates = new ArrayList<TimezonedDate>(); 047 048 /** 049 * Gets the version of the iCalendar object being parsed. 050 * @return the iCalendar version 051 */ 052 public ICalVersion getVersion() { 053 return version; 054 } 055 056 /** 057 * Sets the version of the iCalendar object being parsed. 058 * @param version the iCalendar version 059 */ 060 public void setVersion(ICalVersion version) { 061 this.version = version; 062 } 063 064 /** 065 * Adds a parsed date to this parse context so its timezone can be applied 066 * to it after the iCalendar object has been parsed (if it has one). 067 * @param icalDate the parsed date 068 * @param property the property that the date value belongs to 069 * @param parameters the property's parameters 070 */ 071 public void addDate(ICalDate icalDate, ICalProperty property, ICalParameters parameters) { 072 if (!icalDate.hasTime()) { 073 //dates don't have timezones 074 return; 075 } 076 077 if (icalDate.getRawComponents().isUtc()) { 078 //it's a UTC date, so it was already parsed under the correct timezone 079 return; 080 } 081 082 //TODO handle UTC offsets within the date strings (not part of iCal standard) 083 String tzid = parameters.getTimezoneId(); 084 if (tzid == null) { 085 addFloatingDate(property, icalDate); 086 } else { 087 addTimezonedDate(tzid, property, icalDate); 088 } 089 } 090 091 /** 092 * Keeps track of a date-time property value that uses a timezone so it can 093 * be parsed later. Timezones cannot be handled until the entire iCalendar 094 * object has been parsed. 095 * @param tzid the timezone ID (TZID parameter) 096 * @param property the property 097 * @param date the date object that was assigned to the property object 098 */ 099 public void addTimezonedDate(String tzid, ICalProperty property, ICalDate date) { 100 timezonedDates.put(tzid, new TimezonedDate(date, property)); 101 } 102 103 /** 104 * Gets the list of date-time property values that use a timezone. 105 * @return the date-time property values that use a timezone (key = TZID; 106 * value = the property) 107 */ 108 public ListMultimap<String, TimezonedDate> getTimezonedDates() { 109 return timezonedDates; 110 } 111 112 /** 113 * Keeps track of a date-time property that does not have a timezone 114 * (floating time), so it can be added to the {@link TimezoneInfo} object 115 * after the iCalendar object is parsed. 116 * @param property the property 117 */ 118 public void addFloatingDate(ICalProperty property, ICalDate date) { 119 floatingDates.add(new TimezonedDate(date, property)); 120 } 121 122 /** 123 * Gets the date-time properties that are in floating time (lacking a 124 * timezone). 125 * @return the floating date-time properties 126 */ 127 public List<TimezonedDate> getFloatingDates() { 128 return floatingDates; 129 } 130 131 /** 132 * Adds a parse warning. 133 * @param code the warning code 134 * @param args the warning message arguments 135 */ 136 public void addWarning(int code, Object... args) { 137 warnings.add(Warning.parse(code, args)); 138 } 139 140 /** 141 * Adds a parse warning. 142 * @param message the warning message 143 */ 144 public void addWarning(String message) { 145 warnings.add(new Warning(message)); 146 } 147 148 /** 149 * Gets the parse warnings. 150 * @return the parse warnings 151 */ 152 public List<Warning> getWarnings() { 153 return warnings; 154 } 155 156 /** 157 * Represents a property whose date-time value has a timezone. 158 * @author Michael Angstadt 159 */ 160 public static class TimezonedDate { 161 private final ICalDate date; 162 private final ICalProperty property; 163 164 /** 165 * @param date the date object that was assigned to the property object 166 * @param property the property object 167 */ 168 public TimezonedDate(ICalDate date, ICalProperty property) { 169 this.date = date; 170 this.property = property; 171 } 172 173 /** 174 * Gets the date object that was assigned to the property object (should 175 * be parsed under the JVM's default timezone) 176 * @return the date object 177 */ 178 public ICalDate getDate() { 179 return date; 180 } 181 182 /** 183 * Gets the property object. 184 * @return the property 185 */ 186 public ICalProperty getProperty() { 187 return property; 188 } 189 190 @Override 191 public int hashCode() { 192 final int prime = 31; 193 int result = 1; 194 result = prime * result + ((date == null) ? 0 : date.hashCode()); 195 result = prime * result + ((property == null) ? 0 : property.hashCode()); 196 return result; 197 } 198 199 @Override 200 public boolean equals(Object obj) { 201 if (this == obj) return true; 202 if (obj == null) return false; 203 if (getClass() != obj.getClass()) return false; 204 TimezonedDate other = (TimezonedDate) obj; 205 if (date == null) { 206 if (other.date != null) return false; 207 } else if (!date.equals(other.date)) return false; 208 if (property == null) { 209 if (other.property != null) return false; 210 } else if (!property.equals(other.property)) return false; 211 return true; 212 } 213 } 214}