All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xerces.impl.dv.xs.AbstractDateTimeDV Maven / Gradle / Ivy

Go to download

Xerces2 is the next generation of high performance, fully compliant XML parsers in the Apache Xerces family. This new version of Xerces introduces the Xerces Native Interface (XNI), a complete framework for building parser components and configurations that is extremely modular and easy to program. The Apache Xerces2 parser is the reference implementation of XNI but other parser components, configurations, and parsers can be written using the Xerces Native Interface. For complete design and implementation documents, refer to the XNI Manual. Xerces2 is a fully conforming XML Schema 1.0 processor. A partial experimental implementation of the XML Schema 1.1 Structures and Datatypes Working Drafts (December 2009) and an experimental implementation of the XML Schema Definition Language (XSD): Component Designators (SCD) Candidate Recommendation (January 2010) are provided for evaluation. For more information, refer to the XML Schema page. Xerces2 also provides a complete implementation of the Document Object Model Level 3 Core and Load/Save W3C Recommendations and provides a complete implementation of the XML Inclusions (XInclude) W3C Recommendation. It also provides support for OASIS XML Catalogs v1.1. Xerces2 is able to parse documents written according to the XML 1.1 Recommendation, except that it does not yet provide an option to enable normalization checking as described in section 2.13 of this specification. It also handles namespaces according to the XML Namespaces 1.1 Recommendation, and will correctly serialize XML 1.1 documents if the DOM level 3 load/save APIs are in use.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.xerces.impl.dv.xs;

import java.math.BigDecimal;

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl;
import org.apache.xerces.xs.datatypes.XSDateTime;

/**
 * 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 965250 2010-07-18 16:04:58Z mrglavas $
 */
public abstract class AbstractDateTimeDV extends TypeValidator {
	
	//debugging
	private static final boolean DEBUG=false;
	
	//define shared variables for date/time
	
	
	//define constants to be used in assigning default values for
	//all date/time excluding duration
	protected final static int YEAR=2000;
	protected final static int MONTH=01;
	protected final static int DAY = 01;
    
    protected static final DatatypeFactory datatypeFactory = new DatatypeFactoryImpl();
	
	public short getAllowedFacets(){
		return ( XSSimpleTypeDecl.FACET_PATTERN | XSSimpleTypeDecl.FACET_WHITESPACE | XSSimpleTypeDecl.FACET_ENUMERATION |XSSimpleTypeDecl.FACET_MAXINCLUSIVE |XSSimpleTypeDecl.FACET_MININCLUSIVE | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE  | XSSimpleTypeDecl.FACET_MINEXCLUSIVE  );
	}//getAllowedFacets()
	
	
	// distinguishes between identity and equality for date/time values
	// ie: two values representing the same "moment in time" but with different 
	// remembered timezones are now equal but not identical.
	public boolean isIdentical (Object value1, 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;
	}//isIdentical()
	
	// the parameters are in compiled form (from getActualValue)
	public int compare (Object value1, Object value2) {
		return compareDates(((DateTimeData)value1),
				((DateTimeData)value2), true);
	}//compare()
	
	/**
	 * 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
	 */
	protected short compareDates(DateTimeData date1, DateTimeData date2, boolean strict) {
		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;
		
	}
	
	/**
	 * 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
	 */
	protected short compareOrder(DateTimeData date1, DateTimeData 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;
	}
	
