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 }