001    /*
002     * The MIT License
003     * Copyright (c) 2012 Microsoft Corporation
004     *
005     * Permission is hereby granted, free of charge, to any person obtaining a copy
006     * of this software and associated documentation files (the "Software"), to deal
007     * in the Software without restriction, including without limitation the rights
008     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009     * copies of the Software, and to permit persons to whom the Software is
010     * furnished to do so, subject to the following conditions:
011     *
012     * The above copyright notice and this permission notice shall be included in
013     * all copies or substantial portions of the Software.
014     *
015     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021     * THE SOFTWARE.
022     */
023    
024    package microsoft.exchange.webservices.data.property.complex.recurrence.pattern;
025    
026    import microsoft.exchange.webservices.data.attribute.EditorBrowsable;
027    import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
028    import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
029    import microsoft.exchange.webservices.data.core.EwsUtilities;
030    import microsoft.exchange.webservices.data.core.ExchangeService;
031    import microsoft.exchange.webservices.data.core.XmlElementNames;
032    import microsoft.exchange.webservices.data.core.enumeration.property.time.DayOfTheWeek;
033    import microsoft.exchange.webservices.data.core.enumeration.property.time.DayOfTheWeekIndex;
034    import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState;
035    import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
036    import microsoft.exchange.webservices.data.core.enumeration.property.time.Month;
037    import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
038    import microsoft.exchange.webservices.data.core.exception.misc.ArgumentException;
039    import microsoft.exchange.webservices.data.core.exception.misc.ArgumentOutOfRangeException;
040    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
041    import microsoft.exchange.webservices.data.property.complex.ComplexProperty;
042    import microsoft.exchange.webservices.data.property.complex.IComplexPropertyChangedDelegate;
043    import microsoft.exchange.webservices.data.property.complex.recurrence.DayOfTheWeekCollection;
044    import microsoft.exchange.webservices.data.property.complex.recurrence.range.EndDateRecurrenceRange;
045    import microsoft.exchange.webservices.data.property.complex.recurrence.range.NoEndRecurrenceRange;
046    import microsoft.exchange.webservices.data.property.complex.recurrence.range.NumberedRecurrenceRange;
047    import microsoft.exchange.webservices.data.property.complex.recurrence.range.RecurrenceRange;
048    
049    import java.util.ArrayList;
050    import java.util.Arrays;
051    import java.util.Calendar;
052    import java.util.Date;
053    import java.util.Iterator;
054    
055    /**
056     * Represents a recurrence pattern, as used by Appointment and Task item.
057     */
058    public abstract class Recurrence extends ComplexProperty {
059    
060      /**
061       * The start date.
062       */
063      private Date startDate;
064    
065      /**
066       * The number of occurrences.
067       */
068      private Integer numberOfOccurrences;
069    
070      /**
071       * The end date.
072       */
073      private Date endDate;
074    
075      /**
076       * Initializes a new instance.
077       */
078      public Recurrence() {
079        super();
080      }
081    
082      /**
083       * Initializes a new instance.
084       *
085       * @param startDate the start date
086       */
087      public Recurrence(Date startDate) {
088        this();
089        this.startDate = startDate;
090      }
091    
092      /**
093       * Gets the name of the XML element.
094       *
095       * @return the xml element name
096       */
097      public abstract String getXmlElementName();
098    
099      /**
100       * Gets a value indicating whether this instance is regeneration pattern.
101       *
102       * @return true, if is regeneration pattern
103       */
104      public boolean isRegenerationPattern() {
105        return false;
106      }
107    
108      /**
109       * Write property to XML.
110       *
111       * @param writer the writer
112       * @throws Exception the exception
113       */
114      public void internalWritePropertiesToXml(EwsServiceXmlWriter writer) throws Exception {
115      }
116    
117      /**
118       * Writes elements to XML.
119       *
120       * @param writer the writer
121       * @throws Exception the exception
122       */
123      @Override
124      public final void writeElementsToXml(EwsServiceXmlWriter writer)
125          throws Exception {
126        writer.writeStartElement(XmlNamespace.Types, this.getXmlElementName());
127        this.internalWritePropertiesToXml(writer);
128        writer.writeEndElement();
129    
130        RecurrenceRange range = null;
131    
132        if (!this.hasEnd()) {
133          range = new NoEndRecurrenceRange(this.getStartDate());
134        } else if (this.getNumberOfOccurrences() != null) {
135          range = new NumberedRecurrenceRange(this.startDate,
136              this.numberOfOccurrences);
137        } else {
138          if (this.getEndDate() != null) {
139            range = new EndDateRecurrenceRange(this.getStartDate(), this
140                .getEndDate());
141          }
142        }
143        if (range != null) {
144          range.writeToXml(writer, range.getXmlElementName());
145        }
146    
147      }
148    
149      /**
150       * Gets a property value or throw if null. *
151       *
152       * @param <T>   the generic type
153       * @param cls   the cls
154       * @param value the value
155       * @param name  the name
156       * @return Property value
157       * @throws ServiceValidationException the service validation exception
158       */
159      public <T> T getFieldValueOrThrowIfNull(Class<T> cls, Object value,
160          String name) throws ServiceValidationException {
161        if (value != null) {
162          return (T) value;
163        } else {
164          throw new ServiceValidationException(String.format(
165              "The recurrence pattern's %s property must be specified.",
166              name));
167        }
168      }
169    
170      /**
171       * Gets the date and time when the recurrence start.
172       *
173       * @return Date
174       * @throws ServiceValidationException the service validation exception
175       */
176      public Date getStartDate() throws ServiceValidationException {
177        return this.getFieldValueOrThrowIfNull(Date.class, this.startDate,
178            "StartDate");
179    
180      }
181    
182      /**
183       * sets the date and time when the recurrence start.
184       *
185       * @param value the new start date
186       */
187      public void setStartDate(Date value) {
188        this.startDate = value;
189      }
190    
191      /**
192       * Gets a value indicating whether the pattern has a fixed number of
193       * occurrences or an end date.
194       *
195       * @return boolean
196       */
197      public boolean hasEnd() {
198    
199        return ((this.numberOfOccurrences != null) || (this.endDate != null));
200      }
201    
202      /**
203       * Sets up this recurrence so that it never ends. Calling NeverEnds is
204       * equivalent to setting both NumberOfOccurrences and EndDate to null.
205       */
206      public void neverEnds() {
207        this.numberOfOccurrences = null;
208        this.endDate = null;
209        this.changed();
210      }
211    
212      /**
213       * Validates this instance.
214       *
215       * @throws Exception
216       */
217      @Override
218      public void internalValidate() throws Exception {
219        super.internalValidate();
220    
221        if (this.startDate == null) {
222          throw new ServiceValidationException("The recurrence pattern's StartDate property must be specified.");
223        }
224      }
225    
226      /**
227       * Gets the number of occurrences after which the recurrence ends.
228       * Setting NumberOfOccurrences resets EndDate.
229       *
230       * @return the number of occurrences
231       */
232      public Integer getNumberOfOccurrences() {
233        return this.numberOfOccurrences;
234    
235      }
236    
237      /**
238       * Gets the number of occurrences after which the recurrence ends.
239       * Setting NumberOfOccurrences resets EndDate.
240       *
241       * @param value the new number of occurrences
242       * @throws ArgumentException the argument exception
243       */
244      public void setNumberOfOccurrences(Integer value) throws ArgumentException {
245        if (value < 1) {
246          throw new ArgumentException("NumberOfOccurrences must be greater than 0.");
247        }
248    
249        if (this.canSetFieldValue(this.numberOfOccurrences, value)) {
250          numberOfOccurrences = value;
251          this.changed();
252        }
253    
254        this.endDate = null;
255    
256      }
257    
258      /**
259       * Gets the date after which the recurrence ends. Setting EndDate resets
260       * NumberOfOccurrences.
261       *
262       * @return the end date
263       */
264      public Date getEndDate() {
265    
266        return this.endDate;
267      }
268    
269      /**
270       * sets the date after which the recurrence ends. Setting EndDate resets
271       * NumberOfOccurrences.
272       *
273       * @param value the new end date
274       */
275      public void setEndDate(Date value) {
276    
277        if (this.canSetFieldValue(this.endDate, value)) {
278          this.endDate = value;
279          this.changed();
280        }
281    
282        this.numberOfOccurrences = null;
283    
284      }
285    
286      /**
287       * Represents a recurrence pattern where each occurrence happens a specific
288       * number of days after the previous one.
289       */
290      public final static class DailyPattern extends IntervalPattern {
291    
292        /**
293         * Gets the name of the XML element.
294         *
295         * @return the xml element name
296         */
297        @Override
298        public String getXmlElementName() {
299          return XmlElementNames.DailyRecurrence;
300        }
301    
302        /**
303         * Initializes a new instance of the DailyPattern class.
304         */
305    
306        public DailyPattern() {
307          super();
308        }
309    
310        /**
311         * Initializes a new instance of the DailyPattern class.
312         *
313         * @param startDate The date and time when the recurrence starts.
314         * @param interval  The number of days between each occurrence.
315         * @throws ArgumentOutOfRangeException the argument out of range exception
316         */
317        public DailyPattern(Date startDate, int interval)
318            throws ArgumentOutOfRangeException {
319          super(startDate, interval);
320        }
321    
322      }
323    
324    
325      /**
326       * Represents a regeneration pattern, as used with recurring tasks, where
327       * each occurrence happens a specified number of days after the previous one
328       * is completed.
329       */
330    
331      public final static class DailyRegenerationPattern extends IntervalPattern {
332    
333        /**
334         * Initializes a new instance of the DailyRegenerationPattern class.
335         */
336        public DailyRegenerationPattern() {
337          super();
338        }
339    
340        /**
341         * Initializes a new instance of the DailyRegenerationPattern class.
342         *
343         * @param startDate The date and time when the recurrence starts.
344         * @param interval  The number of days between each occurrence.
345         * @throws ArgumentOutOfRangeException the argument out of range exception
346         */
347        public DailyRegenerationPattern(Date startDate, int interval)
348            throws ArgumentOutOfRangeException {
349          super(startDate, interval);
350    
351        }
352    
353        /**
354         * Gets the name of the XML element.
355         *
356         * @return the xml element name
357         */
358        public String getXmlElementName() {
359          return XmlElementNames.DailyRegeneration;
360        }
361    
362        /**
363         * Gets a value indicating whether this instance is a regeneration
364         * pattern.
365         *
366         * @return true, if is regeneration pattern
367         */
368        public boolean isRegenerationPattern() {
369          return true;
370        }
371    
372      }
373    
374    
375      /**
376       * Represents a recurrence pattern where each occurrence happens at a
377       * specific interval after the previous one.
378       * [EditorBrowsable(EditorBrowsableState.Never)]
379       */
380      @EditorBrowsable(state = EditorBrowsableState.Never)
381      public abstract static class IntervalPattern extends Recurrence {
382    
383        /**
384         * The interval.
385         */
386        private int interval = 1;
387    
388        /**
389         * Initializes a new instance of the IntervalPattern class.
390         */
391        public IntervalPattern() {
392          super();
393        }
394    
395        /**
396         * Initializes a new instance of the IntervalPattern class.
397         *
398         * @param startDate The date and time when the recurrence starts.
399         * @param interval  The number of days between each occurrence.
400         * @throws ArgumentOutOfRangeException the argument out of range exception
401         */
402        public IntervalPattern(Date startDate, int interval)
403            throws ArgumentOutOfRangeException {
404    
405          super(startDate);
406          if (interval < 1) {
407            throw new ArgumentOutOfRangeException("interval", "The interval must be greater than or equal to 1.");
408          }
409    
410          this.setInterval(interval);
411        }
412    
413        /**
414         * Write property to XML.
415         *
416         * @param writer the writer
417         * @throws Exception the exception
418         */
419        @Override
420        public void internalWritePropertiesToXml(EwsServiceXmlWriter writer) throws Exception {
421          super.internalWritePropertiesToXml(writer);
422    
423          writer.writeElementValue(XmlNamespace.Types,
424              XmlElementNames.Interval, this.getInterval());
425        }
426    
427        /**
428         * Tries to read element from XML.
429         *
430         * @param reader the reader
431         * @return true, if successful
432         * @throws Exception the exception
433         */
434        @Override
435        public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
436            throws Exception {
437          if (super.tryReadElementFromXml(reader)) {
438            return true;
439          } else {
440    
441            if (reader.getLocalName().equals(XmlElementNames.Interval)) {
442              this.interval = reader.readElementValue(Integer.class);
443              return true;
444            } else {
445              return false;
446            }
447          }
448        }
449    
450        /**
451         * Gets the interval between occurrences.
452         *
453         * @return the interval
454         */
455        public int getInterval() {
456          return this.interval;
457        }
458    
459        /**
460         * Sets the interval.
461         *
462         * @param value the new interval
463         * @throws ArgumentOutOfRangeException the argument out of range exception
464         */
465        public void setInterval(int value) throws ArgumentOutOfRangeException {
466    
467          if (value < 1) {
468            throw new ArgumentOutOfRangeException("value", "The interval must be greater than or equal to 1.");
469          }
470    
471          if (this.canSetFieldValue(this.interval, value)) {
472            this.interval = value;
473            this.changed();
474          }
475    
476        }
477    
478      }
479    
480    
481      /**
482       * Represents a recurrence pattern where each occurrence happens on a
483       * specific day a specific number of months after the previous one.
484       */
485    
486      public final static class MonthlyPattern extends IntervalPattern {
487    
488        /**
489         * The day of month.
490         */
491        private Integer dayOfMonth;
492    
493        /**
494         * Initializes a new instance of the MonthlyPattern class.
495         */
496        public MonthlyPattern() {
497          super();
498    
499        }
500    
501        /**
502         * Initializes a new instance of the MonthlyPattern class.
503         *
504         * @param startDate  the start date
505         * @param interval   the interval
506         * @param dayOfMonth the day of month
507         * @throws ArgumentOutOfRangeException the argument out of range exception
508         */
509        public MonthlyPattern(Date startDate, int interval, int dayOfMonth)
510            throws ArgumentOutOfRangeException {
511          super(startDate, interval);
512    
513          this.setDayOfMonth(dayOfMonth);
514        }
515    
516        // / Gets the name of the XML element.
517    
518        /*
519         * (non-Javadoc)
520         *
521         * @see microsoft.exchange.webservices.Recurrence#getXmlElementName()
522         */
523        @Override
524        public String getXmlElementName() {
525          return XmlElementNames.AbsoluteMonthlyRecurrence;
526        }
527    
528        /**
529         * Write property to XML.
530         *
531         * @param writer the writer
532         * @throws Exception the exception
533         */
534        @Override
535        public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
536            throws Exception {
537          super.internalWritePropertiesToXml(writer);
538    
539          writer.writeElementValue(XmlNamespace.Types,
540              XmlElementNames.DayOfMonth, this.getDayOfMonth());
541        }
542    
543        /**
544         * Tries to read element from XML.
545         *
546         * @param reader the reader
547         * @return True if appropriate element was read.
548         * @throws Exception the exception
549         */
550        @Override
551        public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
552            throws Exception {
553          if (super.tryReadElementFromXml(reader)) {
554            return true;
555          } else {
556            if (reader.getLocalName().equals(XmlElementNames.DayOfMonth)) {
557              this.dayOfMonth = reader.readElementValue(Integer.class);
558              return true;
559            } else {
560              return false;
561            }
562          }
563        }
564    
565        /**
566         * Validates this instance.
567         *
568         * @throws Exception
569         */
570        @Override
571        public void internalValidate() throws Exception {
572          super.internalValidate();
573    
574          if (this.dayOfMonth == null) {
575            throw new ServiceValidationException("DayOfMonth must be between 1 and 31.");
576          }
577        }
578    
579        /**
580         * Gets the day of month.
581         *
582         * @return the day of month
583         * @throws ServiceValidationException the service validation exception
584         */
585        public int getDayOfMonth() throws ServiceValidationException {
586          return this.getFieldValueOrThrowIfNull(Integer.class, this.dayOfMonth,
587              "DayOfMonth");
588    
589        }
590    
591        /**
592         * Sets the day of month.
593         *
594         * @param value the new day of month
595         * @throws ArgumentOutOfRangeException the argument out of range exception
596         */
597        public void setDayOfMonth(int value)
598            throws ArgumentOutOfRangeException {
599          if (value < 1 || value > 31) {
600            throw new ArgumentOutOfRangeException("DayOfMonth", "DayOfMonth must be between 1 and 31.");
601          }
602    
603          if (this.canSetFieldValue(this.dayOfMonth, value)) {
604            this.dayOfMonth = value;
605            this.changed();
606          }
607        }
608      }
609    
610    
611      /**
612       * Represents a regeneration pattern, as used with recurring tasks, where
613       * each occurrence happens a specified number of months after the previous
614       * one is completed.
615       */
616      public final static class MonthlyRegenerationPattern extends
617          IntervalPattern {
618    
619        /**
620         * Instantiates a new monthly regeneration pattern.
621         */
622        public MonthlyRegenerationPattern() {
623          super();
624    
625        }
626    
627        /**
628         * Instantiates a new monthly regeneration pattern.
629         *
630         * @param startDate the start date
631         * @param interval  the interval
632         * @throws ArgumentOutOfRangeException the argument out of range exception
633         */
634        public MonthlyRegenerationPattern(Date startDate, int interval)
635            throws ArgumentOutOfRangeException {
636          super(startDate, interval);
637    
638        }
639    
640        /**
641         * Gets the name of the XML element. <value>The name of the XML
642         * element.</value>
643         *
644         * @return the xml element name
645         */
646        @Override
647        public String getXmlElementName() {
648          return XmlElementNames.MonthlyRegeneration;
649        }
650    
651        /**
652         * Gets a value indicating whether this instance is regeneration
653         * pattern. <value> <c>true</c> if this instance is regeneration
654         * pattern; otherwise, <c>false</c>. </value>
655         *
656         * @return true, if is regeneration pattern
657         */
658        public boolean isRegenerationPattern() {
659          return true;
660        }
661      }
662    
663    
664      /**
665       * Represents a recurrence pattern where each occurrence happens on a
666       * relative day a specific number of months after the previous one.
667       */
668      public final static class RelativeMonthlyPattern extends IntervalPattern {
669    
670        /**
671         * The day of the week.
672         */
673        private DayOfTheWeek dayOfTheWeek;
674    
675        /**
676         * The day of the week index.
677         */
678        private DayOfTheWeekIndex dayOfTheWeekIndex;
679    
680        // / Initializes a new instance of the <see
681        // cref="RelativeMonthlyPattern"/> class.
682    
683        /**
684         * Instantiates a new relative monthly pattern.
685         */
686        public RelativeMonthlyPattern() {
687          super();
688        }
689    
690        /**
691         * Instantiates a new relative monthly pattern.
692         *
693         * @param startDate         the start date
694         * @param interval          the interval
695         * @param dayOfTheWeek      the day of the week
696         * @param dayOfTheWeekIndex the day of the week index
697         * @throws ArgumentOutOfRangeException the argument out of range exception
698         */
699        public RelativeMonthlyPattern(Date startDate, int interval,
700            DayOfTheWeek dayOfTheWeek, DayOfTheWeekIndex dayOfTheWeekIndex)
701            throws ArgumentOutOfRangeException {
702          super(startDate, interval);
703    
704          this.setDayOfTheWeek(dayOfTheWeek);
705          this.setDayOfTheWeekIndex(dayOfTheWeekIndex);
706        }
707    
708        /**
709         * Gets the name of the XML element.
710         *
711         * @return the xml element name
712         */
713        @Override
714        public String getXmlElementName() {
715          return XmlElementNames.RelativeMonthlyRecurrence;
716        }
717    
718        /**
719         * Write property to XML.
720         *
721         * @param writer the writer
722         * @throws Exception the exception
723         */
724        @Override
725        public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
726            throws Exception {
727          super.internalWritePropertiesToXml(writer);
728    
729          writer.writeElementValue(XmlNamespace.Types,
730              XmlElementNames.DaysOfWeek, this.getDayOfTheWeek());
731    
732          writer
733              .writeElementValue(XmlNamespace.Types,
734                  XmlElementNames.DayOfWeekIndex, this
735                      .getDayOfTheWeekIndex());
736        }
737    
738        /**
739         * Tries to read element from XML.
740         *
741         * @param reader the reader
742         * @return True if appropriate element was read.
743         * @throws Exception the exception
744         */
745        @Override
746        public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
747            throws Exception {
748          if (super.tryReadElementFromXml(reader)) {
749            return true;
750          } else {
751            if (reader.getLocalName().equals(XmlElementNames.DaysOfWeek)) {
752    
753              this.dayOfTheWeek = reader
754                  .readElementValue(DayOfTheWeek.class);
755              return true;
756            } else if (reader.getLocalName().equals(
757                XmlElementNames.DayOfWeekIndex)) {
758    
759              this.dayOfTheWeekIndex = reader
760                  .readElementValue(DayOfTheWeekIndex.class);
761              return true;
762            } else {
763    
764              return false;
765            }
766          }
767        }
768    
769        /**
770         * Validates this instance.
771         *
772         * @throws Exception
773         */
774        @Override
775        public void internalValidate() throws Exception {
776          super.internalValidate();
777    
778          if (this.dayOfTheWeek == null) {
779            throw new ServiceValidationException(
780                "The recurrence pattern's property DayOfTheWeek must be specified.");
781          }
782    
783          if (this.dayOfTheWeekIndex == null) {
784            throw new ServiceValidationException(
785                "The recurrence pattern's DayOfWeekIndex property must be specified.");
786          }
787        }
788    
789        /**
790         * Day of the week index.
791         *
792         * @return the day of the week index
793         * @throws ServiceValidationException the service validation exception
794         */
795        public DayOfTheWeekIndex getDayOfTheWeekIndex()
796            throws ServiceValidationException {
797          return this.getFieldValueOrThrowIfNull(DayOfTheWeekIndex.class,
798              this.dayOfTheWeekIndex, "DayOfTheWeekIndex");
799        }
800    
801        /**
802         * Day of the week index.
803         *
804         * @param value the value
805         */
806        public void setDayOfTheWeekIndex(DayOfTheWeekIndex value) {
807          if (this.canSetFieldValue(this.dayOfTheWeekIndex, value)) {
808            this.dayOfTheWeekIndex = value;
809            this.changed();
810          }
811    
812        }
813    
814        /**
815         * Gets the day of the week.
816         *
817         * @return the day of the week
818         * @throws ServiceValidationException the service validation exception
819         */
820        public DayOfTheWeek getDayOfTheWeek()
821            throws ServiceValidationException {
822          return this.getFieldValueOrThrowIfNull(DayOfTheWeek.class,
823              this.dayOfTheWeek, "DayOfTheWeek");
824    
825        }
826    
827        /**
828         * Sets the day of the week.
829         *
830         * @param value the new day of the week
831         */
832        public void setDayOfTheWeek(DayOfTheWeek value) {
833    
834          if (this.canSetFieldValue(this.dayOfTheWeek, value)) {
835            this.dayOfTheWeek = value;
836            this.changed();
837          }
838        }
839      }
840    
841    
842      /**
843       * The Class RelativeYearlyPattern.
844       */
845      public final static class RelativeYearlyPattern extends Recurrence {
846    
847        /**
848         * The day of the week.
849         */
850        private DayOfTheWeek dayOfTheWeek;
851    
852        /**
853         * The day of the week index.
854         */
855        private DayOfTheWeekIndex dayOfTheWeekIndex;
856    
857        /**
858         * The month.
859         */
860        private Month month;
861    
862        /**
863         * Gets the name of the XML element. <value>The name of the XML
864         * element.</value>
865         *
866         * @return the xml element name
867         */
868        @Override
869        public String getXmlElementName() {
870          return XmlElementNames.RelativeYearlyRecurrence;
871        }
872    
873        /**
874         * Write property to XML.
875         *
876         * @param writer the writer
877         * @throws Exception the exception
878         */
879        @Override
880        public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
881            throws Exception {
882          super.internalWritePropertiesToXml(writer);
883    
884          writer.writeElementValue(XmlNamespace.Types,
885              XmlElementNames.DaysOfWeek, this.dayOfTheWeek);
886    
887          writer.writeElementValue(XmlNamespace.Types,
888              XmlElementNames.DayOfWeekIndex, this.dayOfTheWeekIndex);
889    
890          writer.writeElementValue(XmlNamespace.Types, XmlElementNames.Month,
891              this.month);
892        }
893    
894        /**
895         * Tries to read element from XML.
896         *
897         * @param reader the reader
898         * @return True if element was read.
899         * @throws Exception the exception
900         */
901        @Override
902        public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
903            throws Exception {
904          if (super.tryReadElementFromXml(reader)) {
905            return true;
906          } else {
907            if (reader.getLocalName().equals(XmlElementNames.DaysOfWeek)) {
908    
909              this.dayOfTheWeek = reader
910                  .readElementValue(DayOfTheWeek.class);
911              return true;
912            } else if (reader.getLocalName().equals(
913                XmlElementNames.DayOfWeekIndex)) {
914    
915              this.dayOfTheWeekIndex = reader
916                  .readElementValue(DayOfTheWeekIndex.class);
917              return true;
918            } else if (reader.getLocalName().equals(XmlElementNames.Month)) {
919    
920              this.month = reader.readElementValue(Month.class);
921              return true;
922            } else {
923    
924              return false;
925            }
926          }
927        }
928    
929        /**
930         * Instantiates a new relative yearly pattern.
931         */
932        public RelativeYearlyPattern() {
933          super();
934    
935        }
936    
937        /**
938         * Instantiates a new relative yearly pattern.
939         *
940         * @param startDate         the start date
941         * @param month             the month
942         * @param dayOfTheWeek      the day of the week
943         * @param dayOfTheWeekIndex the day of the week index
944         */
945        public RelativeYearlyPattern(Date startDate, Month month,
946            DayOfTheWeek dayOfTheWeek,
947            DayOfTheWeekIndex dayOfTheWeekIndex) {
948          super(startDate);
949    
950          this.month = month;
951          this.dayOfTheWeek = dayOfTheWeek;
952          this.dayOfTheWeekIndex = dayOfTheWeekIndex;
953        }
954    
955        /**
956         * Validates this instance.
957         *
958         * @throws Exception
959         */
960        @Override
961        public void internalValidate() throws Exception {
962          super.internalValidate();
963    
964          if (this.dayOfTheWeekIndex == null) {
965            throw new ServiceValidationException(
966                "The recurrence pattern's DayOfWeekIndex property must be specified.");
967          }
968    
969          if (this.dayOfTheWeek == null) {
970            throw new ServiceValidationException(
971                "The recurrence pattern's property DayOfTheWeek must be specified.");
972          }
973    
974          if (this.month == null) {
975            throw new ServiceValidationException("The recurrence pattern's Month property must be specified.");
976          }
977        }
978    
979        /**
980         * Gets the relative position of the day specified in DayOfTheWeek
981         * within the month.
982         *
983         * @return the day of the week index
984         * @throws ServiceValidationException the service validation exception
985         */
986        public DayOfTheWeekIndex getDayOfTheWeekIndex()
987            throws ServiceValidationException {
988    
989          return this.getFieldValueOrThrowIfNull(DayOfTheWeekIndex.class,
990              this.dayOfTheWeekIndex, "DayOfTheWeekIndex");
991        }
992    
993        /**
994         * Sets the relative position of the day specified in DayOfTheWeek
995         * within the month.
996         *
997         * @param value the new day of the week index
998         */
999        public void setDayOfTheWeekIndex(DayOfTheWeekIndex value) {
1000    
1001          if (this.canSetFieldValue(this.dayOfTheWeekIndex, value)) {
1002            this.dayOfTheWeekIndex = value;
1003            this.changed();
1004          }
1005        }
1006    
1007        /**
1008         * Gets the day of the week.
1009         *
1010         * @return the day of the week
1011         * @throws ServiceValidationException the service validation exception
1012         */
1013        public DayOfTheWeek getDayOfTheWeek()
1014            throws ServiceValidationException {
1015    
1016          return this.getFieldValueOrThrowIfNull(DayOfTheWeek.class,
1017              this.dayOfTheWeek, "DayOfTheWeek");
1018        }
1019    
1020        /**
1021         * Sets the day of the week.
1022         *
1023         * @param value the new day of the week
1024         */
1025        public void setDayOfTheWeek(DayOfTheWeek value) {
1026    
1027          if (this.canSetFieldValue(this.dayOfTheWeek, value)) {
1028            this.dayOfTheWeek = value;
1029            this.changed();
1030          }
1031        }
1032    
1033        /**
1034         * Gets the month.
1035         *
1036         * @return the month
1037         * @throws ServiceValidationException the service validation exception
1038         */
1039        public Month getMonth() throws ServiceValidationException {
1040    
1041          return this.getFieldValueOrThrowIfNull(Month.class, this.month,
1042              "Month");
1043    
1044        }
1045    
1046        /**
1047         * Sets the month.
1048         *
1049         * @param value the new month
1050         */
1051        public void setMonth(Month value) {
1052    
1053          if (this.canSetFieldValue(this.month, value)) {
1054            this.month = value;
1055            this.changed();
1056          }
1057        }
1058      }
1059    
1060    
1061      /**
1062       * Represents a recurrence pattern where each occurrence happens on specific
1063       * days a specific number of weeks after the previous one.
1064       */
1065      public final static class WeeklyPattern extends IntervalPattern implements IComplexPropertyChangedDelegate {
1066    
1067        /**
1068         * The days of the week.
1069         */
1070        private DayOfTheWeekCollection daysOfTheWeek =
1071            new DayOfTheWeekCollection();
1072    
1073        private Calendar firstDayOfWeek;
1074    
1075        /**
1076         * Initializes a new instance of the WeeklyPattern class. specific days
1077         * a specific number of weeks after the previous one.
1078         */
1079        public WeeklyPattern() {
1080          super();
1081    
1082          this.daysOfTheWeek.addOnChangeEvent(this);
1083        }
1084    
1085        /**
1086         * Initializes a new instance of the WeeklyPattern class.
1087         *
1088         * @param startDate     the start date
1089         * @param interval      the interval
1090         * @param daysOfTheWeek the days of the week
1091         * @throws ArgumentOutOfRangeException the argument out of range exception
1092         */
1093        public WeeklyPattern(Date startDate, int interval,
1094            DayOfTheWeek... daysOfTheWeek)
1095            throws ArgumentOutOfRangeException {
1096          super(startDate, interval);
1097    
1098          ArrayList<DayOfTheWeek> toProcess = new ArrayList<DayOfTheWeek>(
1099              Arrays.asList(daysOfTheWeek));
1100          Iterator<DayOfTheWeek> idaysOfTheWeek = toProcess.iterator();
1101          this.daysOfTheWeek.addRange(idaysOfTheWeek);
1102        }
1103    
1104        /**
1105         * Change event handler.
1106         *
1107         * @param complexProperty the complex property
1108         */
1109        private void daysOfTheWeekChanged(ComplexProperty complexProperty) {
1110          this.changed();
1111        }
1112    
1113        /**
1114         * Gets the name of the XML element. <value>The name of the XML
1115         * element.</value>
1116         *
1117         * @return the xml element name
1118         */
1119        @Override
1120        public String getXmlElementName() {
1121          return XmlElementNames.WeeklyRecurrence;
1122        }
1123    
1124        /**
1125         * Write property to XML.
1126         *
1127         * @param writer the writer
1128         * @throws Exception the exception
1129         */
1130        @Override
1131        public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
1132            throws Exception {
1133          super.internalWritePropertiesToXml(writer);
1134    
1135          this.getDaysOfTheWeek().writeToXml(writer,
1136              XmlElementNames.DaysOfWeek);
1137          if (this.firstDayOfWeek != null) {
1138    
1139            EwsUtilities
1140                .validatePropertyVersion((ExchangeService) writer.getService(), ExchangeVersion.Exchange2010_SP1,
1141                                         "FirstDayOfWeek");
1142    
1143            writer.writeElementValue(
1144                XmlNamespace.Types,
1145                XmlElementNames.FirstDayOfWeek,
1146                this.firstDayOfWeek);
1147          }
1148    
1149        }
1150    
1151        /**
1152         * Tries to read element from XML.
1153         *
1154         * @param reader the reader
1155         * @return True if appropriate element was read.
1156         * @throws Exception the exception
1157         */
1158        @Override
1159        public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
1160            throws Exception {
1161          if (super.tryReadElementFromXml(reader)) {
1162            return true;
1163          } else {
1164            if (reader.getLocalName().equals(XmlElementNames.DaysOfWeek)) {
1165    
1166              this.getDaysOfTheWeek().loadFromXml(reader,
1167                  reader.getLocalName());
1168              return true;
1169            } else if (reader.getLocalName().equals(XmlElementNames.FirstDayOfWeek)) {
1170              this.firstDayOfWeek = reader.
1171                  readElementValue(Calendar.class,
1172                      XmlNamespace.Types,
1173                      XmlElementNames.FirstDayOfWeek);
1174              return true;
1175            } else {
1176    
1177              return false;
1178            }
1179          }
1180        }
1181    
1182        /**
1183         * Validates this instance.
1184         *
1185         * @throws Exception
1186         */
1187        @Override
1188        public void internalValidate() throws Exception {
1189          super.internalValidate();
1190    
1191          if (this.getDaysOfTheWeek().getCount() == 0) {
1192            throw new ServiceValidationException(
1193                "The recurrence pattern's property DaysOfTheWeek must contain at least one day of the week.");
1194          }
1195        }
1196    
1197        /**
1198         * Gets the list of the days of the week when occurrences happen.
1199         *
1200         * @return the days of the week
1201         */
1202        public DayOfTheWeekCollection getDaysOfTheWeek() {
1203          return this.daysOfTheWeek;
1204        }
1205    
1206        public Calendar getFirstDayOfWeek() throws ServiceValidationException {
1207          return this.getFieldValueOrThrowIfNull(Calendar.class,
1208              this.firstDayOfWeek, "FirstDayOfWeek");
1209        }
1210    
1211        public void setFirstDayOfWeek(Calendar value) {
1212          if (this.canSetFieldValue(this.firstDayOfWeek, value)) {
1213            this.firstDayOfWeek = value;
1214            this.changed();
1215          }
1216        }
1217    
1218        /*
1219         * (non-Javadoc)
1220         *
1221         * @see
1222         * microsoft.exchange.webservices.
1223         * ComplexPropertyChangedDelegateInterface#
1224         * complexPropertyChanged(microsoft.exchange.webservices.ComplexProperty
1225         * )
1226         */
1227        @Override
1228        public void complexPropertyChanged(ComplexProperty complexProperty) {
1229          this.daysOfTheWeekChanged(complexProperty);
1230        }
1231    
1232      }
1233    
1234    
1235      /**
1236       * Represents a regeneration pattern, as used with recurring tasks, where
1237       * each occurrence happens a specified number of weeks after the previous
1238       * one is completed.
1239       */
1240      public final static class WeeklyRegenerationPattern extends
1241          IntervalPattern {
1242    
1243        /**
1244         * Initializes a new instance of the WeeklyRegenerationPattern class.
1245         */
1246        public WeeklyRegenerationPattern() {
1247    
1248          super();
1249        }
1250    
1251        /**
1252         * Initializes a new instance of the WeeklyRegenerationPattern class.
1253         *
1254         * @param startDate the start date
1255         * @param interval  the interval
1256         * @throws ArgumentOutOfRangeException the argument out of range exception
1257         */
1258        public WeeklyRegenerationPattern(Date startDate, int interval)
1259            throws ArgumentOutOfRangeException {
1260          super(startDate, interval);
1261    
1262        }
1263    
1264        /**
1265         * Gets the name of the XML element. <value>The name of the XML
1266         * element.</value>
1267         *
1268         * @return the xml element name
1269         */
1270        @Override
1271        public String getXmlElementName() {
1272          return XmlElementNames.WeeklyRegeneration;
1273        }
1274    
1275        /**
1276         * Gets a value indicating whether this instance is regeneration
1277         * pattern. <value> <c>true</c> if this instance is regeneration
1278         * pattern; otherwise, <c>false</c>. </value>
1279         *
1280         * @return true, if is regeneration pattern
1281         */
1282        public boolean isRegenerationPattern() {
1283          return true;
1284        }
1285      }
1286    
1287    
1288      /**
1289       * Represents a recurrence pattern where each occurrence happens on a
1290       * specific day every year.
1291       */
1292      public final static class YearlyPattern extends Recurrence {
1293    
1294        /**
1295         * The month.
1296         */
1297        private Month month;
1298    
1299        /**
1300         * The day of month.
1301         */
1302        private Integer dayOfMonth;
1303    
1304        /**
1305         * Initializes a new instance of the YearlyPattern class.
1306         */
1307        public YearlyPattern() {
1308          super();
1309    
1310        }
1311    
1312        /**
1313         * Initializes a new instance of the YearlyPattern class.
1314         *
1315         * @param startDate  the start date
1316         * @param month      the month
1317         * @param dayOfMonth the day of month
1318         */
1319        public YearlyPattern(Date startDate, Month month, int dayOfMonth) {
1320          super(startDate);
1321    
1322          this.month = month;
1323          this.dayOfMonth = dayOfMonth;
1324        }
1325    
1326        /**
1327         * Gets the name of the XML element. <value>The name of the XML
1328         * element.</value>
1329         *
1330         * @return the xml element name
1331         */
1332        @Override
1333        public String getXmlElementName() {
1334          return XmlElementNames.AbsoluteYearlyRecurrence;
1335        }
1336    
1337        /**
1338         * Write property to XML.
1339         *
1340         * @param writer the writer
1341         * @throws Exception the exception
1342         */
1343        @Override
1344        public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
1345            throws Exception {
1346          super.internalWritePropertiesToXml(writer);
1347    
1348          writer.writeElementValue(XmlNamespace.Types,
1349              XmlElementNames.DayOfMonth, this.getDayOfMonth());
1350    
1351          writer.writeElementValue(XmlNamespace.Types, XmlElementNames.Month,
1352              this.getMonth());
1353        }
1354    
1355        /**
1356         * Tries to read element from XML.
1357         *
1358         * @param reader the reader
1359         * @return True if element was read
1360         * @throws Exception the exception
1361         */
1362        @Override
1363        public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
1364            throws Exception {
1365          if (super.tryReadElementFromXml(reader)) {
1366            return true;
1367          } else {
1368            if (reader.getLocalName().equals(XmlElementNames.DayOfMonth)) {
1369    
1370              this.dayOfMonth = reader.readElementValue(Integer.class);
1371              return true;
1372            } else if (reader.getLocalName().equals(XmlElementNames.Month)) {
1373    
1374              this.month = reader.readElementValue(Month.class);
1375              return true;
1376            } else {
1377    
1378              return false;
1379            }
1380          }
1381        }
1382    
1383        /**
1384         * Validates this instance.
1385         *
1386         * @throws Exception
1387         */
1388        @Override
1389        public void internalValidate() throws Exception {
1390          super.internalValidate();
1391    
1392          if (this.month == null) {
1393            throw new ServiceValidationException("The recurrence pattern's Month property must be specified.");
1394          }
1395    
1396          if (this.dayOfMonth == null) {
1397            throw new ServiceValidationException(
1398                "The recurrence pattern's DayOfMonth property must be specified.");
1399          }
1400        }
1401    
1402        /**
1403         * Gets the month of the year when each occurrence happens.
1404         *
1405         * @return the month
1406         * @throws ServiceValidationException the service validation exception
1407         */
1408        public Month getMonth() throws ServiceValidationException {
1409          return this.getFieldValueOrThrowIfNull(Month.class, this.month,
1410              "Month");
1411        }
1412    
1413        /**
1414         * Sets the month.
1415         *
1416         * @param value the new month
1417         */
1418        public void setMonth(Month value) {
1419    
1420          if (this.canSetFieldValue(this.month, value)) {
1421            this.month = value;
1422            this.changed();
1423          }
1424        }
1425    
1426        /**
1427         * Gets the day of the month when each occurrence happens. DayOfMonth
1428         * must be between 1 and 31.
1429         *
1430         * @return the day of month
1431         * @throws ServiceValidationException the service validation exception
1432         */
1433        public int getDayOfMonth() throws ServiceValidationException {
1434    
1435          return this.getFieldValueOrThrowIfNull(Integer.class, this.dayOfMonth,
1436              "DayOfMonth");
1437    
1438        }
1439    
1440        /**
1441         * Sets the day of the month when each occurrence happens. DayOfMonth
1442         * must be between 1 and 31.
1443         *
1444         * @param value the new day of month
1445         * @throws ArgumentOutOfRangeException the argument out of range exception
1446         */
1447        public void setDayOfMonth(int value)
1448            throws ArgumentOutOfRangeException {
1449    
1450          if (value < 1 || value > 31) {
1451            throw new ArgumentOutOfRangeException("DayOfMonth", "DayOfMonth must be between 1 and 31.");
1452          }
1453    
1454          if (this.canSetFieldValue(this.dayOfMonth, value)) {
1455            this.dayOfMonth = value;
1456            this.changed();
1457          }
1458        }
1459      }
1460    
1461    
1462      /**
1463       * Represents a regeneration pattern, as used with recurring tasks, where
1464       * each occurrence happens a specified number of years after the previous
1465       * one is completed.
1466       */
1467      public final static class YearlyRegenerationPattern extends
1468          IntervalPattern {
1469    
1470        /**
1471         * Gets the name of the XML element. <value>The name of the XML
1472         * element.</value>
1473         *
1474         * @return the xml element name
1475         */
1476        @Override
1477        public String getXmlElementName() {
1478          return XmlElementNames.YearlyRegeneration;
1479        }
1480    
1481        /**
1482         * Gets a value indicating whether this instance is regeneration
1483         * pattern.
1484         *
1485         * @return true, if is regeneration pattern
1486         */
1487        public boolean isRegenerationPattern() {
1488          return true;
1489        }
1490    
1491        /**
1492         * Initializes a new instance of the YearlyRegenerationPattern class.
1493         */
1494        public YearlyRegenerationPattern() {
1495          super();
1496    
1497        }
1498    
1499        /**
1500         * Initializes a new instance of the YearlyRegenerationPattern class.
1501         *
1502         * @param startDate the start date
1503         * @param interval  the interval
1504         * @throws ArgumentOutOfRangeException the argument out of range exception
1505         */
1506        public YearlyRegenerationPattern(Date startDate, int interval)
1507            throws ArgumentOutOfRangeException {
1508          super(startDate, interval);
1509    
1510        }
1511      }
1512    }