	/**
	 * Parses time hh:mm:ss.sss and time zone if any
	 *
	 * @param start
	 * @param end
	 * @param data
	 * @exception RuntimeException
	 */
	protected  void getTime (String buffer, int start, int end, DateTimeData data) throws 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);
		}
	}
	
	/**
	 * Parses date CCYY-MM-DD
	 *
	 * @param buffer
	 * @param start start position
	 * @param end end position
	 * @param date
	 * @exception RuntimeException
	 */
	protected int getDate (String buffer, int start, int end, DateTimeData date) throws 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;
	}
	
	/**
	 * Parses date CCYY-MM
	 *
	 * @param buffer
	 * @param start start position
	 * @param end end position
	 * @param date
	 * @exception RuntimeException
	 */
	protected int getYearMonth (String buffer, int start, int end, DateTimeData date) throws 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
	}
	
	/**
	 * Shared code from Date and YearMonth datatypes.
	 * Finds if time zone sign is present
	 *
	 * @param end
	 * @param date
	 * @exception RuntimeException
	 */
	protected void parseTimeZone (String buffer, int start, int end, DateTimeData date) throws 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);
			}
		}
	}
	
	/**
	 * Parses time zone: 'Z' or {+,-} followed by  hh:mm
	 *
	 * @param data
	 * @param sign
	 * @exception RuntimeException
	 */
	protected void getTimeZone (String buffer, DateTimeData data, int sign, int end) throws 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);
		}
	}
	
	/**
	 * 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
	 */
	protected  int indexOf (String buffer, int start, int end, char ch) {
		for ( int i=start;i12 ) {
			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");
		}
		
	}
	
	/**
	 * Return index of UTC char: 'Z', '+', '-'
	 *
	 * @param start
	 * @param end
	 * @return index of the UTC character that was found
	 */
	protected int findUTCSign (String buffer, int start, int end) {
		int c;
		for ( int i=start;itrue if the character at start is 'Z', '+' or '-'.
     */
    protected final boolean isNextCharUTCSign(String buffer, int start, int end) {
        if (start < end) {
            char c = buffer.charAt(start);
            return (c == 'Z' || c == '+' || c == '-');
        }
        return false;
    }
	
	/**
	 * 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
	 */
	protected  int parseInt (String buffer, int start, int end)
	throws NumberFormatException{
		//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;
	}
	
	// parse Year differently to support negative value.
	protected int parseIntYear (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;
		
	}
	
	/**
	 * If timezone present - normalize dateTime  [E Adding durations to dateTimes]
	 *
	 * @param date   CCYY-MM-DDThh:mm:ss+03
	 */
	protected void normalize(DateTimeData date) {
		
		// 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';
	}
	
	
	/**
     * @param date
     */
    protected void saveUnnormalized(DateTimeData 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;
    }

    /**
	 * Resets object representation of date/time
	 *
	 * @param data   date/time object
	 */
	protected void resetDateObj(DateTimeData data) {
		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;
	}
	
	/**
	 * 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
	 */
	protected int maxDayInMonthFor(int year, int 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;
		}
	}
	
	private boolean isLeapYear(int year) {
		
		//REVISIT: should we take care about Julian calendar?
		return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));
	}
	
	//
	// help function described in W3C PR Schema [E Adding durations to dateTimes]
	//
	protected int mod (int a, int b, int quotient) {
		//modulo(a, b) = a - fQuotient(a,b)*b
		return (a - quotient*b) ;
	}
	
	//
	// help function described in W3C PR Schema [E Adding durations to dateTimes]
	//
	protected int fQuotient (int a, int b) {
		
		//fQuotient(a, b) = the greatest integer less than or equal to a/b
		return (int)Math.floor((float)a/b);
	}
	
	//
	// help function described in W3C PR Schema [E Adding durations to dateTimes]
	//
	protected int modulo (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) ;
	}
	
	//
	// help function described in W3C PR Schema [E Adding durations to dateTimes]
	//
	protected int fQuotient (int temp, int low, int high) {
		//fQuotient(a - low, high - low)
		
		return fQuotient(temp - low, high - low);
	}
	
	
	protected String dateToString(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 final void append(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 void append(StringBuffer message, double value) {
	    if (value < 0) {
	        message.append('-');
	        value = -value;
	    }
	    if (value < 10) {
	        message.append('0');
	    }
	    append2(message, value);
	}
    
    protected final void append2(StringBuffer message, double value) {
        final int intValue = (int) value;
        if (value == intValue) {
            message.append(intValue);
        }
        else {
            append3(message, value);
        }
    }
    
    private void append3(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');
            }
        }
    }
	
	protected double parseSecond(String buffer, int start, int end)
	throws NumberFormatException {
		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));
	}
	
	//
	//Private help functions
	//
	
	private void cloneDate (DateTimeData finalValue, 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;
	}
	
	/**
	 * Represents date time data
	 */
	static final class DateTimeData implements XSDateTime {
		int year, month, day, hour, minute, utc;
		double second;
		int timezoneHr, timezoneMin;
        private String originalValue;
        boolean normalized = true;
        
        int unNormYear;
        int unNormMonth;
        int unNormDay;
        int unNormHour;
        int unNormMinute;
        double unNormSecond;
		
		// used for comparisons - to decide the 'interesting' portions of
		// a date/time based data type.
		int position;
		// a pointer to the type that was used go generate this data
		// note that this is not the actual simple type, but one of the
		// statically created XXXDV objects, so this won't cause any GC problem.
		final AbstractDateTimeDV type;
		private String canonical;
		public DateTimeData(String originalValue, AbstractDateTimeDV type) {
            this.originalValue = originalValue;
			this.type = type;
		}
		public DateTimeData(int year, int month, int day, int hour, int minute,
				double second, int utc, String originalValue, boolean normalized, AbstractDateTimeDV type) {
			this.year = year;
			this.month = month;
			this.day = day;
			this.hour = hour;
			this.minute = minute;
			this.second = second;
			this.utc = utc;
			this.type = type;
            this.originalValue = originalValue;
		}
		public boolean equals(Object obj) {
			if (!(obj instanceof DateTimeData))
				return false;
			return type.compareDates(this, (DateTimeData)obj, true)==0;
		}
		public synchronized String toString() {
			if (canonical == null) {
				canonical = type.dateToString(this);
			}
			return canonical;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getYear()
		 */
		public int getYears() {
            if(type instanceof DurationDV)
                return 0;
			return normalized?year:unNormYear;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getMonth()
		 */
		public int getMonths() {
            if(type instanceof DurationDV) {
                return year*12 + month;
            }
			return normalized?month:unNormMonth;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getDay()
		 */
		public int getDays() {
            if(type instanceof DurationDV)
                return 0;
			return normalized?day:unNormDay;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getHour()
		 */
		public int getHours() {
            if(type instanceof DurationDV)
                return 0;
			return normalized?hour:unNormHour;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getMinutes()
		 */
		public int getMinutes() {
            if(type instanceof DurationDV)
                return 0;
			return normalized?minute:unNormMinute;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getSeconds()
		 */
		public double getSeconds() {
            if(type instanceof DurationDV) {
                return day*24*60*60 + hour*60*60 + minute*60 + second;
            }
			return normalized?second:unNormSecond;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#hasTimeZone()
		 */
		public boolean hasTimeZone() {
			return utc != 0;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getTimeZoneHours()
		 */
		public int getTimeZoneHours() {
			return timezoneHr;
		}
		/* (non-Javadoc)
		 * @see org.apache.xerces.xs.datatypes.XSDateTime#getTimeZoneMinutes()
		 */
		public int getTimeZoneMinutes() {
			return timezoneMin;
		}
        /* (non-Javadoc)
         * @see org.apache.xerces.xs.datatypes.XSDateTime#getLexicalValue()
         */
        public String getLexicalValue() {
            return originalValue;
        }
        /* (non-Javadoc)
         * @see org.apache.xerces.xs.datatypes.XSDateTime#normalize()
         */
        public XSDateTime normalize() {
            if(!normalized) {
                DateTimeData dt = (DateTimeData)this.clone();
                dt.normalized = true;
                return dt;
            }
            return this;
        }
        /* (non-Javadoc)
         * @see org.apache.xerces.xs.datatypes.XSDateTime#isNormalized()
         */
        public boolean isNormalized() {
            return normalized;
        }
        
        public Object clone() {
            DateTimeData dt = new DateTimeData(this.year, this.month, this.day, this.hour, 
                        this.minute, this.second, this.utc, this.originalValue, this.normalized, this.type);
            dt.canonical = this.canonical;
            dt.position = position;
            dt.timezoneHr = this.timezoneHr;
            dt.timezoneMin = this.timezoneMin;
            dt.unNormYear = this.unNormYear;
            dt.unNormMonth = this.unNormMonth;
            dt.unNormDay = this.unNormDay;
            dt.unNormHour = this.unNormHour;
            dt.unNormMinute = this.unNormMinute;
            dt.unNormSecond = this.unNormSecond;
            return dt;
        }
        
        /* (non-Javadoc)
         * @see org.apache.xerces.xs.datatypes.XSDateTime#getXMLGregorianCalendar()
         */
        public XMLGregorianCalendar getXMLGregorianCalendar() {
            return type.getXMLGregorianCalendar(this);
        }
        /* (non-Javadoc)
         * @see org.apache.xerces.xs.datatypes.XSDateTime#getDuration()
         */
        public Duration getDuration() {
            return type.getDuration(this);
        }
	}

    protected XMLGregorianCalendar getXMLGregorianCalendar(DateTimeData data) {
        return null;
    }

    protected Duration getDuration(DateTimeData data) {
        return null;
    }
    
    protected final BigDecimal getFractionalSecondsAsBigDecimal(DateTimeData data) {
        final StringBuffer buf = new StringBuffer();
        append3(buf, data.unNormSecond);
        String value = buf.toString();
        final int index = value.indexOf('.');
        if (index == -1) {
            return null;
        }
        value = value.substring(index);
        final BigDecimal _val = new BigDecimal(value);
        if (_val.compareTo(BigDecimal.valueOf(0)) == 0) {
            return null;
        }
        return _val;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy