| /******************************************************************************* |
| * 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.processor.DynamicContext; |
| import org.eclipse.wst.xml.xpath2.processor.DynamicError; |
| import org.eclipse.wst.xml.xpath2.processor.ResultSequence; |
| import org.eclipse.wst.xml.xpath2.processor.ResultSequenceFactory; |
| 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; |
| |
| /** |
| * 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 string_value() { |
| 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 context) 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 { |
| ResultSequence rs = ResultSequenceFactory.create_new(); |
| |
| if (arg.empty()) |
| return rs; |
| |
| 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); |
| |
| rs.add(duration); |
| |
| return rs; |
| } |
| |
| 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.string_value()); |
| } |
| /** |
| * 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.string_value(); // 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"); |
| } |
| |
| } |