View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 1999-2002,2004,2005 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *      http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package com.sun.org.apache.xerces.internal.impl.dv.xs;
22  
23  import java.math.BigDecimal;
24  import java.math.BigInteger;
25  
26  import javax.xml.datatype.DatatypeConstants;
27  import javax.xml.datatype.Duration;
28  
29  import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
30  import com.sun.org.apache.xerces.internal.impl.dv.ValidationContext;
31  
32  /**
33   * Validator for <duration> datatype (W3C Schema Datatypes)
34   *
35   * @xerces.internal
36   *
37   * @author Elena Litani
38   * @author Gopal Sharma, SUN Microsystem Inc.
39   * @version $Id: DurationDV.java,v 1.7 2010-11-01 04:39:47 joehw Exp $
40   */
41  public class DurationDV extends AbstractDateTimeDV {
42  
43          public static final int DURATION_TYPE = 0;
44          public static final int YEARMONTHDURATION_TYPE = 1;
45          public static final int DAYTIMEDURATION_TYPE = 2;
46      // order-relation on duration is a partial order. The dates below are used to
47      // for comparison of 2 durations, based on the fact that
48      // duration x and y is x<=y iff s+x<=s+y
49      // see 3.2.6 duration W3C schema datatype specs
50      //
51      // the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}
52      private final static DateTimeData[] DATETIMES= {
53          new DateTimeData(1696, 9, 1, 0, 0, 0, 'Z', null, true, null),
54          new DateTimeData(1697, 2, 1, 0, 0, 0, 'Z', null, true, null),
55          new DateTimeData(1903, 3, 1, 0, 0, 0, 'Z', null, true, null),
56          new DateTimeData(1903, 7, 1, 0, 0, 0, 'Z', null, true, null)};
57  
58      public Object getActualValue(String content, ValidationContext context) throws InvalidDatatypeValueException{
59          try{
60              return parse(content, DURATION_TYPE);
61          } catch (Exception ex) {
62              throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{content, "duration"});
63          }
64      }
65  
66      /**
67       * Parses, validates and computes normalized version of duration object
68       *
69       * @param str    The lexical representation of duration object PnYn MnDTnH nMnS
70       * @param durationType
71       * @return normalized date representation
72       * @exception SchemaDateTimeException Invalid lexical representation
73       */
74      protected DateTimeData parse(String str, int durationType) throws SchemaDateTimeException{
75          int len = str.length();
76          DateTimeData date= new DateTimeData(str, this);
77  
78          int start = 0;
79          char c=str.charAt(start++);
80          if ( c!='P' && c!='-' ) {
81              throw new SchemaDateTimeException();
82          }
83          else {
84              date.utc=(c=='-')?'-':0;
85              if ( c=='-' && str.charAt(start++)!='P' ) {
86                  throw new SchemaDateTimeException();
87              }
88          }
89  
90          int negate = 1;
91          //negative duration
92          if ( date.utc=='-' ) {
93              negate = -1;
94  
95          }
96          //at least one number and designator must be seen after P
97          boolean designator = false;
98  
99          int endDate = indexOf (str, start, len, 'T');
100         if ( endDate == -1 ) {
101             endDate = len;
102         }
103         else if (durationType == YEARMONTHDURATION_TYPE) {
104             throw new SchemaDateTimeException();
105         }
106 
107         //find 'Y'
108         int end = indexOf (str, start, endDate, 'Y');
109         if ( end!=-1 ) {
110 
111             if (durationType == DAYTIMEDURATION_TYPE) {
112                 throw new SchemaDateTimeException();
113             }
114 
115             //scan year
116             date.year=negate * parseInt(str,start,end);
117             start = end+1;
118             designator = true;
119         }
120 
121         end = indexOf (str, start, endDate, 'M');
122         if ( end!=-1 ) {
123 
124             if (durationType == DAYTIMEDURATION_TYPE) {
125                 throw new SchemaDateTimeException();
126             }
127 
128             //scan month
129             date.month=negate * parseInt(str,start,end);
130             start = end+1;
131             designator = true;
132         }
133 
134         end = indexOf (str, start, endDate, 'D');
135         if ( end!=-1 ) {
136 
137             if(durationType == YEARMONTHDURATION_TYPE) {
138                 throw new SchemaDateTimeException();
139             }
140 
141             //scan day
142             date.day=negate * parseInt(str,start,end);
143             start = end+1;
144             designator = true;
145         }
146 
147         if ( len == endDate && start!=len ) {
148             throw new SchemaDateTimeException();
149         }
150         if ( len !=endDate ) {
151 
152             //scan hours, minutes, seconds
153             //REVISIT: can any item include a decimal fraction or only seconds?
154             //
155 
156             end = indexOf (str, ++start, len, 'H');
157             if ( end!=-1 ) {
158                 //scan hours
159                 date.hour=negate * parseInt(str,start,end);
160                 start=end+1;
161                 designator = true;
162             }
163 
164             end = indexOf (str, start, len, 'M');
165             if ( end!=-1 ) {
166                 //scan min
167                 date.minute=negate * parseInt(str,start,end);
168                 start=end+1;
169                 designator = true;
170             }
171 
172             end = indexOf (str, start, len, 'S');
173             if ( end!=-1 ) {
174                 //scan seconds
175                 date.second = negate * parseSecond(str, start, end);
176                 start=end+1;
177                 designator = true;
178             }
179             // no additional data shouls appear after last item
180             // P1Y1M1DT is illigal value as well
181             if ( start != len || str.charAt(--start)=='T' ) {
182                 throw new SchemaDateTimeException();
183             }
184         }
185 
186         if ( !designator ) {
187             throw new SchemaDateTimeException();
188         }
189 
190         return date;
191     }
192 
193     /**
194      * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
195      *
196      * @param date1  Unnormalized duration
197      * @param date2  Unnormalized duration
198      * @param strict (min/max)Exclusive strict == true ( LESS_THAN ) or ( GREATER_THAN )
199      *               (min/max)Inclusive strict == false (LESS_EQUAL) or (GREATER_EQUAL)
200      * @return INDETERMINATE if the order relationship between date1 and date2 is indeterminate.
201      * EQUAL if the order relation between date1 and date2 is EQUAL.
202      * If the strict parameter is true, return LESS_THAN if date1 is less than date2 and
203      * return GREATER_THAN if date1 is greater than date2.
204      * If the strict parameter is false, return LESS_THAN if date1 is less than OR equal to date2 and
205      * return GREATER_THAN if date1 is greater than OR equal to date2
206      */
207     protected  short compareDates(DateTimeData date1, DateTimeData date2, boolean strict) {
208 
209         //REVISIT: this is unoptimazed vs of comparing 2 durations
210         //         Algorithm is described in 3.2.6.2 W3C Schema Datatype specs
211         //
212 
213         //add constA to both durations
214         short resultA, resultB= INDETERMINATE;
215         //try and see if the objects are equal
216         resultA = compareOrder (date1, date2);
217         if ( resultA == 0 ) {
218             return 0;
219         }
220 
221         DateTimeData[] result = new DateTimeData[2];
222         result[0] = new DateTimeData(null, this);
223         result[1] = new DateTimeData(null, this);
224 
225         //long comparison algorithm is required
226         DateTimeData tempA = addDuration (date1, DATETIMES[0], result[0]);
227         DateTimeData tempB = addDuration (date2, DATETIMES[0], result[1]);
228         resultA =  compareOrder(tempA, tempB);
229         if ( resultA == INDETERMINATE ) {
230             return INDETERMINATE;
231         }
232 
233         tempA = addDuration(date1, DATETIMES[1], result[0]);
234         tempB = addDuration(date2, DATETIMES[1], result[1]);
235         resultB = compareOrder(tempA, tempB);
236         resultA = compareResults(resultA, resultB, strict);
237         if (resultA == INDETERMINATE) {
238             return INDETERMINATE;
239         }
240 
241         tempA = addDuration(date1, DATETIMES[2], result[0]);
242         tempB = addDuration(date2, DATETIMES[2], result[1]);
243         resultB = compareOrder(tempA, tempB);
244         resultA = compareResults(resultA, resultB, strict);
245         if (resultA == INDETERMINATE) {
246             return INDETERMINATE;
247         }
248 
249         tempA = addDuration(date1, DATETIMES[3], result[0]);
250         tempB = addDuration(date2, DATETIMES[3], result[1]);
251         resultB = compareOrder(tempA, tempB);
252         resultA = compareResults(resultA, resultB, strict);
253 
254         return resultA;
255     }
256 
257     private short compareResults(short resultA, short resultB, boolean strict){
258 
259       if ( resultB == INDETERMINATE ) {
260             return INDETERMINATE;
261         }
262         else if ( resultA!=resultB && strict ) {
263             return INDETERMINATE;
264         }
265         else if ( resultA!=resultB && !strict ) {
266             if ( resultA!=0 && resultB!=0 ) {
267                 return INDETERMINATE;
268             }
269             else {
270                 return (resultA!=0)?resultA:resultB;
271             }
272         }
273         return resultA;
274     }
275 
276     private DateTimeData addDuration(DateTimeData date, DateTimeData addto, DateTimeData duration) {
277 
278         //REVISIT: some code could be shared between normalize() and this method,
279         //         however is it worth moving it? The structures are different...
280         //
281 
282         resetDateObj(duration);
283         //add months (may be modified additionaly below)
284         int temp = addto.month + date.month;
285         duration.month = modulo (temp, 1, 13);
286         int carry = fQuotient (temp, 1, 13);
287 
288         //add years (may be modified additionaly below)
289         duration.year=addto.year + date.year + carry;
290 
291         //add seconds
292         double dtemp = addto.second + date.second;
293         carry = (int)Math.floor(dtemp/60);
294         duration.second = dtemp - carry*60;
295 
296         //add minutes
297         temp = addto.minute +date.minute + carry;
298         carry = fQuotient (temp, 60);
299         duration.minute= mod(temp, 60, carry);
300 
301         //add hours
302         temp = addto.hour + date.hour + carry;
303         carry = fQuotient(temp, 24);
304         duration.hour = mod(temp, 24, carry);
305 
306 
307         duration.day=addto.day + date.day + carry;
308 
309         while ( true ) {
310 
311             temp=maxDayInMonthFor(duration.year, duration.month);
312             if ( duration.day < 1 ) { //original duration was negative
313                 duration.day = duration.day + maxDayInMonthFor(duration.year, duration.month-1);
314                 carry=-1;
315             }
316             else if ( duration.day > temp ) {
317                 duration.day = duration.day - temp;
318                 carry=1;
319             }
320             else {
321                 break;
322             }
323             temp = duration.month+carry;
324             duration.month = modulo(temp, 1, 13);
325             duration.year = duration.year+fQuotient(temp, 1, 13);
326         }
327 
328         duration.utc='Z';
329         return duration;
330     }
331 
332     protected double parseSecond(String buffer, int start, int end)
333         throws NumberFormatException {
334         int dot = -1;
335         for (int i = start; i < end; i++) {
336             char ch = buffer.charAt(i);
337             if (ch == '.')
338                 dot = i;
339             else if (ch > '9' || ch < '0')
340                 throw new NumberFormatException("'" + buffer + "' has wrong format");
341         }
342         if (dot+1 == end) {
343             throw new NumberFormatException("'" + buffer + "' has wrong format");
344         }
345         double value = Double.parseDouble(buffer.substring(start, end));
346         if (value == Double.POSITIVE_INFINITY) {
347             throw new NumberFormatException("'" + buffer + "' has wrong format");
348         }
349         return value;
350     }
351 
352     protected String dateToString(DateTimeData date) {
353         StringBuffer message = new StringBuffer(30);
354         if ( date.year<0 || date.month<0 || date.day<0
355                 || date.hour<0 || date.minute<0 || date.second<0) {
356             message.append('-');
357         }
358         message.append('P');
359         message.append((date.year < 0?-1:1) * date.year);
360         message.append('Y');
361         message.append((date.month < 0?-1:1) * date.month);
362         message.append('M');
363         message.append((date.day < 0?-1:1) * date.day);
364         message.append('D');
365         message.append('T');
366         message.append((date.hour < 0?-1:1) * date.hour);
367         message.append('H');
368         message.append((date.minute < 0?-1:1) * date.minute);
369         message.append('M');
370         append2(message, (date.second < 0?-1:1) * date.second);
371         message.append('S');
372 
373         return message.toString();
374     }
375 
376     protected Duration getDuration(DateTimeData date) {
377         int sign = 1;
378         if ( date.year<0 || date.month<0 || date.day<0
379                 || date.hour<0 || date.minute<0 || date.second<0) {
380             sign = -1;
381         }
382         return datatypeFactory.newDuration(sign == 1,
383                 date.year != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.year):null,
384                 date.month != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.month):null,
385                 date.day != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.day):null,
386                 date.hour != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.hour):null,
387                 date.minute != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.minute):null,
388                 date.second != DatatypeConstants.FIELD_UNDEFINED?new BigDecimal(String.valueOf(sign*date.second)):null);
389     }
390 }