001    /*
002     * The MIT License
003     * Copyright (c) 2012 Microsoft Corporation
004     *
005     * Permission is hereby granted, free of charge, to any person obtaining a copy
006     * of this software and associated documentation files (the "Software"), to deal
007     * in the Software without restriction, including without limitation the rights
008     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009     * copies of the Software, and to permit persons to whom the Software is
010     * furnished to do so, subject to the following conditions:
011     *
012     * The above copyright notice and this permission notice shall be included in
013     * all copies or substantial portions of the Software.
014     *
015     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021     * THE SOFTWARE.
022     */
023    
024    package microsoft.exchange.webservices.data.property.complex.time;
025    
026    import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
027    import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
028    import microsoft.exchange.webservices.data.core.EwsUtilities;
029    import microsoft.exchange.webservices.data.core.XmlAttributeNames;
030    import microsoft.exchange.webservices.data.core.XmlElementNames;
031    import microsoft.exchange.webservices.data.core.exception.service.local.InvalidOrUnsupportedTimeZoneDefinitionException;
032    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
033    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
034    import microsoft.exchange.webservices.data.misc.TimeSpan;
035    import microsoft.exchange.webservices.data.property.complex.ComplexProperty;
036    
037    import java.util.ArrayList;
038    import java.util.List;
039    
040    /**
041     * Represents a group of time zone period transitions.
042     */
043    public class TimeZoneTransitionGroup extends ComplexProperty {
044    
045      /**
046       * The time zone definition.
047       */
048      private TimeZoneDefinition timeZoneDefinition;
049    
050      /**
051       * The id.
052       */
053      private String id;
054    
055      /**
056       * The transitions.
057       */
058      private List<TimeZoneTransition> transitions =
059          new ArrayList<TimeZoneTransition>();
060    
061      /**
062       * The transition to standard.
063       */
064      private TimeZoneTransition transitionToStandard;
065    
066      /**
067       * The transition to daylight.
068       */
069      private TimeZoneTransition transitionToDaylight;
070    
071      /**
072       * The Constant PeriodTarget.
073       */
074      private final static String PeriodTarget = "Period";
075    
076      /**
077       * The Constant GroupTarget.
078       */
079      private final static String GroupTarget = "Group";
080    
081    
082      /**
083       * Loads from XML.
084       *
085       * @param reader the reader
086       * @throws Exception the exception
087       */
088      public void loadFromXml(EwsServiceXmlReader reader) throws Exception {
089        this.loadFromXml(reader, XmlElementNames.TransitionsGroup);
090      }
091    
092      /**
093       * Writes to XML.
094       *
095       * @param writer the writer
096       * @throws Exception the exception
097       */
098      public void writeToXml(EwsServiceXmlWriter writer) throws Exception {
099        this.writeToXml(writer, XmlElementNames.TransitionsGroup);
100      }
101    
102      /**
103       * Reads the attribute from XML.
104       *
105       * @param reader the reader
106       * @throws Exception the exception
107       */
108      @Override
109      public void readAttributesFromXml(EwsServiceXmlReader reader)
110          throws Exception {
111        this.id = reader.readAttributeValue(XmlAttributeNames.Id);
112      }
113    
114      /**
115       * Writes the attribute to XML.
116       *
117       * @param writer the writer
118       * @throws ServiceXmlSerializationException the service xml serialization exception
119       */
120      @Override
121      public void writeAttributesToXml(EwsServiceXmlWriter writer)
122          throws ServiceXmlSerializationException {
123        writer.writeAttributeValue(XmlAttributeNames.Id, this.id);
124      }
125    
126      /**
127       * Writes the attribute to XML.
128       *
129       * @param reader the reader
130       * @return true, if successful
131       * @throws Exception the exception
132       */
133      @Override
134      public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
135          throws Exception {
136        reader.ensureCurrentNodeIsStartElement();
137    
138        TimeZoneTransition transition = TimeZoneTransition.create(
139            this.timeZoneDefinition, reader.getLocalName());
140    
141        transition.loadFromXml(reader);
142    
143        EwsUtilities
144            .ewsAssert(transition.getTargetPeriod() != null, "TimeZoneTransitionGroup.TryReadElementFromXml",
145                       "The transition's target period is null.");
146    
147        this.transitions.add(transition);
148    
149        return true;
150      }
151    
152      /**
153       * Writes elements to XML.
154       *
155       * @param writer the writer
156       * @throws Exception the exception
157       */
158      @Override
159      public void writeElementsToXml(EwsServiceXmlWriter writer)
160          throws Exception {
161        for (TimeZoneTransition transition : this.transitions) {
162          transition.writeToXml(writer);
163        }
164      }
165    
166      /**
167       * Validates this transition group.
168       *
169       * @throws InvalidOrUnsupportedTimeZoneDefinitionException thrown when time zone definition is not valid.
170       */
171      public void validate() throws ServiceLocalException {
172        // There must be exactly one or two transitions in the group.
173        if (this.transitions.size() < 1 || this.transitions.size() > 2) {
174          throw new InvalidOrUnsupportedTimeZoneDefinitionException();
175        }
176    
177        // If there is only one transition, it must be of type
178        // TimeZoneTransition
179        if (this.transitions.size() == 1
180            && !(this.transitions.get(0).getClass() ==
181            TimeZoneTransition.class)) {
182          throw new InvalidOrUnsupportedTimeZoneDefinitionException();
183        }
184    
185        // If there are two transitions, none of them should be of type
186        // TimeZoneTransition
187        if (this.transitions.size() == 2) {
188          for (TimeZoneTransition transition : this.transitions) {
189            if (transition.getClass() == TimeZoneTransition.class) {
190              throw new InvalidOrUnsupportedTimeZoneDefinitionException();
191            }
192          }
193        }
194    
195        // All the transitions in the group must be to a period.
196        for (TimeZoneTransition transition : this.transitions) {
197          if (transition.getTargetPeriod() == null) {
198            throw new InvalidOrUnsupportedTimeZoneDefinitionException();
199          }
200        }
201      }
202    
203      /**
204       * The Class CustomTimeZoneCreateParams.
205       */
206      protected static class CustomTimeZoneCreateParams {
207    
208        /**
209         * The base offset to utc.
210         */
211        private TimeSpan baseOffsetToUtc;
212    
213        /**
214         * The standard display name.
215         */
216        private String standardDisplayName;
217    
218        /**
219         * The daylight display name.
220         */
221        private String daylightDisplayName;
222    
223        /**
224         * Initializes a new instance of the class.
225         */
226        protected CustomTimeZoneCreateParams() {
227        }
228    
229        /**
230         * Gets  the base offset to UTC.
231         *
232         * @return the base offset to utc
233         */
234        protected TimeSpan getBaseOffsetToUtc() {
235          return this.baseOffsetToUtc;
236        }
237    
238        /**
239         * Sets the base offset to utc.
240         *
241         * @param baseOffsetToUtc the new base offset to utc
242         */
243        protected void setBaseOffsetToUtc(TimeSpan baseOffsetToUtc) {
244          this.baseOffsetToUtc = baseOffsetToUtc;
245        }
246    
247        /**
248         * Gets the display name of the standard period.
249         *
250         * @return the standard display name
251         */
252        protected String getStandardDisplayName() {
253          return this.standardDisplayName;
254        }
255    
256        /**
257         * Sets the standard display name.
258         *
259         * @param standardDisplayName the new standard display name
260         */
261        protected void setStandardDisplayName(String standardDisplayName) {
262          this.standardDisplayName = standardDisplayName;
263        }
264    
265        /**
266         * Gets the display name of the daylight period.
267         *
268         * @return the daylight display name
269         */
270        protected String getDaylightDisplayName() {
271          return this.daylightDisplayName;
272        }
273    
274        /**
275         * Sets the daylight display name.
276         *
277         * @param daylightDisplayName the new daylight display name
278         */
279        protected void setDaylightDisplayName(String daylightDisplayName) {
280          this.daylightDisplayName = daylightDisplayName;
281        }
282    
283        /**
284         * Gets a value indicating whether the custom time zone should have a
285         * daylight period. <value> <c>true</c> if the custom time zone should
286         * have a daylight period; otherwise, <c>false</c>. </value>
287         *
288         * @return the checks for daylight period
289         */
290        protected boolean getHasDaylightPeriod() {
291          return (!(this.daylightDisplayName == null ||
292              this.daylightDisplayName.isEmpty()));
293        }
294      }
295    
296      /**
297       * Gets a value indicating whether this group contains a transition to the
298       * Daylight period. <value><c>true</c> if this group contains a transition
299       * to daylight; otherwise, <c>false</c>.</value>
300       *
301       * @return the supports daylight
302       */
303      protected boolean getSupportsDaylight() {
304        return this.transitions.size() == 2;
305      }
306    
307      /**
308       * Initializes the private members holding references to the transitions to
309       * the Daylight and Standard periods.
310       *
311       * @throws InvalidOrUnsupportedTimeZoneDefinitionException thrown when time zone definition is not valid.
312       */
313      private void initializeTransitions() throws ServiceLocalException {
314        if (this.transitionToStandard == null) {
315          for (TimeZoneTransition transition : this.transitions) {
316            if (transition.getTargetPeriod().isStandardPeriod() ||
317                (this.transitions.size() == 1)) {
318              this.transitionToStandard = transition;
319            } else {
320              this.transitionToDaylight = transition;
321            }
322          }
323        }
324    
325        // If we didn't find a Standard period, this is an invalid time zone
326        // group.
327        if (this.transitionToStandard == null) {
328          throw new InvalidOrUnsupportedTimeZoneDefinitionException();
329        }
330      }
331    
332      /**
333       * Gets the transition to the Daylight period.
334       *
335       * @return the transition to daylight
336       * @throws ServiceLocalException the service local exception
337       */
338      private TimeZoneTransition getTransitionToDaylight()
339          throws ServiceLocalException {
340        this.initializeTransitions();
341        return this.transitionToDaylight;
342      }
343    
344      /**
345       * Gets the transition to the Standard period.
346       *
347       * @return the transition to standard
348       * @throws ServiceLocalException the service local exception
349       */
350      private TimeZoneTransition getTransitionToStandard()
351          throws ServiceLocalException {
352        this.initializeTransitions();
353        return this.transitionToStandard;
354      }
355    
356      /**
357       * Gets the offset to UTC based on this group's transitions.
358       *
359       * @return the custom time zone creation params
360       */
361      protected CustomTimeZoneCreateParams getCustomTimeZoneCreationParams() {
362        CustomTimeZoneCreateParams result = new CustomTimeZoneCreateParams();
363    
364        if (this.transitionToDaylight != null) {
365          result.setDaylightDisplayName(this.transitionToDaylight
366              .getTargetPeriod().getName());
367        }
368    
369        result.setStandardDisplayName(this.transitionToStandard
370            .getTargetPeriod().getName());
371    
372        // Assume that the standard period's offset is the base offset to UTC.
373        // EWS returns a positive offset for time zones that are behind UTC, and
374        // a negative one for time zones ahead of UTC. TimeZoneInfo does it the
375        // other
376        // way around.
377        // result.BaseOffsetToUtc =
378        // -this.TransitionToStandard.TargetPeriod.Bias;
379    
380        return result;
381      }
382    
383      /**
384       * Initializes a new instance of the class.
385       *
386       * @param timeZoneDefinition the time zone definition
387       */
388      public TimeZoneTransitionGroup(TimeZoneDefinition timeZoneDefinition) {
389        super();
390        this.timeZoneDefinition = timeZoneDefinition;
391      }
392    
393      /**
394       * Initializes a new instance of the class.
395       *
396       * @param timeZoneDefinition the time zone definition
397       * @param id                 the id
398       */
399      public TimeZoneTransitionGroup(TimeZoneDefinition timeZoneDefinition, String id) {
400        this(timeZoneDefinition);
401        this.id = id;
402      }
403    
404      /**
405       * Gets the id of this group.
406       *
407       * @return the id
408       */
409      public String getId() {
410        return this.id;
411      }
412    
413      /**
414       * Sets the id.
415       *
416       * @param id the new id
417       */
418      public void setId(String id) {
419        this.id = id;
420      }
421    
422      /**
423       * Gets the transitions in this group.
424       *
425       * @return the transitions
426       */
427      public List<TimeZoneTransition> getTransitions() {
428        return this.transitions;
429      }
430    }