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.component; michael@0: michael@0: import java.util.HashMap; michael@0: import java.util.Map; michael@0: michael@0: import net.fortuna.ical4j.model.DateTime; michael@0: import net.fortuna.ical4j.model.Dur; michael@0: import net.fortuna.ical4j.model.Property; michael@0: import net.fortuna.ical4j.model.PropertyList; michael@0: import net.fortuna.ical4j.model.ValidationException; michael@0: import net.fortuna.ical4j.model.Validator; michael@0: import net.fortuna.ical4j.model.property.Action; michael@0: import net.fortuna.ical4j.model.property.Attach; michael@0: import net.fortuna.ical4j.model.property.Description; michael@0: import net.fortuna.ical4j.model.property.Duration; michael@0: import net.fortuna.ical4j.model.property.Method; michael@0: import net.fortuna.ical4j.model.property.Repeat; michael@0: import net.fortuna.ical4j.model.property.Summary; michael@0: import net.fortuna.ical4j.model.property.Trigger; michael@0: import net.fortuna.ical4j.util.PropertyValidator; michael@0: michael@0: /** michael@0: * $Id$ [Apr 5, 2004] michael@0: * michael@0: * Defines an iCalendar VALARM component. michael@0: * michael@0: *
michael@0:  *    4.6.6 Alarm Component
michael@0:  *
michael@0:  *       Component Name: VALARM
michael@0:  *
michael@0:  *       Purpose: Provide a grouping of component properties that define an
michael@0:  *       alarm.
michael@0:  *
michael@0:  *       Formal Definition: A "VALARM" calendar component is defined by the
michael@0:  *       following notation:
michael@0:  *
michael@0:  *              alarmc     = "BEGIN" ":" "VALARM" CRLF
michael@0:  *                           (audioprop / dispprop / emailprop / procprop)
michael@0:  *                           "END" ":" "VALARM" CRLF
michael@0:  *
michael@0:  *         audioprop  = 2*(
michael@0:  *
michael@0:  *                    ; 'action' and 'trigger' are both REQUIRED,
michael@0:  *                    ; but MUST NOT occur more than once
michael@0:  *
michael@0:  *                    action / trigger /
michael@0:  *
michael@0:  *                    ; 'duration' and 'repeat' are both optional,
michael@0:  *                    ; and MUST NOT occur more than once each,
michael@0:  *                    ; but if one occurs, so MUST the other
michael@0:  *
michael@0:  *                    duration / repeat /
michael@0:  *
michael@0:  *                    ; the following is optional,
michael@0:  *                    ; but MUST NOT occur more than once
michael@0:  *
michael@0:  *                    attach /
michael@0:  *
michael@0:  *                    ; the following is optional,
michael@0:  *                    ; and MAY occur more than once
michael@0:  *
michael@0:  *                    x-prop
michael@0:  *
michael@0:  *                    )
michael@0:  *
michael@0:  *
michael@0:  *
michael@0:  *         dispprop   = 3*(
michael@0:  *
michael@0:  *                    ; the following are all REQUIRED,
michael@0:  *                    ; but MUST NOT occur more than once
michael@0:  *
michael@0:  *                    action / description / trigger /
michael@0:  *
michael@0:  *                    ; 'duration' and 'repeat' are both optional,
michael@0:  *                    ; and MUST NOT occur more than once each,
michael@0:  *                    ; but if one occurs, so MUST the other
michael@0:  *
michael@0:  *                    duration / repeat /
michael@0:  *
michael@0:  *                    ; the following is optional,
michael@0:  *                    ; and MAY occur more than once
michael@0:  *
michael@0:  *                    *x-prop
michael@0:  *
michael@0:  *                    )
michael@0:  *
michael@0:  *
michael@0:  *
michael@0:  *         emailprop  = 5*(
michael@0:  *
michael@0:  *                    ; the following are all REQUIRED,
michael@0:  *                    ; but MUST NOT occur more than once
michael@0:  *
michael@0:  *                    action / description / trigger / summary
michael@0:  *
michael@0:  *                    ; the following is REQUIRED,
michael@0:  *                    ; and MAY occur more than once
michael@0:  *
michael@0:  *                    attendee /
michael@0:  *
michael@0:  *                    ; 'duration' and 'repeat' are both optional,
michael@0:  *                    ; and MUST NOT occur more than once each,
michael@0:  *                    ; but if one occurs, so MUST the other
michael@0:  *
michael@0:  *                    duration / repeat /
michael@0:  *
michael@0:  *                    ; the following are optional,
michael@0:  *                    ; and MAY occur more than once
michael@0:  *
michael@0:  *                    attach / x-prop
michael@0:  *
michael@0:  *                    )
michael@0:  *
michael@0:  *
michael@0:  *
michael@0:  *         procprop   = 3*(
michael@0:  *
michael@0:  *                    ; the following are all REQUIRED,
michael@0:  *                    ; but MUST NOT occur more than once
michael@0:  *
michael@0:  *                    action / attach / trigger /
michael@0:  *
michael@0:  *                    ; 'duration' and 'repeat' are both optional,
michael@0:  *                    ; and MUST NOT occur more than once each,
michael@0:  *                    ; but if one occurs, so MUST the other
michael@0:  *
michael@0:  *                    duration / repeat /
michael@0:  *
michael@0:  *                    ; 'description' is optional,
michael@0:  *                    ; and MUST NOT occur more than once
michael@0:  *
michael@0:  *                    description /
michael@0:  *
michael@0:  *                    ; the following is optional,
michael@0:  *                    ; and MAY occur more than once
michael@0:  *
michael@0:  *                    x-prop
michael@0:  *
michael@0:  *                    )
michael@0:  * 
michael@0: * michael@0: * Example 1 - Creating an alarm to trigger at a specific time: michael@0: * michael@0: *

michael@0:  * java.util.Calendar cal = java.util.Calendar.getInstance();
michael@0:  * cal.set(java.util.Calendar.MONTH, java.util.Calendar.DECEMBER);
michael@0:  * cal.set(java.util.Calendar.DAY_OF_MONTH, 25);
michael@0:  *
michael@0:  * VAlarm christmas = new VAlarm(cal.getTime());
michael@0:  * 
michael@0: * michael@0: * Example 2 - Creating an alarm to trigger one (1) hour before the scheduled start of the parent event/the parent todo michael@0: * is due: michael@0: * michael@0: *

michael@0:  * VAlarm reminder = new VAlarm(new Dur(0, -1, 0, 0));
michael@0:  *
michael@0:  * // repeat reminder four (4) more times every fifteen (15) minutes..
michael@0:  * reminder.getProperties().add(new Repeat(4));
michael@0:  * reminder.getProperties().add(new Duration(new Dur(0, 0, 15, 0)));
michael@0:  *
michael@0:  * // display a message..
michael@0:  * reminder.getProperties().add(Action.DISPLAY);
michael@0:  * reminder.getProperties().add(new Description("Progress Meeting at 9:30am"));
michael@0:  * 
michael@0: * michael@0: * @author Ben Fortuna michael@0: */ michael@0: public class VAlarm extends CalendarComponent { michael@0: michael@0: private static final long serialVersionUID = -8193965477414653802L; michael@0: michael@0: private final Map actionValidators = new HashMap(); michael@0: { michael@0: actionValidators.put(Action.AUDIO, new AudioValidator()); michael@0: actionValidators.put(Action.DISPLAY, new DisplayValidator()); michael@0: actionValidators.put(Action.EMAIL, new EmailValidator()); michael@0: actionValidators.put(Action.PROCEDURE, new ProcedureValidator()); michael@0: } michael@0: michael@0: private final Validator itipValidator = new ITIPValidator(); michael@0: michael@0: /** michael@0: * Default constructor. michael@0: */ michael@0: public VAlarm() { michael@0: super(VALARM); michael@0: } michael@0: michael@0: /** michael@0: * Constructor. michael@0: * @param properties a list of properties michael@0: */ michael@0: public VAlarm(final PropertyList properties) { michael@0: super(VALARM, properties); michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VALARM instance that will trigger at the specified time. michael@0: * @param trigger the time the alarm will trigger michael@0: */ michael@0: public VAlarm(final DateTime trigger) { michael@0: this(); michael@0: getProperties().add(new Trigger(trigger)); michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VALARM instance that will trigger at the specified time relative to the event/todo component. michael@0: * @param trigger a duration of time relative to the parent component that the alarm will trigger at michael@0: */ michael@0: public VAlarm(final Dur trigger) { michael@0: this(); michael@0: getProperties().add(new Trigger(trigger)); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final void validate(final boolean recurse) michael@0: throws ValidationException { michael@0: michael@0: /* michael@0: * ; 'action' and 'trigger' are both REQUIRED, ; but MUST NOT occur more than once action / trigger / michael@0: */ michael@0: PropertyValidator.getInstance().assertOne(Property.ACTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.TRIGGER, getProperties()); michael@0: michael@0: /* michael@0: * ; 'duration' and 'repeat' are both optional, ; and MUST NOT occur more than once each, ; but if one occurs, michael@0: * so MUST the other duration / repeat / michael@0: */ michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.REPEAT, getProperties()); michael@0: michael@0: try { michael@0: PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.REPEAT, getProperties()); michael@0: } michael@0: catch (ValidationException ve) { michael@0: PropertyValidator.getInstance().assertOne(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.REPEAT, getProperties()); michael@0: } michael@0: michael@0: /* michael@0: * ; the following is optional, ; and MAY occur more than once x-prop michael@0: */ michael@0: michael@0: final Validator actionValidator = (Validator) actionValidators.get(getAction()); michael@0: if (actionValidator != null) { michael@0: actionValidator.validate(); michael@0: } michael@0: michael@0: if (recurse) { michael@0: validateProperties(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: protected Validator getValidator(Method method) { michael@0: return itipValidator; michael@0: } michael@0: michael@0: private class AudioValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public void validate() throws ValidationException { michael@0: /* michael@0: * ; the following is optional, ; but MUST NOT occur more than once attach / michael@0: */ michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.ATTACH, getProperties()); michael@0: } michael@0: } michael@0: michael@0: private class DisplayValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public void validate() throws ValidationException { michael@0: /* michael@0: * ; the following are all REQUIRED, ; but MUST NOT occur more than once action / description / trigger / michael@0: */ michael@0: PropertyValidator.getInstance().assertOne(Property.DESCRIPTION, getProperties()); michael@0: } michael@0: } michael@0: michael@0: private class EmailValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public void validate() throws ValidationException { michael@0: /* michael@0: * ; the following are all REQUIRED, michael@0: * ; but MUST NOT occur more than once action / description / trigger / summary michael@0: * ; the following is REQUIRED, michael@0: * ; and MAY occur more than once attendee / michael@0: * ; 'duration' and 'repeat' are both optional, michael@0: * ; and MUST NOT occur more than once each, michael@0: * ; but if one occurs, so MUST the other duration / repeat / michael@0: * ; the following are optional, michael@0: * ; and MAY occur more than once attach / x-prop michael@0: */ michael@0: PropertyValidator.getInstance().assertOne(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); michael@0: } michael@0: } michael@0: michael@0: private class ProcedureValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public void validate() throws ValidationException { michael@0: /* michael@0: * ; the following are all REQUIRED, michael@0: * ; but MUST NOT occur more than once action / attach / trigger / michael@0: * ; 'duration' and 'repeat' are both optional, michael@0: * ; and MUST NOT occur more than once each, michael@0: * ; but if one occurs, so MUST the other duration / repeat / michael@0: * ; 'description' is optional, michael@0: * ; and MUST NOT occur more than once description / michael@0: * ; the following is optional, ; and MAY occur more than once x-prop michael@0: */ michael@0: PropertyValidator.getInstance().assertOne(Property.ATTACH, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Common validation for all iTIP methods. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * VALARM              0+
michael@0:      *     ACTION          1
michael@0:      *     ATTACH          0+
michael@0:      *     DESCRIPTION     0 or 1
michael@0:      *     DURATION        0 or 1  if present REPEAT MUST be present
michael@0:      *     REPEAT          0 or 1  if present DURATION MUST be present
michael@0:      *     SUMMARY         0 or 1
michael@0:      *     TRIGGER         1
michael@0:      *     X-PROPERTY      0+
michael@0:      * 
michael@0: */ michael@0: private class ITIPValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOne(Property.ACTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.TRIGGER, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.REPEAT, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Returns the mandatory action property. michael@0: * @return the ACTION property or null if not specified michael@0: */ michael@0: public final Action getAction() { michael@0: return (Action) getProperty(Property.ACTION); michael@0: } michael@0: michael@0: /** michael@0: * Returns the mandatory trigger property. michael@0: * @return the TRIGGER property or null if not specified michael@0: */ michael@0: public final Trigger getTrigger() { michael@0: return (Trigger) getProperty(Property.TRIGGER); michael@0: } michael@0: michael@0: /** michael@0: * Returns the optional duration property. michael@0: * @return the DURATION property or null if not specified michael@0: */ michael@0: public final Duration getDuration() { michael@0: return (Duration) getProperty(Property.DURATION); michael@0: } michael@0: michael@0: /** michael@0: * Returns the optional repeat property. michael@0: * @return the REPEAT property or null if not specified michael@0: */ michael@0: public final Repeat getRepeat() { michael@0: return (Repeat) getProperty(Property.REPEAT); michael@0: } michael@0: michael@0: /** michael@0: * Returns the optional attachment property. michael@0: * @return the ATTACH property or null if not specified michael@0: */ michael@0: public final Attach getAttachment() { michael@0: return (Attach) getProperty(Property.ATTACH); michael@0: } michael@0: michael@0: /** michael@0: * Returns the optional description property. michael@0: * @return the DESCRIPTION property or null if not specified michael@0: */ michael@0: public final Description getDescription() { michael@0: return (Description) getProperty(Property.DESCRIPTION); michael@0: } michael@0: michael@0: /** michael@0: * Returns the optional summary property. michael@0: * @return the SUMMARY property or null if not specified michael@0: */ michael@0: public final Summary getSummary() { michael@0: return (Summary) getProperty(Property.SUMMARY); michael@0: } michael@0: }