/*
* $Id: CalendarBuilder.java,v 1.22 2006/05/27 13:20:06 fortuna Exp $
*
* Created: Apr 5, 2004
*
* Copyright (c) 2004, Ben Fortuna
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* o Neither the name of Ben Fortuna nor the names of any other contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.fortuna.ical4j.data;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.text.ParseException;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.ComponentFactory;
import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.ParameterFactoryImpl;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyFactoryImpl;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.component.VTimeZone;
import net.fortuna.ical4j.model.component.VToDo;
import net.fortuna.ical4j.model.parameter.TzId;
import net.fortuna.ical4j.model.property.DateListProperty;
import net.fortuna.ical4j.model.property.DateProperty;
import net.fortuna.ical4j.util.Constants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Parses and builds an iCalendar model from an input stream.
* Note that this class is not thread-safe.
*
* @version 2.0
* @author Ben Fortuna
*/
public class CalendarBuilder implements ContentHandler {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Log log = LogFactory.getLog(CalendarBuilder.class);
private CalendarParser parser;
private TimeZoneRegistry registry;
protected Calendar calendar;
protected Component component;
protected Component subComponent;
protected Property property;
/**
* Default constructor.
*/
public CalendarBuilder() {
this(new CalendarParserImpl(), TimeZoneRegistryFactory.getInstance().createRegistry());
}
/**
* Constructs a new calendar builder using the specified
* calendar parser.
* @param parser a calendar parser used to parse calendar files
*/
public CalendarBuilder(final CalendarParser parser) {
this(parser, TimeZoneRegistryFactory.getInstance().createRegistry());
}
/**
* Constructs a new calendar builder using the specified
* timezone registry.
* @param parser a calendar parser used to parse calendar files
*/
public CalendarBuilder(final TimeZoneRegistry registry) {
this(new CalendarParserImpl(), registry);
}
/**
* Constructs a new instance using the specified parser and registry.
* @param parser a calendar parser used to construct the calendar
* @param registry a timezone registry used to retrieve timezones and
* register additional timezone information found in the calendar
*/
public CalendarBuilder(final CalendarParser parser, final TimeZoneRegistry registry) {
this.parser = parser;
this.registry = registry;
}
/**
* Builds an iCalendar model from the specified input stream.
*
* @param in
* @return a calendar
* @throws IOException
* @throws ParserException
*/
public Calendar build(final InputStream in) throws IOException,
ParserException {
return build(new InputStreamReader(in, DEFAULT_CHARSET));
}
/**
* Builds an iCalendar model from the specified reader.
* An <code>UnfoldingReader</code> is applied to the specified
* reader to ensure the data stream is correctly unfolded where
* appropriate.
*
* @param in
* @return a calendar
* @throws IOException
* @throws ParserException
*/
public Calendar build(final Reader in) throws IOException, ParserException {
return build(new UnfoldingReader(in));
}
/**
* Build an iCalendar model by parsing data from the specified reader.
* @param uin an unfolding reader to read data from
* @return a calendar model
* @throws IOException
* @throws ParserException
*/
public Calendar build(final UnfoldingReader uin) throws IOException, ParserException {
// re-initialise..
calendar = null;
component = null;
subComponent = null;
property = null;
parser.parse(uin, this);
return calendar;
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#endCalendar()
*/
public void endCalendar() {
// do nothing..
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#endComponent(java.lang.String)
*/
public void endComponent(final String name) {
if (component != null) {
if (subComponent != null) {
if (component instanceof VTimeZone) {
((VTimeZone) component).getObservances().add(subComponent);
}
else if (component instanceof VEvent) {
((VEvent) component).getAlarms().add(subComponent);
}
else if (component instanceof VToDo) {
((VToDo) component).getAlarms().add(subComponent);
}
subComponent = null;
}
else {
calendar.getComponents().add(component);
if (component instanceof VTimeZone && registry != null) {
// register the timezone for use with iCalendar objects..
registry.register(new TimeZone((VTimeZone) component));
}
component = null;
}
}
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#endProperty(java.lang.String)
*/
public void endProperty(final String name) {
if (property != null) {
// replace with a constant instance if applicable..
property = Constants.forProperty(property);
if (component != null) {
if (subComponent != null) {
subComponent.getProperties().add(property);
}
else {
component.getProperties().add(property);
}
}
else if (calendar != null) {
calendar.getProperties().add(property);
}
property = null;
}
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#parameter(java.lang.String, java.lang.String)
*/
public void parameter(final String name, final String value) throws URISyntaxException {
if (property != null) {
// parameter names are case-insensitive, but convert to upper case to simplify further processing
Parameter param = ParameterFactoryImpl.getInstance().createParameter(name.toUpperCase(), value);
property.getParameters().add(param);
if (param instanceof TzId && registry != null) {
TimeZone timezone = registry.getTimeZone(param.getValue());
if (timezone != null) {
try {
((DateProperty) property).setTimeZone(timezone);
}
catch (Exception e) {
try {
((DateListProperty) property).setTimeZone(timezone);
}
catch (Exception e2) {
log.warn("Error setting timezone [" + param + "] on property [" + property.getName() + "]", e);
}
}
}
}
}
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#propertyValue(java.lang.String)
*/
public void propertyValue(final String value) throws URISyntaxException, ParseException, IOException {
if (property != null) {
property.setValue(value);
}
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#startCalendar()
*/
public void startCalendar() {
calendar = new Calendar();
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#startComponent(java.lang.String)
*/
public void startComponent(final String name) {
if (component != null) {
subComponent = ComponentFactory.getInstance().createComponent(name);
}
else {
component = ComponentFactory.getInstance().createComponent(name);
}
}
/* (non-Javadoc)
* @see net.fortuna.ical4j.data.ContentHandler#startProperty(java.lang.String)
*/
public void startProperty(final String name) {
// property names are case-insensitive, but convert to upper case to simplify further processing
property = PropertyFactoryImpl.getInstance().createProperty(name.toUpperCase());
}
/**
* Returns the timezone registry used in the construction of calendars.
* @return a timezone registry
*/
public final TimeZoneRegistry getRegistry() {
return registry;
}
}