blob: bd3a1fd6c065bffaf786dab9c79f7106eabd4e47 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2010 Andrea Bittau, University College London, and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0
* David Carver (STAR) - bug 282223 - Implemented XSDuration type for castable checking.
* Mukul Gandhi - bug 280798 - PsychoPath support for JDK 1.4
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal.types;
import org.eclipse.wst.xml.xpath2.api.DynamicContext;
import org.eclipse.wst.xml.xpath2.api.ResultBuffer;
import org.eclipse.wst.xml.xpath2.api.ResultSequence;
import org.eclipse.wst.xml.xpath2.api.typesystem.TypeDefinition;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.internal.function.CmpEq;
import org.eclipse.wst.xml.xpath2.processor.internal.function.CmpGt;
import org.eclipse.wst.xml.xpath2.processor.internal.function.CmpLt;
import org.eclipse.wst.xml.xpath2.processor.internal.types.builtin.BuiltinTypeLibrary;
/**
* A representation of the xs:duration data type. Other duration implementations
* should inherit from this implementation.
*
* @since 1.1 This used to be an abstract class but was incorrectly implemented
* as such.
*/
public class XSDuration extends CtrType implements CmpEq, CmpLt, CmpGt, Cloneable {
private static final String XS_DURATION = "xs:duration";
protected int _year;
protected int _month;
protected int _days;
protected int _hours;
protected int _minutes;
protected double _seconds;
protected boolean _negative;
/**
* Initializes to the supplied parameters. If more than 24 hours is
* supplied, the number of days is adjusted accordingly. The same occurs for
* minutes and seconds
*
* @param years
* Number of years in this duration of time.
* @param months
* Number of months in this duration of time.
* @param days
* Number of days in this duration of time
* @param hours
* Number of hours in this duration of time
* @param minutes
* Number of minutes in this duration of time
* @param seconds
* Number of seconds in this duration of time
* @param negative
* True if this duration of time represents a backwards passage
* through time. False otherwise
*/
public XSDuration(int years, int months, int days, int hours, int minutes,
double seconds, boolean negative) {
_year = years;
_month = months;
_days = days;
_hours = hours;
_minutes = minutes;
_seconds = seconds;
_negative = negative;
if (_month >= 12) {
_year += _month / 12;
_month = _month % 12;
}
if (_seconds >= 60) {
int isec = (int) _seconds;
double rem = _seconds - (isec);
_minutes += isec / 60;
_seconds = isec % 60;
_seconds += rem;
}
if (_minutes >= 60) {
_hours += _minutes / 60;
_minutes = _minutes % 60;
}
if (_hours >= 24) {
_days += _hours / 24;
_hours = _hours % 24;
}
}
/**
* Initialises to the given number of seconds
*
* @param secs
* Number of seconds in the duration of time
*/
public XSDuration(double secs) {
this(0, 0, 0, 0, 0, Math.abs(secs), secs < 0);
}
/**
* Initialises to a duration of no time (0days, 0hours, 0minutes, 0seconds)
*/
public XSDuration() {
this(0, 0, 0, 0, 0, 0.0, false);
}
public String type_name() {
return "duration";
}
public String string_type() {
return XS_DURATION;
}
/**
* Retrieves a String representation of the duration stored
*
* @return String representation of the duration stored
*/
public String getStringValue() {
String ret = "";
boolean did_something = false;
String tret = "";
if (negative() && !(days() == 0 && hours() == 0 && seconds() == 0))
ret += "-";
ret += "P";
int years = year();
if (years != 0)
ret += years + "Y";
int months = month();
if (months != 0) {
ret += months + "M";
}
if (days() != 0) {
ret += days() + "D";
did_something = true;
}
// do the "time" bit
int hours = hours();
int minutes = minutes();
double seconds = seconds();
if (hours != 0) {
tret += hours + "H";
did_something = true;
}
if (minutes != 0) {
tret += minutes + "M";
did_something = true;
}
if (seconds != 0) {
String doubStr = (new Double(seconds).toString());
if (doubStr.endsWith(".0")) {
// string value of x.0 seconds is xS. e.g, 7.0S is converted to
// 7S.
tret += doubStr.substring(0, doubStr.indexOf(".0")) + "S";
} else {
tret += seconds + "S";
}
did_something = true;
} else if (!did_something) {
tret += "0S";
}
if ((year() == 0 && month() == 0) || (hours > 0 || minutes > 0 || seconds > 0)) {
if (tret.length() > 0) {
ret += "T" + tret;
}
}
return ret;
}
/**
* Retrieves the number of days within the duration of time stored
*
* @return Number of days within the duration of time stored
*/
public int days() {
return _days;
}
/**
* Retrieves the number of minutes (max 60) within the duration of time
* stored
*
* @return Number of minutes within the duration of time stored
*/
public int minutes() {
return _minutes;
}
/**
* Retrieves the number of hours (max 24) within the duration of time stored
*
* @return Number of hours within the duration of time stored
*/
public int hours() {
return _hours;
}
/**
* Retrieves the number of seconds (max 60) within the duration of time
* stored
*
* @return Number of seconds within the duration of time stored
*/
public double seconds() {
return _seconds;
}
/**
* Equality comparison between this and the supplied duration of time.
*
* @param arg
* The duration of time to compare with
* @return True if they both represent the duration of time. False otherwise
* @throws DynamicError
*/
public boolean eq(AnyType arg, DynamicContext dynamicContext) throws DynamicError {
XSDuration val = (XSDuration) NumericType.get_single_type(arg,
XSDuration.class);
return value() == val.value();
}
/**
* Comparison between this and the supplied duration of time.
*
* @param arg
* The duration of time to compare with
* @return True if the supplied time represents a larger duration than that
* stored. False otherwise
* @throws DynamicError
*/
public boolean lt(AnyType arg, DynamicContext context) throws DynamicError {
XSDuration val = (XSDuration) NumericType.get_single_type(arg,
XSDayTimeDuration.class);
return value() < val.value();
}
/**
* Comparison between this and the supplied duration of time.
*
* @param arg
* The duration of time to compare with
* @return True if the supplied time represents a smaller duration than that
* stored. False otherwise
* @throws DynamicError
*/
public boolean gt(AnyType arg, DynamicContext context) throws DynamicError {
XSDuration val = (XSDuration) NumericType.get_single_type(arg,
XSDayTimeDuration.class);
return value() > val.value();
}
/**
* Retrieves whether this duration represents a backward passage through
* time
*
* @return True if this duration represents a backward passage through time.
* False otherwise
*/
public boolean negative() {
return _negative;
}
/**
* Retrieves the duration of time stored as the number of seconds within it
*
* @return Number of seconds making up this duration of time
*/
public double value() {
double ret = days() * 24 * 60 * 60;
ret += hours() * 60 * 60;
ret += minutes() * 60;
ret += seconds();
if (negative())
ret *= -1;
return ret;
}
public double time_value() {
double ret = 0;
ret += hours() * 60 * 60;
ret += minutes() * 60;
ret += seconds();
if (negative())
ret *= -1;
return ret;
}
/**
* Creates a new ResultSequence consisting of the extractable time duration
* from the supplied ResultSequence
*
* @param arg
* The ResultSequence from which to extract
* @return New ResultSequence consisting of the time duration extracted
* @throws DynamicError
*/
public ResultSequence constructor(ResultSequence arg) throws DynamicError {
if (arg.empty())
return ResultBuffer.EMPTY;
AnyAtomicType aat = (AnyAtomicType) arg.first();
if (aat instanceof NumericType || aat instanceof CalendarType ||
aat instanceof XSBoolean || aat instanceof XSBase64Binary ||
aat instanceof XSHexBinary || aat instanceof XSAnyURI) {
throw DynamicError.invalidType();
}
if (!(isCastable(aat))) {
throw DynamicError.cant_cast(null);
}
XSDuration duration = castDuration(aat);
if (duration == null)
throw DynamicError.cant_cast(null);
return duration;
}
private XSDuration castDuration(AnyAtomicType aat) {
if (aat instanceof XSDuration) {
XSDuration duration = (XSDuration) aat;
return new XSDuration(duration.year(), duration.month(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.negative());
}
return parseDTDuration(aat.getStringValue());
}
/**
* Creates a new XSDayTimeDuration by parsing the supplied String
* represented duration of time
*
* @param str
* String represented duration of time
* @return New XSDayTimeDuration representing the duration of time supplied
*/
public static XSDuration parseDTDuration(String str) {
boolean negative = false;
int years = 0;
int months = 0;
int days = 0;
int hours = 0;
int minutes = 0;
double seconds = 0;
// string following the P
String pstr = "";
String tstr = "";
// get the negative and pstr
if (str.startsWith("-P")) {
negative = true;
pstr = str.substring(2, str.length());
} else if (str.startsWith("P")) {
negative = false;
pstr = str.substring(1, str.length());
} else
return null;
try {
int index = pstr.indexOf('Y');
boolean did_something = false;
if (index != -1) {
String digit = pstr.substring(0, index);
years = Integer.parseInt(digit);
pstr = pstr.substring(index + 1, pstr.length());
did_something = true;
}
index = pstr.indexOf('M');
if (index != -1) {
String digit = pstr.substring(0, index);
months = Integer.parseInt(digit);
pstr = pstr.substring(index + 1, pstr.length());
did_something = true;
}
// get the days
index = pstr.indexOf('D');
if (index == -1) {
if (pstr.startsWith("T")) {
tstr = pstr.substring(1, pstr.length());
}
} else {
String digit = pstr.substring(0, index);
days = Integer.parseInt(digit);
tstr = pstr.substring(index + 1, pstr.length());
if (tstr.startsWith("T")) {
tstr = tstr.substring(1, tstr.length());
} else {
tstr = "";
did_something = true;
}
}
// do the T str
// hour
index = tstr.indexOf('H');
if (index != -1) {
String digit = tstr.substring(0, index);
hours = Integer.parseInt(digit);
tstr = tstr.substring(index + 1, tstr.length());
did_something = true;
}
// minute
index = tstr.indexOf('M');
if (index != -1) {
String digit = tstr.substring(0, index);
minutes = Integer.parseInt(digit);
tstr = tstr.substring(index + 1, tstr.length());
did_something = true;
}
// seconds
index = tstr.indexOf('S');
if (index != -1) {
String digit = tstr.substring(0, index);
seconds = Double.parseDouble(digit);
tstr = tstr.substring(index + 1, tstr.length());
did_something = true;
}
if (!did_something) {
return null;
}
} catch (NumberFormatException err) {
return null;
}
return new XSDuration(years, months, days, hours, minutes, seconds,
negative);
}
public Object clone() throws CloneNotSupportedException {
return new XSDuration(year(), month(), days(), hours(), minutes(),
seconds(), negative());
}
/**
* Retrieves the number of years within the duration of time stored
*
* @return Number of years within the duration of time stored
*/
public int year() {
return _year;
}
/**
* Retrieves the number of months within the duration of time stored
*
* @return Number of months within the duration of time stored
*/
public int month() {
return _month;
}
protected boolean isCastable(AnyAtomicType aat) {
String value = aat.getStringValue(); // get this once so we don't recreate everytime.
String type = aat.string_type();
if (type.equals("xs:string") || type.equals("xs:untypedAtomic")) {
if (isDurationValue(value)) {
return true; // We might be able to cast this.
}
}
// We can cast from ourself or derivations of ourselves.
if (aat instanceof XSDuration) {
return true;
}
return false;
}
private boolean isDurationValue(String value) {
return value.startsWith("P") || value.startsWith("-P");
}
public TypeDefinition getTypeDefinition() {
return BuiltinTypeLibrary.XS_DURATION;
}
public Object getNativeValue() {
return _datatypeFactory.newDuration(! negative(), year(), month(), days(), hours(), minutes(), (int)seconds());
}
}