001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999, 2000 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 2001, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058: package org.apache.xerces.validators.datatype;
059:
060: import java.util.Hashtable;
061:
062: /**
063: * Validator for <duration> datatype (W3C Schema Datatypes)
064: *
065: * @author Elena Litani
066: * @version $Id: DurationDatatypeValidator.java,v 1.6 2001/05/31 21:51:46 elena Exp $
067: */
068:
069: public class DurationDatatypeValidator extends DateTimeValidator {
070:
071: // order-relation on duration is a partial order. The dates below are used to
072: // for comparison of 2 durations, based on the fact that
073: // duration x and y is x<=y iff s+x<=s+y
074: // see 3.2.6 duration W3C schema datatype specs
075: //
076: // the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}
077: private final static int[][] DATETIMES = {
078: { 1696, 9, 1, 0, 0, 0, 0, 'Z' },
079: { 1697, 2, 1, 0, 0, 0, 0, 'Z' },
080: { 1903, 3, 1, 0, 0, 0, 0, 'Z' },
081: { 1903, 7, 1, 0, 0, 0, 0, 'Z' } };
082:
083: private int[][] fDuration = null;
084:
085: public DurationDatatypeValidator()
086: throws InvalidDatatypeFacetException {
087: super ();
088: }
089:
090: public DurationDatatypeValidator(DatatypeValidator base,
091: Hashtable facets, boolean derivedByList)
092: throws InvalidDatatypeFacetException {
093:
094: super (base, facets, derivedByList);
095: }
096:
097: /**
098: * Parses, validates and computes normalized version of duration object
099: *
100: * @param str The lexical representation of duration object PnYn MnDTnH nMnS
101: * @param date uninitialized date object
102: * @return normalized date representation
103: * @exception Exception Invalid lexical representation
104: */
105: protected int[] parse(String str, int[] date)
106: throws SchemaDateTimeException {
107:
108: //PnYn MnDTnH nMnS: -P1Y2M3DT10H30M
109: resetBuffer(str);
110:
111: //create structure to hold an object
112: if (date == null) {
113: date = new int[TOTAL_SIZE];
114: }
115: resetDateObj(date);
116:
117: char c = fBuffer.charAt(fStart++);
118: if (c != 'P' && c != '-') {
119: throw new SchemaDateTimeException();
120: } else {
121: date[utc] = (c == '-') ? '-' : 0;
122: if (c == '-' && fBuffer.charAt(fStart++) != 'P') {
123: throw new SchemaDateTimeException();
124: }
125: }
126:
127: int negate = 1;
128: //negative duration
129: if (date[utc] == '-') {
130: negate = -1;
131:
132: }
133: //at least one number and designator must be seen after P
134: boolean designator = false;
135:
136: int endDate = indexOf(fStart, fEnd, 'T');
137: if (endDate == -1) {
138: endDate = fEnd;
139: }
140: //find 'Y'
141: int end = indexOf(fStart, endDate, 'Y');
142: if (end != -1) {
143: //scan year
144: date[CY] = negate * parseInt(fStart, end);
145: fStart = end + 1;
146: designator = true;
147: }
148:
149: end = indexOf(fStart, endDate, 'M');
150: if (end != -1) {
151: //scan month
152: date[M] = negate * parseInt(fStart, end);
153: fStart = end + 1;
154: designator = true;
155: }
156:
157: end = indexOf(fStart, endDate, 'D');
158: if (end != -1) {
159: //scan day
160: date[D] = negate * parseInt(fStart, end);
161: fStart = end + 1;
162: designator = true;
163: }
164:
165: if (fEnd == endDate && fStart != fEnd) {
166: throw new SchemaDateTimeException();
167: }
168: if (fEnd != endDate) {
169:
170: //scan hours, minutes, seconds
171: //REVISIT: can any item include a decimal fraction or only seconds?
172: //
173:
174: end = indexOf(++fStart, fEnd, 'H');
175: if (end != -1) {
176: //scan hours
177: date[h] = negate * parseInt(fStart, end);
178: fStart = end + 1;
179: designator = true;
180: }
181:
182: end = indexOf(fStart, fEnd, 'M');
183: if (end != -1) {
184: //scan min
185: date[m] = negate * parseInt(fStart, end);
186: fStart = end + 1;
187: designator = true;
188: }
189:
190: end = indexOf(fStart, fEnd, 'S');
191: if (end != -1) {
192: //scan seconds
193: int mlsec = indexOf(fStart, end, '.');
194: if (mlsec > 0) {
195: date[s] = negate * parseInt(fStart, mlsec);
196: date[ms] = negate * parseInt(mlsec + 1, end);
197: } else {
198: date[s] = negate * parseInt(fStart, end);
199: }
200: fStart = end + 1;
201: designator = true;
202: }
203: // no additional data shouls appear after last item
204: // P1Y1M1DT is illigal value as well
205: if (fStart != fEnd || fBuffer.charAt(--fStart) == 'T') {
206: throw new SchemaDateTimeException();
207: }
208: }
209:
210: if (!designator) {
211: throw new SchemaDateTimeException();
212: }
213:
214: return date;
215: }
216:
217: /**
218: * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
219: *
220: * @param date1 Unnormalized duration
221: * @param date2 Unnormalized duration
222: * @param strict (min/max)Exclusive strict == true ( LESS_THAN ) or ( GREATER_THAN )
223: * (min/max)Inclusive strict == false (LESS_EQUAL) or (GREATER_EQUAL)
224: * @return
225: */
226: protected short compareDates(int[] date1, int[] date2,
227: boolean strict) {
228:
229: //REVISIT: this is unoptimazed vs of comparing 2 durations
230: // Algorithm is described in 3.2.6.2 W3C Schema Datatype specs
231: //
232:
233: //add constA to both durations
234: short resultA, resultB = INDETERMINATE;
235:
236: //try and see if the objects are equal
237: resultA = compareOrder(date1, date2);
238: if (resultA == EQUAL) {
239: return EQUAL;
240: }
241: if (fDuration == null) {
242: fDuration = new int[2][TOTAL_SIZE];
243: }
244: //long comparison algorithm is required
245: int[] tempA = addDuration(date1, 0, fDuration[0]);
246: int[] tempB = addDuration(date2, 0, fDuration[1]);
247: resultA = compareOrder(tempA, tempB);
248: if (resultA == INDETERMINATE) {
249: return INDETERMINATE;
250: }
251:
252: tempA = addDuration(date1, 1, fDuration[0]);
253: tempB = addDuration(date2, 1, fDuration[1]);
254: resultB = compareOrder(tempA, tempB);
255: resultA = compareResults(resultA, resultB, strict);
256: if (resultA == INDETERMINATE) {
257: return INDETERMINATE;
258: }
259:
260: tempA = addDuration(date1, 2, fDuration[0]);
261: tempB = addDuration(date2, 2, fDuration[1]);
262: resultB = compareOrder(tempA, tempB);
263: resultA = compareResults(resultA, resultB, strict);
264: if (resultA == INDETERMINATE) {
265: return INDETERMINATE;
266: }
267:
268: tempA = addDuration(date1, 3, fDuration[0]);
269: tempB = addDuration(date2, 3, fDuration[1]);
270: resultB = compareOrder(tempA, tempB);
271: resultA = compareResults(resultA, resultB, strict);
272:
273: return resultA;
274: }
275:
276: private short compareResults(short resultA, short resultB,
277: boolean strict) {
278:
279: if (resultB == INDETERMINATE) {
280: return INDETERMINATE;
281: } else if (resultA != resultB && strict) {
282: return INDETERMINATE;
283: } else if (resultA != resultB && !strict) {
284: if (resultA != EQUAL && resultB != EQUAL) {
285: return INDETERMINATE;
286: } else {
287: return (resultA != EQUAL) ? resultA : resultB;
288: }
289: }
290: return resultA;
291: }
292:
293: private int[] addDuration(int[] date, int index, int[] duration) {
294:
295: //REVISIT: some code could be shared between normalize() and this method,
296: // however is it worth moving it? The structures are different...
297: //
298:
299: resetDateObj(duration);
300: //add months (may be modified additionaly below)
301: int temp = DATETIMES[index][M] + date[M];
302: duration[M] = modulo(temp, 1, 13);
303: int carry = fQuotient(temp, 1, 13);
304:
305: //add years (may be modified additionaly below)
306: duration[CY] = DATETIMES[index][CY] + date[CY] + carry;
307:
308: //add seconds
309: temp = DATETIMES[index][s] + date[s];
310: carry = fQuotient(temp, 60);
311: duration[s] = mod(temp, 60, carry);
312:
313: //add minutes
314: temp = DATETIMES[index][m] + date[m] + carry;
315: carry = fQuotient(temp, 60);
316: duration[m] = mod(temp, 60, carry);
317:
318: //add hours
319: temp = DATETIMES[index][h] + date[h] + carry;
320: carry = fQuotient(temp, 24);
321: duration[h] = mod(temp, 24, carry);
322:
323: duration[D] = DATETIMES[index][D] + date[D] + carry;
324:
325: while (true) {
326:
327: temp = maxDayInMonthFor(duration[CY], duration[M]);
328: if (duration[D] < 1) { //original duration was negative
329: duration[D] = duration[D]
330: + maxDayInMonthFor(duration[CY],
331: duration[M] - 1);
332: carry = -1;
333: } else if (duration[D] > temp) {
334: duration[D] = duration[D] - temp;
335: carry = 1;
336: } else {
337: break;
338: }
339: temp = duration[M] + carry;
340: duration[M] = modulo(temp, 1, 13);
341: duration[CY] = duration[CY] + fQuotient(temp, 1, 13);
342: }
343:
344: duration[utc] = 'Z';
345: return duration;
346: }
347:
348: protected String dateToString(int[] date) {
349: message.setLength(0);
350: int negate = 1;
351: if (date[CY] < 0) {
352: message.append('-');
353: negate = -1;
354: }
355: message.append('P');
356: message.append(negate * date[CY]);
357: message.append('Y');
358: message.append(negate * date[M]);
359: message.append('M');
360: message.append(negate * date[D]);
361: message.append('D');
362: message.append('T');
363: message.append(negate * date[h]);
364: message.append('H');
365: message.append(negate * date[m]);
366: message.append('M');
367: message.append(negate * date[s]);
368: message.append('.');
369: message.append(negate * date[ms]);
370: message.append('S');
371:
372: return message.toString();
373: }
374: }
|