0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/util/tags/sakai_2-4-1/util-impl/impl/src/java/org/sakaiproject/time/impl/BasicTimeService.java $
0003: * $Id: BasicTimeService.java 10790 2006-06-16 17:17:39Z bkirschn@umich.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.time.impl;
0021:
0022: import java.text.DateFormat;
0023: import java.text.SimpleDateFormat;
0024: import java.util.GregorianCalendar;
0025: import java.util.Hashtable;
0026: import java.util.StringTokenizer;
0027: import java.util.TimeZone;
0028: import java.util.Locale;
0029:
0030: import org.apache.commons.logging.Log;
0031: import org.apache.commons.logging.LogFactory;
0032: import org.sakaiproject.util.ResourceLoader;
0033: import org.sakaiproject.entity.api.ResourceProperties;
0034: import org.sakaiproject.time.api.Time;
0035: import org.sakaiproject.time.api.TimeBreakdown;
0036: import org.sakaiproject.time.api.TimeRange;
0037: import org.sakaiproject.time.api.TimeService;
0038: import org.sakaiproject.tool.cover.SessionManager;
0039: import org.sakaiproject.user.api.Preferences;
0040: import org.sakaiproject.user.cover.PreferencesService;
0041:
0042: /**
0043: * <p>
0044: * BasicTimeService implements the Sakai TimeService
0045: * </p>
0046: */
0047: public class BasicTimeService implements TimeService {
0048: /** Our log (commons). */
0049: private static Log M_log = LogFactory.getLog(TimeService.class);
0050:
0051: /** The time zone for our GMT times. */
0052: protected TimeZone M_tz = null;
0053:
0054: /**
0055: * a calendar to clone for GMT time construction
0056: */
0057: protected GregorianCalendar M_GCal = null;
0058:
0059: /**
0060: * The formatter for our special GMT format(s)
0061: */
0062: protected DateFormat M_fmtA = null;
0063:
0064: protected DateFormat M_fmtB = null;
0065:
0066: protected DateFormat M_fmtC = null;
0067:
0068: protected DateFormat M_fmtD = null;
0069:
0070: protected DateFormat M_fmtE = null;
0071:
0072: protected DateFormat M_fmtG = null;
0073:
0074: // Map of Timezone/Locales to LocalTzFormat objects
0075: private Hashtable M_localeTzMap = new Hashtable();
0076:
0077: // Map of userIds to Timezone/Locales
0078: private Hashtable M_userTzMap = new Hashtable();
0079:
0080: // Default Timezone/Locale
0081: protected String[] M_tz_locale_default = new String[] {
0082: TimeZone.getDefault().getID(),
0083: Locale.getDefault().toString() };
0084:
0085: // Used for fetching user's default language locale
0086: ResourceLoader rl = new ResourceLoader();
0087:
0088: /**********************************************************************************************************************************************************************************************************************************************************
0089: * Dependencies and their setter methods
0090: *********************************************************************************************************************************************************************************************************************************************************/
0091:
0092: /**********************************************************************************************************************************************************************************************************************************************************
0093: * Init and Destroy
0094: *********************************************************************************************************************************************************************************************************************************************************/
0095:
0096: /**
0097: * Final initialization, once all dependencies are set.
0098: */
0099: public void init() {
0100: /** The time zone for our GMT times. */
0101: M_tz = TimeZone.getTimeZone("GMT");
0102:
0103: M_log.info("init()");
0104:
0105: /**
0106: * a calendar to clone for GMT time construction
0107: */
0108: M_GCal = getCalendar(M_tz, 0, 0, 0, 0, 0, 0, 0);
0109:
0110: // Note: formatting for GMT time representations
0111: M_fmtA = (DateFormat) (new SimpleDateFormat("yyyyMMddHHmmssSSS"));
0112: M_fmtB = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
0113: DateFormat.SHORT);
0114: M_fmtC = DateFormat.getTimeInstance(DateFormat.SHORT);
0115: M_fmtD = DateFormat.getDateInstance(DateFormat.MEDIUM);
0116: M_fmtE = (DateFormat) (new SimpleDateFormat("yyyyMMddHHmmss"));
0117: M_fmtG = (DateFormat) (new SimpleDateFormat("yyyy/DDD/HH/")); // that's year, day of year, hour
0118:
0119: M_fmtA.setTimeZone(M_tz);
0120: M_fmtB.setTimeZone(M_tz);
0121: M_fmtC.setTimeZone(M_tz);
0122: M_fmtD.setTimeZone(M_tz);
0123: M_fmtE.setTimeZone(M_tz);
0124: M_fmtG.setTimeZone(M_tz);
0125: }
0126:
0127: /**
0128: * Final cleanup.
0129: */
0130: public void destroy() {
0131: M_log.info("destroy()");
0132: }
0133:
0134: /** Return string with user's prefered timezone _and_ prefered locale
0135: ** (dates are formatted according to the locale)
0136: **/
0137: protected String[] getUserTimezoneLocale() {
0138: // Check if we already cached this user's timezone
0139: String userId = SessionManager.getCurrentSessionUserId();
0140: if (userId == null)
0141: return M_tz_locale_default;
0142:
0143: String[] timeZoneLocale = (String[]) M_userTzMap.get(userId);
0144: if (timeZoneLocale != null)
0145: return timeZoneLocale;
0146:
0147: // Otherwise, get the user's preferred time zone
0148: Preferences prefs = PreferencesService.getPreferences(userId);
0149: ResourceProperties tzProps = prefs
0150: .getProperties(TimeService.APPLICATION_ID);
0151: String timeZone = tzProps.getProperty(TimeService.TIMEZONE_KEY);
0152:
0153: if (timeZone == null || timeZone.equals(""))
0154: timeZone = TimeZone.getDefault().getID();
0155:
0156: // Now, get user's prefered locale
0157: String localeId = rl.getLocale().toString();
0158:
0159: timeZoneLocale = new String[] { timeZone, localeId };
0160:
0161: M_userTzMap.put(userId, timeZoneLocale);
0162:
0163: return timeZoneLocale;
0164: }
0165:
0166: protected LocalTzFormat getLocalTzFormat(String[] timeZoneLocale) {
0167: LocalTzFormat tzFormat = (LocalTzFormat) M_localeTzMap
0168: .get(timeZoneLocale);
0169:
0170: if (tzFormat == null) {
0171: tzFormat = new LocalTzFormat(timeZoneLocale[0],
0172: timeZoneLocale[1]);
0173: M_localeTzMap.put(timeZoneLocale, tzFormat);
0174: }
0175:
0176: return tzFormat;
0177: }
0178:
0179: /**********************************************************************************************************************************************************************************************************************************************************
0180: * Work interface methods: org.sakai.service.time.TimeService
0181: *********************************************************************************************************************************************************************************************************************************************************/
0182:
0183: /**
0184: * no arg constructor
0185: */
0186: public BasicTimeService() {
0187: }
0188:
0189: /**
0190: * {@inheritDoc}
0191: */
0192: public Time newTime() {
0193: return new MyTime();
0194: }
0195:
0196: /**
0197: * {@inheritDoc}
0198: */
0199: public Time newTimeGmt(String value) {
0200: return new MyTime(value);
0201: }
0202:
0203: /**
0204: * {@inheritDoc}
0205: */
0206: public Time newTime(long value) {
0207: return new MyTime(value);
0208: }
0209:
0210: /**
0211: * {@inheritDoc}
0212: */
0213: public Time newTime(GregorianCalendar cal) {
0214: return new MyTime(cal.getTimeInMillis());
0215: }
0216:
0217: /**
0218: * {@inheritDoc}
0219: */
0220: public Time newTimeGmt(int year, int month, int day, int hour,
0221: int minute, int second, int millisecond) {
0222: return new MyTime(M_tz, year, month, day, hour, minute, second,
0223: millisecond);
0224: }
0225:
0226: /**
0227: * {@inheritDoc}
0228: */
0229: public Time newTimeGmt(TimeBreakdown breakdown) {
0230: return new MyTime(M_tz, breakdown);
0231: }
0232:
0233: /**
0234: * {@inheritDoc}
0235: */
0236: public Time newTimeLocal(int year, int month, int day, int hour,
0237: int minute, int second, int millisecond) {
0238: TimeZone tz_local = getLocalTzFormat(getUserTimezoneLocale()).M_tz_local;
0239: return new MyTime(tz_local, year, month, day, hour, minute,
0240: second, millisecond);
0241: }
0242:
0243: /**
0244: * {@inheritDoc}
0245: */
0246: public Time newTimeLocal(TimeBreakdown breakdown) {
0247: TimeZone tz_local = getLocalTzFormat(getUserTimezoneLocale()).M_tz_local;
0248: return new MyTime(tz_local, breakdown);
0249: }
0250:
0251: /**
0252: * {@inheritDoc}
0253: */
0254: public TimeBreakdown newTimeBreakdown(int year, int month, int day,
0255: int hour, int minute, int second, int millisecond) {
0256: return new MyTimeBreakdown(year, month, day, hour, minute,
0257: second, millisecond);
0258: }
0259:
0260: /**
0261: * {@inheritDoc}
0262: */
0263: public TimeRange newTimeRange(Time start, Time end,
0264: boolean startIncluded, boolean endIncluded) {
0265: return new MyTimeRange(start, end, startIncluded, endIncluded);
0266: }
0267:
0268: /**
0269: * {@inheritDoc}
0270: */
0271: public TimeRange newTimeRange(String value) {
0272: return new MyTimeRange(value);
0273: }
0274:
0275: /**
0276: * {@inheritDoc}
0277: */
0278: public TimeRange newTimeRange(Time startAndEnd) {
0279: return new MyTimeRange(startAndEnd);
0280: }
0281:
0282: /**
0283: * {@inheritDoc}
0284: */
0285: public TimeRange newTimeRange(long start, long duration) {
0286: return new MyTimeRange(start, duration);
0287: }
0288:
0289: /**
0290: * {@inheritDoc}
0291: */
0292: public TimeRange newTimeRange(Time start, Time end) {
0293: return new MyTimeRange(start, end);
0294: }
0295:
0296: /**
0297: * {@inheritDoc}
0298: */
0299: public TimeZone getLocalTimeZone() {
0300: return getLocalTzFormat(getUserTimezoneLocale()).M_tz_local;
0301: }
0302:
0303: /**
0304: * {@inheritDoc}
0305: */
0306: public boolean clearLocalTimeZone(String userId) {
0307: M_userTzMap.remove(userId);
0308: return true;
0309: }
0310:
0311: /**
0312: * {@inheritDoc}
0313: */
0314: public GregorianCalendar getCalendar(TimeZone zone, int year,
0315: int month, int day, int hour, int min, int sec, int ms) {
0316: GregorianCalendar rv = new GregorianCalendar(year, month, day,
0317: hour, min, sec);
0318: rv.setTimeZone(zone);
0319: rv.set(GregorianCalendar.MILLISECOND, ms);
0320:
0321: return rv;
0322: }
0323:
0324: /**
0325: * Compare two Time for differences, either may be null
0326: *
0327: * @param a
0328: * One Time.
0329: * @param b
0330: * The other Time.
0331: * @return true if the Times are different, false if they are the same.
0332: */
0333: public boolean different(Time a, Time b) {
0334: // if both null, they are the same
0335: if ((a == null) && (b == null))
0336: return false;
0337:
0338: // if either are null (they both are not), they are different
0339: if ((a == null) || (b == null))
0340: return true;
0341:
0342: // now we know neither are null, so compare
0343: return (!a.equals(b));
0344: }
0345:
0346: /**********************************************************************************************************************************************************************************************************************************************************
0347: * LocalTzFormat -- maintains a local timezone, locale and DateFormats
0348: *********************************************************************************************************************************************************************************************************************************************************/
0349:
0350: protected class LocalTzFormat {
0351: // The time zone for our local times
0352: public TimeZone M_tz_local = null;
0353:
0354: // The Locale for our local date/time formatting
0355: public Locale M_locale = null;
0356:
0357: // a calendar to clone for GMT time construction
0358: public GregorianCalendar M_GCall = null;
0359:
0360: // The formatter for our special local timezone format(s)
0361: public DateFormat M_fmtAl = null;
0362:
0363: public DateFormat M_fmtBl = null;
0364:
0365: public DateFormat M_fmtBlz = null;
0366:
0367: public DateFormat M_fmtCl = null;
0368:
0369: public DateFormat M_fmtClz = null;
0370:
0371: public DateFormat M_fmtDl = null;
0372:
0373: public DateFormat M_fmtD2 = null;
0374:
0375: public DateFormat M_fmtFl = null;
0376:
0377: private LocalTzFormat() {
0378: }; // disable default constructor
0379:
0380: public LocalTzFormat(String timeZoneId, String localeId) {
0381: M_tz_local = TimeZone.getTimeZone(timeZoneId);
0382:
0383: Locale M_locale = null;
0384: String langLoc[] = localeId.split("_");
0385: if (langLoc.length >= 2)
0386: M_locale = new Locale(langLoc[0], langLoc[1]);
0387: else
0388: M_locale = new Locale(langLoc[0]);
0389:
0390: M_fmtAl = (DateFormat) (new SimpleDateFormat(
0391: "yyyyMMddHHmmssSSS"));
0392: M_fmtBl = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
0393: DateFormat.SHORT, M_locale);
0394: M_fmtBlz = DateFormat.getDateTimeInstance(
0395: DateFormat.MEDIUM, DateFormat.LONG, M_locale);
0396: M_fmtCl = DateFormat.getTimeInstance(DateFormat.SHORT,
0397: M_locale);
0398: M_fmtClz = DateFormat.getTimeInstance(DateFormat.LONG,
0399: M_locale);
0400: M_fmtDl = DateFormat.getDateInstance(DateFormat.MEDIUM,
0401: M_locale);
0402: M_fmtD2 = DateFormat.getDateInstance(DateFormat.SHORT,
0403: M_locale);
0404: M_fmtFl = (DateFormat) (new SimpleDateFormat("HH:mm:ss"));
0405:
0406: // Strip the seconds from the Blz and Clz (default) formats
0407: try {
0408: SimpleDateFormat sdf = ((SimpleDateFormat) M_fmtBlz);
0409: String pattern = sdf.toLocalizedPattern();
0410: pattern = pattern.replaceAll(":ss", "");
0411: sdf.applyLocalizedPattern(pattern);
0412:
0413: sdf = ((SimpleDateFormat) M_fmtClz);
0414: pattern = sdf.toLocalizedPattern();
0415: pattern = pattern.replaceAll(":ss", "");
0416: sdf.applyLocalizedPattern(pattern);
0417: } catch (ClassCastException e) {
0418: // ignore -- not all locales support this
0419: }
0420:
0421: M_fmtAl.setTimeZone(M_tz_local);
0422: M_fmtBl.setTimeZone(M_tz_local);
0423: M_fmtBlz.setTimeZone(M_tz_local);
0424: M_fmtCl.setTimeZone(M_tz_local);
0425: M_fmtClz.setTimeZone(M_tz_local);
0426: M_fmtDl.setTimeZone(M_tz_local);
0427: M_fmtD2.setTimeZone(M_tz_local);
0428: M_fmtFl.setTimeZone(M_tz_local);
0429:
0430: M_GCall = getCalendar(M_tz_local, 0, 0, 0, 0, 0, 0, 0);
0431: }
0432: }
0433:
0434: /**********************************************************************************************************************************************************************************************************************************************************
0435: * TimeRange implementation
0436: *********************************************************************************************************************************************************************************************************************************************************/
0437:
0438: public class MyTimeRange implements TimeRange {
0439: // ends included?
0440: protected boolean m_startIncluded = true;
0441:
0442: protected boolean m_endIncluded = true;
0443:
0444: // start and end times
0445: protected Time m_startTime = null;
0446:
0447: protected Time m_endTime = null;
0448:
0449: public Object clone() {
0450: TimeRange obj = newTimeRange((Time) m_startTime.clone(),
0451: (Time) m_endTime.clone(), m_startIncluded,
0452: m_endIncluded);
0453:
0454: return obj;
0455:
0456: } // clone
0457:
0458: /**
0459: * construct from a two times, and start and end inclusion booleans
0460: *
0461: * @param start:
0462: * start time
0463: * @param end:
0464: * end time
0465: * @param startIncluded:
0466: * true if start is part of the range
0467: * @param endIncluded:
0468: * true of end is part of the range
0469: */
0470: public MyTimeRange(Time start, Time end, boolean startIncluded,
0471: boolean endIncluded) {
0472: m_startTime = start;
0473: m_endTime = end;
0474: m_startIncluded = startIncluded;
0475: m_endIncluded = endIncluded;
0476:
0477: // start time must be <= end time
0478: if (m_startTime.getTime() > m_endTime.getTime()) {
0479: // reverse them to fix
0480: Time t = m_startTime;
0481: m_startTime = m_endTime;
0482: m_endTime = t;
0483: }
0484:
0485: } // TimeRange
0486:
0487: /**
0488: * construct from a string, in our format
0489: *
0490: * @param str
0491: * the time range string
0492: */
0493: public MyTimeRange(String str) {
0494: parse(str, null, null);
0495:
0496: } // TimeRange
0497:
0498: /**
0499: * construct from a single time
0500: *
0501: * @param startAndEnd:
0502: * the single time value for the range
0503: */
0504: public MyTimeRange(Time startAndEnd) {
0505: this (startAndEnd, startAndEnd, true, true);
0506:
0507: } // TimeRange
0508:
0509: /**
0510: * construct from a time long and a duration long in ms
0511: *
0512: * @param start
0513: * time value
0514: * @param duration
0515: * ms duration
0516: */
0517: public MyTimeRange(long start, long duration) {
0518: m_startTime = newTime(start);
0519: m_endTime = newTime(start + duration);
0520: m_startIncluded = true;
0521: m_endIncluded = true;
0522:
0523: // start time must be <= end time
0524: if (m_startTime.getTime() > m_endTime.getTime()) {
0525: // reverse them to fix
0526: Time t = m_startTime;
0527: m_startTime = m_endTime;
0528: m_endTime = t;
0529: }
0530:
0531: } // TimeRange
0532:
0533: /**
0534: * construct from a two times - inclusive
0535: *
0536: * @param start:
0537: * the start time
0538: * @param end:
0539: * the end time
0540: */
0541: public MyTimeRange(Time start, Time end) {
0542: this (start, end, true, true);
0543:
0544: } // TimeRange
0545:
0546: /**
0547: * is this time in my range?
0548: *
0549: * @param time:
0550: * the time to check for inclusion
0551: * @return true if the time is in the range, false if not
0552: */
0553: public boolean contains(Time time) {
0554: // assume in range, unless proven otherwise
0555: boolean inRange = true;
0556:
0557: // if out of the range...
0558: if (time.before(m_startTime) || time.after(m_endTime)) {
0559: inRange = false;
0560: }
0561:
0562: // if at begin and begin not in range
0563: else if ((!m_startIncluded) && time.equals(m_startTime)) {
0564: inRange = false;
0565: }
0566:
0567: // if at the end and end not in range
0568: else if ((!m_endIncluded) && time.equals(m_endTime)) {
0569: inRange = false;
0570: }
0571:
0572: return inRange;
0573:
0574: } // contains
0575:
0576: /**
0577: * do I overlap this other range at all?
0578: *
0579: * @param range:
0580: * the time range to check for overlap
0581: * @return true if any time in range is in my range is in the other range, false if not
0582: */
0583: public boolean overlaps(TimeRange range) {
0584: boolean overlaps = false;
0585:
0586: // null range?
0587:
0588: // if my start is in the other range
0589: if (range.contains(firstTime())) {
0590: overlaps = true;
0591: }
0592:
0593: // if my end is in the other range
0594: else if (range.contains(lastTime())) {
0595: overlaps = true;
0596: }
0597:
0598: // if I contain the other range
0599: else if (contains(range)) {
0600: overlaps = true;
0601: }
0602:
0603: return overlaps;
0604:
0605: } // overlaps
0606:
0607: /**
0608: * do I completely contain this other range?
0609: *
0610: * @param range:
0611: * the time range to check for containment
0612: * @return true if range is within my time ramge
0613: */
0614: public boolean contains(TimeRange range) {
0615: // I must contain both is start and end
0616: return (contains(range.firstTime()) && contains(range
0617: .lastTime()));
0618:
0619: } // contains
0620:
0621: /**
0622: * what is the first time range included?
0623: *
0624: * @return the first time actually in the range
0625: */
0626: public Time firstTime() {
0627: return firstTime(1);
0628:
0629: } // firstTime
0630:
0631: /**
0632: * what is the last time range included?
0633: *
0634: * @return the last time actually in the range
0635: */
0636: public Time lastTime() {
0637: return lastTime(1);
0638:
0639: } // lastTime
0640:
0641: /**
0642: * what is the first time range included?
0643: *
0644: * @param fudge
0645: * How many ms to advance if the first is not included.
0646: * @return the first time actually in the range
0647: */
0648: public Time firstTime(long fudge) {
0649: // if the start is included, return this
0650: if (m_startIncluded) {
0651: return m_startTime;
0652: }
0653:
0654: // if not, return a time one ms after start
0655: Time fudgeStartTime = (Time) m_startTime.clone();
0656: fudgeStartTime.setTime(m_startTime.getTime() + fudge);
0657: return fudgeStartTime;
0658:
0659: } // firstTime
0660:
0661: /**
0662: * what is the last time range included?
0663: *
0664: * @param fudge
0665: * How many ms to decrease if the first is not included.
0666: * @return the last time actually in the range
0667: */
0668: public Time lastTime(long fudge) {
0669: // if the end is included, return this
0670: if (m_endIncluded) {
0671: return m_endTime;
0672: }
0673:
0674: // if not, return a time one ms before end
0675: Time fudgeEndTime = (Time) m_endTime.clone();
0676: fudgeEndTime.setTime(m_endTime.getTime() - fudge);
0677: return fudgeEndTime;
0678:
0679: } // lastTime
0680:
0681: /**
0682: * format the range
0683: *
0684: * @return a string representation of the time range
0685: */
0686: public String toString() {
0687: // a place to build the string (slightly larger)
0688: StringBuffer buf = new StringBuffer(64);
0689:
0690: // start with the start value, always used
0691: buf.append(m_startTime);
0692:
0693: // more that single value?
0694: if (!m_startTime.equals(m_endTime)) {
0695: // what separator to use?
0696: if (m_startIncluded && m_endIncluded) {
0697: buf.append('-');
0698: } else if ((!m_startIncluded) && (!m_endIncluded)) {
0699: buf.append('~');
0700: } else if (!m_startIncluded) {
0701: buf.append('[');
0702: } else {
0703: buf.append(']');
0704: }
0705:
0706: // add the end
0707: buf.append(m_endTime);
0708: }
0709:
0710: // return the answer as a string
0711: return buf.toString();
0712:
0713: } // toString
0714:
0715: /**
0716: * format the range - human readable
0717: *
0718: * @return a string representation of the time range, human readable
0719: */
0720: public String toStringHR() {
0721: // a place to build the string (slightly larger)
0722: StringBuffer buf = new StringBuffer(64);
0723:
0724: // start with the start value, always used
0725: buf.append(m_startTime.toStringGmtFull());
0726:
0727: // more that single value?
0728: if (!m_startTime.equals(m_endTime)) {
0729: // what separator to use?
0730: if (m_startIncluded && m_endIncluded) {
0731: buf.append(" - ");
0732: } else if ((!m_startIncluded) && (!m_endIncluded)) {
0733: buf.append(" ~ ");
0734: } else if (!m_startIncluded) {
0735: buf.append(" [ ");
0736: } else {
0737: buf.append(" ] ");
0738: }
0739:
0740: // add the end
0741: buf.append(m_endTime.toStringGmtFull());
0742: }
0743:
0744: // return the answer as a string
0745: return buf.toString();
0746:
0747: } // toStringHR
0748:
0749: /**
0750: * equals to another time range
0751: */
0752: public boolean equals(Object obj) {
0753: boolean equals = false;
0754:
0755: if (obj instanceof MyTimeRange) {
0756: equals = ((((MyTimeRange) obj).m_startIncluded == m_startIncluded)
0757: && (((MyTimeRange) obj).m_endIncluded == m_endIncluded)
0758: && (((MyTimeRange) obj).m_startTime
0759: .equals(m_startTime)) && (((MyTimeRange) obj).m_endTime
0760: .equals(m_endTime)));
0761: }
0762:
0763: return equals;
0764:
0765: } // equals
0766:
0767: /**
0768: * compute the duration, in ms, of the time range
0769: */
0770: public long duration() {
0771: return (lastTime().getTime() - firstTime().getTime());
0772:
0773: } // duration
0774:
0775: /**
0776: * parse from a string - resolve fully earliest ('!') and latest ('*') and durations ('=')
0777: *
0778: * @param str
0779: * the string to parse
0780: * @param earliest
0781: * Time to substitute for any 'earliest' values
0782: * @param latest
0783: * the Time to use for 'latest'
0784: */
0785: protected void parse(String str, Time earliest, Time latest) {
0786: try {
0787: // separate the string by '[]~-'
0788: // (we do want the delimiters as tokens, thus the true param)
0789: StringTokenizer tokenizer = new StringTokenizer(str,
0790: "[]~-", true);
0791:
0792: int tokenCount = 0;
0793: long startMs = -1;
0794: long endMs = -1;
0795: m_startTime = null;
0796: m_endTime = null;
0797:
0798: while (tokenizer.hasMoreTokens()) {
0799: tokenCount++;
0800: String next = tokenizer.nextToken();
0801:
0802: switch (tokenCount) {
0803: case 1: {
0804: if (next.charAt(0) == '=') {
0805: // use the rest as a duration in ms
0806: startMs = Long.parseLong(next.substring(1));
0807: }
0808:
0809: else {
0810: m_startTime = newTimeGmt(next);
0811: }
0812:
0813: }
0814: break;
0815:
0816: case 2: {
0817: // set the inclusions
0818: switch (next.charAt(0)) {
0819: // start not included
0820: case '[': {
0821: m_startIncluded = false;
0822: m_endIncluded = true;
0823:
0824: }
0825: break;
0826:
0827: // end not included
0828: case ']': {
0829: m_startIncluded = true;
0830: m_endIncluded = false;
0831:
0832: }
0833: break;
0834:
0835: // neither included
0836: case '~': {
0837: m_startIncluded = false;
0838: m_endIncluded = false;
0839:
0840: }
0841: break;
0842:
0843: // both included
0844: case '-': {
0845: m_startIncluded = true;
0846: m_endIncluded = true;
0847:
0848: }
0849: break;
0850:
0851: // trouble!
0852: default: {
0853: throw new Exception(next.charAt(0)
0854: + " invalid");
0855: }
0856: } // switch (next[0])
0857:
0858: }
0859: break;
0860:
0861: case 3: {
0862: if (next.charAt(0) == '=') {
0863: // use the rest as a duration in ms
0864: endMs = Long.parseLong(next.substring(1));
0865: }
0866:
0867: else {
0868: m_endTime = newTimeGmt(next);
0869: }
0870:
0871: }
0872: break;
0873:
0874: // trouble!
0875: default: {
0876: throw new Exception(">3 tokens");
0877: }
0878: } // switch (tokenCount)
0879:
0880: } // while (tokenizer.hasMoreTokens())
0881:
0882: // if either start or end was in duration, adjust (but not both!)
0883: if ((startMs != -1) && (endMs != -1)) {
0884: throw new Exception("==");
0885: }
0886:
0887: if (startMs != -1) {
0888: if (m_endTime == null) {
0889: throw new Exception("=, * null");
0890: }
0891: m_startTime = newTime(m_endTime.getTime() - startMs);
0892: } else if (endMs != -1) {
0893: if (m_startTime == null) {
0894: throw new Exception("=, ! null");
0895: }
0896: m_endTime = newTime(m_startTime.getTime() + endMs);
0897: }
0898:
0899: // if there is only one token
0900: if (tokenCount == 1) {
0901: // end is start, both included
0902: m_endTime = m_startTime;
0903: m_startIncluded = true;
0904: m_endIncluded = true;
0905: }
0906:
0907: // start time must be <= end time
0908: if (m_startTime.getTime() > m_endTime.getTime()) {
0909: // reverse them to fix
0910: Time t = m_startTime;
0911: m_startTime = m_endTime;
0912: m_endTime = t;
0913: }
0914: } catch (Exception e) {
0915: M_log.warn("parse: exception parsing: " + str + " : "
0916: + e.toString());
0917:
0918: // set a now range, just to have something
0919: m_startTime = newTime();
0920: m_endTime = m_startTime;
0921: m_startIncluded = true;
0922: m_endIncluded = true;
0923: }
0924: }
0925:
0926: /**
0927: * Shift the time range back an intervel
0928: *
0929: * @param i
0930: * time intervel in ms
0931: */
0932: public void shiftBackward(long i) {
0933: m_startTime.setTime(m_startTime.getTime() - i);
0934: m_endTime.setTime(m_endTime.getTime() - i);
0935: }
0936:
0937: /**
0938: * Shift the time range forward an intervel
0939: *
0940: * @param i
0941: * time intervel in ms
0942: */
0943: public void shiftForward(long i) {
0944: m_startTime.setTime(m_startTime.getTime() + i);
0945: m_endTime.setTime(m_endTime.getTime() + i);
0946: }
0947:
0948: /**
0949: * Enlarge or shrink the time range by multiplying a zooming factor
0950: *
0951: * @param f
0952: * zooming factor
0953: */
0954: public void zoom(double f) {
0955: long oldRange = m_endTime.getTime() - m_startTime.getTime();
0956: long center = m_startTime.getTime() + oldRange / 2;
0957: long newRange = (long) ((double) oldRange * f);
0958:
0959: m_startTime.setTime(center - newRange / 2);
0960: m_endTime.setTime(center + newRange / 2);
0961: }
0962:
0963: /**
0964: * Adjust this time range based on the difference between the origRange and the modRange, if any
0965: *
0966: * @param original
0967: * the original time range.
0968: * @param modified
0969: * the modified time range.
0970: */
0971: public void adjust(TimeRange original, TimeRange modified) {
0972: if (original.equals(modified))
0973: return;
0974:
0975: // adjust for the change in the start time
0976: m_startTime
0977: .setTime(m_startTime.getTime()
0978: + (((MyTimeRange) modified).m_startTime
0979: .getTime() - ((MyTimeRange) original).m_startTime
0980: .getTime()));
0981:
0982: // adjust for the change in the end time
0983: m_endTime
0984: .setTime(m_endTime.getTime()
0985: + (((MyTimeRange) modified).m_endTime
0986: .getTime() - ((MyTimeRange) original).m_endTime
0987: .getTime()));
0988:
0989: } // adjust
0990:
0991: /**
0992: * check if the time range is really just a single time
0993: *
0994: * @return true if the time range is a single time, false if it is not
0995: */
0996: public boolean isSingleTime() {
0997: return (m_startTime.equals(m_endTime) && m_startIncluded && m_endIncluded);
0998:
0999: } // isSingleTime
1000:
1001: } // class TimeRange
1002:
1003: /**********************************************************************************************************************************************************************************************************************************************************
1004: * TimeBreakdown implementation
1005: *********************************************************************************************************************************************************************************************************************************************************/
1006:
1007: public class MyTimeBreakdown implements TimeBreakdown {
1008: /** The parts. */
1009: protected int year;
1010:
1011: protected int month;
1012:
1013: protected int day;
1014:
1015: protected int hour;
1016:
1017: protected int min;
1018:
1019: protected int sec;
1020:
1021: protected int ms;
1022:
1023: public MyTimeBreakdown(int y, int m, int d, int h, int minutes,
1024: int s, int milliseconds) {
1025: year = y;
1026: month = m;
1027: day = d;
1028: hour = h;
1029: min = minutes;
1030: sec = s;
1031: ms = milliseconds;
1032: }
1033:
1034: public MyTimeBreakdown(TimeBreakdown other) {
1035: year = ((MyTimeBreakdown) other).year;
1036: month = ((MyTimeBreakdown) other).month;
1037: day = ((MyTimeBreakdown) other).day;
1038: hour = ((MyTimeBreakdown) other).hour;
1039: min = ((MyTimeBreakdown) other).min;
1040: sec = ((MyTimeBreakdown) other).sec;
1041: ms = ((MyTimeBreakdown) other).ms;
1042: }
1043:
1044: public String toString() {
1045: return "year: " + year + " month: " + month + " day: "
1046: + day + " hour: " + hour + " min: " + min
1047: + " sec: " + sec + " ms: " + ms;
1048: }
1049:
1050: public int getYear() {
1051: return year;
1052: }
1053:
1054: public int getMonth() {
1055: return month;
1056: }
1057:
1058: public int getDay() {
1059: return day;
1060: }
1061:
1062: public int getHour() {
1063: return hour;
1064: }
1065:
1066: public int getMin() {
1067: return min;
1068: }
1069:
1070: public int getSec() {
1071: return sec;
1072: }
1073:
1074: public int getMs() {
1075: return ms;
1076: }
1077:
1078: /**
1079: * @param i
1080: */
1081: public void setDay(int i) {
1082: day = i;
1083: }
1084:
1085: /**
1086: * @param i
1087: */
1088: public void setHour(int i) {
1089: hour = i;
1090: }
1091:
1092: /**
1093: * @param i
1094: */
1095: public void setMin(int i) {
1096: min = i;
1097: }
1098:
1099: /**
1100: * @param i
1101: */
1102: public void setMonth(int i) {
1103: month = i;
1104: }
1105:
1106: /**
1107: * @param i
1108: */
1109: public void setMs(int i) {
1110: ms = i;
1111: }
1112:
1113: /**
1114: * @param i
1115: */
1116: public void setSec(int i) {
1117: sec = i;
1118: }
1119:
1120: /**
1121: * @param i
1122: */
1123: public void setYear(int i) {
1124: year = i;
1125: }
1126: }
1127: }
|