1.1 --- a/src/net/fortuna/ical4j/data/CalendarBuilder.java Thu Feb 12 18:02:00 2015 +0100 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,435 +0,0 @@ 1.4 -/** 1.5 - * Copyright (c) 2012, Ben Fortuna 1.6 - * All rights reserved. 1.7 - * 1.8 - * Redistribution and use in source and binary forms, with or without 1.9 - * modification, are permitted provided that the following conditions 1.10 - * are met: 1.11 - * 1.12 - * o Redistributions of source code must retain the above copyright 1.13 - * notice, this list of conditions and the following disclaimer. 1.14 - * 1.15 - * o Redistributions in binary form must reproduce the above copyright 1.16 - * notice, this list of conditions and the following disclaimer in the 1.17 - * documentation and/or other materials provided with the distribution. 1.18 - * 1.19 - * o Neither the name of Ben Fortuna nor the names of any other contributors 1.20 - * may be used to endorse or promote products derived from this software 1.21 - * without specific prior written permission. 1.22 - * 1.23 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.24 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.25 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.26 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 1.27 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 1.28 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 1.29 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 1.30 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 1.31 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 1.32 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 1.33 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.34 - */ 1.35 -package net.fortuna.ical4j.data; 1.36 - 1.37 -import java.io.IOException; 1.38 -import java.io.InputStream; 1.39 -import java.io.InputStreamReader; 1.40 -import java.io.Reader; 1.41 -import java.net.URISyntaxException; 1.42 -import java.nio.charset.Charset; 1.43 -import java.text.ParseException; 1.44 -import java.util.ArrayList; 1.45 -import java.util.Iterator; 1.46 -import java.util.List; 1.47 - 1.48 -import net.fortuna.ical4j.model.Calendar; 1.49 -import net.fortuna.ical4j.model.CalendarException; 1.50 -import net.fortuna.ical4j.model.Component; 1.51 -import net.fortuna.ical4j.model.ComponentFactory; 1.52 -import net.fortuna.ical4j.model.Escapable; 1.53 -import net.fortuna.ical4j.model.Parameter; 1.54 -import net.fortuna.ical4j.model.ParameterFactory; 1.55 -import net.fortuna.ical4j.model.ParameterFactoryRegistry; 1.56 -import net.fortuna.ical4j.model.Property; 1.57 -import net.fortuna.ical4j.model.PropertyFactory; 1.58 -import net.fortuna.ical4j.model.PropertyFactoryRegistry; 1.59 -import net.fortuna.ical4j.model.TimeZone; 1.60 -import net.fortuna.ical4j.model.TimeZoneRegistry; 1.61 -import net.fortuna.ical4j.model.TimeZoneRegistryFactory; 1.62 -import net.fortuna.ical4j.model.component.VAvailability; 1.63 -import net.fortuna.ical4j.model.component.VEvent; 1.64 -import net.fortuna.ical4j.model.component.VTimeZone; 1.65 -import net.fortuna.ical4j.model.component.VToDo; 1.66 -import net.fortuna.ical4j.model.parameter.TzId; 1.67 -import net.fortuna.ical4j.model.property.DateListProperty; 1.68 -import net.fortuna.ical4j.model.property.DateProperty; 1.69 -import net.fortuna.ical4j.model.property.XProperty; 1.70 -import net.fortuna.ical4j.util.CompatibilityHints; 1.71 -import net.fortuna.ical4j.util.Constants; 1.72 -import net.fortuna.ical4j.util.Strings; 1.73 - 1.74 -import org.apache.commons.logging.Log; 1.75 -import org.apache.commons.logging.LogFactory; 1.76 - 1.77 -/** 1.78 - * Parses and builds an iCalendar model from an input stream. Note that this class is not thread-safe. 1.79 - * @version 2.0 1.80 - * @author Ben Fortuna 1.81 - * 1.82 - * <pre> 1.83 - * $Id$ 1.84 - * 1.85 - * Created: Apr 5, 2004 1.86 - * </pre> 1.87 - * 1.88 - */ 1.89 -public class CalendarBuilder { 1.90 - 1.91 - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 1.92 - 1.93 - private final CalendarParser parser; 1.94 - 1.95 - private final ContentHandler contentHandler; 1.96 - 1.97 - private final TimeZoneRegistry tzRegistry; 1.98 - 1.99 - private List datesMissingTimezones; 1.100 - 1.101 - /** 1.102 - * The calendar instance created by the builder. 1.103 - */ 1.104 - protected Calendar calendar; 1.105 - 1.106 - /** 1.107 - * The current component instance created by the builder. 1.108 - */ 1.109 - protected Component component; 1.110 - 1.111 - /** 1.112 - * The current sub-component instance created by the builder. 1.113 - */ 1.114 - protected Component subComponent; 1.115 - 1.116 - /** 1.117 - * The current property instance created by the builder. 1.118 - */ 1.119 - protected Property property; 1.120 - 1.121 - /** 1.122 - * Default constructor. 1.123 - */ 1.124 - public CalendarBuilder() { 1.125 - this(CalendarParserFactory.getInstance().createParser(), new PropertyFactoryRegistry(), 1.126 - new ParameterFactoryRegistry(), TimeZoneRegistryFactory.getInstance().createRegistry()); 1.127 - } 1.128 - 1.129 - /** 1.130 - * Constructs a new calendar builder using the specified calendar parser. 1.131 - * @param parser a calendar parser used to parse calendar files 1.132 - */ 1.133 - public CalendarBuilder(final CalendarParser parser) { 1.134 - this(parser, new PropertyFactoryRegistry(), new ParameterFactoryRegistry(), 1.135 - TimeZoneRegistryFactory.getInstance().createRegistry()); 1.136 - } 1.137 - 1.138 - /** 1.139 - * Constructs a new calendar builder using the specified timezone registry. 1.140 - * @param tzRegistry a timezone registry to populate with discovered timezones 1.141 - */ 1.142 - public CalendarBuilder(final TimeZoneRegistry tzRegistry) { 1.143 - this(CalendarParserFactory.getInstance().createParser(), new PropertyFactoryRegistry(), 1.144 - new ParameterFactoryRegistry(), tzRegistry); 1.145 - } 1.146 - 1.147 - /** 1.148 - * Constructs a new instance using the specified parser and registry. 1.149 - * @param parser a calendar parser used to construct the calendar 1.150 - * @param tzRegistry a timezone registry used to retrieve {@link TimeZone}s and 1.151 - * register additional timezone information found 1.152 - * in the calendar 1.153 - */ 1.154 - public CalendarBuilder(CalendarParser parser, TimeZoneRegistry tzRegistry) { 1.155 - this(parser, new PropertyFactoryRegistry(), new ParameterFactoryRegistry(), tzRegistry); 1.156 - } 1.157 - 1.158 - /** 1.159 - * @param parser a custom calendar parser 1.160 - * @param propertyFactoryRegistry registry for non-standard property factories 1.161 - * @param parameterFactoryRegistry registry for non-standard parameter factories 1.162 - * @param tzRegistry a custom timezone registry 1.163 - */ 1.164 - public CalendarBuilder(CalendarParser parser, PropertyFactoryRegistry propertyFactoryRegistry, 1.165 - ParameterFactoryRegistry parameterFactoryRegistry, TimeZoneRegistry tzRegistry) { 1.166 - 1.167 - this.parser = parser; 1.168 - this.tzRegistry = tzRegistry; 1.169 - this.contentHandler = new ContentHandlerImpl(ComponentFactory.getInstance(), 1.170 - propertyFactoryRegistry, parameterFactoryRegistry); 1.171 - } 1.172 - 1.173 - /** 1.174 - * Builds an iCalendar model from the specified input stream. 1.175 - * @param in an input stream to read calendar data from 1.176 - * @return a calendar parsed from the specified input stream 1.177 - * @throws IOException where an error occurs reading data from the specified stream 1.178 - * @throws ParserException where an error occurs parsing data from the stream 1.179 - */ 1.180 - public Calendar build(final InputStream in) throws IOException, 1.181 - ParserException { 1.182 - return build(new InputStreamReader(in, DEFAULT_CHARSET)); 1.183 - } 1.184 - 1.185 - /** 1.186 - * Builds an iCalendar model from the specified reader. An <code>UnfoldingReader</code> is applied to the 1.187 - * specified reader to ensure the data stream is correctly unfolded where appropriate. 1.188 - * @param in a reader to read calendar data from 1.189 - * @return a calendar parsed from the specified reader 1.190 - * @throws IOException where an error occurs reading data from the specified reader 1.191 - * @throws ParserException where an error occurs parsing data from the reader 1.192 - */ 1.193 - public Calendar build(final Reader in) throws IOException, ParserException { 1.194 - return build(new UnfoldingReader(in)); 1.195 - } 1.196 - 1.197 - /** 1.198 - * Build an iCalendar model by parsing data from the specified reader. 1.199 - * @param uin an unfolding reader to read data from 1.200 - * @return a calendar parsed from the specified reader 1.201 - * @throws IOException where an error occurs reading data from the specified reader 1.202 - * @throws ParserException where an error occurs parsing data from the reader 1.203 - */ 1.204 - public Calendar build(final UnfoldingReader uin) throws IOException, 1.205 - ParserException { 1.206 - // re-initialise.. 1.207 - calendar = null; 1.208 - component = null; 1.209 - subComponent = null; 1.210 - property = null; 1.211 - datesMissingTimezones = new ArrayList(); 1.212 - 1.213 - parser.parse(uin, contentHandler); 1.214 - 1.215 - if (datesMissingTimezones.size() > 0 && tzRegistry != null) { 1.216 - resolveTimezones(); 1.217 - } 1.218 - 1.219 - return calendar; 1.220 - } 1.221 - 1.222 - private class ContentHandlerImpl implements ContentHandler { 1.223 - 1.224 - private final ComponentFactory componentFactory; 1.225 - 1.226 - private final PropertyFactory propertyFactory; 1.227 - 1.228 - private final ParameterFactory parameterFactory; 1.229 - 1.230 - public ContentHandlerImpl(ComponentFactory componentFactory, PropertyFactory propertyFactory, 1.231 - ParameterFactory parameterFactory) { 1.232 - 1.233 - this.componentFactory = componentFactory; 1.234 - this.propertyFactory = propertyFactory; 1.235 - this.parameterFactory = parameterFactory; 1.236 - } 1.237 - 1.238 - public void endCalendar() { 1.239 - // do nothing.. 1.240 - } 1.241 - 1.242 - public void endComponent(final String name) { 1.243 - assertComponent(component); 1.244 - 1.245 - if (subComponent != null) { 1.246 - if (component instanceof VTimeZone) { 1.247 - ((VTimeZone) component).getObservances().add(subComponent); 1.248 - } 1.249 - else if (component instanceof VEvent) { 1.250 - ((VEvent) component).getAlarms().add(subComponent); 1.251 - } 1.252 - else if (component instanceof VToDo) { 1.253 - ((VToDo) component).getAlarms().add(subComponent); 1.254 - } 1.255 - else if (component instanceof VAvailability) { 1.256 - ((VAvailability) component).getAvailable().add(subComponent); 1.257 - } 1.258 - subComponent = null; 1.259 - } 1.260 - else { 1.261 - calendar.getComponents().add(component); 1.262 - if (component instanceof VTimeZone && tzRegistry != null) { 1.263 - // register the timezone for use with iCalendar objects.. 1.264 - tzRegistry.register(new TimeZone((VTimeZone) component)); 1.265 - } 1.266 - component = null; 1.267 - } 1.268 - } 1.269 - 1.270 - public void endProperty(final String name) { 1.271 - assertProperty(property); 1.272 - 1.273 - // replace with a constant instance if applicable.. 1.274 - property = Constants.forProperty(property); 1.275 - if (component != null) { 1.276 - if (subComponent != null) { 1.277 - subComponent.getProperties().add(property); 1.278 - } 1.279 - else { 1.280 - component.getProperties().add(property); 1.281 - } 1.282 - } 1.283 - else if (calendar != null) { 1.284 - calendar.getProperties().add(property); 1.285 - } 1.286 - 1.287 - property = null; 1.288 - } 1.289 - 1.290 - public void parameter(final String name, final String value) throws URISyntaxException { 1.291 - assertProperty(property); 1.292 - 1.293 - // parameter names are case-insensitive, but convert to upper case to simplify further processing 1.294 - final Parameter param = parameterFactory.createParameter(name.toUpperCase(), Strings.escapeNewline(value)); 1.295 - property.getParameters().add(param); 1.296 - if (param instanceof TzId && tzRegistry != null && !(property instanceof XProperty)) { 1.297 - final TimeZone timezone = tzRegistry.getTimeZone(param.getValue()); 1.298 - if (timezone != null) { 1.299 - updateTimeZone(property, timezone); 1.300 - } else { 1.301 - // VTIMEZONE may be defined later, so so keep 1.302 - // track of dates until all components have been 1.303 - // parsed, and then try again later 1.304 - datesMissingTimezones.add(property); 1.305 - } 1.306 - } 1.307 - } 1.308 - 1.309 - /** 1.310 - * {@inheritDoc} 1.311 - */ 1.312 - public void propertyValue(final String value) throws URISyntaxException, 1.313 - ParseException, IOException { 1.314 - 1.315 - assertProperty(property); 1.316 - 1.317 - if (property instanceof Escapable) { 1.318 - property.setValue(Strings.unescape(value)); 1.319 - } 1.320 - else { 1.321 - property.setValue(value); 1.322 - } 1.323 - } 1.324 - 1.325 - /** 1.326 - * {@inheritDoc} 1.327 - */ 1.328 - public void startCalendar() { 1.329 - calendar = new Calendar(); 1.330 - } 1.331 - 1.332 - /** 1.333 - * {@inheritDoc} 1.334 - */ 1.335 - public void startComponent(final String name) { 1.336 - if (component != null) { 1.337 - subComponent = componentFactory.createComponent(name); 1.338 - } 1.339 - else { 1.340 - component = componentFactory.createComponent(name); 1.341 - } 1.342 - } 1.343 - 1.344 - /** 1.345 - * {@inheritDoc} 1.346 - */ 1.347 - public void startProperty(final String name) { 1.348 - // property names are case-insensitive, but convert to upper case to simplify further processing 1.349 - property = propertyFactory.createProperty(name.toUpperCase()); 1.350 - } 1.351 - } 1.352 - 1.353 - private void assertComponent(Component component) { 1.354 - if (component == null) { 1.355 - throw new CalendarException("Expected component not initialised"); 1.356 - } 1.357 - } 1.358 - 1.359 - private void assertProperty(Property property) { 1.360 - if (property == null) { 1.361 - throw new CalendarException("Expected property not initialised"); 1.362 - } 1.363 - } 1.364 - 1.365 - /** 1.366 - * Returns the timezone registry used in the construction of calendars. 1.367 - * @return a timezone registry 1.368 - */ 1.369 - public final TimeZoneRegistry getRegistry() { 1.370 - return tzRegistry; 1.371 - } 1.372 - 1.373 - private void updateTimeZone(Property property, TimeZone timezone) { 1.374 - try { 1.375 - ((DateProperty) property).setTimeZone(timezone); 1.376 - } 1.377 - catch (ClassCastException e) { 1.378 - try { 1.379 - ((DateListProperty) property).setTimeZone(timezone); 1.380 - } 1.381 - catch (ClassCastException e2) { 1.382 - if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { 1.383 - Log log = LogFactory.getLog(CalendarBuilder.class); 1.384 - log.warn("Error setting timezone [" + timezone.getID() 1.385 - + "] on property [" + property.getName() 1.386 - + "]", e); 1.387 - } 1.388 - else { 1.389 - throw e2; 1.390 - } 1.391 - } 1.392 - } 1.393 - } 1.394 - 1.395 - private void resolveTimezones() 1.396 - throws IOException { 1.397 - 1.398 - // Go through each property and try to resolve the TZID. 1.399 - for (final Iterator it = datesMissingTimezones.iterator();it.hasNext();) { 1.400 - final Property property = (Property) it.next(); 1.401 - final Parameter tzParam = property.getParameter(Parameter.TZID); 1.402 - 1.403 - // tzParam might be null: 1.404 - if (tzParam == null) { 1.405 - continue; 1.406 - } 1.407 - 1.408 - //lookup timezone 1.409 - final TimeZone timezone = tzRegistry.getTimeZone(tzParam.getValue()); 1.410 - 1.411 - // If timezone found, then update date property 1.412 - if (timezone != null) { 1.413 - // Get the String representation of date(s) as 1.414 - // we will need this after changing the timezone 1.415 - final String strDate = property.getValue(); 1.416 - 1.417 - // Change the timezone 1.418 - if(property instanceof DateProperty) { 1.419 - ((DateProperty) property).setTimeZone(timezone); 1.420 - } 1.421 - else if(property instanceof DateListProperty) { 1.422 - ((DateListProperty) property).setTimeZone(timezone); 1.423 - } 1.424 - 1.425 - // Reset value 1.426 - try { 1.427 - property.setValue(strDate); 1.428 - } catch (ParseException e) { 1.429 - // shouldn't happen as its already been parsed 1.430 - throw new CalendarException(e); 1.431 - } catch (URISyntaxException e) { 1.432 - // shouldn't happen as its already been parsed 1.433 - throw new CalendarException(e); 1.434 - } 1.435 - } 1.436 - } 1.437 - } 1.438 -}