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
024package microsoft.exchange.webservices.data.property.complex.recurrence.pattern;
025
026import microsoft.exchange.webservices.data.attribute.EditorBrowsable;
027import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
028import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
029import microsoft.exchange.webservices.data.core.EwsUtilities;
030import microsoft.exchange.webservices.data.core.ExchangeService;
031import microsoft.exchange.webservices.data.core.XmlElementNames;
032import microsoft.exchange.webservices.data.core.enumeration.property.time.DayOfTheWeek;
033import microsoft.exchange.webservices.data.core.enumeration.property.time.DayOfTheWeekIndex;
034import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState;
035import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
036import microsoft.exchange.webservices.data.core.enumeration.property.time.Month;
037import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
038import microsoft.exchange.webservices.data.core.exception.misc.ArgumentException;
039import microsoft.exchange.webservices.data.core.exception.misc.ArgumentOutOfRangeException;
040import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
041import microsoft.exchange.webservices.data.property.complex.ComplexProperty;
042import microsoft.exchange.webservices.data.property.complex.IComplexPropertyChangedDelegate;
043import microsoft.exchange.webservices.data.property.complex.recurrence.DayOfTheWeekCollection;
044import microsoft.exchange.webservices.data.property.complex.recurrence.range.EndDateRecurrenceRange;
045import microsoft.exchange.webservices.data.property.complex.recurrence.range.NoEndRecurrenceRange;
046import microsoft.exchange.webservices.data.property.complex.recurrence.range.NumberedRecurrenceRange;
047import microsoft.exchange.webservices.data.property.complex.recurrence.range.RecurrenceRange;
048
049import java.util.ArrayList;
050import java.util.Arrays;
051import java.util.Calendar;
052import java.util.Date;
053import java.util.Iterator;
054
055/**
056 * Represents a recurrence pattern, as used by Appointment and Task item.
057 */
058public 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}