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.
642     *
643     * @return the xml element name
644     */
645    @Override
646    public String getXmlElementName() {
647      return XmlElementNames.MonthlyRegeneration;
648    }
649
650    /**
651     * Gets a value indicating whether this instance is regeneration
652     * pattern. <em>true</em> if this instance is regeneration
653     * pattern; otherwise, <em>false</em>.
654     *
655     * @return true, if is regeneration pattern
656     */
657    public boolean isRegenerationPattern() {
658      return true;
659    }
660  }
661
662
663  /**
664   * Represents a recurrence pattern where each occurrence happens on a
665   * relative day a specific number of months after the previous one.
666   */
667  public final static class RelativeMonthlyPattern extends IntervalPattern {
668
669    /**
670     * The day of the week.
671     */
672    private DayOfTheWeek dayOfTheWeek;
673
674    /**
675     * The day of the week index.
676     */
677    private DayOfTheWeekIndex dayOfTheWeekIndex;
678
679    // / Initializes a new instance of the <see
680    // cref="RelativeMonthlyPattern"/> class.
681
682    /**
683     * Instantiates a new relative monthly pattern.
684     */
685    public RelativeMonthlyPattern() {
686      super();
687    }
688
689    /**
690     * Instantiates a new relative monthly pattern.
691     *
692     * @param startDate         the start date
693     * @param interval          the interval
694     * @param dayOfTheWeek      the day of the week
695     * @param dayOfTheWeekIndex the day of the week index
696     * @throws ArgumentOutOfRangeException the argument out of range exception
697     */
698    public RelativeMonthlyPattern(Date startDate, int interval,
699        DayOfTheWeek dayOfTheWeek, DayOfTheWeekIndex dayOfTheWeekIndex)
700        throws ArgumentOutOfRangeException {
701      super(startDate, interval);
702
703      this.setDayOfTheWeek(dayOfTheWeek);
704      this.setDayOfTheWeekIndex(dayOfTheWeekIndex);
705    }
706
707    /**
708     * Gets the name of the XML element.
709     *
710     * @return the xml element name
711     */
712    @Override
713    public String getXmlElementName() {
714      return XmlElementNames.RelativeMonthlyRecurrence;
715    }
716
717    /**
718     * Write property to XML.
719     *
720     * @param writer the writer
721     * @throws Exception the exception
722     */
723    @Override
724    public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
725        throws Exception {
726      super.internalWritePropertiesToXml(writer);
727
728      writer.writeElementValue(XmlNamespace.Types,
729          XmlElementNames.DaysOfWeek, this.getDayOfTheWeek());
730
731      writer
732          .writeElementValue(XmlNamespace.Types,
733              XmlElementNames.DayOfWeekIndex, this
734                  .getDayOfTheWeekIndex());
735    }
736
737    /**
738     * Tries to read element from XML.
739     *
740     * @param reader the reader
741     * @return True if appropriate element was read.
742     * @throws Exception the exception
743     */
744    @Override
745    public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
746        throws Exception {
747      if (super.tryReadElementFromXml(reader)) {
748        return true;
749      } else {
750        if (reader.getLocalName().equals(XmlElementNames.DaysOfWeek)) {
751
752          this.dayOfTheWeek = reader
753              .readElementValue(DayOfTheWeek.class);
754          return true;
755        } else if (reader.getLocalName().equals(
756            XmlElementNames.DayOfWeekIndex)) {
757
758          this.dayOfTheWeekIndex = reader
759              .readElementValue(DayOfTheWeekIndex.class);
760          return true;
761        } else {
762
763          return false;
764        }
765      }
766    }
767
768    /**
769     * Validates this instance.
770     *
771     * @throws Exception
772     */
773    @Override
774    public void internalValidate() throws Exception {
775      super.internalValidate();
776
777      if (this.dayOfTheWeek == null) {
778        throw new ServiceValidationException(
779            "The recurrence pattern's property DayOfTheWeek must be specified.");
780      }
781
782      if (this.dayOfTheWeekIndex == null) {
783        throw new ServiceValidationException(
784            "The recurrence pattern's DayOfWeekIndex property must be specified.");
785      }
786    }
787
788    /**
789     * Day of the week index.
790     *
791     * @return the day of the week index
792     * @throws ServiceValidationException the service validation exception
793     */
794    public DayOfTheWeekIndex getDayOfTheWeekIndex()
795        throws ServiceValidationException {
796      return this.getFieldValueOrThrowIfNull(DayOfTheWeekIndex.class,
797          this.dayOfTheWeekIndex, "DayOfTheWeekIndex");
798    }
799
800    /**
801     * Day of the week index.
802     *
803     * @param value the value
804     */
805    public void setDayOfTheWeekIndex(DayOfTheWeekIndex value) {
806      if (this.canSetFieldValue(this.dayOfTheWeekIndex, value)) {
807        this.dayOfTheWeekIndex = value;
808        this.changed();
809      }
810
811    }
812
813    /**
814     * Gets the day of the week.
815     *
816     * @return the day of the week
817     * @throws ServiceValidationException the service validation exception
818     */
819    public DayOfTheWeek getDayOfTheWeek()
820        throws ServiceValidationException {
821      return this.getFieldValueOrThrowIfNull(DayOfTheWeek.class,
822          this.dayOfTheWeek, "DayOfTheWeek");
823
824    }
825
826    /**
827     * Sets the day of the week.
828     *
829     * @param value the new day of the week
830     */
831    public void setDayOfTheWeek(DayOfTheWeek value) {
832
833      if (this.canSetFieldValue(this.dayOfTheWeek, value)) {
834        this.dayOfTheWeek = value;
835        this.changed();
836      }
837    }
838  }
839
840
841  /**
842   * The Class RelativeYearlyPattern.
843   */
844  public final static class RelativeYearlyPattern extends Recurrence {
845
846    /**
847     * The day of the week.
848     */
849    private DayOfTheWeek dayOfTheWeek;
850
851    /**
852     * The day of the week index.
853     */
854    private DayOfTheWeekIndex dayOfTheWeekIndex;
855
856    /**
857     * The month.
858     */
859    private Month month;
860
861    /**
862     * Gets the name of the XML element.
863     *
864     * @return the xml element name
865     */
866    @Override
867    public String getXmlElementName() {
868      return XmlElementNames.RelativeYearlyRecurrence;
869    }
870
871    /**
872     * Write property to XML.
873     *
874     * @param writer the writer
875     * @throws Exception the exception
876     */
877    @Override
878    public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
879        throws Exception {
880      super.internalWritePropertiesToXml(writer);
881
882      writer.writeElementValue(XmlNamespace.Types,
883          XmlElementNames.DaysOfWeek, this.dayOfTheWeek);
884
885      writer.writeElementValue(XmlNamespace.Types,
886          XmlElementNames.DayOfWeekIndex, this.dayOfTheWeekIndex);
887
888      writer.writeElementValue(XmlNamespace.Types, XmlElementNames.Month,
889          this.month);
890    }
891
892    /**
893     * Tries to read element from XML.
894     *
895     * @param reader the reader
896     * @return True if element was read.
897     * @throws Exception the exception
898     */
899    @Override
900    public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
901        throws Exception {
902      if (super.tryReadElementFromXml(reader)) {
903        return true;
904      } else {
905        if (reader.getLocalName().equals(XmlElementNames.DaysOfWeek)) {
906
907          this.dayOfTheWeek = reader
908              .readElementValue(DayOfTheWeek.class);
909          return true;
910        } else if (reader.getLocalName().equals(
911            XmlElementNames.DayOfWeekIndex)) {
912
913          this.dayOfTheWeekIndex = reader
914              .readElementValue(DayOfTheWeekIndex.class);
915          return true;
916        } else if (reader.getLocalName().equals(XmlElementNames.Month)) {
917
918          this.month = reader.readElementValue(Month.class);
919          return true;
920        } else {
921
922          return false;
923        }
924      }
925    }
926
927    /**
928     * Instantiates a new relative yearly pattern.
929     */
930    public RelativeYearlyPattern() {
931      super();
932
933    }
934
935    /**
936     * Instantiates a new relative yearly pattern.
937     *
938     * @param startDate         the start date
939     * @param month             the month
940     * @param dayOfTheWeek      the day of the week
941     * @param dayOfTheWeekIndex the day of the week index
942     */
943    public RelativeYearlyPattern(Date startDate, Month month,
944        DayOfTheWeek dayOfTheWeek,
945        DayOfTheWeekIndex dayOfTheWeekIndex) {
946      super(startDate);
947
948      this.month = month;
949      this.dayOfTheWeek = dayOfTheWeek;
950      this.dayOfTheWeekIndex = dayOfTheWeekIndex;
951    }
952
953    /**
954     * Validates this instance.
955     *
956     * @throws Exception
957     */
958    @Override
959    public void internalValidate() throws Exception {
960      super.internalValidate();
961
962      if (this.dayOfTheWeekIndex == null) {
963        throw new ServiceValidationException(
964            "The recurrence pattern's DayOfWeekIndex property must be specified.");
965      }
966
967      if (this.dayOfTheWeek == null) {
968        throw new ServiceValidationException(
969            "The recurrence pattern's property DayOfTheWeek must be specified.");
970      }
971
972      if (this.month == null) {
973        throw new ServiceValidationException("The recurrence pattern's Month property must be specified.");
974      }
975    }
976
977    /**
978     * Gets the relative position of the day specified in DayOfTheWeek
979     * within the month.
980     *
981     * @return the day of the week index
982     * @throws ServiceValidationException the service validation exception
983     */
984    public DayOfTheWeekIndex getDayOfTheWeekIndex()
985        throws ServiceValidationException {
986
987      return this.getFieldValueOrThrowIfNull(DayOfTheWeekIndex.class,
988          this.dayOfTheWeekIndex, "DayOfTheWeekIndex");
989    }
990
991    /**
992     * Sets the relative position of the day specified in DayOfTheWeek
993     * within the month.
994     *
995     * @param value the new day of the week index
996     */
997    public void setDayOfTheWeekIndex(DayOfTheWeekIndex value) {
998
999      if (this.canSetFieldValue(this.dayOfTheWeekIndex, value)) {
1000        this.dayOfTheWeekIndex = value;
1001        this.changed();
1002      }
1003    }
1004
1005    /**
1006     * Gets the day of the week.
1007     *
1008     * @return the day of the week
1009     * @throws ServiceValidationException the service validation exception
1010     */
1011    public DayOfTheWeek getDayOfTheWeek()
1012        throws ServiceValidationException {
1013
1014      return this.getFieldValueOrThrowIfNull(DayOfTheWeek.class,
1015          this.dayOfTheWeek, "DayOfTheWeek");
1016    }
1017
1018    /**
1019     * Sets the day of the week.
1020     *
1021     * @param value the new day of the week
1022     */
1023    public void setDayOfTheWeek(DayOfTheWeek value) {
1024
1025      if (this.canSetFieldValue(this.dayOfTheWeek, value)) {
1026        this.dayOfTheWeek = value;
1027        this.changed();
1028      }
1029    }
1030
1031    /**
1032     * Gets the month.
1033     *
1034     * @return the month
1035     * @throws ServiceValidationException the service validation exception
1036     */
1037    public Month getMonth() throws ServiceValidationException {
1038
1039      return this.getFieldValueOrThrowIfNull(Month.class, this.month,
1040          "Month");
1041
1042    }
1043
1044    /**
1045     * Sets the month.
1046     *
1047     * @param value the new month
1048     */
1049    public void setMonth(Month value) {
1050
1051      if (this.canSetFieldValue(this.month, value)) {
1052        this.month = value;
1053        this.changed();
1054      }
1055    }
1056  }
1057
1058
1059  /**
1060   * Represents a recurrence pattern where each occurrence happens on specific
1061   * days a specific number of weeks after the previous one.
1062   */
1063  public final static class WeeklyPattern extends IntervalPattern implements IComplexPropertyChangedDelegate {
1064
1065    /**
1066     * The days of the week.
1067     */
1068    private DayOfTheWeekCollection daysOfTheWeek =
1069        new DayOfTheWeekCollection();
1070
1071    private Calendar firstDayOfWeek;
1072
1073    /**
1074     * Initializes a new instance of the WeeklyPattern class. specific days
1075     * a specific number of weeks after the previous one.
1076     */
1077    public WeeklyPattern() {
1078      super();
1079
1080      this.daysOfTheWeek.addOnChangeEvent(this);
1081    }
1082
1083    /**
1084     * Initializes a new instance of the WeeklyPattern class.
1085     *
1086     * @param startDate     the start date
1087     * @param interval      the interval
1088     * @param daysOfTheWeek the days of the week
1089     * @throws ArgumentOutOfRangeException the argument out of range exception
1090     */
1091    public WeeklyPattern(Date startDate, int interval,
1092        DayOfTheWeek... daysOfTheWeek)
1093        throws ArgumentOutOfRangeException {
1094      super(startDate, interval);
1095
1096      ArrayList<DayOfTheWeek> toProcess = new ArrayList<DayOfTheWeek>(
1097          Arrays.asList(daysOfTheWeek));
1098      Iterator<DayOfTheWeek> idaysOfTheWeek = toProcess.iterator();
1099      this.daysOfTheWeek.addRange(idaysOfTheWeek);
1100    }
1101
1102    /**
1103     * Change event handler.
1104     *
1105     * @param complexProperty the complex property
1106     */
1107    private void daysOfTheWeekChanged(ComplexProperty complexProperty) {
1108      this.changed();
1109    }
1110
1111    /**
1112     * Gets the name of the XML element.
1113     *
1114     * @return the xml element name
1115     */
1116    @Override
1117    public String getXmlElementName() {
1118      return XmlElementNames.WeeklyRecurrence;
1119    }
1120
1121    /**
1122     * Write property to XML.
1123     *
1124     * @param writer the writer
1125     * @throws Exception the exception
1126     */
1127    @Override
1128    public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
1129        throws Exception {
1130      super.internalWritePropertiesToXml(writer);
1131
1132      this.getDaysOfTheWeek().writeToXml(writer,
1133          XmlElementNames.DaysOfWeek);
1134      if (this.firstDayOfWeek != null) {
1135
1136        EwsUtilities
1137            .validatePropertyVersion((ExchangeService) writer.getService(), ExchangeVersion.Exchange2010_SP1,
1138                                     "FirstDayOfWeek");
1139
1140        writer.writeElementValue(
1141            XmlNamespace.Types,
1142            XmlElementNames.FirstDayOfWeek,
1143            this.firstDayOfWeek);
1144      }
1145
1146    }
1147
1148    /**
1149     * Tries to read element from XML.
1150     *
1151     * @param reader the reader
1152     * @return True if appropriate element was read.
1153     * @throws Exception the exception
1154     */
1155    @Override
1156    public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
1157        throws Exception {
1158      if (super.tryReadElementFromXml(reader)) {
1159        return true;
1160      } else {
1161        if (reader.getLocalName().equals(XmlElementNames.DaysOfWeek)) {
1162
1163          this.getDaysOfTheWeek().loadFromXml(reader,
1164              reader.getLocalName());
1165          return true;
1166        } else if (reader.getLocalName().equals(XmlElementNames.FirstDayOfWeek)) {
1167          this.firstDayOfWeek = reader.
1168              readElementValue(Calendar.class,
1169                  XmlNamespace.Types,
1170                  XmlElementNames.FirstDayOfWeek);
1171          return true;
1172        } else {
1173
1174          return false;
1175        }
1176      }
1177    }
1178
1179    /**
1180     * Validates this instance.
1181     *
1182     * @throws Exception
1183     */
1184    @Override
1185    public void internalValidate() throws Exception {
1186      super.internalValidate();
1187
1188      if (this.getDaysOfTheWeek().getCount() == 0) {
1189        throw new ServiceValidationException(
1190            "The recurrence pattern's property DaysOfTheWeek must contain at least one day of the week.");
1191      }
1192    }
1193
1194    /**
1195     * Gets the list of the days of the week when occurrences happen.
1196     *
1197     * @return the days of the week
1198     */
1199    public DayOfTheWeekCollection getDaysOfTheWeek() {
1200      return this.daysOfTheWeek;
1201    }
1202
1203    public Calendar getFirstDayOfWeek() throws ServiceValidationException {
1204      return this.getFieldValueOrThrowIfNull(Calendar.class,
1205          this.firstDayOfWeek, "FirstDayOfWeek");
1206    }
1207
1208    public void setFirstDayOfWeek(Calendar value) {
1209      if (this.canSetFieldValue(this.firstDayOfWeek, value)) {
1210        this.firstDayOfWeek = value;
1211        this.changed();
1212      }
1213    }
1214
1215    /*
1216     * (non-Javadoc)
1217     *
1218     * @see
1219     * microsoft.exchange.webservices.
1220     * ComplexPropertyChangedDelegateInterface#
1221     * complexPropertyChanged(microsoft.exchange.webservices.ComplexProperty
1222     * )
1223     */
1224    @Override
1225    public void complexPropertyChanged(ComplexProperty complexProperty) {
1226      this.daysOfTheWeekChanged(complexProperty);
1227    }
1228
1229  }
1230
1231
1232  /**
1233   * Represents a regeneration pattern, as used with recurring tasks, where
1234   * each occurrence happens a specified number of weeks after the previous
1235   * one is completed.
1236   */
1237  public final static class WeeklyRegenerationPattern extends
1238      IntervalPattern {
1239
1240    /**
1241     * Initializes a new instance of the WeeklyRegenerationPattern class.
1242     */
1243    public WeeklyRegenerationPattern() {
1244
1245      super();
1246    }
1247
1248    /**
1249     * Initializes a new instance of the WeeklyRegenerationPattern class.
1250     *
1251     * @param startDate the start date
1252     * @param interval  the interval
1253     * @throws ArgumentOutOfRangeException the argument out of range exception
1254     */
1255    public WeeklyRegenerationPattern(Date startDate, int interval)
1256        throws ArgumentOutOfRangeException {
1257      super(startDate, interval);
1258
1259    }
1260
1261    /**
1262     * Gets the name of the XML element.
1263     *
1264     * @return the xml element name
1265     */
1266    @Override
1267    public String getXmlElementName() {
1268      return XmlElementNames.WeeklyRegeneration;
1269    }
1270
1271    /**
1272     * Gets a value indicating whether this instance is regeneration
1273     * pattern. <em>true</em> if this instance is regeneration
1274     * pattern; otherwise, <em>false</em>.
1275     *
1276     * @return true, if is regeneration pattern
1277     */
1278    public boolean isRegenerationPattern() {
1279      return true;
1280    }
1281  }
1282
1283
1284  /**
1285   * Represents a recurrence pattern where each occurrence happens on a
1286   * specific day every year.
1287   */
1288  public final static class YearlyPattern extends Recurrence {
1289
1290    /**
1291     * The month.
1292     */
1293    private Month month;
1294
1295    /**
1296     * The day of month.
1297     */
1298    private Integer dayOfMonth;
1299
1300    /**
1301     * Initializes a new instance of the YearlyPattern class.
1302     */
1303    public YearlyPattern() {
1304      super();
1305
1306    }
1307
1308    /**
1309     * Initializes a new instance of the YearlyPattern class.
1310     *
1311     * @param startDate  the start date
1312     * @param month      the month
1313     * @param dayOfMonth the day of month
1314     */
1315    public YearlyPattern(Date startDate, Month month, int dayOfMonth) {
1316      super(startDate);
1317
1318      this.month = month;
1319      this.dayOfMonth = dayOfMonth;
1320    }
1321
1322    /**
1323     * Gets the name of the XML element.
1324     *
1325     * @return the xml element name
1326     */
1327    @Override
1328    public String getXmlElementName() {
1329      return XmlElementNames.AbsoluteYearlyRecurrence;
1330    }
1331
1332    /**
1333     * Write property to XML.
1334     *
1335     * @param writer the writer
1336     * @throws Exception the exception
1337     */
1338    @Override
1339    public void internalWritePropertiesToXml(EwsServiceXmlWriter writer)
1340        throws Exception {
1341      super.internalWritePropertiesToXml(writer);
1342
1343      writer.writeElementValue(XmlNamespace.Types,
1344          XmlElementNames.DayOfMonth, this.getDayOfMonth());
1345
1346      writer.writeElementValue(XmlNamespace.Types, XmlElementNames.Month,
1347          this.getMonth());
1348    }
1349
1350    /**
1351     * Tries to read element from XML.
1352     *
1353     * @param reader the reader
1354     * @return True if element was read
1355     * @throws Exception the exception
1356     */
1357    @Override
1358    public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
1359        throws Exception {
1360      if (super.tryReadElementFromXml(reader)) {
1361        return true;
1362      } else {
1363        if (reader.getLocalName().equals(XmlElementNames.DayOfMonth)) {
1364
1365          this.dayOfMonth = reader.readElementValue(Integer.class);
1366          return true;
1367        } else if (reader.getLocalName().equals(XmlElementNames.Month)) {
1368
1369          this.month = reader.readElementValue(Month.class);
1370          return true;
1371        } else {
1372
1373          return false;
1374        }
1375      }
1376    }
1377
1378    /**
1379     * Validates this instance.
1380     *
1381     * @throws Exception
1382     */
1383    @Override
1384    public void internalValidate() throws Exception {
1385      super.internalValidate();
1386
1387      if (this.month == null) {
1388        throw new ServiceValidationException("The recurrence pattern's Month property must be specified.");
1389      }
1390
1391      if (this.dayOfMonth == null) {
1392        throw new ServiceValidationException(
1393            "The recurrence pattern's DayOfMonth property must be specified.");
1394      }
1395    }
1396
1397    /**
1398     * Gets the month of the year when each occurrence happens.
1399     *
1400     * @return the month
1401     * @throws ServiceValidationException the service validation exception
1402     */
1403    public Month getMonth() throws ServiceValidationException {
1404      return this.getFieldValueOrThrowIfNull(Month.class, this.month,
1405          "Month");
1406    }
1407
1408    /**
1409     * Sets the month.
1410     *
1411     * @param value the new month
1412     */
1413    public void setMonth(Month value) {
1414
1415      if (this.canSetFieldValue(this.month, value)) {
1416        this.month = value;
1417        this.changed();
1418      }
1419    }
1420
1421    /**
1422     * Gets the day of the month when each occurrence happens. DayOfMonth
1423     * must be between 1 and 31.
1424     *
1425     * @return the day of month
1426     * @throws ServiceValidationException the service validation exception
1427     */
1428    public int getDayOfMonth() throws ServiceValidationException {
1429
1430      return this.getFieldValueOrThrowIfNull(Integer.class, this.dayOfMonth,
1431          "DayOfMonth");
1432
1433    }
1434
1435    /**
1436     * Sets the day of the month when each occurrence happens. DayOfMonth
1437     * must be between 1 and 31.
1438     *
1439     * @param value the new day of month
1440     * @throws ArgumentOutOfRangeException the argument out of range exception
1441     */
1442    public void setDayOfMonth(int value)
1443        throws ArgumentOutOfRangeException {
1444
1445      if (value < 1 || value > 31) {
1446        throw new ArgumentOutOfRangeException("DayOfMonth", "DayOfMonth must be between 1 and 31.");
1447      }
1448
1449      if (this.canSetFieldValue(this.dayOfMonth, value)) {
1450        this.dayOfMonth = value;
1451        this.changed();
1452      }
1453    }
1454  }
1455
1456
1457  /**
1458   * Represents a regeneration pattern, as used with recurring tasks, where
1459   * each occurrence happens a specified number of years after the previous
1460   * one is completed.
1461   */
1462  public final static class YearlyRegenerationPattern extends
1463      IntervalPattern {
1464
1465    /**
1466     * Gets the name of the XML element.
1467     *
1468     * @return the xml element name
1469     */
1470    @Override
1471    public String getXmlElementName() {
1472      return XmlElementNames.YearlyRegeneration;
1473    }
1474
1475    /**
1476     * Gets a value indicating whether this instance is regeneration
1477     * pattern.
1478     *
1479     * @return true, if is regeneration pattern
1480     */
1481    public boolean isRegenerationPattern() {
1482      return true;
1483    }
1484
1485    /**
1486     * Initializes a new instance of the YearlyRegenerationPattern class.
1487     */
1488    public YearlyRegenerationPattern() {
1489      super();
1490
1491    }
1492
1493    /**
1494     * Initializes a new instance of the YearlyRegenerationPattern class.
1495     *
1496     * @param startDate the start date
1497     * @param interval  the interval
1498     * @throws ArgumentOutOfRangeException the argument out of range exception
1499     */
1500    public YearlyRegenerationPattern(Date startDate, int interval)
1501        throws ArgumentOutOfRangeException {
1502      super(startDate, interval);
1503
1504    }
1505  }
1506}