001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.axis2.databinding.types;
021:
022: import java.io.Serializable;
023: import java.util.Calendar;
024:
025: /**
026: * Implementation of the XML Schema type duration. Duration supports a minimum fractional second
027: * precision of milliseconds.
028: *
029: * @see <a href="http://www.w3.org/TR/xmlschema-2/#duration">XML Schema 3.2.6</a>
030: */
031: public class Duration implements Serializable {
032:
033: private static final long serialVersionUID = -3736760992541369098L;
034:
035: boolean isNegative;
036: int years;
037: int months;
038: int days;
039: int hours;
040: int minutes;
041: double seconds;
042:
043: /** Default no-arg constructor */
044: public Duration() {
045: }
046:
047: /**
048: * @param negative
049: * @param aYears
050: * @param aMonths
051: * @param aDays
052: * @param aHours
053: * @param aMinutes
054: * @param aSeconds
055: */
056: public Duration(boolean negative, int aYears, int aMonths,
057: int aDays, int aHours, int aMinutes, double aSeconds) {
058: isNegative = negative;
059: years = aYears;
060: months = aMonths;
061: days = aDays;
062: hours = aHours;
063: minutes = aMinutes;
064: setSeconds(aSeconds);
065: }
066:
067: /**
068: * Constructs Duration from a String in an xsd:duration format - PnYnMnDTnHnMnS.
069: *
070: * @param duration String
071: * @throws IllegalArgumentException if the string doesn't parse correctly.
072: */
073: public Duration(String duration) throws IllegalArgumentException {
074: int position = 1;
075: int timePosition = duration.indexOf("T");
076:
077: // P is required but P by itself is invalid
078: if (duration.indexOf("P") == -1 || duration.equals("P")) {
079: throw new IllegalArgumentException();
080: // Messages.getMessage("badDuration"));
081: }
082:
083: // if present, time cannot be empty
084: if (duration.lastIndexOf("T") == duration.length() - 1) {
085: throw new IllegalArgumentException();
086: // Messages.getMessage("badDuration"));
087: }
088:
089: // check the sign
090: if (duration.startsWith("-")) {
091: isNegative = true;
092: position++;
093: }
094:
095: // parse time part
096: if (timePosition != -1) {
097: parseTime(duration.substring(timePosition + 1));
098: } else {
099: timePosition = duration.length();
100: }
101:
102: // parse date part
103: if (position != timePosition) {
104: parseDate(duration.substring(position, timePosition));
105: }
106: }
107:
108: /**
109: * Constructs Duration from a Calendar.
110: *
111: * @param calendar Calendar
112: * @throws IllegalArgumentException if the calendar object does not represent any date nor
113: * time.
114: */
115: public Duration(boolean negative, Calendar calendar)
116: throws IllegalArgumentException {
117: this .isNegative = negative;
118: this .years = calendar.get(Calendar.YEAR);
119: this .months = calendar.get(Calendar.MONTH);
120: this .days = calendar.get(Calendar.DATE);
121: this .hours = calendar.get(Calendar.HOUR);
122: this .minutes = calendar.get(Calendar.MINUTE);
123: this .seconds = calendar.get(Calendar.SECOND);
124: this .seconds += ((double) calendar.get(Calendar.MILLISECOND)) / 100;
125: if (years == 0 && months == 0 && days == 0 && hours == 0
126: && minutes == 0 && seconds == 0) {
127: throw new IllegalArgumentException();
128: //Messages.getMessage("badCalendarForDuration"));
129: }
130: }
131:
132: /**
133: * This method parses the time portion of a String that represents xsd:duration - nHnMnS.
134: *
135: * @param time
136: * @throws IllegalArgumentException if time does not match pattern
137: */
138: public void parseTime(String time) throws IllegalArgumentException {
139: if (time.length() == 0 || time.indexOf("-") != -1) {
140: throw new IllegalArgumentException();
141: //Messages.getMessage("badTimeDuration"));
142: }
143:
144: // check if time ends with either H, M, or S
145: if (!time.endsWith("H") && !time.endsWith("M")
146: && !time.endsWith("S")) {
147: throw new IllegalArgumentException();
148: //Messages.getMessage("badTimeDuration"));
149: }
150:
151: try {
152: // parse string and extract hours, minutes, and seconds
153: int start = 0;
154:
155: // Hours
156: int end = time.indexOf("H");
157: // if there is H in a string but there is no value for hours,
158: // throw an exception
159: if (start == end) {
160: throw new IllegalArgumentException();
161: //Messages.getMessage("badTimeDuration"));
162: }
163: if (end != -1) {
164: hours = Integer.parseInt(time.substring(0, end));
165: start = end + 1;
166: }
167:
168: // Minutes
169: end = time.indexOf("M");
170: // if there is M in a string but there is no value for hours,
171: // throw an exception
172: if (start == end) {
173: throw new IllegalArgumentException();
174: // Messages.getMessage("badTimeDuration"));
175: }
176:
177: if (end != -1) {
178: minutes = Integer.parseInt(time.substring(start, end));
179: start = end + 1;
180: }
181:
182: // Seconds
183: end = time.indexOf("S");
184: // if there is S in a string but there is no value for hours,
185: // throw an exception
186: if (start == end) {
187: throw new IllegalArgumentException();
188: //Messages.getMessage("badTimeDuration"));
189: }
190:
191: if (end != -1) {
192: setSeconds(Double.parseDouble(time
193: .substring(start, end)));
194: }
195: } catch (NumberFormatException e) {
196: throw new IllegalArgumentException();
197: //Messages.getMessage("badTimeDuration"));
198: }
199: }
200:
201: /**
202: * This method parses the date portion of a String that represents xsd:duration - nYnMnD.
203: *
204: * @param date
205: * @throws IllegalArgumentException if date does not match pattern
206: */
207: public void parseDate(String date) throws IllegalArgumentException {
208: if (date.length() == 0 || date.indexOf("-") != -1) {
209: throw new IllegalArgumentException();
210: //Messages.getMessage("badDateDuration"));
211: }
212:
213: // check if date string ends with either Y, M, or D
214: if (!date.endsWith("Y") && !date.endsWith("M")
215: && !date.endsWith("D")) {
216: throw new IllegalArgumentException();
217: //Messages.getMessage("badDateDuration"));
218: }
219:
220: // catch any parsing exception
221: try {
222: // parse string and extract years, months, days
223: int start = 0;
224: int end = date.indexOf("Y");
225:
226: // if there is Y in a string but there is no value for years,
227: // throw an exception
228: if (start == end) {
229: throw new IllegalArgumentException();
230: // Messages.getMessage("badDateDuration"));
231: }
232: if (end != -1) {
233: years = Integer.parseInt(date.substring(0, end));
234: start = end + 1;
235: }
236:
237: // months
238: end = date.indexOf("M");
239: // if there is M in a string but there is no value for months,
240: // throw an exception
241: if (start == end) {
242: throw new IllegalArgumentException();
243: //Messages.getMessage("badDateDuration"));
244: }
245: if (end != -1) {
246: months = Integer.parseInt(date.substring(start, end));
247: start = end + 1;
248: }
249:
250: end = date.indexOf("D");
251: // if there is D in a string but there is no value for days,
252: // throw an exception
253: if (start == end) {
254: throw new IllegalArgumentException();
255: // Messages.getMessage("badDateDuration"));
256: }
257: if (end != -1) {
258: days = Integer.parseInt(date.substring(start, end));
259: }
260: } catch (NumberFormatException e) {
261: throw new IllegalArgumentException();
262: //Messages.getMessage("badDateDuration"));
263: }
264: }
265:
266: /**
267: *
268: */
269: public boolean isNegative() {
270: return isNegative;
271: }
272:
273: /**
274: *
275: */
276: public int getYears() {
277: return years;
278: }
279:
280: /**
281: *
282: */
283: public int getMonths() {
284: return months;
285: }
286:
287: /**
288: *
289: */
290: public int getDays() {
291: return days;
292: }
293:
294: /**
295: *
296: */
297: public int getHours() {
298: return hours;
299: }
300:
301: /**
302: *
303: */
304: public int getMinutes() {
305: return minutes;
306: }
307:
308: /**
309: *
310: */
311: public double getSeconds() {
312: return seconds;
313: }
314:
315: /** @param negative */
316: public void setNegative(boolean negative) {
317: isNegative = negative;
318: }
319:
320: /** @param years */
321: public void setYears(int years) {
322: this .years = years;
323: }
324:
325: /** @param months */
326: public void setMonths(int months) {
327: this .months = months;
328: }
329:
330: /** @param days */
331: public void setDays(int days) {
332: this .days = days;
333: }
334:
335: /** @param hours */
336: public void setHours(int hours) {
337: this .hours = hours;
338: }
339:
340: /** @param minutes */
341: public void setMinutes(int minutes) {
342: this .minutes = minutes;
343: }
344:
345: /**
346: * @param seconds
347: * @deprecated use {@link #setSeconds(double) setSeconds(double)} instead
348: */
349: public void setSeconds(int seconds) {
350: this .seconds = seconds;
351: }
352:
353: /**
354: * Sets the seconds. NOTE: The fractional value of seconds is rounded up to milliseconds.
355: *
356: * @param seconds double
357: */
358: public void setSeconds(double seconds) {
359: this .seconds = ((double) (Math.round(seconds * 100))) / 100;
360: }
361:
362: /** This returns the xml representation of an xsd:duration object. */
363: public String toString() {
364: StringBuffer duration = new StringBuffer();
365:
366: duration.append("P");
367:
368: if (years != 0) {
369: duration.append(years).append("Y");
370: }
371: if (months != 0) {
372: duration.append(months).append("M");
373: }
374: if (days != 0) {
375: duration.append(days).append("D");
376: }
377: if (hours != 0 || minutes != 0 || seconds != 0.0) {
378: duration.append("T");
379:
380: if (hours != 0) {
381: duration.append(hours).append("H");
382:
383: }
384: if (minutes != 0) {
385: duration.append(minutes).append("M");
386:
387: }
388: if (seconds != 0) {
389: if (seconds == (int) seconds) {
390: duration.append((int) seconds).append("S");
391: } else {
392: duration.append(seconds).append("S");
393: }
394: }
395: }
396:
397: if (duration.length() == 1) {
398: duration.append("T0S");
399: }
400:
401: if (isNegative) {
402: duration.insert(0, "-");
403: }
404:
405: return duration.toString();
406: }
407:
408: /**
409: * The equals method compares the time represented by duration object, not its string
410: * representation. Hence, a duration object representing 65 minutes is considered equal to a
411: * duration object representing 1 hour and 5 minutes.
412: *
413: * @param object
414: */
415: public boolean equals(Object object) {
416: if (!(object instanceof Duration)) {
417: return false;
418: }
419:
420: Duration duration = (Duration) object;
421:
422: return this .isNegative == duration.isNegative
423: && this .getAsCalendar()
424: .equals(duration.getAsCalendar());
425: }
426:
427: public long compare(Duration duration) {
428: return this .getAsCalendar().getTimeInMillis()
429: - duration.getAsCalendar().getTimeInMillis();
430: }
431:
432: public int hashCode() {
433: int hashCode = 0;
434:
435: if (isNegative) {
436: hashCode++;
437: }
438: hashCode += years;
439: hashCode += months;
440: hashCode += days;
441: hashCode += hours;
442: hashCode += minutes;
443: hashCode += seconds;
444: // milliseconds
445: hashCode += (seconds * 100) % 100;
446:
447: return hashCode;
448: }
449:
450: /**
451: * Returns duration as a calendar. Due to the way a Calendar class works, the values for
452: * particular fields may not be the same as obtained through getter methods. For example, if a
453: * duration's object getMonths returns 20, a similar call on a calendar object will return 1
454: * year and 8 months.
455: *
456: * @return Calendar
457: */
458: public Calendar getAsCalendar() {
459: return getAsCalendar(Calendar.getInstance());
460: }
461:
462: /**
463: * Returns duration as a calendar. Due to the way a Calendar class works, the values for
464: * particular fields may not be the same as obtained through getter methods. For example, if a
465: * Duration's object getMonths returns 20, a similar call on a Calendar object will return 1
466: * year and 8 months.
467: *
468: * @param startTime Calendar
469: * @return Calendar
470: */
471: public Calendar getAsCalendar(Calendar startTime) {
472: Calendar ret = (Calendar) startTime.clone();
473: ret.set(Calendar.YEAR, years);
474: ret.set(Calendar.MONTH, months);
475: ret.set(Calendar.DATE, days);
476: ret.set(Calendar.HOUR, hours);
477: ret.set(Calendar.MINUTE, minutes);
478: ret.set(Calendar.SECOND, (int) seconds);
479: ret.set(Calendar.MILLISECOND, (int) (seconds * 100 - Math
480: .round(seconds) * 100));
481: return ret;
482: }
483: }
|