286N/A/*
286N/A * reserved comment block
286N/A * DO NOT REMOVE OR ALTER!
286N/A */
286N/A/*
286N/A * Copyright 1999-2002,2004,2005 The Apache Software Foundation.
286N/A *
286N/A * Licensed under the Apache License, Version 2.0 (the "License");
286N/A * you may not use this file except in compliance with the License.
286N/A * You may obtain a copy of the License at
286N/A *
286N/A * http://www.apache.org/licenses/LICENSE-2.0
286N/A *
286N/A * Unless required by applicable law or agreed to in writing, software
286N/A * distributed under the License is distributed on an "AS IS" BASIS,
286N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
286N/A * See the License for the specific language governing permissions and
286N/A * limitations under the License.
286N/A */
286N/A
286N/Apackage com.sun.org.apache.xerces.internal.impl.dv.xs;
286N/A
286N/Aimport java.math.BigDecimal;
286N/Aimport java.math.BigInteger;
286N/A
286N/Aimport javax.xml.datatype.DatatypeConstants;
286N/Aimport javax.xml.datatype.Duration;
286N/A
286N/Aimport com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
286N/Aimport com.sun.org.apache.xerces.internal.impl.dv.ValidationContext;
286N/A
286N/A/**
286N/A * Validator for <duration> datatype (W3C Schema Datatypes)
286N/A *
286N/A * @xerces.internal
286N/A *
286N/A * @author Elena Litani
286N/A * @author Gopal Sharma, SUN Microsystem Inc.
286N/A * @version $Id: DurationDV.java,v 1.7 2010-11-01 04:39:47 joehw Exp $
286N/A */
286N/Apublic class DurationDV extends AbstractDateTimeDV {
286N/A
286N/A public static final int DURATION_TYPE = 0;
286N/A public static final int YEARMONTHDURATION_TYPE = 1;
286N/A public static final int DAYTIMEDURATION_TYPE = 2;
286N/A // order-relation on duration is a partial order. The dates below are used to
286N/A // for comparison of 2 durations, based on the fact that
286N/A // duration x and y is x<=y iff s+x<=s+y
286N/A // see 3.2.6 duration W3C schema datatype specs
286N/A //
286N/A // the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}
286N/A private final static DateTimeData[] DATETIMES= {
286N/A new DateTimeData(1696, 9, 1, 0, 0, 0, 'Z', null, true, null),
286N/A new DateTimeData(1697, 2, 1, 0, 0, 0, 'Z', null, true, null),
286N/A new DateTimeData(1903, 3, 1, 0, 0, 0, 'Z', null, true, null),
286N/A new DateTimeData(1903, 7, 1, 0, 0, 0, 'Z', null, true, null)};
286N/A
286N/A public Object getActualValue(String content, ValidationContext context) throws InvalidDatatypeValueException{
286N/A try{
286N/A return parse(content, DURATION_TYPE);
286N/A } catch (Exception ex) {
286N/A throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{content, "duration"});
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Parses, validates and computes normalized version of duration object
286N/A *
286N/A * @param str The lexical representation of duration object PnYn MnDTnH nMnS
286N/A * @param durationType
286N/A * @return normalized date representation
286N/A * @exception SchemaDateTimeException Invalid lexical representation
286N/A */
286N/A protected DateTimeData parse(String str, int durationType) throws SchemaDateTimeException{
286N/A int len = str.length();
286N/A DateTimeData date= new DateTimeData(str, this);
286N/A
286N/A int start = 0;
286N/A char c=str.charAt(start++);
286N/A if ( c!='P' && c!='-' ) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A else {
286N/A date.utc=(c=='-')?'-':0;
286N/A if ( c=='-' && str.charAt(start++)!='P' ) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A }
286N/A
286N/A int negate = 1;
286N/A //negative duration
286N/A if ( date.utc=='-' ) {
286N/A negate = -1;
286N/A
286N/A }
286N/A //at least one number and designator must be seen after P
286N/A boolean designator = false;
286N/A
286N/A int endDate = indexOf (str, start, len, 'T');
286N/A if ( endDate == -1 ) {
286N/A endDate = len;
286N/A }
286N/A else if (durationType == YEARMONTHDURATION_TYPE) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A
286N/A //find 'Y'
286N/A int end = indexOf (str, start, endDate, 'Y');
286N/A if ( end!=-1 ) {
286N/A
286N/A if (durationType == DAYTIMEDURATION_TYPE) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A
286N/A //scan year
286N/A date.year=negate * parseInt(str,start,end);
286N/A start = end+1;
286N/A designator = true;
286N/A }
286N/A
286N/A end = indexOf (str, start, endDate, 'M');
286N/A if ( end!=-1 ) {
286N/A
286N/A if (durationType == DAYTIMEDURATION_TYPE) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A
286N/A //scan month
286N/A date.month=negate * parseInt(str,start,end);
286N/A start = end+1;
286N/A designator = true;
286N/A }
286N/A
286N/A end = indexOf (str, start, endDate, 'D');
286N/A if ( end!=-1 ) {
286N/A
286N/A if(durationType == YEARMONTHDURATION_TYPE) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A
286N/A //scan day
286N/A date.day=negate * parseInt(str,start,end);
286N/A start = end+1;
286N/A designator = true;
286N/A }
286N/A
286N/A if ( len == endDate && start!=len ) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A if ( len !=endDate ) {
286N/A
286N/A //scan hours, minutes, seconds
286N/A //REVISIT: can any item include a decimal fraction or only seconds?
286N/A //
286N/A
286N/A end = indexOf (str, ++start, len, 'H');
286N/A if ( end!=-1 ) {
286N/A //scan hours
286N/A date.hour=negate * parseInt(str,start,end);
286N/A start=end+1;
286N/A designator = true;
286N/A }
286N/A
286N/A end = indexOf (str, start, len, 'M');
286N/A if ( end!=-1 ) {
286N/A //scan min
286N/A date.minute=negate * parseInt(str,start,end);
286N/A start=end+1;
286N/A designator = true;
286N/A }
286N/A
286N/A end = indexOf (str, start, len, 'S');
286N/A if ( end!=-1 ) {
286N/A //scan seconds
286N/A date.second = negate * parseSecond(str, start, end);
286N/A start=end+1;
286N/A designator = true;
286N/A }
286N/A // no additional data shouls appear after last item
286N/A // P1Y1M1DT is illigal value as well
286N/A if ( start != len || str.charAt(--start)=='T' ) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A }
286N/A
286N/A if ( !designator ) {
286N/A throw new SchemaDateTimeException();
286N/A }
286N/A
286N/A return date;
286N/A }
286N/A
286N/A /**
286N/A * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
286N/A *
286N/A * @param date1 Unnormalized duration
286N/A * @param date2 Unnormalized duration
286N/A * @param strict (min/max)Exclusive strict == true ( LESS_THAN ) or ( GREATER_THAN )
286N/A * (min/max)Inclusive strict == false (LESS_EQUAL) or (GREATER_EQUAL)
286N/A * @return INDETERMINATE if the order relationship between date1 and date2 is indeterminate.
286N/A * EQUAL if the order relation between date1 and date2 is EQUAL.
286N/A * If the strict parameter is true, return LESS_THAN if date1 is less than date2 and
286N/A * return GREATER_THAN if date1 is greater than date2.
286N/A * If the strict parameter is false, return LESS_THAN if date1 is less than OR equal to date2 and
286N/A * return GREATER_THAN if date1 is greater than OR equal to date2
286N/A */
286N/A protected short compareDates(DateTimeData date1, DateTimeData date2, boolean strict) {
286N/A
286N/A //REVISIT: this is unoptimazed vs of comparing 2 durations
286N/A // Algorithm is described in 3.2.6.2 W3C Schema Datatype specs
286N/A //
286N/A
286N/A //add constA to both durations
286N/A short resultA, resultB= INDETERMINATE;
286N/A //try and see if the objects are equal
286N/A resultA = compareOrder (date1, date2);
286N/A if ( resultA == 0 ) {
286N/A return 0;
286N/A }
286N/A
286N/A DateTimeData[] result = new DateTimeData[2];
286N/A result[0] = new DateTimeData(null, this);
286N/A result[1] = new DateTimeData(null, this);
286N/A
286N/A //long comparison algorithm is required
286N/A DateTimeData tempA = addDuration (date1, DATETIMES[0], result[0]);
286N/A DateTimeData tempB = addDuration (date2, DATETIMES[0], result[1]);
286N/A resultA = compareOrder(tempA, tempB);
286N/A if ( resultA == INDETERMINATE ) {
286N/A return INDETERMINATE;
286N/A }
286N/A
286N/A tempA = addDuration(date1, DATETIMES[1], result[0]);
286N/A tempB = addDuration(date2, DATETIMES[1], result[1]);
286N/A resultB = compareOrder(tempA, tempB);
286N/A resultA = compareResults(resultA, resultB, strict);
286N/A if (resultA == INDETERMINATE) {
286N/A return INDETERMINATE;
286N/A }
286N/A
286N/A tempA = addDuration(date1, DATETIMES[2], result[0]);
286N/A tempB = addDuration(date2, DATETIMES[2], result[1]);
286N/A resultB = compareOrder(tempA, tempB);
286N/A resultA = compareResults(resultA, resultB, strict);
286N/A if (resultA == INDETERMINATE) {
286N/A return INDETERMINATE;
286N/A }
286N/A
286N/A tempA = addDuration(date1, DATETIMES[3], result[0]);
286N/A tempB = addDuration(date2, DATETIMES[3], result[1]);
286N/A resultB = compareOrder(tempA, tempB);
286N/A resultA = compareResults(resultA, resultB, strict);
286N/A
286N/A return resultA;
286N/A }
286N/A
286N/A private short compareResults(short resultA, short resultB, boolean strict){
286N/A
286N/A if ( resultB == INDETERMINATE ) {
286N/A return INDETERMINATE;
286N/A }
286N/A else if ( resultA!=resultB && strict ) {
286N/A return INDETERMINATE;
286N/A }
286N/A else if ( resultA!=resultB && !strict ) {
286N/A if ( resultA!=0 && resultB!=0 ) {
286N/A return INDETERMINATE;
286N/A }
286N/A else {
286N/A return (resultA!=0)?resultA:resultB;
286N/A }
286N/A }
286N/A return resultA;
286N/A }
286N/A
286N/A private DateTimeData addDuration(DateTimeData date, DateTimeData addto, DateTimeData duration) {
286N/A
286N/A //REVISIT: some code could be shared between normalize() and this method,
286N/A // however is it worth moving it? The structures are different...
286N/A //
286N/A
286N/A resetDateObj(duration);
286N/A //add months (may be modified additionaly below)
286N/A int temp = addto.month + date.month;
286N/A duration.month = modulo (temp, 1, 13);
286N/A int carry = fQuotient (temp, 1, 13);
286N/A
286N/A //add years (may be modified additionaly below)
286N/A duration.year=addto.year + date.year + carry;
286N/A
286N/A //add seconds
286N/A double dtemp = addto.second + date.second;
286N/A carry = (int)Math.floor(dtemp/60);
286N/A duration.second = dtemp - carry*60;
286N/A
286N/A //add minutes
286N/A temp = addto.minute +date.minute + carry;
286N/A carry = fQuotient (temp, 60);
286N/A duration.minute= mod(temp, 60, carry);
286N/A
286N/A //add hours
286N/A temp = addto.hour + date.hour + carry;
286N/A carry = fQuotient(temp, 24);
286N/A duration.hour = mod(temp, 24, carry);
286N/A
286N/A
286N/A duration.day=addto.day + date.day + carry;
286N/A
286N/A while ( true ) {
286N/A
286N/A temp=maxDayInMonthFor(duration.year, duration.month);
286N/A if ( duration.day < 1 ) { //original duration was negative
286N/A duration.day = duration.day + maxDayInMonthFor(duration.year, duration.month-1);
286N/A carry=-1;
286N/A }
286N/A else if ( duration.day > temp ) {
286N/A duration.day = duration.day - temp;
286N/A carry=1;
286N/A }
286N/A else {
286N/A break;
286N/A }
286N/A temp = duration.month+carry;
286N/A duration.month = modulo(temp, 1, 13);
286N/A duration.year = duration.year+fQuotient(temp, 1, 13);
286N/A }
286N/A
286N/A duration.utc='Z';
286N/A return duration;
286N/A }
286N/A
286N/A protected double parseSecond(String buffer, int start, int end)
286N/A throws NumberFormatException {
286N/A int dot = -1;
286N/A for (int i = start; i < end; i++) {
286N/A char ch = buffer.charAt(i);
286N/A if (ch == '.')
286N/A dot = i;
286N/A else if (ch > '9' || ch < '0')
286N/A throw new NumberFormatException("'" + buffer + "' has wrong format");
286N/A }
286N/A if (dot+1 == end) {
286N/A throw new NumberFormatException("'" + buffer + "' has wrong format");
286N/A }
286N/A double value = Double.parseDouble(buffer.substring(start, end));
286N/A if (value == Double.POSITIVE_INFINITY) {
286N/A throw new NumberFormatException("'" + buffer + "' has wrong format");
286N/A }
286N/A return value;
286N/A }
286N/A
286N/A protected String dateToString(DateTimeData date) {
286N/A StringBuffer message = new StringBuffer(30);
286N/A if ( date.year<0 || date.month<0 || date.day<0
286N/A || date.hour<0 || date.minute<0 || date.second<0) {
286N/A message.append('-');
286N/A }
286N/A message.append('P');
286N/A message.append((date.year < 0?-1:1) * date.year);
286N/A message.append('Y');
286N/A message.append((date.month < 0?-1:1) * date.month);
286N/A message.append('M');
286N/A message.append((date.day < 0?-1:1) * date.day);
286N/A message.append('D');
286N/A message.append('T');
286N/A message.append((date.hour < 0?-1:1) * date.hour);
286N/A message.append('H');
286N/A message.append((date.minute < 0?-1:1) * date.minute);
286N/A message.append('M');
286N/A append2(message, (date.second < 0?-1:1) * date.second);
286N/A message.append('S');
286N/A
286N/A return message.toString();
286N/A }
286N/A
286N/A protected Duration getDuration(DateTimeData date) {
286N/A int sign = 1;
286N/A if ( date.year<0 || date.month<0 || date.day<0
286N/A || date.hour<0 || date.minute<0 || date.second<0) {
286N/A sign = -1;
286N/A }
286N/A return datatypeFactory.newDuration(sign == 1,
286N/A date.year != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.year):null,
286N/A date.month != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.month):null,
286N/A date.day != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.day):null,
286N/A date.hour != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.hour):null,
286N/A date.minute != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.minute):null,
286N/A date.second != DatatypeConstants.FIELD_UNDEFINED?new BigDecimal(String.valueOf(sign*date.second)):null);
286N/A }
286N/A}