michael@0: /** michael@0: * Copyright (c) 2012, Ben Fortuna michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * michael@0: * o Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * michael@0: * o Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * michael@0: * o Neither the name of Ben Fortuna nor the names of any other contributors michael@0: * may be used to endorse or promote products derived from this software michael@0: * without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR michael@0: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF michael@0: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING michael@0: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS michael@0: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: package net.fortuna.ical4j.model; michael@0: michael@0: import java.io.IOException; michael@0: import java.net.URISyntaxException; michael@0: import java.text.ParseException; michael@0: import net.fortuna.ical4j.model.parameter.Value; michael@0: import net.fortuna.ical4j.model.property.XProperty; michael@0: michael@0: import net.fortuna.ical4j.util.Strings; michael@0: michael@4: import org.apache.commons.lang3.builder.EqualsBuilder; michael@4: import org.apache.commons.lang3.builder.HashCodeBuilder; michael@0: michael@0: /** michael@0: * Defines an iCalendar property. Subclasses of this class provide additional validation and typed values for specific michael@0: * iCalendar properties. michael@0: * michael@0: * Note that subclasses must provide a reference to the factory used to create the michael@0: * property to support property cloning (copy). If no factory is specified an michael@0: * {@link UnsupportedOperationException} will be thrown by the {@link #copy()} method. michael@0: * michael@0: * @author Ben Fortuna michael@0: * michael@0: * $Id$ [Apr 5, 2004] michael@0: */ michael@0: public abstract class Property extends Content { michael@0: michael@0: private static final long serialVersionUID = 7048785558435608687L; michael@0: michael@0: // iCalendar properties.. michael@0: michael@0: /** michael@0: * Product identifier property name. michael@0: */ michael@0: public static final String PRODID = "PRODID"; michael@0: michael@0: /** michael@0: * iCalendar version property name. michael@0: */ michael@0: public static final String VERSION = "VERSION"; michael@0: michael@0: /** michael@0: * Calendar scale property name. michael@0: */ michael@0: public static final String CALSCALE = "CALSCALE"; michael@0: michael@0: /** michael@0: * iTIP method property name. michael@0: */ michael@0: public static final String METHOD = "METHOD"; michael@0: michael@0: // Component properties.. michael@0: michael@0: /** michael@0: * Busy type property name. michael@0: */ michael@0: public static final String BUSYTYPE = "BUSYTYPE"; michael@0: michael@0: /** michael@0: * Classifier property name. michael@0: */ michael@0: public static final String CLASS = "CLASS"; michael@0: michael@0: /** michael@0: * Creation date property name. michael@0: */ michael@0: public static final String CREATED = "CREATED"; michael@0: michael@0: /** michael@0: * Description property name. michael@0: */ michael@0: public static final String DESCRIPTION = "DESCRIPTION"; michael@0: michael@0: /** michael@0: * Start date property name. michael@0: */ michael@0: public static final String DTSTART = "DTSTART"; michael@0: michael@0: /** michael@0: * Geographic location property name. michael@0: */ michael@0: public static final String GEO = "GEO"; michael@0: michael@0: /** michael@0: * Last modified date property name. michael@0: */ michael@0: public static final String LAST_MODIFIED = "LAST-MODIFIED"; michael@0: michael@0: /** michael@0: * Location property name. michael@0: */ michael@0: public static final String LOCATION = "LOCATION"; michael@0: michael@0: /** michael@0: * Organiser property name. michael@0: */ michael@0: public static final String ORGANIZER = "ORGANIZER"; michael@0: michael@0: /** michael@0: * Percentage complete property name. michael@0: */ michael@0: public static final String PERCENT_COMPLETE = "PERCENT-COMPLETE"; michael@0: michael@0: /** michael@0: * Prority property name. michael@0: */ michael@0: public static final String PRIORITY = "PRIORITY"; michael@0: michael@0: /** michael@0: * Date-stamp property name. michael@0: */ michael@0: public static final String DTSTAMP = "DTSTAMP"; michael@0: michael@0: /** michael@0: * Sequence property name. michael@0: */ michael@0: public static final String SEQUENCE = "SEQUENCE"; michael@0: michael@0: /** michael@0: * Status property name. michael@0: */ michael@0: public static final String STATUS = "STATUS"; michael@0: michael@0: /** michael@0: * Summary property name. michael@0: */ michael@0: public static final String SUMMARY = "SUMMARY"; michael@0: michael@0: /** michael@0: * Transparency property name. michael@0: */ michael@0: public static final String TRANSP = "TRANSP"; michael@0: michael@0: /** michael@0: * Unique identifier property name. michael@0: */ michael@0: public static final String UID = "UID"; michael@0: michael@0: /** michael@0: * Uniform resource locator property name. michael@0: */ michael@0: public static final String URL = "URL"; michael@0: michael@0: /** michael@0: * Recurrence identifier property name. michael@0: */ michael@0: public static final String RECURRENCE_ID = "RECURRENCE-ID"; michael@0: michael@0: /** michael@0: * Completed date property name. michael@0: */ michael@0: public static final String COMPLETED = "COMPLETED"; michael@0: michael@0: /** michael@0: * Due date property name. michael@0: */ michael@0: public static final String DUE = "DUE"; michael@0: michael@0: /** michael@0: * Free/busy property name. michael@0: */ michael@0: public static final String FREEBUSY = "FREEBUSY"; michael@0: michael@0: /** michael@0: * Timezone identifier property name. michael@0: */ michael@0: public static final String TZID = "TZID"; michael@0: michael@0: /** michael@0: * Timezone name property name. michael@0: */ michael@0: public static final String TZNAME = "TZNAME"; michael@0: michael@0: /** michael@0: * Prior timezone offset property name. michael@0: */ michael@0: public static final String TZOFFSETFROM = "TZOFFSETFROM"; michael@0: michael@0: /** michael@0: * New timezone offset property name. michael@0: */ michael@0: public static final String TZOFFSETTO = "TZOFFSETTO"; michael@0: michael@0: /** michael@0: * URL for timezone definition property name. michael@0: */ michael@0: public static final String TZURL = "TZURL"; michael@0: michael@0: /** michael@0: * Alarm action property name. michael@0: */ michael@0: public static final String ACTION = "ACTION"; michael@0: michael@0: /** michael@0: * Repeat rule property name. michael@0: */ michael@0: public static final String REPEAT = "REPEAT"; michael@0: michael@0: /** michael@0: * Alarm trigger property name. michael@0: */ michael@0: public static final String TRIGGER = "TRIGGER"; michael@0: michael@0: /** michael@0: * Request status property name. michael@0: */ michael@0: public static final String REQUEST_STATUS = "REQUEST-STATUS"; michael@0: michael@0: /** michael@0: * End date property name. michael@0: */ michael@0: public static final String DTEND = "DTEND"; michael@0: michael@0: /** michael@0: * Duration property name. michael@0: */ michael@0: public static final String DURATION = "DURATION"; michael@0: michael@0: /** michael@0: * Attachment property name. michael@0: */ michael@0: public static final String ATTACH = "ATTACH"; michael@0: michael@0: /** michael@0: * Attendee property name. michael@0: */ michael@0: public static final String ATTENDEE = "ATTENDEE"; michael@0: michael@0: /** michael@0: * Categories property name. michael@0: */ michael@0: public static final String CATEGORIES = "CATEGORIES"; michael@0: michael@0: /** michael@0: * Comment property name. michael@0: */ michael@0: public static final String COMMENT = "COMMENT"; michael@0: michael@0: /** michael@0: * Contact property name. michael@0: */ michael@0: public static final String CONTACT = "CONTACT"; michael@0: michael@0: /** michael@0: * Exclusion date property name. michael@0: */ michael@0: public static final String EXDATE = "EXDATE"; michael@0: michael@0: /** michael@0: * Exclusion rule property name. michael@0: */ michael@0: public static final String EXRULE = "EXRULE"; michael@0: michael@0: /** michael@0: * Relationship property name. michael@0: */ michael@0: public static final String RELATED_TO = "RELATED-TO"; michael@0: michael@0: /** michael@0: * Resources property name. michael@0: */ michael@0: public static final String RESOURCES = "RESOURCES"; michael@0: michael@0: /** michael@0: * Recurrence date property name. michael@0: */ michael@0: public static final String RDATE = "RDATE"; michael@0: michael@0: /** michael@0: * Recurrence rule property name. michael@0: */ michael@0: public static final String RRULE = "RRULE"; michael@0: michael@0: /** michael@0: * Prefix for non-standard properties. michael@0: */ michael@0: public static final String EXPERIMENTAL_PREFIX = "X-"; michael@0: michael@0: /** michael@0: * VVENUE country property name. michael@0: */ michael@0: public static final String COUNTRY = "COUNTRY"; michael@0: michael@0: /** michael@0: * VVENUE extended address property name. michael@0: */ michael@0: public static final String EXTENDED_ADDRESS = "EXTENDED-ADDRESS"; michael@0: michael@0: /** michael@0: * VVENUE locality property name. michael@0: */ michael@0: public static final String LOCALITY = "LOCALITY"; michael@0: michael@0: /** michael@0: * VVENUE location type property name. michael@0: */ michael@0: public static final String LOCATION_TYPE = "LOCATION-TYPE"; michael@0: michael@0: /** michael@0: * VVENUE name property name. michael@0: */ michael@0: public static final String NAME = "NAME"; michael@0: michael@0: /** michael@0: * VVENUE postal code property name. michael@0: */ michael@0: public static final String POSTALCODE = "POSTAL-CODE"; michael@0: michael@0: /** michael@0: * VVENUE region property name. michael@0: */ michael@0: public static final String REGION = "REGION"; michael@0: michael@0: /** michael@0: * VVENUE street address property name. michael@0: */ michael@0: public static final String STREET_ADDRESS = "STREET-ADDRESS"; michael@0: michael@0: /** michael@0: * VVENUE telephone property name. michael@0: */ michael@0: public static final String TEL = "TEL"; michael@0: michael@0: private String name; michael@0: michael@0: private ParameterList parameters; michael@0: michael@0: private final PropertyFactory factory; michael@0: michael@0: /** michael@0: * Constructor. michael@0: * @param aName property name michael@0: * @param factory the factory used to create the property instance michael@0: */ michael@0: protected Property(final String aName, PropertyFactory factory) { michael@0: this(aName, new ParameterList(), factory); michael@0: } michael@0: michael@0: /** michael@0: * Constructor made protected to enforce the use of PropertyFactory for property instantiation. michael@0: * @param aName property name michael@0: * @param aList a list of parameters michael@0: */ michael@0: // protected Property(final String aName, final ParameterList aList) { michael@0: // this(aName, aList, PropertyFactoryImpl.getInstance()); michael@0: // } michael@0: michael@0: /** michael@0: * @param aName a property identifier michael@0: * @param aList a list of initial parameters michael@0: * @param factory the factory used to create the property instance michael@0: */ michael@0: protected Property(final String aName, final ParameterList aList, PropertyFactory factory) { michael@0: this.name = aName; michael@0: this.parameters = aList; michael@0: this.factory = factory; michael@0: } michael@0: michael@0: /** michael@0: * Creates a deep copy of the specified property. That is, the name, parameter list, and value are duplicated from michael@0: * the specified property. This constructor should only be called from sub-classes to ensure type integrity is michael@0: * maintained. michael@0: * @param property a property to copy michael@0: * @throws URISyntaxException where the specified property contains an invalid URI value michael@0: * @throws ParseException where the specified property has invalid data michael@0: * @throws IOException where an error occurs reading data from the specified property michael@0: * @deprecated Use {@link #copy()} instead michael@0: */ michael@0: protected Property(final Property property) throws IOException, michael@0: URISyntaxException, ParseException { michael@0: this(property.getName(), new ParameterList(property.getParameters(), false), michael@0: property.factory); michael@0: setValue(property.getValue()); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final String toString() { michael@0: final StringBuffer buffer = new StringBuffer(); michael@0: buffer.append(getName()); michael@0: if (getParameters() != null) { michael@0: buffer.append(getParameters()); michael@0: } michael@0: buffer.append(':'); michael@0: boolean needsEscape = false; michael@0: if (this instanceof XProperty) { michael@0: Value valParam = (Value)getParameter(Parameter.VALUE); michael@0: if (valParam == null || valParam.equals(Value.TEXT)) { michael@0: needsEscape = true; michael@0: } michael@0: } else if (this instanceof Escapable) { michael@0: needsEscape = true; michael@0: } michael@0: if (needsEscape) { michael@0: buffer.append(Strings.escape(Strings.valueOf(getValue()))); michael@0: } michael@0: else { michael@0: buffer.append(Strings.valueOf(getValue())); michael@0: } michael@0: buffer.append(Strings.LINE_SEPARATOR); michael@0: michael@0: return buffer.toString(); michael@0: } michael@0: michael@0: /** michael@0: * Indicates whether this property is a calendar property. michael@0: * @return boolean michael@0: */ michael@0: public boolean isCalendarProperty() { michael@0: michael@0: return PRODID.equalsIgnoreCase(getName()) michael@0: || VERSION.equalsIgnoreCase(getName()) michael@0: || CALSCALE.equalsIgnoreCase(getName()) michael@0: || METHOD.equalsIgnoreCase(getName()); michael@0: } michael@0: michael@0: /** michael@0: * @return Returns the name. michael@0: */ michael@0: public final String getName() { michael@0: return name; michael@0: } michael@0: michael@0: /** michael@0: * @return Returns the parameters. michael@0: */ michael@0: public final ParameterList getParameters() { michael@0: return parameters; michael@0: } michael@0: michael@0: /** michael@0: * Convenience method for retrieving a list of named parameters. michael@0: * @param name name of parameters to retrieve michael@0: * @return a parameter list containing only parameters with the specified name michael@0: */ michael@0: public final ParameterList getParameters(final String name) { michael@0: return getParameters().getParameters(name); michael@0: } michael@0: michael@0: /** michael@0: * Convenience method for retrieving a single parameter. michael@0: * @param name name of the parameter to retrieve michael@0: * @return the first parameter from the parameter list with the specified name michael@0: */ michael@0: public final Parameter getParameter(final String name) { michael@0: return getParameters().getParameter(name); michael@0: } michael@0: michael@0: /** michael@0: * Sets the current value of the property. michael@0: * @param aValue a string representation of the property value michael@0: * @throws IOException possibly thrown by setting the value of certain properties michael@0: * @throws URISyntaxException possibly thrown by setting the value of certain properties michael@0: * @throws ParseException possibly thrown by setting the value of certain properties michael@0: */ michael@0: public abstract void setValue(String aValue) throws IOException, michael@0: URISyntaxException, ParseException; michael@0: michael@0: /** michael@0: * Perform validation on a property. michael@0: * @throws ValidationException where the property is not in a valid state michael@0: */ michael@0: public abstract void validate() throws ValidationException; michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final boolean equals(final Object arg0) { michael@0: if (arg0 instanceof Property) { michael@0: final Property p = (Property) arg0; michael@0: if (getName().equals(p.getName())) { michael@0: return new EqualsBuilder().append(getValue(), p.getValue()) michael@0: .append(getParameters(), p.getParameters()).isEquals(); michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: return super.equals(arg0); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public int hashCode() { michael@0: // as property name is case-insensitive generate hash for uppercase.. michael@0: return new HashCodeBuilder().append(getName().toUpperCase()).append( michael@0: getValue()).append(getParameters()).toHashCode(); michael@0: } michael@0: michael@0: /** michael@0: * Create a (deep) copy of this property. michael@0: * @return the copy of the property michael@0: * @throws IOException where an error occurs reading property data michael@0: * @throws URISyntaxException where the property contains an invalid URI value michael@0: * @throws ParseException where the property contains an invalid date value michael@0: */ michael@0: public Property copy() throws IOException, URISyntaxException, ParseException { michael@0: if (factory == null) { michael@0: throw new UnsupportedOperationException("No factory specified"); michael@0: } michael@0: // Deep copy parameter list.. michael@0: final ParameterList params = new ParameterList(getParameters(), false); michael@0: return factory.createProperty(getName(), params, getValue()); michael@0: } michael@0: }