blob: 67cbcfd4770d90a69f9ccafe03bf44dce0434a61 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999, 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xerces.impl.dv.xs_new;
import org.apache.xerces.impl.dv.InvalidDatatypeValueException;
import org.apache.xerces.impl.validation.ValidationContext;
/**
* Validator for <duration> datatype (W3C Schema Datatypes)
*
* @author Elena Litani
* @author Gopal Sharma, SUN Microsystem Inc.
* @version $Id$
*/
public class DurationDV extends AbstractDateTimeDV {
// order-relation on duration is a partial order. The dates below are used to
// for comparison of 2 durations, based on the fact that
// duration x and y is x<=y iff s+x<=s+y
// see 3.2.6 duration W3C schema datatype specs
//
// the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}
private final static int[][] DATETIMES= {
{1696, 9, 1, 0, 0, 0, 0, 'Z'},
{1697, 2, 1, 0, 0, 0, 0, 'Z'},
{1903, 3, 1, 0, 0, 0, 0, 'Z'},
{1903, 7, 1, 0, 0, 0, 0, 'Z'}};
private int[][] fDuration = null;
public Object getActualValue(String content, ValidationContext context) throws InvalidDatatypeValueException{
try{
return parse(content, null);
} catch (Exception ex) {
throw new InvalidDatatypeValueException("not a valid duration");
}
}
/**
* Parses, validates and computes normalized version of duration object
*
* @param str The lexical representation of duration object PnYn MnDTnH nMnS
* @param date uninitialized date object
* @return normalized date representation
* @exception Exception Invalid lexical representation
*/
protected int[] parse(String str, int[] date) throws SchemaDateTimeException{
//PnYn MnDTnH nMnS: -P1Y2M3DT10H30M
resetBuffer(str);
//create structure to hold an object
if ( date== null ) {
date=new int[TOTAL_SIZE];
}
resetDateObj(date);
char c=fBuffer.charAt(fStart++);
if ( c!='P' && c!='-' ) {
throw new SchemaDateTimeException();
}
else {
date[utc]=(c=='-')?'-':0;
if ( c=='-' && fBuffer.charAt(fStart++)!='P' ) {
throw new SchemaDateTimeException();
}
}
int negate = 1;
//negative duration
if ( date[utc]=='-' ) {
negate = -1;
}
//at least one number and designator must be seen after P
boolean designator = false;
int endDate = indexOf (fStart, fEnd, 'T');
if ( endDate == -1 ) {
endDate = fEnd;
}
//find 'Y'
int end = indexOf (fStart, endDate, 'Y');
if ( end!=-1 ) {
//scan year
date[CY]=negate * parseInt(fStart,end);
fStart = end+1;
designator = true;
}
end = indexOf (fStart, endDate, 'M');
if ( end!=-1 ) {
//scan month
date[M]=negate * parseInt(fStart,end);
fStart = end+1;
designator = true;
}
end = indexOf (fStart, endDate, 'D');
if ( end!=-1 ) {
//scan day
date[D]=negate * parseInt(fStart,end);
fStart = end+1;
designator = true;
}
if ( fEnd == endDate && fStart!=fEnd ) {
throw new SchemaDateTimeException();
}
if ( fEnd !=endDate ) {
//scan hours, minutes, seconds
//REVISIT: can any item include a decimal fraction or only seconds?
//
end = indexOf (++fStart, fEnd, 'H');
if ( end!=-1 ) {
//scan hours
date[h]=negate * parseInt(fStart,end);
fStart=end+1;
designator = true;
}
end = indexOf (fStart, fEnd, 'M');
if ( end!=-1 ) {
//scan min
date[m]=negate * parseInt(fStart,end);
fStart=end+1;
designator = true;
}
end = indexOf (fStart, fEnd, 'S');
if ( end!=-1 ) {
//scan seconds
int mlsec = indexOf (fStart, end, '.');
if ( mlsec >0 ) {
date[s] = negate * parseInt (fStart, mlsec);
date[ms] = negate * parseInt (mlsec+1, end);
}
else {
date[s]=negate * parseInt(fStart,end);
}
fStart=end+1;
designator = true;
}
// no additional data shouls appear after last item
// P1Y1M1DT is illigal value as well
if ( fStart != fEnd || fBuffer.charAt(--fStart)=='T' ) {
throw new SchemaDateTimeException();
}
}
if ( !designator ) {
throw new SchemaDateTimeException();
}
return date;
}
/**
* Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
*
* @param date1 Unnormalized duration
* @param date2 Unnormalized duration
* @param strict (min/max)Exclusive strict == true ( LESS_THAN ) or ( GREATER_THAN )
* (min/max)Inclusive strict == false (LESS_EQUAL) or (GREATER_EQUAL)
* @return
*/
protected short compareDates(int[] date1, int[] date2, boolean strict) {
//REVISIT: this is unoptimazed vs of comparing 2 durations
// Algorithm is described in 3.2.6.2 W3C Schema Datatype specs
//
//add constA to both durations
short resultA, resultB= INDETERMINATE;
//try and see if the objects are equal
resultA = compareOrder (date1, date2);
if ( resultA == 0 ) {
return 0;
}
if ( fDuration == null ) {
fDuration = new int[2][TOTAL_SIZE];
}
//long comparison algorithm is required
int[] tempA = addDuration (date1, 0, fDuration[0]);
int[] tempB = addDuration (date2, 0, fDuration[1]);
resultA = compareOrder(tempA, tempB);
if ( resultA == INDETERMINATE ) {
return INDETERMINATE;
}
tempA = addDuration(date1, 1, fDuration[0]);
tempB = addDuration(date2, 1, fDuration[1]);
resultB = compareOrder(tempA, tempB);
resultA = compareResults(resultA, resultB, strict);
if (resultA == INDETERMINATE) {
return INDETERMINATE;
}
tempA = addDuration(date1, 2, fDuration[0]);
tempB = addDuration(date2, 2, fDuration[1]);
resultB = compareOrder(tempA, tempB);
resultA = compareResults(resultA, resultB, strict);
if (resultA == INDETERMINATE) {
return INDETERMINATE;
}
tempA = addDuration(date1, 3, fDuration[0]);
tempB = addDuration(date2, 3, fDuration[1]);
resultB = compareOrder(tempA, tempB);
resultA = compareResults(resultA, resultB, strict);
return resultA;
}
private short compareResults(short resultA, short resultB, boolean strict){
if ( resultB == INDETERMINATE ) {
return INDETERMINATE;
}
else if ( resultA!=resultB && strict ) {
return INDETERMINATE;
}
else if ( resultA!=resultB && !strict ) {
if ( resultA!=0 && resultB!=0 ) {
return INDETERMINATE;
}
else {
return (resultA!=0)?resultA:resultB;
}
}
return resultA;
}
private int[] addDuration(int[] date, int index, int[] duration) {
//REVISIT: some code could be shared between normalize() and this method,
// however is it worth moving it? The structures are different...
//
resetDateObj(duration);
//add months (may be modified additionaly below)
int temp = DATETIMES[index][M] + date[M];
duration[M] = modulo (temp, 1, 13);
int carry = fQuotient (temp, 1, 13);
//add years (may be modified additionaly below)
duration[CY]=DATETIMES[index][CY] + date[CY] + carry;
//add seconds
temp = DATETIMES[index][s] + date[s];
carry = fQuotient (temp, 60);
duration[s] = mod(temp, 60, carry);
//add minutes
temp = DATETIMES[index][m] +date[m] + carry;
carry = fQuotient (temp, 60);
duration[m]= mod(temp, 60, carry);
//add hours
temp = DATETIMES[index][h] + date[h] + carry;
carry = fQuotient(temp, 24);
duration[h] = mod(temp, 24, carry);
duration[D]=DATETIMES[index][D] + date[D] + carry;
while ( true ) {
temp=maxDayInMonthFor(duration[CY], duration[M]);
if ( duration[D] < 1 ) { //original duration was negative
duration[D] = duration[D] + maxDayInMonthFor(duration[CY], duration[M]-1);
carry=-1;
}
else if ( duration[D] > temp ) {
duration[D] = duration[D] - temp;
carry=1;
}
else {
break;
}
temp = duration[M]+carry;
duration[M] = modulo(temp, 1, 13);
duration[CY] = duration[CY]+fQuotient(temp, 1, 13);
}
duration[utc]='Z';
return duration;
}
protected String dateToString(int[] date) {
message.setLength(0);
int negate = 1;
if ( date[CY]<0 ) {
message.append('-');
negate=-1;
}
message.append('P');
message.append(negate * date[CY]);
message.append('Y');
message.append(negate * date[M]);
message.append('M');
message.append(negate * date[D]);
message.append('D');
message.append('T');
message.append(negate * date[h]);
message.append('H');
message.append(negate * date[m]);
message.append('M');
message.append(negate * date[s]);
message.append('.');
message.append(negate * date[ms]);
message.append('S');
return message.toString();
}
}