1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2003 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 2001, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.impl.dv.xs;
  58. /**
  59. * This is the base class of all date/time datatype validators.
  60. * It implements common code for parsing, validating and comparing datatypes.
  61. * Classes that extend this class, must implement parse() method.
  62. *
  63. * REVISIT: There are many instance variables, which would cause problems
  64. * when we support grammar caching. A grammar is possibly used by
  65. * two parser instances at the same time, then the same simple type
  66. * decl object can be used to validate two strings at the same time.
  67. * -SG
  68. *
  69. * @author Elena Litani
  70. * @author Len Berman
  71. * @author Gopal Sharma, SUN Microsystems Inc.
  72. *
  73. * @version $Id: AbstractDateTimeDV.java,v 1.12 2003/06/16 18:15:51 sandygao Exp $
  74. */
  75. public abstract class AbstractDateTimeDV extends TypeValidator {
  76. //debugging
  77. private static final boolean DEBUG=false;
  78. //define shared variables for date/time
  79. //define constants
  80. protected final static int CY = 0, M = 1, D = 2, h = 3,
  81. m = 4, s = 5, ms = 6, utc=7, hh=0, mm=1;
  82. //size for all objects must have the same fields:
  83. //CCYY, MM, DD, h, m, s, ms + timeZone
  84. protected final static int TOTAL_SIZE = 8;
  85. //define constants to be used in assigning default values for
  86. //all date/time excluding duration
  87. protected final static int YEAR=2000;
  88. protected final static int MONTH=01;
  89. protected final static int DAY = 15;
  90. public short getAllowedFacets(){
  91. return ( XSSimpleTypeDecl.FACET_PATTERN | XSSimpleTypeDecl.FACET_WHITESPACE | XSSimpleTypeDecl.FACET_ENUMERATION |XSSimpleTypeDecl.FACET_MAXINCLUSIVE |XSSimpleTypeDecl.FACET_MININCLUSIVE | XSSimpleTypeDecl.FACET_MAXEXCLUSIVE | XSSimpleTypeDecl.FACET_MINEXCLUSIVE );
  92. }//getAllowedFacets()
  93. // the parameters are in compiled form (from getActualValue)
  94. public int compare (Object value1, Object value2) {
  95. return compareDates(((DateTimeData)value1).data,
  96. ((DateTimeData)value2).data, true);
  97. }//compare()
  98. /**
  99. * Compare algorithm described in dateDime (3.2.7).
  100. * Duration datatype overwrites this method
  101. *
  102. * @param date1 normalized date representation of the first value
  103. * @param date2 normalized date representation of the second value
  104. * @param strict
  105. * @return less, greater, less_equal, greater_equal, equal
  106. */
  107. protected short compareDates(int[] date1, int[] date2, boolean strict) {
  108. if ( date1[utc]==date2[utc] ) {
  109. return compareOrder(date1, date2);
  110. }
  111. short c1, c2;
  112. int[] tempDate = new int[TOTAL_SIZE];
  113. int[] timeZone = new int[2];
  114. if ( date1[utc]=='Z' ) {
  115. //compare date1<=date1<=(date2 with time zone -14)
  116. //
  117. cloneDate(date2, tempDate); //clones date1 value to global temporary storage: fTempDate
  118. timeZone[hh]=14;
  119. timeZone[mm]=0;
  120. tempDate[utc]='+';
  121. normalize(tempDate, timeZone);
  122. c1 = compareOrder(date1, tempDate);
  123. if (c1 == LESS_THAN)
  124. return c1;
  125. //compare date1>=(date2 with time zone +14)
  126. //
  127. cloneDate(date2, tempDate); //clones date1 value to global temporary storage: tempDate
  128. timeZone[hh]=14;
  129. timeZone[mm]=0;
  130. tempDate[utc]='-';
  131. normalize(tempDate, timeZone);
  132. c2 = compareOrder(date1, tempDate);
  133. if (c2 == GREATER_THAN)
  134. return c2;
  135. return INDETERMINATE;
  136. }
  137. else if ( date2[utc]=='Z' ) {
  138. //compare (date1 with time zone -14)<=date2
  139. //
  140. cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate
  141. timeZone[hh]=14;
  142. timeZone[mm]=0;
  143. tempDate[utc]='-';
  144. if (DEBUG) {
  145. System.out.println("tempDate=" + dateToString(tempDate));
  146. }
  147. normalize(tempDate, timeZone);
  148. c1 = compareOrder(tempDate, date2);
  149. if (DEBUG) {
  150. System.out.println("date=" + dateToString(date2));
  151. System.out.println("tempDate=" + dateToString(tempDate));
  152. }
  153. if (c1 == LESS_THAN)
  154. return c1;
  155. //compare (date1 with time zone +14)<=date2
  156. //
  157. cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate
  158. timeZone[hh]=14;
  159. timeZone[mm]=0;
  160. tempDate[utc]='+';
  161. normalize(tempDate, timeZone);
  162. c2 = compareOrder(tempDate, date2);
  163. if (DEBUG) {
  164. System.out.println("tempDate=" + dateToString(tempDate));
  165. }
  166. if (c2 == GREATER_THAN)
  167. return c2;
  168. return INDETERMINATE;
  169. }
  170. return INDETERMINATE;
  171. }
  172. /**
  173. * Given normalized values, determines order-relation
  174. * between give date/time objects.
  175. *
  176. * @param date1 date/time object
  177. * @param date2 date/time object
  178. * @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
  179. */
  180. protected short compareOrder (int[] date1, int[] date2) {
  181. for ( int i=0;i<TOTAL_SIZE;i++ ) {
  182. if ( date1[i]<date2[i] ) {
  183. return -1;
  184. }
  185. else if ( date1[i]>date2[i] ) {
  186. return 1;
  187. }
  188. }
  189. return 0;
  190. }
  191. /**
  192. * Parses time hh:mm:ss.sss and time zone if any
  193. *
  194. * @param start
  195. * @param end
  196. * @param data
  197. * @exception RuntimeException
  198. */
  199. protected void getTime (String buffer, int start, int end, int[] data, int[] timeZone) throws RuntimeException{
  200. int stop = start+2;
  201. //get hours (hh)
  202. data[h]=parseInt(buffer, start,stop);
  203. //get minutes (mm)
  204. if (buffer.charAt(stop++)!=':') {
  205. throw new RuntimeException("Error in parsing time zone" );
  206. }
  207. start = stop;
  208. stop = stop+2;
  209. data[m]=parseInt(buffer, start,stop);
  210. //get seconds (ss)
  211. if (buffer.charAt(stop++)!=':') {
  212. throw new RuntimeException("Error in parsing time zone" );
  213. }
  214. start = stop;
  215. stop = stop+2;
  216. data[s]=parseInt(buffer, start,stop);
  217. if (stop == end)
  218. return;
  219. //get miliseconds (ms)
  220. start = stop;
  221. int milisec = buffer.charAt(start) == '.' ? start : -1;
  222. //find UTC sign if any
  223. int sign = findUTCSign(buffer, start, end);
  224. //parse miliseconds
  225. if ( milisec != -1 ) {
  226. // The end of millisecond part is between . and
  227. // either the end of the UTC sign
  228. start = sign < 0 ? end : sign;
  229. data[ms]=parseInt(buffer, milisec+1, start);
  230. }
  231. //parse UTC time zone (hh:mm)
  232. if ( sign>0 ) {
  233. if (start != sign)
  234. throw new RuntimeException("Error in parsing time zone" );
  235. getTimeZone(buffer, data, sign, end, timeZone);
  236. }
  237. else if (start != end) {
  238. throw new RuntimeException("Error in parsing time zone" );
  239. }
  240. }
  241. /**
  242. * Parses date CCYY-MM-DD
  243. *
  244. * @param start
  245. * @param end
  246. * @param data
  247. * @exception RuntimeException
  248. */
  249. protected int getDate (String buffer, int start, int end, int[] date) throws RuntimeException{
  250. start = getYearMonth(buffer, start, end, date);
  251. if (buffer.charAt(start++) !='-') {
  252. throw new RuntimeException("CCYY-MM must be followed by '-' sign");
  253. }
  254. int stop = start + 2;
  255. date[D]=parseInt(buffer, start, stop);
  256. return stop;
  257. }
  258. /**
  259. * Parses date CCYY-MM
  260. *
  261. * @param start
  262. * @param end
  263. * @param data
  264. * @exception RuntimeException
  265. */
  266. protected int getYearMonth (String buffer, int start, int end, int[] date) throws RuntimeException{
  267. if ( buffer.charAt(0)=='-' ) {
  268. // REVISIT: date starts with preceding '-' sign
  269. // do we have to do anything with it?
  270. //
  271. start++;
  272. }
  273. int i = indexOf(buffer, start, end, '-');
  274. if ( i==-1 ) throw new RuntimeException("Year separator is missing or misplaced");
  275. int length = i-start;
  276. if (length<4) {
  277. throw new RuntimeException("Year must have 'CCYY' format");
  278. }
  279. else if (length > 4 && buffer.charAt(start)=='0'){
  280. throw new RuntimeException("Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden");
  281. }
  282. date[CY]= parseIntYear(buffer, i);
  283. if (buffer.charAt(i)!='-') {
  284. throw new RuntimeException("CCYY must be followed by '-' sign");
  285. }
  286. start = ++i;
  287. i = start +2;
  288. date[M]=parseInt(buffer, start, i);
  289. return i; //fStart points right after the MONTH
  290. }
  291. /**
  292. * Shared code from Date and YearMonth datatypes.
  293. * Finds if time zone sign is present
  294. *
  295. * @param end
  296. * @param date
  297. * @exception RuntimeException
  298. */
  299. protected void parseTimeZone (String buffer, int start, int end, int[] date, int[] timeZone) throws RuntimeException{
  300. //fStart points right after the date
  301. if ( start<end ) {
  302. int sign = findUTCSign(buffer, start, end);
  303. if ( sign<0 ) {
  304. throw new RuntimeException ("Error in month parsing");
  305. }
  306. else {
  307. getTimeZone(buffer, date, sign, end, timeZone);
  308. }
  309. }
  310. }
  311. /**
  312. * Parses time zone: 'Z' or {+,-} followed by hh:mm
  313. *
  314. * @param data
  315. * @param sign
  316. * @exception RuntimeException
  317. */
  318. protected void getTimeZone (String buffer, int[] data, int sign, int end, int[] timeZone) throws RuntimeException{
  319. data[utc]=buffer.charAt(sign);
  320. if ( buffer.charAt(sign) == 'Z' ) {
  321. if (end>(++sign)) {
  322. throw new RuntimeException("Error in parsing time zone");
  323. }
  324. return;
  325. }
  326. if ( sign<=(end-6) ) {
  327. //parse [hh]
  328. int stop = ++sign+2;
  329. timeZone[hh]=parseInt(buffer, sign, stop);
  330. if (buffer.charAt(stop++)!=':') {
  331. throw new RuntimeException("Error in parsing time zone" );
  332. }
  333. //parse [ss]
  334. timeZone[mm]=parseInt(buffer, stop, stop+2);
  335. if ( stop+2!=end ) {
  336. throw new RuntimeException("Error in parsing time zone");
  337. }
  338. }
  339. else {
  340. throw new RuntimeException("Error in parsing time zone");
  341. }
  342. if ( DEBUG ) {
  343. System.out.println("time[hh]="+timeZone[hh] + " time[mm]=" +timeZone[mm]);
  344. }
  345. }
  346. /**
  347. * Computes index of given char within StringBuffer
  348. *
  349. * @param start
  350. * @param end
  351. * @param ch character to look for in StringBuffer
  352. * @return index of ch within StringBuffer
  353. */
  354. protected int indexOf (String buffer, int start, int end, char ch) {
  355. for ( int i=start;i<end;i++ ) {
  356. if ( buffer.charAt(i) == ch ) {
  357. return i;
  358. }
  359. }
  360. return -1;
  361. }
  362. /**
  363. * Validates given date/time object accoring to W3C PR Schema
  364. * [D.1 ISO 8601 Conventions]
  365. *
  366. * @param data
  367. */
  368. protected void validateDateTime (int[] data, int[] timeZone) {
  369. //REVISIT: should we throw an exception for not valid dates
  370. // or reporting an error message should be sufficient?
  371. if ( data[CY]==0 ) {
  372. throw new RuntimeException("The year \"0000\" is an illegal year value");
  373. }
  374. if ( data[M]<1 || data[M]>12 ) {
  375. throw new RuntimeException("The month must have values 1 to 12");
  376. }
  377. //validate days
  378. if ( data[D]>maxDayInMonthFor(data[CY], data[M]) || data[D]<1 ) {
  379. throw new RuntimeException("The day must have values 1 to 31");
  380. }
  381. //validate hours
  382. if ( data[h]>23 || data[h]<0 ) {
  383. if (data[h] == 24 && data[m] == 0 && data[s] == 0 && data[ms] == 0) {
  384. data[h] = 0;
  385. if (++data[D] > maxDayInMonthFor(data[CY], data[M])) {
  386. data[D] = 1;
  387. if (++data[M] > 12) {
  388. data[M] = 1;
  389. if (++data[CY] == 0)
  390. data[CY] = 1;
  391. }
  392. }
  393. }
  394. else {
  395. throw new RuntimeException("Hour must have values 0-23, unless 24:00:00");
  396. }
  397. }
  398. //validate
  399. if ( data[m]>59 || data[m]<0 ) {
  400. throw new RuntimeException("Minute must have values 0-59");
  401. }
  402. //validate
  403. if ( data[s]>60 || data[s]<0 ) {
  404. throw new RuntimeException("Second must have values 0-60");
  405. }
  406. //validate
  407. if ( timeZone[hh]>14 || timeZone[hh]<-14 ) {
  408. throw new RuntimeException("Time zone should have range -14..+14");
  409. }
  410. //validate
  411. if ( timeZone[mm]>59 || timeZone[mm]<-59 ) {
  412. throw new RuntimeException("Minute must have values 0-59");
  413. }
  414. }
  415. /**
  416. * Return index of UTC char: 'Z', '+', '-'
  417. *
  418. * @param start
  419. * @param end
  420. * @return index of the UTC character that was found
  421. */
  422. protected int findUTCSign (String buffer, int start, int end) {
  423. int c;
  424. for ( int i=start;i<end;i++ ) {
  425. c=buffer.charAt(i);
  426. if ( c == 'Z' || c=='+' || c=='-' ) {
  427. return i;
  428. }
  429. }
  430. return -1;
  431. }
  432. /**
  433. * Given start and end position, parses string value
  434. *
  435. * @param value string to parse
  436. * @param start Start position
  437. * @param end end position
  438. * @return return integer representation of characters
  439. */
  440. protected int parseInt (String buffer, int start, int end)
  441. throws NumberFormatException{
  442. //REVISIT: more testing on this parsing needs to be done.
  443. int radix=10;
  444. int result = 0;
  445. int digit=0;
  446. int limit = -Integer.MAX_VALUE;
  447. int multmin = limit / radix;
  448. int i = start;
  449. do {
  450. digit = getDigit(buffer.charAt(i));
  451. if ( digit < 0 ) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  452. if ( result < multmin ) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  453. result *= radix;
  454. if ( result < limit + digit ) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  455. result -= digit;
  456. }while ( ++i < end );
  457. return -result;
  458. }
  459. // parse Year differently to support negative value.
  460. protected int parseIntYear (String buffer, int end){
  461. int radix=10;
  462. int result = 0;
  463. boolean negative = false;
  464. int i=0;
  465. int limit;
  466. int multmin;
  467. int digit=0;
  468. if (buffer.charAt(0) == '-'){
  469. negative = true;
  470. limit = Integer.MIN_VALUE;
  471. i++;
  472. }
  473. else{
  474. limit = -Integer.MAX_VALUE;
  475. }
  476. multmin = limit / radix;
  477. while (i < end)
  478. {
  479. digit = getDigit(buffer.charAt(i++));
  480. if (digit < 0) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  481. if (result < multmin) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  482. result *= radix;
  483. if (result < limit + digit) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  484. result -= digit;
  485. }
  486. if (negative)
  487. {
  488. if (i > 1) return result;
  489. else throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
  490. }
  491. return -result;
  492. }
  493. /**
  494. * If timezone present - normalize dateTime [E Adding durations to dateTimes]
  495. *
  496. * @param date CCYY-MM-DDThh:mm:ss+03
  497. * @return CCYY-MM-DDThh:mm:ssZ
  498. */
  499. protected void normalize (int[] date, int[] timeZone) {
  500. // REVISIT: we have common code in addDuration() for durations
  501. // should consider reorganizing it.
  502. //
  503. //add minutes (from time zone)
  504. int negate = 1;
  505. if (date[utc]=='+') {
  506. negate = -1;
  507. }
  508. if ( DEBUG ) {
  509. System.out.println("==>date[m]"+date[m]);
  510. System.out.println("==>timeZone[mm]" +timeZone[mm]);
  511. }
  512. int temp = date[m] + negate*timeZone[mm];
  513. int carry = fQuotient (temp, 60);
  514. date[m]= mod(temp, 60, carry);
  515. if ( DEBUG ) {
  516. System.out.println("==>carry: " + carry);
  517. }
  518. //add hours
  519. temp = date[h] + negate*timeZone[hh] + carry;
  520. carry = fQuotient(temp, 24);
  521. date[h]=mod(temp, 24, carry);
  522. if ( DEBUG ) {
  523. System.out.println("==>date[h]"+date[h]);
  524. System.out.println("==>carry: " + carry);
  525. }
  526. date[D]=date[D]+carry;
  527. while ( true ) {
  528. temp=maxDayInMonthFor(date[CY], date[M]);
  529. if (date[D]<1) {
  530. date[D] = date[D] + maxDayInMonthFor(date[CY], date[M]-1);
  531. carry=-1;
  532. }
  533. else if ( date[D]>temp ) {
  534. date[D]=date[D]-temp;
  535. carry=1;
  536. }
  537. else {
  538. break;
  539. }
  540. temp=date[M]+carry;
  541. date[M]=modulo(temp, 1, 13);
  542. date[CY]=date[CY]+fQuotient(temp, 1, 13);
  543. }
  544. date[utc]='Z';
  545. }
  546. /**
  547. * Resets object representation of date/time
  548. *
  549. * @param data date/time object
  550. */
  551. protected void resetDateObj (int[] data) {
  552. for ( int i=0;i<TOTAL_SIZE;i++ ) {
  553. data[i]=0;
  554. }
  555. }
  556. /**
  557. * Given {year,month} computes maximum
  558. * number of days for given month
  559. *
  560. * @param year
  561. * @param month
  562. * @return integer containg the number of days in a given month
  563. */
  564. protected int maxDayInMonthFor(int year, int month) {
  565. //validate days
  566. if ( month==4 || month==6 || month==9 || month==11 ) {
  567. return 30;
  568. }
  569. else if ( month==2 ) {
  570. if ( isLeapYear(year) ) {
  571. return 29;
  572. }
  573. else {
  574. return 28;
  575. }
  576. }
  577. else {
  578. return 31;
  579. }
  580. }
  581. private boolean isLeapYear(int year) {
  582. //REVISIT: should we take care about Julian calendar?
  583. return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));
  584. }
  585. //
  586. // help function described in W3C PR Schema [E Adding durations to dateTimes]
  587. //
  588. protected int mod (int a, int b, int quotient) {
  589. //modulo(a, b) = a - fQuotient(a,b)*b
  590. return (a - quotient*b) ;
  591. }
  592. //
  593. // help function described in W3C PR Schema [E Adding durations to dateTimes]
  594. //
  595. protected int fQuotient (int a, int b) {
  596. //fQuotient(a, b) = the greatest integer less than or equal to a/b
  597. return (int)Math.floor((float)ab);
  598. }
  599. //
  600. // help function described in W3C PR Schema [E Adding durations to dateTimes]
  601. //
  602. protected int modulo (int temp, int low, int high) {
  603. //modulo(a - low, high - low) + low
  604. int a = temp - low;
  605. int b = high - low;
  606. return (mod (a, b, fQuotient(a, b)) + low) ;
  607. }
  608. //
  609. // help function described in W3C PR Schema [E Adding durations to dateTimes]
  610. //
  611. protected int fQuotient (int temp, int low, int high) {
  612. //fQuotient(a - low, high - low)
  613. return fQuotient(temp - low, high - low);
  614. }
  615. protected String dateToString(int[] date) {
  616. StringBuffer message = new StringBuffer(25);
  617. append(message, date[CY], 4);
  618. message.append('-');
  619. append(message, date[M], 2);
  620. message.append('-');
  621. append(message, date[D], 2);
  622. message.append('T');
  623. append(message, date[h], 2);
  624. message.append(':');
  625. append(message, date[m], 2);
  626. message.append(':');
  627. append(message, date[s], 2);
  628. message.append('.');
  629. message.append(date[ms]);
  630. append(message, (char)date[utc], 0);
  631. return message.toString();
  632. }
  633. protected void append(StringBuffer message, int value, int nch) {
  634. if (value < 0) {
  635. message.append('-');
  636. value = -value;
  637. }
  638. if (nch == 4) {
  639. if (value < 10)
  640. message.append("000");
  641. else if (value < 100)
  642. message.append("00");
  643. else if (value < 1000)
  644. message.append("0");
  645. message.append(value);
  646. }
  647. else if (nch == 2) {
  648. if (value < 10)
  649. message.append('0');
  650. message.append(value);
  651. }
  652. else {
  653. if (value != 0)
  654. message.append((char)value);
  655. }
  656. }
  657. //
  658. //Private help functions
  659. //
  660. private void cloneDate (int[] finalValue, int[] tempDate) {
  661. System.arraycopy(finalValue, 0, tempDate, 0, TOTAL_SIZE);
  662. }
  663. /**
  664. * Represents date time data
  665. */
  666. static final class DateTimeData {
  667. // actual data stored in an int array
  668. final int[] data;
  669. // a pointer to the type that was used go generate this data
  670. // note that this is not the actual simple type, but one of the
  671. // statically created XXXDV objects, so this won't cause any GC problem.
  672. final AbstractDateTimeDV type;
  673. private String canonical;
  674. public DateTimeData(int[] data, AbstractDateTimeDV type) {
  675. this.data = data;
  676. this.type = type;
  677. }
  678. public boolean equals(Object obj) {
  679. if (!(obj instanceof DateTimeData))
  680. return false;
  681. int[] odata = ((DateTimeData)obj).data;
  682. return type.compareDates(data, odata, true)==0;
  683. }
  684. public synchronized String toString() {
  685. if (canonical == null) {
  686. canonical = type.dateToString(data);
  687. }
  688. return canonical;
  689. }
  690. }
  691. }