blob: a99e162f311de05ec1a7e09d67fb14836ba1dfbf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.databinding.types;
import java.io.Serializable;
import java.util.Calendar;
/**
* Implementation of the XML Schema type duration. Duration supports a minimum fractional second
* precision of milliseconds.
*
* @see <a href="http://www.w3.org/TR/xmlschema-2/#duration">XML Schema 3.2.6</a>
*/
public class Duration implements Serializable {
private static final long serialVersionUID = -3736760992541369098L;
boolean isNegative;
int years;
int months;
int days;
int hours;
int minutes;
double seconds;
/** Default no-arg constructor */
public Duration() {
}
/**
* @param negative
* @param aYears
* @param aMonths
* @param aDays
* @param aHours
* @param aMinutes
* @param aSeconds
*/
public Duration(boolean negative, int aYears, int aMonths, int aDays,
int aHours, int aMinutes, double aSeconds) {
isNegative = negative;
years = aYears;
months = aMonths;
days = aDays;
hours = aHours;
minutes = aMinutes;
setSeconds(aSeconds);
}
/**
* Constructs Duration from a String in an xsd:duration format - PnYnMnDTnHnMnS.
*
* @param duration String
* @throws IllegalArgumentException if the string doesn't parse correctly.
*/
public Duration(String duration) throws IllegalArgumentException {
int position = 1;
int timePosition = duration.indexOf("T");
// P is required but P by itself is invalid
if (duration.indexOf("P") == -1 || duration.equals("P")) {
throw new IllegalArgumentException();
// Messages.getMessage("badDuration"));
}
// if present, time cannot be empty
if (duration.lastIndexOf("T") == duration.length() - 1) {
throw new IllegalArgumentException();
// Messages.getMessage("badDuration"));
}
// check the sign
if (duration.startsWith("-")) {
isNegative = true;
position++;
}
// parse time part
if (timePosition != -1) {
parseTime(duration.substring(timePosition + 1));
} else {
timePosition = duration.length();
}
// parse date part
if (position != timePosition) {
parseDate(duration.substring(position, timePosition));
}
}
/**
* Constructs Duration from a Calendar.
*
* @param calendar Calendar
* @throws IllegalArgumentException if the calendar object does not represent any date nor
* time.
*/
public Duration(boolean negative, Calendar calendar) throws
IllegalArgumentException {
this.isNegative = negative;
this.years = calendar.get(Calendar.YEAR);
this.months = calendar.get(Calendar.MONTH);
this.days = calendar.get(Calendar.DATE);
this.hours = calendar.get(Calendar.HOUR);
this.minutes = calendar.get(Calendar.MINUTE);
this.seconds = calendar.get(Calendar.SECOND);
this.seconds += ((double)calendar.get(Calendar.MILLISECOND)) / 100;
if (years == 0 && months == 0 && days == 0 && hours == 0 &&
minutes == 0 && seconds == 0) {
throw new IllegalArgumentException();
//Messages.getMessage("badCalendarForDuration"));
}
}
/**
* This method parses the time portion of a String that represents xsd:duration - nHnMnS.
*
* @param time
* @throws IllegalArgumentException if time does not match pattern
*/
public void parseTime(String time) throws IllegalArgumentException {
if (time.length() == 0 || time.indexOf("-") != -1) {
throw new IllegalArgumentException();
//Messages.getMessage("badTimeDuration"));
}
// check if time ends with either H, M, or S
if (!time.endsWith("H") && !time.endsWith("M") && !time.endsWith("S")) {
throw new IllegalArgumentException();
//Messages.getMessage("badTimeDuration"));
}
try {
// parse string and extract hours, minutes, and seconds
int start = 0;
// Hours
int end = time.indexOf("H");
// if there is H in a string but there is no value for hours,
// throw an exception
if (start == end) {
throw new IllegalArgumentException();
//Messages.getMessage("badTimeDuration"));
}
if (end != -1) {
hours = Integer.parseInt(time.substring(0, end));
start = end + 1;
}
// Minutes
end = time.indexOf("M");
// if there is M in a string but there is no value for hours,
// throw an exception
if (start == end) {
throw new IllegalArgumentException();
// Messages.getMessage("badTimeDuration"));
}
if (end != -1) {
minutes = Integer.parseInt(time.substring(start, end));
start = end + 1;
}
// Seconds
end = time.indexOf("S");
// if there is S in a string but there is no value for hours,
// throw an exception
if (start == end) {
throw new IllegalArgumentException();
//Messages.getMessage("badTimeDuration"));
}
if (end != -1) {
setSeconds(Double.parseDouble(time.substring(start, end)));
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException();
//Messages.getMessage("badTimeDuration"));
}
}
/**
* This method parses the date portion of a String that represents xsd:duration - nYnMnD.
*
* @param date
* @throws IllegalArgumentException if date does not match pattern
*/
public void parseDate(String date) throws IllegalArgumentException {
if (date.length() == 0 || date.indexOf("-") != -1) {
throw new IllegalArgumentException();
//Messages.getMessage("badDateDuration"));
}
// check if date string ends with either Y, M, or D
if (!date.endsWith("Y") && !date.endsWith("M") && !date.endsWith("D")) {
throw new IllegalArgumentException();
//Messages.getMessage("badDateDuration"));
}
// catch any parsing exception
try {
// parse string and extract years, months, days
int start = 0;
int end = date.indexOf("Y");
// if there is Y in a string but there is no value for years,
// throw an exception
if (start == end) {
throw new IllegalArgumentException();
// Messages.getMessage("badDateDuration"));
}
if (end != -1) {
years = Integer.parseInt(date.substring(0, end));
start = end + 1;
}
// months
end = date.indexOf("M");
// if there is M in a string but there is no value for months,
// throw an exception
if (start == end) {
throw new IllegalArgumentException();
//Messages.getMessage("badDateDuration"));
}
if (end != -1) {
months = Integer.parseInt(date.substring(start, end));
start = end + 1;
}
end = date.indexOf("D");
// if there is D in a string but there is no value for days,
// throw an exception
if (start == end) {
throw new IllegalArgumentException();
// Messages.getMessage("badDateDuration"));
}
if (end != -1) {
days = Integer.parseInt(date.substring(start, end));
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException();
//Messages.getMessage("badDateDuration"));
}
}
/**
*
*/
public boolean isNegative() {
return isNegative;
}
/**
*
*/
public int getYears() {
return years;
}
/**
*
*/
public int getMonths() {
return months;
}
/**
*
*/
public int getDays() {
return days;
}
/**
*
*/
public int getHours() {
return hours;
}
/**
*
*/
public int getMinutes() {
return minutes;
}
/**
*
*/
public double getSeconds() {
return seconds;
}
/** @param negative */
public void setNegative(boolean negative) {
isNegative = negative;
}
/** @param years */
public void setYears(int years) {
this.years = years;
}
/** @param months */
public void setMonths(int months) {
this.months = months;
}
/** @param days */
public void setDays(int days) {
this.days = days;
}
/** @param hours */
public void setHours(int hours) {
this.hours = hours;
}
/** @param minutes */
public void setMinutes(int minutes) {
this.minutes = minutes;
}
/**
* @param seconds
* @deprecated use {@link #setSeconds(double) setSeconds(double)} instead
*/
public void setSeconds(int seconds) {
this.seconds = seconds;
}
/**
* Sets the seconds. NOTE: The fractional value of seconds is rounded up to milliseconds.
*
* @param seconds double
*/
public void setSeconds(double seconds) {
this.seconds = ((double)(Math.round(seconds * 100))) / 100;
}
/** This returns the xml representation of an xsd:duration object. */
public String toString() {
StringBuffer duration = new StringBuffer();
duration.append("P");
if (years != 0) {
duration.append(years).append("Y");
}
if (months != 0) {
duration.append(months).append("M");
}
if (days != 0) {
duration.append(days).append("D");
}
if (hours != 0 || minutes != 0 || seconds != 0.0) {
duration.append("T");
if (hours != 0) {
duration.append(hours).append("H");
}
if (minutes != 0) {
duration.append(minutes).append("M");
}
if (seconds != 0) {
if (seconds == (int)seconds) {
duration.append((int)seconds).append("S");
} else {
duration.append(seconds).append("S");
}
}
}
if (duration.length() == 1) {
duration.append("T0S");
}
if (isNegative) {
duration.insert(0, "-");
}
return duration.toString();
}
/**
* The equals method compares the time represented by duration object, not its string
* representation. Hence, a duration object representing 65 minutes is considered equal to a
* duration object representing 1 hour and 5 minutes.
*
* @param object
*/
public boolean equals(Object object) {
if (!(object instanceof Duration)) {
return false;
}
Duration duration = (Duration)object;
return this.isNegative == duration.isNegative &&
this.getAsCalendar().equals(duration.getAsCalendar());
}
public long compare(Duration duration) {
return this.getAsCalendar().getTimeInMillis() - duration.getAsCalendar().getTimeInMillis();
}
public int hashCode() {
int hashCode = 0;
if (isNegative) {
hashCode++;
}
hashCode += years;
hashCode += months;
hashCode += days;
hashCode += hours;
hashCode += minutes;
hashCode += seconds;
// milliseconds
hashCode += (seconds * 100) % 100;
return hashCode;
}
/**
* Returns duration as a calendar. Due to the way a Calendar class works, the values for
* particular fields may not be the same as obtained through getter methods. For example, if a
* duration's object getMonths returns 20, a similar call on a calendar object will return 1
* year and 8 months.
*
* @return Calendar
*/
public Calendar getAsCalendar() {
return getAsCalendar(Calendar.getInstance());
}
/**
* Returns duration as a calendar. Due to the way a Calendar class works, the values for
* particular fields may not be the same as obtained through getter methods. For example, if a
* Duration's object getMonths returns 20, a similar call on a Calendar object will return 1
* year and 8 months.
*
* @param startTime Calendar
* @return Calendar
*/
public Calendar getAsCalendar(Calendar startTime) {
Calendar ret = (Calendar)startTime.clone();
ret.set(Calendar.YEAR, years);
ret.set(Calendar.MONTH, months);
ret.set(Calendar.DATE, days);
ret.set(Calendar.HOUR, hours);
ret.set(Calendar.MINUTE, minutes);
ret.set(Calendar.SECOND, (int)seconds);
ret.set(Calendar.MILLISECOND,
(int)(seconds * 100 - Math.round(seconds) * 100));
return ret;
}
}