001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/sam/trunk/component/src/java/org/sakaiproject/tool/assessment/qti/util/Iso8601TimeInterval.java $
003: * $Id: Iso8601TimeInterval.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the"License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.assessment.qti.util;
021:
022: import java.util.Calendar;
023: import java.util.regex.Matcher;
024: import java.util.regex.Pattern;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.sakaiproject.tool.assessment.qti.exception.Iso8601FormatException;
030:
031: /**
032: * Based on ISO8601 Specification.
033: *
034: * @author <a href="mailto:lance@indiana.edu">Lance Speelmon</a>
035: * @version $Id: Iso8601TimeInterval.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
036: */
037: public class Iso8601TimeInterval {
038: private static Log log = LogFactory
039: .getLog(Iso8601TimeInterval.class);
040: private static final long SECONDS = 1000L;
041: private static final long MINUTES = 60L * SECONDS;
042: private static final long HOURS = 60L * MINUTES;
043: private static final long DAYS = 24L * HOURS;
044: private static final long WEEKS = 7L * DAYS;
045: private static final long MONTHS = 30L * DAYS;
046: private static final long YEARS = 365L * DAYS;
047:
048: // i.e.: P2Y10M15DT10H30M20S
049: private static final Pattern PATTERN = Pattern
050: .compile("-?P(?:(\\d*)Y)?(?:(\\d*)M)?(?:(\\d*)W)?(?:(\\d*)D)?T?(?:(\\d*)H)?(?:(\\d*)M)?(?:(\\d*)S)?");
051:
052: // one part of the interval has a fixed date/time
053: private boolean bounded = false;
054: private boolean recurring = false;
055: private String iso8601TimeInterval;
056: private Long duration;
057: private Calendar begin;
058: private Calendar end;
059: private Integer years;
060: private Integer months;
061: private Integer weeks;
062: private Integer days;
063: private Integer hours;
064: private Integer minutes;
065: private Integer seconds;
066: private boolean negative = false;
067:
068: /**
069: * Creates a new Iso8601TimeInterval object.
070: *
071: * @param iso8601TimeInterval DOCUMENTATION PENDING
072: *
073: * @throws Iso8601FormatException DOCUMENTATION PENDING
074: */
075: public Iso8601TimeInterval(String iso8601TimeInterval)
076: throws Iso8601FormatException {
077: if (log.isDebugEnabled()) {
078: log.debug("new TimeInterval(String " + iso8601TimeInterval
079: + ")");
080: }
081:
082: this .iso8601TimeInterval = iso8601TimeInterval;
083: this .duration = new Long(parseLong(iso8601TimeInterval));
084: }
085:
086: /**
087: * Creates a new Iso8601TimeInterval object.
088: *
089: * @param ms DOCUMENTATION PENDING
090: */
091: public Iso8601TimeInterval(long ms) {
092: if (log.isDebugEnabled()) {
093: log.debug("new Iso8601TimeInterval(long " + ms + ")");
094: }
095:
096: this .duration = new Long(ms);
097: }
098:
099: /**
100: * DOCUMENTATION PENDING
101: *
102: * @param iso8601TimeInterval DOCUMENTATION PENDING
103: *
104: * @return DOCUMENTATION PENDING
105: *
106: * @throws Iso8601FormatException DOCUMENTATION PENDING
107: */
108: private long parseLong(String iso8601TimeInterval)
109: throws Iso8601FormatException {
110: if (log.isDebugEnabled()) {
111: log.debug("parseLong(String " + iso8601TimeInterval + ")");
112: }
113:
114: if (iso8601TimeInterval == null) {
115: throw new Iso8601FormatException(
116: "illegal String iso8601TimeInterval argument: "
117: + iso8601TimeInterval);
118: }
119:
120: iso8601TimeInterval = iso8601TimeInterval.toUpperCase();
121: Matcher matcher = PATTERN.matcher(iso8601TimeInterval);
122: if (matcher.matches()) {
123: if (iso8601TimeInterval.indexOf("-") == 0) {
124: log.debug("negative = true");
125: negative = true;
126: }
127:
128: if (log.isDebugEnabled()) {
129: for (int i = 1; i <= matcher.groupCount(); i++) {
130: log.debug("matcher.group(" + i + ")="
131: + matcher.group(i));
132: }
133: }
134:
135: String tmp = null;
136:
137: /* years */
138: int years = 0;
139: tmp = matcher.group(1);
140: if (tmp != null) {
141: years = Integer.parseInt(tmp);
142: this .years = new Integer(tmp);
143: }
144:
145: /* months */
146: int months = 0;
147: tmp = matcher.group(2);
148: if (tmp != null) {
149: this .months = new Integer(tmp);
150: months = this .months.intValue();
151: }
152:
153: /* weeks */
154: int weeks = 0;
155: tmp = matcher.group(3);
156: if (tmp != null) {
157: this .weeks = new Integer(tmp);
158: weeks = this .weeks.intValue();
159: }
160:
161: /* days */
162: int days = 0;
163: tmp = matcher.group(4);
164: if (tmp != null) {
165: this .days = new Integer(tmp);
166: days = this .days.intValue();
167: }
168:
169: /* hours */
170: int hours = 0;
171: tmp = matcher.group(5);
172: if (tmp != null) {
173: this .hours = new Integer(tmp);
174: hours = this .hours.intValue();
175: }
176:
177: /* minutes */
178: int minutes = 0;
179: tmp = matcher.group(6);
180: if (tmp != null) {
181: this .minutes = new Integer(tmp);
182: minutes = this .minutes.intValue();
183: }
184:
185: /* seconds */
186: int seconds = 0;
187: tmp = matcher.group(7);
188: if (tmp != null) {
189: this .seconds = new Integer(tmp);
190: seconds = this .seconds.intValue();
191: }
192:
193: if (log.isDebugEnabled()) {
194: log.debug("years=" + years + ", months=" + months
195: + ", weeks=" + weeks + ", days=" + days
196: + ", hours=" + hours + ", minutes=" + minutes
197: + ", seconds=" + seconds);
198: }
199:
200: long durtmp = (years * YEARS) + (months * MONTHS)
201: + (weeks * WEEKS) + (days * DAYS) + (hours * HOURS)
202: + (minutes * MINUTES) + (seconds * SECONDS);
203: if (negative) {
204: return -durtmp;
205: } else {
206: return durtmp;
207: }
208: } else {
209: throw new Iso8601FormatException("Invalid ISO8601 format: "
210: + iso8601TimeInterval);
211: }
212: }
213:
214: /**
215: * DOCUMENTATION PENDING
216: *
217: * @return DOCUMENTATION PENDING
218: */
219: public boolean isBounded() {
220: log.debug("isBounded()");
221:
222: // return bounded;
223: throw new UnsupportedOperationException();
224: }
225:
226: /**
227: * DOCUMENTATION PENDING
228: *
229: * @return DOCUMENTATION PENDING
230: */
231: public boolean isRecurring() {
232: // return recurring;
233: throw new UnsupportedOperationException();
234: }
235:
236: /**
237: * DOCUMENTATION PENDING
238: *
239: * @return DOCUMENTATION PENDING
240: */
241: public long getDuration() {
242: log.debug("getDuration()");
243:
244: return duration.longValue();
245: }
246:
247: /**
248: * DOCUMENTATION PENDING
249: *
250: * @return DOCUMENTATION PENDING
251: */
252: public Calendar getBegin() {
253: throw new UnsupportedOperationException();
254:
255: // return begin;
256: }
257:
258: /**
259: * DOCUMENTATION PENDING
260: *
261: * @return DOCUMENTATION PENDING
262: */
263: public Calendar getEnd() {
264: throw new UnsupportedOperationException();
265:
266: // return end;
267: }
268:
269: /**
270: * DOCUMENTATION PENDING
271: *
272: * @return DOCUMENTATION PENDING
273: */
274: public String toString() {
275: if (this .iso8601TimeInterval == null) {
276: this .createIso8601TimeInterval();
277: }
278:
279: return this .iso8601TimeInterval;
280: }
281:
282: /**
283: * DOCUMENTATION PENDING
284: */
285: public void createString() {
286: log.debug("regenerateString()");
287: this .reset(); // clear any old bean properties
288: this .createIso8601TimeInterval();
289: }
290:
291: /**
292: * DOCUMENTATION PENDING
293: */
294: private void createIso8601TimeInterval() {
295: log.debug("createIso8601TimeInterval()");
296: if (this .duration == null) {
297: throw new IllegalStateException("duration is null!");
298: }
299:
300: if (this .duration.longValue() < 0) {
301: negative = true;
302: }
303:
304: long remainder = this .duration.longValue();
305:
306: /* anything above weeks incurs a loss of precision */
307:
308: // int years = (int)(remainder / YEARS);
309: // remainder %= YEARS;
310: /* anything above weeks incurs a loss of precision */
311:
312: // int months = (int)(remainder / MONTHS);
313: // remainder %= MONTHS;
314: int weeks = (int) (remainder / WEEKS);
315: remainder %= WEEKS;
316:
317: int days = (int) (remainder / DAYS);
318: remainder %= DAYS;
319:
320: int hours = (int) (remainder / HOURS);
321: remainder %= HOURS;
322:
323: int minutes = (int) (remainder / MINUTES);
324: remainder %= MINUTES;
325:
326: int seconds = (int) (remainder / SECONDS);
327:
328: StringBuffer sb = new StringBuffer();
329: if (negative) {
330: sb.append("-");
331: }
332:
333: sb.append("P");
334: if (weeks != 0) {
335: if (negative) {
336: sb.append(-weeks);
337: this .weeks = new Integer(-weeks);
338: } else {
339: sb.append(weeks);
340: this .weeks = new Integer(weeks);
341: }
342:
343: sb.append("W");
344: }
345:
346: if (days != 0) {
347: if (negative) {
348: sb.append(-days);
349: this .days = new Integer(-days);
350: } else {
351: sb.append(days);
352: this .days = new Integer(days);
353: }
354:
355: sb.append("D");
356: }
357:
358: if ((hours != 0) || (minutes != 0) || (seconds != 0)) {
359: sb.append("T");
360: if (hours != 0) {
361: if (negative) {
362: sb.append(-hours);
363: this .hours = new Integer(-hours);
364: } else {
365: sb.append(hours);
366: this .hours = new Integer(hours);
367: }
368:
369: sb.append("H");
370: }
371:
372: if (minutes != 0) {
373: if (negative) {
374: sb.append(-minutes);
375: this .minutes = new Integer(-minutes);
376: } else {
377: sb.append(minutes);
378: this .minutes = new Integer(minutes);
379: }
380:
381: sb.append("M");
382: }
383:
384: if (seconds != 0) {
385: if (negative) {
386: sb.append(-seconds);
387: this .seconds = new Integer(-seconds);
388: } else {
389: sb.append(seconds);
390: this .seconds = new Integer(seconds);
391: }
392:
393: sb.append("S");
394: }
395: }
396:
397: this .iso8601TimeInterval = sb.toString();
398: }
399:
400: /**
401: * DOCUMENTATION PENDING
402: *
403: * @param args DOCUMENTATION PENDING
404: */
405: public static void main(String[] args) {
406: org.apache.log4j.BasicConfigurator.configure();
407: try {
408: Iso8601TimeInterval ti = new Iso8601TimeInterval(
409: "-P2y10m15dT10H30M20S");
410:
411: // Iso8601TimeInterval ti = new Iso8601TimeInterval("PT1S");
412: // Iso8601TimeInterval ti = new Iso8601TimeInterval("P6W");
413: // Iso8601TimeInterval ti = new Iso8601TimeInterval(null);
414: long duration = ti.getDuration();
415: log.debug("duration=" + duration);
416: log.debug("weeks=" + ti.getWeeks());
417: log.debug("days=" + ti.getDays());
418:
419: Iso8601TimeInterval t2 = new Iso8601TimeInterval(duration);
420: log.debug("t2=" + t2);
421: log.debug("t2.getWeeks()=" + t2.getWeeks());
422: log.debug("t2.getHours()=" + t2.getHours());
423: } catch (Iso8601FormatException ex) {
424: log.error(ex.getMessage(), ex);
425: }
426: }
427:
428: /**
429: * DOCUMENT ME!
430: *
431: * @return
432: */
433: public Integer getDays() {
434: return days;
435: }
436:
437: /**
438: * DOCUMENT ME!
439: *
440: * @return
441: */
442: public Integer getHours() {
443: return hours;
444: }
445:
446: /**
447: * DOCUMENT ME!
448: *
449: * @return
450: */
451: public Integer getMinutes() {
452: return minutes;
453: }
454:
455: /**
456: * DOCUMENT ME!
457: *
458: * @return
459: */
460: public Integer getMonths() {
461: return months;
462: }
463:
464: /**
465: * DOCUMENT ME!
466: *
467: * @return
468: */
469: public Integer getSeconds() {
470: return seconds;
471: }
472:
473: /**
474: * DOCUMENT ME!
475: *
476: * @return
477: */
478: public Integer getWeeks() {
479: return weeks;
480: }
481:
482: /**
483: * DOCUMENT ME!
484: *
485: * @return
486: */
487: public Integer getYears() {
488: return years;
489: }
490:
491: /**
492: * DOCUMENTATION PENDING
493: *
494: * @return DOCUMENTATION PENDING
495: */
496: public boolean isNegative() {
497: return this .negative;
498: }
499:
500: /**
501: * DOCUMENTATION PENDING
502: */
503: private void reset() {
504: log.debug("reset()");
505: this .years = null;
506: this .months = null;
507: this .weeks = null;
508: this .days = null;
509: this .hours = null;
510: this .minutes = null;
511: this .seconds = null;
512: this .negative = false;
513: }
514: }
|