FileDocCategorySizeDatePackage
AbstractDateTimeDV.javaAPI DocApache Xerces 3.0.132059Fri Sep 14 20:33:54 BST 2007org.apache.xerces.impl.dv.xs

AbstractDateTimeDV

public abstract class AbstractDateTimeDV extends TypeValidator
This is the base class of all date/time datatype validators. It implements common code for parsing, validating and comparing datatypes. Classes that extend this class, must implement parse() method. REVISIT: There are many instance variables, which would cause problems when we support grammar caching. A grammar is possibly used by two parser instances at the same time, then the same simple type decl object can be used to validate two strings at the same time. -SG
xerces.internal
author
Elena Litani
author
Len Berman
author
Gopal Sharma, SUN Microsystems Inc.
version
$Id: AbstractDateTimeDV.java 572095 2007-09-02 18:32:43Z mrglavas $

Fields Summary
private static final boolean
DEBUG
protected static final int
YEAR
protected static final int
MONTH
protected static final int
DAY
protected final DatatypeFactory
factory
Constructors Summary
Methods Summary
protected final voidappend(java.lang.StringBuffer message, int value, int nch)

        if (value == Integer.MIN_VALUE) {
            message.append(value);
            return;
        }
		if (value < 0) {
			message.append('-");
			value = -value;
		}
		if (nch == 4) {
			if (value < 10)
				message.append("000");
			else if (value < 100)
				message.append("00");
			else if (value < 1000)
				message.append("0");
			message.append(value);
		}
		else if (nch == 2) {
			if (value < 10)
				message.append('0");
			message.append(value);
		}
		else {
			if (value != 0)
				message.append((char)value);
		}
	
protected final voidappend(java.lang.StringBuffer message, double value)

	    if (value < 0) {
	        message.append('-");
	        value = -value;
	    }
	    if (value < 10) {
	        message.append('0");
	    }
	    append2(message, value);
	
protected final voidappend2(java.lang.StringBuffer message, double value)

        final int intValue = (int) value;
        if (value == intValue) {
            message.append(intValue);
        }
        else {
            append3(message, value);
        }
    
private voidappend3(java.lang.StringBuffer message, double value)

        String d = String.valueOf(value);
        int eIndex = d.indexOf('E");
        if (eIndex == -1) {
            message.append(d);
            return;
        }
        int exp;
        if (value < 1) {
            // Need to convert from scientific notation of the form 
            // n.nnn...E-N (N >= 4) to a normal decimal value.
            try {
                exp = parseInt(d, eIndex+2, d.length());
            }
            // This should never happen. 
            // It's only possible if String.valueOf(double) is broken.
            catch (Exception e) {
                message.append(d);
                return;
            }
            message.append("0.");
            for (int i = 1; i < exp; ++i) {
                message.append('0");
            }
            // Remove trailing zeros.
            int end = eIndex - 1;
            while (end > 0) {
                char c = d.charAt(end);
                if (c != '0") {
                    break;
                }
                --end;
            }
            // Now append the digits to the end. Skip over the decimal point.
            for (int i = 0; i <= end; ++i) {
                char c = d.charAt(i);
                if (c != '.") {
                    message.append(c);
                }
            }
        }
        else {
            // Need to convert from scientific notation of the form 
            // n.nnn...EN (N >= 7) to a normal decimal value.
            try {
                exp = parseInt(d, eIndex+1, d.length());
            }
            // This should never happen. 
            // It's only possible if String.valueOf(double) is broken.
            catch (Exception e) {
                message.append(d);
                return;
            }
            final int integerEnd = exp + 2;
            for (int i = 0; i < eIndex; ++i) {
                char c = d.charAt(i);
                if (c != '.") {
                    if (i == integerEnd) {
                        message.append('.");
                    }
                    message.append(c);
                }
            }
            // Append trailing zeroes if necessary.
            for (int i = integerEnd - eIndex; i > 0; --i) {
                message.append('0");
            }
        }
    
private voidcloneDate(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData finalValue, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData tempDate)

		tempDate.year = finalValue.year;
		tempDate.month = finalValue.month;
		tempDate.day = finalValue.day;
		tempDate.hour = finalValue.hour;
		tempDate.minute = finalValue.minute;
		tempDate.second = finalValue.second;
		tempDate.utc = finalValue.utc;
		tempDate.timezoneHr = finalValue.timezoneHr;
		tempDate.timezoneMin = finalValue.timezoneMin;
	
public intcompare(java.lang.Object value1, java.lang.Object value2)

		return compareDates(((DateTimeData)value1),
				((DateTimeData)value2), true);
	
protected shortcompareDates(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date1, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date2, boolean strict)
Compare algorithm described in dateDime (3.2.7). Duration datatype overwrites this method

param
date1 normalized date representation of the first value
param
date2 normalized date representation of the second value
param
strict
return
less, greater, less_equal, greater_equal, equal

		if (date1.utc == date2.utc) {
			return compareOrder(date1, date2);
		}
		short c1, c2;
		
		DateTimeData tempDate = new DateTimeData(null, this);
		
		if ( date1.utc=='Z" ) {
			
			//compare date1<=date1<=(date2 with time zone -14)
			//
			cloneDate(date2, tempDate); //clones date1 value to global temporary storage: fTempDate
			tempDate.timezoneHr=14;
			tempDate.timezoneMin = 0;
			tempDate.utc='+";
			normalize(tempDate);
			c1 = compareOrder(date1, tempDate);
			if (c1 == LESS_THAN)
				return c1;
			
			//compare date1>=(date2 with time zone +14)
			//
			cloneDate(date2, tempDate); //clones date1 value to global temporary storage: tempDate
			tempDate.timezoneHr = -14;
			tempDate.timezoneMin = 0;
			tempDate.utc='-";
			normalize(tempDate);
			c2 = compareOrder(date1, tempDate);
			if (c2 == GREATER_THAN)
				return c2;
			
			return INDETERMINATE;
		}
		else if ( date2.utc=='Z" ) {
			
			//compare (date1 with time zone -14)<=date2
			//
			cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate
			tempDate.timezoneHr = -14;
			tempDate.timezoneMin = 0;
			tempDate.utc='-";
			if (DEBUG) {
				System.out.println("tempDate=" + dateToString(tempDate));
			}
			normalize(tempDate);
			c1 = compareOrder(tempDate, date2);
			if (DEBUG) {
				System.out.println("date=" + dateToString(date2));
				System.out.println("tempDate=" + dateToString(tempDate));
			}
			if (c1 == LESS_THAN)
				return c1;
			
			//compare (date1 with time zone +14)<=date2
			//
			cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate
			tempDate.timezoneHr = 14;
			tempDate.timezoneMin = 0;
			tempDate.utc='+";
			normalize(tempDate);
			c2 = compareOrder(tempDate, date2);
			if (DEBUG) {
				System.out.println("tempDate=" + dateToString(tempDate));
			}
			if (c2 == GREATER_THAN)
				return c2;
			
			return INDETERMINATE;
		}
		return INDETERMINATE;
		
	
protected shortcompareOrder(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date1, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date2)
Given normalized values, determines order-relation between give date/time objects.

param
date1 date/time object
param
date2 date/time object
return
0 if date1 and date2 are equal, a value less than 0 if date1 is less than date2, a value greater than 0 if date1 is greater than date2

		if(date1.position < 1) {
			if (date1.year < date2.year)
				return -1;
			if (date1.year > date2.year)
				return 1;
		}
		if(date1.position < 2) {
			if (date1.month < date2.month)
				return -1;
			if (date1.month > date2.month)
				return 1;
		}
		if (date1.day < date2.day)
			return -1;
		if (date1.day > date2.day)
			return 1;
		if (date1.hour < date2.hour)
			return -1;
		if (date1.hour > date2.hour)
			return 1;
		if (date1.minute < date2.minute)
			return -1;
		if (date1.minute > date2.minute)
			return 1;
		if (date1.second < date2.second)
			return -1;
		if (date1.second > date2.second)
			return 1;
		if (date1.utc < date2.utc)
			return -1;
		if (date1.utc > date2.utc)
			return 1;
		return 0;
	
protected java.lang.StringdateToString(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date)

		StringBuffer message = new StringBuffer(25);
		append(message, date.year, 4);
		message.append('-");
		append(message, date.month, 2);
		message.append('-");
		append(message, date.day, 2);
		message.append('T");
		append(message, date.hour, 2);
		message.append(':");
		append(message, date.minute, 2);
		message.append(':");
		append(message, date.second);
		append(message, (char)date.utc, 0);
		return message.toString();
	
protected intfQuotient(int a, int b)

		
		//fQuotient(a, b) = the greatest integer less than or equal to a/b
		return (int)Math.floor((float)a/b);
	
protected intfQuotient(int temp, int low, int high)

		//fQuotient(a - low, high - low)
		
		return fQuotient(temp - low, high - low);
	
protected intfindUTCSign(java.lang.String buffer, int start, int end)
Return index of UTC char: 'Z', '+', '-'

param
start
param
end
return
index of the UTC character that was found

		int c;
		for ( int i=start;i<end;i++ ) {
			c=buffer.charAt(i);
			if ( c == 'Z" || c=='+" || c=='-" ) {
				return i;
			}
			
		}
		return -1;
	
public shortgetAllowedFacets()

	
	  
		return ( XSSimpleTypeDecl.FACET_PATTERN | XSSimpleTypeDecl.FACET_WHITESPACE | XSSimpleTypeDecl.FACET_ENUMERATION |XSSimpleTypeDecl.FACET_MAXINCLUSIVE |XSSimpleTypeDecl.FACET_MININCLUSIVE | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE  | XSSimpleTypeDecl.FACET_MINEXCLUSIVE  );
	
protected intgetDate(java.lang.String buffer, int start, int end, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date)
Parses date CCYY-MM-DD

param
buffer
param
start start position
param
end end position
param
date
exception
RuntimeException

		
		start = getYearMonth(buffer, start, end, date);
		
		if (buffer.charAt(start++) !='-") {
			throw new RuntimeException("CCYY-MM must be followed by '-' sign");
		}
		int stop = start + 2;
		date.day=parseInt(buffer, start, stop);
		return stop;
	
protected javax.xml.datatype.DurationgetDuration(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData data)

        return null;
    
protected voidgetTime(java.lang.String buffer, int start, int end, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData data)
Parses time hh:mm:ss.sss and time zone if any

param
start
param
end
param
data
exception
RuntimeException

		
		int stop = start+2;
		
		//get hours (hh)
		data.hour=parseInt(buffer, start,stop);
		
		//get minutes (mm)
		
		if (buffer.charAt(stop++)!=':") {
			throw new RuntimeException("Error in parsing time zone" );
		}
		start = stop;
		stop = stop+2;
		data.minute=parseInt(buffer, start,stop);
		
		//get seconds (ss)
		if (buffer.charAt(stop++)!=':") {
			throw new RuntimeException("Error in parsing time zone" );
		}
		
		//find UTC sign if any
		int sign = findUTCSign(buffer, start, end);
		
		//get seconds (ms)
		start = stop;
		stop = sign < 0 ? end : sign;
		data.second = parseSecond(buffer, start, stop);
		
		//parse UTC time zone (hh:mm)
		if (sign > 0) {
			getTimeZone(buffer, data, sign, end);
		}
	
protected voidgetTimeZone(java.lang.String buffer, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData data, int sign, int end)
Parses time zone: 'Z' or {+,-} followed by hh:mm

param
data
param
sign
exception
RuntimeException

		data.utc=buffer.charAt(sign);
		
		if ( buffer.charAt(sign) == 'Z" ) {
			if (end>(++sign)) {
				throw new RuntimeException("Error in parsing time zone");
			}
			return;
		}
		if ( sign<=(end-6) ) {
			
			int negate = buffer.charAt(sign) == '-"?-1:1;
			//parse hr
			int stop = ++sign+2;
			data.timezoneHr = negate*parseInt(buffer, sign, stop);
			if (buffer.charAt(stop++)!=':") {
				throw new RuntimeException("Error in parsing time zone" );
			}
			
			//parse min
			data.timezoneMin = negate*parseInt(buffer, stop, stop+2);
			
			if ( stop+2!=end ) {
				throw new RuntimeException("Error in parsing time zone");
			}
            if(data.timezoneHr != 0 || data.timezoneMin != 0)
                data.normalized = false;
		}
		else {
			throw new RuntimeException("Error in parsing time zone");
		}
		if ( DEBUG ) {
			System.out.println("time[hh]="+data.timezoneHr + " time[mm]=" +data.timezoneMin);
		}
	
protected javax.xml.datatype.XMLGregorianCalendargetXMLGregorianCalendar(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData data)

        return null;
    
protected intgetYearMonth(java.lang.String buffer, int start, int end, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date)
Parses date CCYY-MM

param
buffer
param
start start position
param
end end position
param
date
exception
RuntimeException

		
		if ( buffer.charAt(0)=='-" ) {
			// REVISIT: date starts with preceding '-' sign
			//          do we have to do anything with it?
			//
			start++;
		}
		int i = indexOf(buffer, start, end, '-");
		if ( i==-1 ) throw new RuntimeException("Year separator is missing or misplaced");
		int length = i-start;
		if (length<4) {
			throw new RuntimeException("Year must have 'CCYY' format");
		}
		else if (length > 4 && buffer.charAt(start)=='0"){
			throw new RuntimeException("Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden");
		}
		date.year= parseIntYear(buffer, i);
		if (buffer.charAt(i)!='-") {
			throw new RuntimeException("CCYY must be followed by '-' sign");
		}
		start = ++i;
		i = start +2;
		date.month=parseInt(buffer, start, i);
		return i; //fStart points right after the MONTH
	
protected intindexOf(java.lang.String buffer, int start, int end, char ch)
Computes index of given char within StringBuffer

param
start
param
end
param
ch character to look for in StringBuffer
return
index of ch within StringBuffer

		for ( int i=start;i<end;i++ ) {
			if ( buffer.charAt(i) == ch ) {
				return i;
			}
		}
		return -1;
	
public booleanisIdentical(java.lang.Object value1, java.lang.Object value2)

		if (!(value1 instanceof DateTimeData) || !(value2 instanceof DateTimeData)) {
			return false;
		}
		
		DateTimeData v1 = (DateTimeData)value1;
		DateTimeData v2 = (DateTimeData)value2;
		
		// original timezones must be the same in addition to date/time values
		// being 'equal'
		if ((v1.timezoneHr == v2.timezoneHr) && (v1.timezoneMin == v2.timezoneMin)) {
			return v1.equals(v2);
		}
		
		return false;
	
private booleanisLeapYear(int year)

		
		//REVISIT: should we take care about Julian calendar?
		return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));
	
protected final booleanisNextCharUTCSign(java.lang.String buffer, int start, int end)
Returns true if the character at start is 'Z', '+' or '-'.

        if (start < end) {
            char c = buffer.charAt(start);
            return (c == 'Z" || c == '+" || c == '-");
        }
        return false;
    
protected intmaxDayInMonthFor(int year, int month)
Given {year,month} computes maximum number of days for given month

param
year
param
month
return
integer containg the number of days in a given month

		//validate days
		if ( month==4 || month==6 || month==9 || month==11 ) {
			return 30;
		}
		else if ( month==2 ) {
			if ( isLeapYear(year) ) {
				return 29;
			}
			else {
				return 28;
			}
		}
		else {
			return 31;
		}
	
protected intmod(int a, int b, int quotient)

		//modulo(a, b) = a - fQuotient(a,b)*b
		return (a - quotient*b) ;
	
protected intmodulo(int temp, int low, int high)

		//modulo(a - low, high - low) + low
		int a = temp - low;
		int b = high - low;
		return (mod (a, b, fQuotient(a, b)) + low) ;
	
protected voidnormalize(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date)
If timezone present - normalize dateTime [E Adding durations to dateTimes]

param
date CCYY-MM-DDThh:mm:ss+03

		
		// REVISIT: we have common code in addDuration() for durations
		//          should consider reorganizing it.
		//
		
		//add minutes (from time zone)
		int negate = -1;
		
		if ( DEBUG ) {
			System.out.println("==>date.minute"+date.minute);
			System.out.println("==>date.timezoneMin" +date.timezoneMin);
		}
		int temp = date.minute + negate * date.timezoneMin;
		int carry = fQuotient (temp, 60);
		date.minute= mod(temp, 60, carry);
		
		if ( DEBUG ) {
			System.out.println("==>carry: " + carry);
		}
		//add hours
		temp = date.hour + negate * date.timezoneHr + carry;
		carry = fQuotient(temp, 24);
		date.hour=mod(temp, 24, carry);
		if ( DEBUG ) {
			System.out.println("==>date.hour"+date.hour);
			System.out.println("==>carry: " + carry);
		}
		
		date.day=date.day+carry;
		
		while ( true ) {
			temp=maxDayInMonthFor(date.year, date.month);
			if (date.day<1) {
				date.day = date.day + maxDayInMonthFor(date.year, date.month-1);
				carry=-1;
			}
			else if ( date.day>temp ) {
				date.day=date.day-temp;
				carry=1;
			}
			else {
				break;
			}
			temp=date.month+carry;
			date.month=modulo(temp, 1, 13);
			date.year=date.year+fQuotient(temp, 1, 13);
            if(date.year == 0 && !Constants.SCHEMA_1_1_SUPPORT) {
                date.year = (date.timezoneHr < 0 || date.timezoneMin < 0)?1:-1;
            }
		}
		date.utc='Z";
	
protected intparseInt(java.lang.String buffer, int start, int end)
Given start and end position, parses string value

param
buffer string to parse
param
start start position
param
end end position
return
return integer representation of characters

		//REVISIT: more testing on this parsing needs to be done.
		int radix=10;
		int result = 0;
		int digit=0;
		int limit = -Integer.MAX_VALUE;
		int multmin = limit / radix;
		int i = start;
		do {
			digit = getDigit(buffer.charAt(i));
			if ( digit < 0 ) throw new NumberFormatException("'" + buffer + "' has wrong format");
			if ( result < multmin ) throw new NumberFormatException("'" + buffer + "' has wrong format");
			result *= radix;
			if ( result < limit + digit ) throw new NumberFormatException("'" + buffer + "' has wrong format");
			result -= digit;
			
		}while ( ++i < end );
		return -result;
	
protected intparseIntYear(java.lang.String buffer, int end)

		int radix=10;
		int result = 0;
		boolean negative = false;
		int i=0;
		int limit;
		int multmin;
		int digit=0;
		
		if (buffer.charAt(0) == '-"){
			negative = true;
			limit = Integer.MIN_VALUE;
			i++;
			
		}
		else{
			limit = -Integer.MAX_VALUE;
		}
		multmin = limit / radix;
		while (i < end)
		{
			digit = getDigit(buffer.charAt(i++));
			if (digit < 0) throw new NumberFormatException("'" + buffer + "' has wrong format");
			if (result < multmin) throw new NumberFormatException("'" + buffer + "' has wrong format");
			result *= radix;
			if (result < limit + digit) throw new NumberFormatException("'" + buffer + "' has wrong format");
			result -= digit;
		}
		
		if (negative)
		{
			if (i > 1) return result;
			else throw new NumberFormatException("'" + buffer + "' has wrong format");
		}
		return -result;
		
	
protected doubleparseSecond(java.lang.String buffer, int start, int end)

		int dot = -1;
		for (int i = start; i < end; i++) {
			char ch = buffer.charAt(i);
			if (ch == '.")
				dot = i;
			else if (ch > '9" || ch < '0")
				throw new NumberFormatException("'" + buffer + "' has wrong format");
		}
		if (dot == -1) {
			if (start+2 != end)
				throw new NumberFormatException("'" + buffer + "' has wrong format");
		}
		else if (start+2 != dot || dot+1 == end) {
			throw new NumberFormatException("'" + buffer + "' has wrong format");
		}
		return Double.parseDouble(buffer.substring(start, end));
	
protected voidparseTimeZone(java.lang.String buffer, int start, int end, org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date)
Shared code from Date and YearMonth datatypes. Finds if time zone sign is present

param
end
param
date
exception
RuntimeException

		
		//fStart points right after the date
		
		if ( start < end ) {
			if (!isNextCharUTCSign(buffer, start, end)) {
				throw new RuntimeException ("Error in month parsing");
			}
			else {
				getTimeZone(buffer, date, start, end);
			}
		}
	
protected voidresetDateObj(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData data)
Resets object representation of date/time

param
data date/time object

		data.year = 0;
		data.month = 0;
		data.day = 0;
		data.hour = 0;
		data.minute = 0;
		data.second = 0;
		data.utc = 0;
		data.timezoneHr = 0;
		data.timezoneMin = 0;
	
protected voidsaveUnnormalized(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData date)

param
date

        date.unNormYear = date.year;
        date.unNormMonth = date.month;
        date.unNormDay = date.day;
        date.unNormHour = date.hour;
        date.unNormMinute = date.minute;
        date.unNormSecond = date.second;
    
protected voidvalidateDateTime(org.apache.xerces.impl.dv.xs.AbstractDateTimeDV$DateTimeData data)
Validates given date/time object accoring to W3C PR Schema [D.1 ISO 8601 Conventions]

param
data

		
		//REVISIT: should we throw an exception for not valid dates
		//          or reporting an error message should be sufficient?
		
		/**
		 * XML Schema 1.1 - RQ-123: Allow year 0000 in date related types.
		 */
		if (!Constants.SCHEMA_1_1_SUPPORT && data.year==0 ) {
			throw new RuntimeException("The year \"0000\" is an illegal year value");
			
		}
		
		if ( data.month<1 || data.month>12 ) {
			throw new RuntimeException("The month must have values 1 to 12");
			
		}
		
		//validate days
		if ( data.day>maxDayInMonthFor(data.year, data.month) || data.day<1 ) {
			throw new RuntimeException("The day must have values 1 to 31");
		}
		
		//validate hours
		if ( data.hour>23 || data.hour<0 ) {
			if (data.hour == 24 && data.minute == 0 && data.second == 0) {
				data.hour = 0;
				if (++data.day > maxDayInMonthFor(data.year, data.month)) {
					data.day = 1;
					if (++data.month > 12) {
						data.month = 1;
						if (Constants.SCHEMA_1_1_SUPPORT) {
							++data.year;
						}
						else if (++data.year == 0) {
							data.year = 1;
						}
					}
				}
			}
			else {
				throw new RuntimeException("Hour must have values 0-23, unless 24:00:00");
			}
		}
		
		//validate
		if ( data.minute>59 || data.minute<0 ) {
			throw new RuntimeException("Minute must have values 0-59");
		}
		
		//validate
		if ( data.second>=60 || data.second<0 ) {
			throw new RuntimeException("Second must have values 0-59");
			
		}
		
		//validate
		if ( data.timezoneHr>14 || data.timezoneHr<-14 ) {
			throw new RuntimeException("Time zone should have range -14:00 to +14:00");
		}
		else {
			if((data.timezoneHr == 14 || data.timezoneHr == -14) && data.timezoneMin != 0)
				throw new RuntimeException("Time zone should have range -14:00 to +14:00");
			else if(data.timezoneMin > 59 || data.timezoneMin < -59)
				throw new RuntimeException("Minute must have values 0-59");
		}