KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > value > MonthDurationValue


1 package net.sf.saxon.value;
2 import net.sf.saxon.expr.XPathContext;
3 import net.sf.saxon.om.FastStringBuffer;
4 import net.sf.saxon.trans.DynamicError;
5 import net.sf.saxon.trans.XPathException;
6 import net.sf.saxon.type.ItemType;
7 import net.sf.saxon.type.Type;
8
9 import java.util.StringTokenizer JavaDoc;
10
11 /**
12 * A value of type xsd:yearMonthDuration
13 */

14
15 public final class MonthDurationValue extends DurationValue implements Comparable JavaDoc {
16
17     /**
18     * Private constructor for internal use
19     */

20
21     private MonthDurationValue() {
22     }
23
24     /**
25     * Constructor: create a duration value from a supplied string, in
26     * ISO 8601 format [+|-]PnYnM
27     */

28
29     public MonthDurationValue(CharSequence JavaDoc s) throws XPathException {
30
31         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(trimWhitespace(s).toString(), "-+PYM", true);
32         try {
33             if (!tok.hasMoreElements()) badDuration("empty string", s);
34             String JavaDoc part = (String JavaDoc)tok.nextElement();
35             if ("+".equals(part)) {
36                 part = (String JavaDoc)tok.nextElement();
37             } else if ("-".equals(part)) {
38                 negative = true;
39                 part = (String JavaDoc)tok.nextElement();
40             }
41             if (!"P".equals(part)) badDuration("missing 'P'", s);
42             int state = 0;
43             while (tok.hasMoreElements()) {
44                 part = (String JavaDoc)tok.nextElement();
45                 if ("T".equals(part)) {
46                     state = 4;
47                     part = (String JavaDoc)tok.nextElement();
48                 }
49                 int value = Integer.parseInt(part);
50                 if (!tok.hasMoreElements()) badDuration("missing unit letter at end", s);
51                 char delim = ((String JavaDoc)tok.nextElement()).charAt(0);
52                 switch (delim) {
53                     case 'Y':
54                         if (state > 0) badDuration("Y is out of sequence", s);
55                         years = value;
56                         state = 1;
57                         break;
58                     case 'M':
59                         if (state == 4 || state==5) {
60                             minutes = value;
61                             state = 6;
62                             break;
63                         } else if (state == 0 || state==1) {
64                             months = value;
65                             state = 2;
66                             break;
67                         } else {
68                             badDuration("M is out of sequence", s);
69                         }
70
71                    default:
72                         badDuration("misplaced " + delim, s);
73                 }
74             }
75             normalize();
76
77         } catch (NumberFormatException JavaDoc err) {
78             badDuration("non-numeric component", s);
79         }
80     }
81
82     /**
83     * Convert to string
84     * @return ISO 8601 representation.
85     */

86
87     public CharSequence JavaDoc getStringValueCS() {
88
89         // The canonical representation has months in the range 0-11
90

91         int mm = years*12 + months;
92         int y = mm / 12;
93         int m = mm % 12;
94
95         FastStringBuffer sb = new FastStringBuffer(32);
96         if (negative) {
97             sb.append('-');
98         }
99         sb.append('P');
100         if (y!=0) {
101             sb.append(y + "Y");
102         }
103         if (m!=0 || y==0) {
104             sb.append(m + "M");
105         }
106         return sb;
107
108     }
109
110     /**
111     * Normalize the value, for example 90M becomes 1H30M
112     */

113
114     public void normalize() {
115         if (months >= 12) {
116             years += (months / 12);
117             months = months % 12;
118         }
119     }
120
121     /**
122     * Get the number of months in the duration
123     */

124
125     public int getLengthInMonths() {
126         return (years*12 + months) * (negative ? -1 : +1);
127     }
128
129     /**
130     * Construct a duration value as a number of months.
131     */

132
133     public static MonthDurationValue fromMonths(int months) {
134         MonthDurationValue mdv = new MonthDurationValue();
135         mdv.negative = (months<0);
136         mdv.months = (months<0 ? -months : months);
137         mdv.normalize();
138         return mdv;
139     }
140
141     /**
142     * Multiply duration by a number
143     */

144
145     public DurationValue multiply(double n, XPathContext context) throws XPathException {
146         double m = (double)getLengthInMonths();
147         double product = n*m;
148         if (Double.isInfinite(product) || product > Integer.MAX_VALUE || product < Integer.MIN_VALUE) {
149             DynamicError err = new DynamicError("Overflow when multiplying/dividing a duration by a number");
150             err.setErrorCode("FODT0002");
151             err.setXPathContext(context);
152             throw err;
153         }
154         return fromMonths((int)product);
155     }
156
157     /**
158      * Find the ratio between two durations
159      * @param other the dividend
160      * @return the ratio, as a double
161      * @throws XPathException
162      */

163
164     public DoubleValue divide(DurationValue other, XPathContext context) throws XPathException {
165         if (other instanceof MonthDurationValue) {
166             return new DoubleValue(
167                     (double)this.getLengthInMonths() / (double)((MonthDurationValue)other).getLengthInMonths());
168         } else {
169             throw new DynamicError("Cannot divide two durations of different type");
170         }
171     }
172
173     /**
174     * Add two year-month-durations
175     */

176
177     public DurationValue add(DurationValue other, XPathContext context) throws XPathException {
178         if (other instanceof MonthDurationValue) {
179             return fromMonths(this.getLengthInMonths() +
180                     ((MonthDurationValue)other).getLengthInMonths());
181         } else {
182             throw new DynamicError("Cannot add two durations of different type");
183         }
184     }
185
186     /**
187     * Subtract two year-month-durations
188     */

189
190     public DurationValue subtract(DurationValue other, XPathContext context) throws XPathException {
191         if (other instanceof MonthDurationValue) {
192             return fromMonths(this.getLengthInMonths() -
193                     ((MonthDurationValue)other).getLengthInMonths());
194         } else {
195             throw new DynamicError("Cannot subtract two durations of different type");
196         }
197     }
198
199     /**
200     * Determine the data type of the exprssion
201     * @return Type.YEAR_MONTH_DURATION,
202     */

203
204     public ItemType getItemType() {
205         return Type.YEAR_MONTH_DURATION_TYPE;
206     }
207
208     /**
209     * Convert to Java object (for passing to external functions)
210     */

211
212     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
213         if (target.isAssignableFrom(DurationValue.class)) {
214             return this;
215         } else if (target==String JavaDoc.class || target==CharSequence JavaDoc.class) {
216             return getStringValue();
217         } else if (target==Object JavaDoc.class) {
218             return getStringValue();
219         } else {
220             throw new DynamicError("Conversion of yearMonthDuration to " + target.getName() +
221                         " is not supported");
222         }
223     }
224
225
226     /**
227     * Compare the value to another duration value
228     * @param other The other dateTime value
229     * @return negative value if this one is the earler, 0 if they are chronologically equal,
230     * positive value if this one is the later. For this purpose, dateTime values with an unknown
231     * timezone are considered to be UTC values (the Comparable interface requires
232     * a total ordering).
233     * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
234     * is declared as Object to satisfy the Comparable interface)
235     */

236
237     public int compareTo(Object JavaDoc other) {
238         if (other instanceof MonthDurationValue) {
239             return this.getLengthInMonths() - ((MonthDurationValue)other).getLengthInMonths();
240         } else {
241             throw new ClassCastException JavaDoc("Cannot compare a yearMonthDuration to an object of class "
242                     + other.getClass());
243         }
244     }
245
246
247 }
248
249 //
250
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
251
// you may not use this file except in compliance with the License. You may obtain a copy of the
252
// License at http://www.mozilla.org/MPL/
253
//
254
// Software distributed under the License is distributed on an "AS IS" basis,
255
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
256
// See the License for the specific language governing rights and limitations under the License.
257
//
258
// The Original Code is: all this file.
259
//
260
// The Initial Developer of the Original Code is Michael H. Kay
261
//
262
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
263
//
264
// Contributor(s): none.
265
//
266

267
Popular Tags