Source Code Cross Referenced for SegmentedTimeline.java in  » Chart » jfreechart » org » jfree » chart » axis » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Chart » jfreechart » org.jfree.chart.axis 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* ===========================================================
0002:         * JFreeChart : a free chart library for the Java(tm) platform
0003:         * ===========================================================
0004:         *
0005:         * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006:         *
0007:         * Project Info:  http://www.jfree.org/jfreechart/index.html
0008:         *
0009:         * This library is free software; you can redistribute it and/or modify it 
0010:         * under the terms of the GNU Lesser General Public License as published by 
0011:         * the Free Software Foundation; either version 2.1 of the License, or 
0012:         * (at your option) any later version.
0013:         *
0014:         * This library is distributed in the hope that it will be useful, but 
0015:         * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
0016:         * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
0017:         * License for more details.
0018:         *
0019:         * You should have received a copy of the GNU Lesser General Public
0020:         * License along with this library; if not, write to the Free Software
0021:         * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
0022:         * USA.  
0023:         *
0024:         * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
0025:         * in the United States and other countries.]
0026:         *
0027:         * -----------------------
0028:         * SegmentedTimeline.java
0029:         * -----------------------
0030:         * (C) Copyright 2003-2007, by Bill Kelemen and Contributors.
0031:         *
0032:         * Original Author:  Bill Kelemen;
0033:         * Contributor(s):   David Gilbert (for Object Refinery Limited);
0034:         *
0035:         * $Id: SegmentedTimeline.java,v 1.9.2.3 2007/02/02 14:32:42 mungady Exp $
0036:         *
0037:         * Changes
0038:         * -------
0039:         * 23-May-2003 : Version 1 (BK);
0040:         * 15-Aug-2003 : Implemented Cloneable (DG);
0041:         * 01-Jun-2004 : Modified to compile with JDK 1.2.2 (DG);
0042:         * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
0043:         * 04-Nov-2004 : Reverted change of 30-Sep-2004, won't work with JDK 1.3 (DG);
0044:         * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
0045:         * ------------- JFREECHART 1.0.x ---------------------------------------------
0046:         * 14-Nov-2006 : Fix in toTimelineValue(long) to avoid stack overflow (DG);
0047:         * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
0048:         * 
0049:         */
0050:
0051:        package org.jfree.chart.axis;
0052:
0053:        import java.io.Serializable;
0054:        import java.util.ArrayList;
0055:        import java.util.Calendar;
0056:        import java.util.Collections;
0057:        import java.util.Date;
0058:        import java.util.GregorianCalendar;
0059:        import java.util.Iterator;
0060:        import java.util.List;
0061:        import java.util.SimpleTimeZone;
0062:        import java.util.TimeZone;
0063:
0064:        /**
0065:         * A {@link Timeline} that implements a "segmented" timeline with included, 
0066:         * excluded and exception segments.
0067:         * <P>
0068:         * A Timeline will present a series of values to be used for an axis. Each
0069:         * Timeline must provide transformation methods between domain values and
0070:         * timeline values.
0071:         * <P>
0072:         * A timeline can be used as parameter to a 
0073:         * {@link org.jfree.chart.axis.DateAxis} to define the values that this axis 
0074:         * supports. This class implements a timeline formed by segments of equal 
0075:         * length (ex. days, hours, minutes) where some segments can be included in the
0076:         * timeline and others excluded. Therefore timelines like "working days" or
0077:         * "working hours" can be created where non-working days or non-working hours 
0078:         * respectively can be removed from the timeline, and therefore from the axis.
0079:         * This creates a smooth plot with equal separation between all included 
0080:         * segments.
0081:         * <P>
0082:         * Because Timelines were created mainly for Date related axis, values are
0083:         * represented as longs instead of doubles. In this case, the domain value is
0084:         * just the number of milliseconds since January 1, 1970, 00:00:00 GMT as 
0085:         * defined by the getTime() method of {@link java.util.Date}.
0086:         * <P>
0087:         * In this class, a segment is defined as a unit of time of fixed length. 
0088:         * Examples of segments are: days, hours, minutes, etc. The size of a segment 
0089:         * is defined as the number of milliseconds in the segment. Some useful segment
0090:         * sizes are defined as constants in this class: DAY_SEGMENT_SIZE, 
0091:         * HOUR_SEGMENT_SIZE, FIFTEEN_MINUTE_SEGMENT_SIZE and MINUTE_SEGMENT_SIZE.
0092:         * <P>
0093:         * Segments are group together to form a Segment Group. Each Segment Group will
0094:         * contain a number of Segments included and a number of Segments excluded. This
0095:         * Segment Group structure will repeat for the whole timeline.
0096:         * <P>
0097:         * For example, a working days SegmentedTimeline would be formed by a group of
0098:         * 7 daily segments, where there are 5 included (Monday through Friday) and 2
0099:         * excluded (Saturday and Sunday) segments.
0100:         * <P>
0101:         * Following is a diagram that explains the major attributes that define a 
0102:         * segment.  Each box is one segment and must be of fixed length (ms, second, 
0103:         * hour, day, etc).
0104:         * <p>
0105:         * <pre>
0106:         * start time
0107:         *   |
0108:         *   v
0109:         *   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 ...
0110:         * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+...
0111:         * |  |  |  |  |  |EE|EE|  |  |  |  |  |EE|EE|  |  |  |  |  |EE|EE|
0112:         * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+...
0113:         *  \____________/ \___/            \_/
0114:         *        \/         |               |
0115:         *     included   excluded        segment
0116:         *     segments   segments         size
0117:         *  \_________  _______/
0118:         *            \/
0119:         *       segment group
0120:         * </pre>
0121:         * Legend:<br>
0122:         * &lt;space&gt; = Included segment<br>
0123:         * EE      = Excluded segments in the base timeline<br>
0124:         * <p>
0125:         * In the example, the following segment attributes are presented:
0126:         * <ul>
0127:         * <li>segment size: the size of each segment in ms.
0128:         * <li>start time: the start of the first segment of the first segment group to
0129:         *     consider.
0130:         * <li>included segments: the number of segments to include in the group.
0131:         * <li>excluded segments: the number of segments to exclude in the group.
0132:         * </ul>
0133:         * <p>
0134:         * Exception Segments are allowed. These exception segments are defined as
0135:         * segments that would have been in the included segments of the Segment Group,
0136:         * but should be excluded for special reasons. In the previous working days
0137:         * SegmentedTimeline example, holidays would be considered exceptions.
0138:         * <P>
0139:         * Additionally the <code>startTime</code>, or start of the first Segment of 
0140:         * the smallest segment group needs to be defined. This startTime could be 
0141:         * relative to January 1, 1970, 00:00:00 GMT or any other date. This creates a 
0142:         * point of reference to start counting Segment Groups. For example, for the 
0143:         * working days SegmentedTimeline, the <code>startTime</code> could be 
0144:         * 00:00:00 GMT of the first Monday after January 1, 1970. In this class, the 
0145:         * constant FIRST_MONDAY_AFTER_1900 refers to a reference point of the first 
0146:         * Monday of the last century.
0147:         * <p>
0148:         * A SegmentedTimeline can include a baseTimeline. This combination of 
0149:         * timelines allows the creation of more complex timelines. For example, in 
0150:         * order to implement a SegmentedTimeline for an intraday stock trading 
0151:         * application, where the trading period is defined as 9:00 AM through 4:00 PM 
0152:         * Monday through Friday, two SegmentedTimelines are used. The first one (the 
0153:         * baseTimeline) would be a working day SegmentedTimeline (daily timeline 
0154:         * Monday through Friday). On top of this baseTimeline, a second one is defined
0155:         * that maps the 9:00 AM to 4:00 PM period. Because the baseTimeline defines a 
0156:         * timeline of Monday through Friday, the resulting (combined) timeline will 
0157:         * expose the period 9:00 AM through 4:00 PM only on Monday through Friday, 
0158:         * and will remove all other intermediate intervals.
0159:         * <P>
0160:         * Two factory methods newMondayThroughFridayTimeline() and
0161:         * newFifteenMinuteTimeline() are provided as examples to create special
0162:         * SegmentedTimelines.
0163:         *
0164:         * @see org.jfree.chart.axis.DateAxis
0165:         */
0166:        public class SegmentedTimeline implements  Timeline, Cloneable,
0167:                Serializable {
0168:
0169:            /** For serialization. */
0170:            private static final long serialVersionUID = 1093779862539903110L;
0171:
0172:            ////////////////////////////////////////////////////////////////////////////
0173:            // predetermined segments sizes
0174:            ////////////////////////////////////////////////////////////////////////////
0175:
0176:            /** Defines a day segment size in ms. */
0177:            public static final long DAY_SEGMENT_SIZE = 24 * 60 * 60 * 1000;
0178:
0179:            /** Defines a one hour segment size in ms. */
0180:            public static final long HOUR_SEGMENT_SIZE = 60 * 60 * 1000;
0181:
0182:            /** Defines a 15-minute segment size in ms. */
0183:            public static final long FIFTEEN_MINUTE_SEGMENT_SIZE = 15 * 60 * 1000;
0184:
0185:            /** Defines a one-minute segment size in ms. */
0186:            public static final long MINUTE_SEGMENT_SIZE = 60 * 1000;
0187:
0188:            ////////////////////////////////////////////////////////////////////////////
0189:            // other constants
0190:            ////////////////////////////////////////////////////////////////////////////
0191:
0192:            /**
0193:             * Utility constant that defines the startTime as the first monday after 
0194:             * 1/1/1970.  This should be used when creating a SegmentedTimeline for 
0195:             * Monday through Friday. See static block below for calculation of this 
0196:             * constant.
0197:             */
0198:            public static long FIRST_MONDAY_AFTER_1900;
0199:
0200:            /**
0201:             * Utility TimeZone object that has no DST and an offset equal to the 
0202:             * default TimeZone. This allows easy arithmetic between days as each one 
0203:             * will have equal size.
0204:             */
0205:            public static TimeZone NO_DST_TIME_ZONE;
0206:
0207:            /**
0208:             * This is the default time zone where the application is running. See 
0209:             * getTime() below where we make use of certain transformations between 
0210:             * times in the default time zone and the no-dst time zone used for our 
0211:             * calculations.
0212:             */
0213:            public static TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
0214:
0215:            /**
0216:             * This will be a utility calendar that has no DST but is shifted relative 
0217:             * to the default time zone's offset.
0218:             */
0219:            private Calendar workingCalendarNoDST = new GregorianCalendar(
0220:                    NO_DST_TIME_ZONE);
0221:
0222:            /**
0223:             * This will be a utility calendar that used the default time zone.
0224:             */
0225:            private Calendar workingCalendar = Calendar.getInstance();
0226:
0227:            ////////////////////////////////////////////////////////////////////////////
0228:            // private attributes
0229:            ////////////////////////////////////////////////////////////////////////////
0230:
0231:            /** Segment size in ms. */
0232:            private long segmentSize;
0233:
0234:            /** Number of consecutive segments to include in a segment group. */
0235:            private int segmentsIncluded;
0236:
0237:            /** Number of consecutive segments to exclude in a segment group. */
0238:            private int segmentsExcluded;
0239:
0240:            /** Number of segments in a group (segmentsIncluded + segmentsExcluded). */
0241:            private int groupSegmentCount;
0242:
0243:            /** 
0244:             * Start of time reference from time zero (1/1/1970). 
0245:             * This is the start of segment #0. 
0246:             */
0247:            private long startTime;
0248:
0249:            /** Consecutive ms in segmentsIncluded (segmentsIncluded * segmentSize). */
0250:            private long segmentsIncludedSize;
0251:
0252:            /** Consecutive ms in segmentsExcluded (segmentsExcluded * segmentSize). */
0253:            private long segmentsExcludedSize;
0254:
0255:            /** ms in a segment group (segmentsIncludedSize + segmentsExcludedSize). */
0256:            private long segmentsGroupSize;
0257:
0258:            /**
0259:             * List of exception segments (exceptions segments that would otherwise be
0260:             * included based on the periodic (included, excluded) grouping).
0261:             */
0262:            private List exceptionSegments = new ArrayList();
0263:
0264:            /**
0265:             * This base timeline is used to specify exceptions at a higher level. For 
0266:             * example, if we are a intraday timeline and want to exclude holidays, 
0267:             * instead of having to exclude all intraday segments for the holiday, 
0268:             * segments from this base timeline can be excluded. This baseTimeline is 
0269:             * always optional and is only a convenience method.
0270:             * <p>
0271:             * Additionally, all excluded segments from this baseTimeline will be 
0272:             * considered exceptions at this level.
0273:             */
0274:            private SegmentedTimeline baseTimeline;
0275:
0276:            /** A flag that controls whether or not to adjust for daylight saving. */
0277:            private boolean adjustForDaylightSaving = false;
0278:
0279:            ////////////////////////////////////////////////////////////////////////////
0280:            // static block
0281:            ////////////////////////////////////////////////////////////////////////////
0282:
0283:            static {
0284:                // make a time zone with no DST for our Calendar calculations
0285:                int offset = TimeZone.getDefault().getRawOffset();
0286:                NO_DST_TIME_ZONE = new SimpleTimeZone(offset, "UTC-" + offset);
0287:
0288:                // calculate midnight of first monday after 1/1/1900 relative to 
0289:                // current locale
0290:                Calendar cal = new GregorianCalendar(NO_DST_TIME_ZONE);
0291:                cal.set(1900, 0, 1, 0, 0, 0);
0292:                cal.set(Calendar.MILLISECOND, 0);
0293:                while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
0294:                    cal.add(Calendar.DATE, 1);
0295:                }
0296:                // FIRST_MONDAY_AFTER_1900 = cal.getTime().getTime();  
0297:                // preceding code won't work with JDK 1.3
0298:                FIRST_MONDAY_AFTER_1900 = cal.getTime().getTime();
0299:            }
0300:
0301:            ////////////////////////////////////////////////////////////////////////////
0302:            // constructors and factory methods
0303:            ////////////////////////////////////////////////////////////////////////////
0304:
0305:            /**
0306:             * Constructs a new segmented timeline, optionaly using another segmented
0307:             * timeline as its base. This chaining of SegmentedTimelines allows further
0308:             * segmentation into smaller timelines.
0309:             *
0310:             * If a base
0311:             *
0312:             * @param segmentSize the size of a segment in ms. This time unit will be
0313:             *        used to compute the included and excluded segments of the 
0314:             *        timeline.
0315:             * @param segmentsIncluded Number of consecutive segments to include.
0316:             * @param segmentsExcluded Number of consecutive segments to exclude.
0317:             */
0318:            public SegmentedTimeline(long segmentSize, int segmentsIncluded,
0319:                    int segmentsExcluded) {
0320:
0321:                this .segmentSize = segmentSize;
0322:                this .segmentsIncluded = segmentsIncluded;
0323:                this .segmentsExcluded = segmentsExcluded;
0324:
0325:                this .groupSegmentCount = this .segmentsIncluded
0326:                        + this .segmentsExcluded;
0327:                this .segmentsIncludedSize = this .segmentsIncluded
0328:                        * this .segmentSize;
0329:                this .segmentsExcludedSize = this .segmentsExcluded
0330:                        * this .segmentSize;
0331:                this .segmentsGroupSize = this .segmentsIncludedSize
0332:                        + this .segmentsExcludedSize;
0333:
0334:            }
0335:
0336:            /**
0337:             * Factory method to create a Monday through Friday SegmentedTimeline.
0338:             * <P>
0339:             * The <code>startTime</code> of the resulting timeline will be midnight 
0340:             * of the first Monday after 1/1/1900.
0341:             *
0342:             * @return A fully initialized SegmentedTimeline.
0343:             */
0344:            public static SegmentedTimeline newMondayThroughFridayTimeline() {
0345:                SegmentedTimeline timeline = new SegmentedTimeline(
0346:                        DAY_SEGMENT_SIZE, 5, 2);
0347:                timeline.setStartTime(FIRST_MONDAY_AFTER_1900);
0348:                return timeline;
0349:            }
0350:
0351:            /**
0352:             * Factory method to create a 15-min, 9:00 AM thought 4:00 PM, Monday 
0353:             * through Friday SegmentedTimeline.
0354:             * <P>
0355:             * This timeline uses a segmentSize of FIFTEEN_MIN_SEGMENT_SIZE. The 
0356:             * segment group is defined as 28 included segments (9:00 AM through 
0357:             * 4:00 PM) and 68 excluded segments (4:00 PM through 9:00 AM the next day).
0358:             * <P>
0359:             * In order to exclude Saturdays and Sundays it uses a baseTimeline that 
0360:             * only includes Monday through Friday days.
0361:             * <P>
0362:             * The <code>startTime</code> of the resulting timeline will be 9:00 AM 
0363:             * after the startTime of the baseTimeline. This will correspond to 9:00 AM
0364:             * of the first Monday after 1/1/1900.
0365:             *
0366:             * @return A fully initialized SegmentedTimeline.
0367:             */
0368:            public static SegmentedTimeline newFifteenMinuteTimeline() {
0369:                SegmentedTimeline timeline = new SegmentedTimeline(
0370:                        FIFTEEN_MINUTE_SEGMENT_SIZE, 28, 68);
0371:                timeline.setStartTime(FIRST_MONDAY_AFTER_1900 + 36
0372:                        * timeline.getSegmentSize());
0373:                timeline.setBaseTimeline(newMondayThroughFridayTimeline());
0374:                return timeline;
0375:            }
0376:
0377:            /**
0378:             * Returns the flag that controls whether or not the daylight saving 
0379:             * adjustment is applied.
0380:             * 
0381:             * @return A boolean.
0382:             */
0383:            public boolean getAdjustForDaylightSaving() {
0384:                return this .adjustForDaylightSaving;
0385:            }
0386:
0387:            /**
0388:             * Sets the flag that controls whether or not the daylight saving adjustment
0389:             * is applied.
0390:             * 
0391:             * @param adjust  the flag.
0392:             */
0393:            public void setAdjustForDaylightSaving(boolean adjust) {
0394:                this .adjustForDaylightSaving = adjust;
0395:            }
0396:
0397:            ////////////////////////////////////////////////////////////////////////////
0398:            // operations
0399:            ////////////////////////////////////////////////////////////////////////////
0400:
0401:            /**
0402:             * Sets the start time for the timeline. This is the beginning of segment 
0403:             * zero.
0404:             *
0405:             * @param millisecond  the start time (encoded as in java.util.Date).
0406:             */
0407:            public void setStartTime(long millisecond) {
0408:                this .startTime = millisecond;
0409:            }
0410:
0411:            /**
0412:             * Returns the start time for the timeline. This is the beginning of 
0413:             * segment zero.
0414:             * 
0415:             * @return The start time.
0416:             */
0417:            public long getStartTime() {
0418:                return this .startTime;
0419:            }
0420:
0421:            /**
0422:             * Returns the number of segments excluded per segment group.
0423:             * 
0424:             * @return The number of segments excluded.
0425:             */
0426:            public int getSegmentsExcluded() {
0427:                return this .segmentsExcluded;
0428:            }
0429:
0430:            /**
0431:             * Returns the size in milliseconds of the segments excluded per segment 
0432:             * group.
0433:             * 
0434:             * @return The size in milliseconds.
0435:             */
0436:            public long getSegmentsExcludedSize() {
0437:                return this .segmentsExcludedSize;
0438:            }
0439:
0440:            /**
0441:             * Returns the number of segments in a segment group. This will be equal to
0442:             * segments included plus segments excluded.
0443:             * 
0444:             * @return The number of segments.
0445:             */
0446:            public int getGroupSegmentCount() {
0447:                return this .groupSegmentCount;
0448:            }
0449:
0450:            /**
0451:             * Returns the size in milliseconds of a segment group. This will be equal 
0452:             * to size of the segments included plus the size of the segments excluded.
0453:             * 
0454:             * @return The segment group size in milliseconds.
0455:             */
0456:            public long getSegmentsGroupSize() {
0457:                return this .segmentsGroupSize;
0458:            }
0459:
0460:            /**
0461:             * Returns the number of segments included per segment group.
0462:             * 
0463:             * @return The number of segments.
0464:             */
0465:            public int getSegmentsIncluded() {
0466:                return this .segmentsIncluded;
0467:            }
0468:
0469:            /**
0470:             * Returns the size in ms of the segments included per segment group.
0471:             * 
0472:             * @return The segment size in milliseconds.
0473:             */
0474:            public long getSegmentsIncludedSize() {
0475:                return this .segmentsIncludedSize;
0476:            }
0477:
0478:            /**
0479:             * Returns the size of one segment in ms.
0480:             * 
0481:             * @return The segment size in milliseconds.
0482:             */
0483:            public long getSegmentSize() {
0484:                return this .segmentSize;
0485:            }
0486:
0487:            /**
0488:             * Returns a list of all the exception segments. This list is not 
0489:             * modifiable.
0490:             * 
0491:             * @return The exception segments.
0492:             */
0493:            public List getExceptionSegments() {
0494:                return Collections.unmodifiableList(this .exceptionSegments);
0495:            }
0496:
0497:            /**
0498:             * Sets the exception segments list.
0499:             * 
0500:             * @param exceptionSegments  the exception segments.
0501:             */
0502:            public void setExceptionSegments(List exceptionSegments) {
0503:                this .exceptionSegments = exceptionSegments;
0504:            }
0505:
0506:            /**
0507:             * Returns our baseTimeline, or <code>null</code> if none.
0508:             * 
0509:             * @return The base timeline.
0510:             */
0511:            public SegmentedTimeline getBaseTimeline() {
0512:                return this .baseTimeline;
0513:            }
0514:
0515:            /**
0516:             * Sets the base timeline.
0517:             * 
0518:             * @param baseTimeline  the timeline.
0519:             */
0520:            public void setBaseTimeline(SegmentedTimeline baseTimeline) {
0521:
0522:                // verify that baseTimeline is compatible with us
0523:                if (baseTimeline != null) {
0524:                    if (baseTimeline.getSegmentSize() < this .segmentSize) {
0525:                        throw new IllegalArgumentException(
0526:                                "baseTimeline.getSegmentSize() is smaller than segmentSize");
0527:                    } else if (baseTimeline.getStartTime() > this .startTime) {
0528:                        throw new IllegalArgumentException(
0529:                                "baseTimeline.getStartTime() is after startTime");
0530:                    } else if ((baseTimeline.getSegmentSize() % this .segmentSize) != 0) {
0531:                        throw new IllegalArgumentException(
0532:                                "baseTimeline.getSegmentSize() is not multiple of "
0533:                                        + "segmentSize");
0534:                    } else if (((this .startTime - baseTimeline.getStartTime()) % this .segmentSize) != 0) {
0535:                        throw new IllegalArgumentException(
0536:                                "baseTimeline is not aligned");
0537:                    }
0538:                }
0539:
0540:                this .baseTimeline = baseTimeline;
0541:            }
0542:
0543:            /**
0544:             * Translates a value relative to the domain value (all Dates) into a value
0545:             * relative to the segmented timeline. The values relative to the segmented
0546:             * timeline are all consecutives starting at zero at the startTime.
0547:             *
0548:             * @param millisecond  the millisecond (as encoded by java.util.Date).
0549:             * 
0550:             * @return The timeline value.
0551:             */
0552:            public long toTimelineValue(long millisecond) {
0553:
0554:                long result;
0555:                long rawMilliseconds = millisecond - this .startTime;
0556:                long groupMilliseconds = rawMilliseconds
0557:                        % this .segmentsGroupSize;
0558:                long groupIndex = rawMilliseconds / this .segmentsGroupSize;
0559:
0560:                if (groupMilliseconds >= this .segmentsIncludedSize) {
0561:                    result = toTimelineValue(this .startTime
0562:                            + this .segmentsGroupSize * (groupIndex + 1));
0563:                } else {
0564:                    Segment segment = getSegment(millisecond);
0565:                    if (segment.inExceptionSegments()) {
0566:                        do {
0567:                            segment = getSegment(millisecond = segment
0568:                                    .getSegmentEnd() + 1);
0569:                        } while (segment.inExceptionSegments());
0570:                        result = toTimelineValue(millisecond);
0571:                    } else {
0572:                        long shiftedSegmentedValue = millisecond
0573:                                - this .startTime;
0574:                        long x = shiftedSegmentedValue % this .segmentsGroupSize;
0575:                        long y = shiftedSegmentedValue / this .segmentsGroupSize;
0576:
0577:                        long wholeExceptionsBeforeDomainValue = getExceptionSegmentCount(
0578:                                this .startTime, millisecond - 1);
0579:
0580:                        //                long partialTimeInException = 0;
0581:                        //                Segment ss = getSegment(millisecond);
0582:                        //                if (ss.inExceptionSegments()) {
0583:                        //                    partialTimeInException = millisecond 
0584:                        //     - ss.getSegmentStart();
0585:                        //                }
0586:
0587:                        if (x < this .segmentsIncludedSize) {
0588:                            result = this .segmentsIncludedSize * y + x
0589:                                    - wholeExceptionsBeforeDomainValue
0590:                                    * this .segmentSize;
0591:                            // - partialTimeInException;; 
0592:                        } else {
0593:                            result = this .segmentsIncludedSize * (y + 1)
0594:                                    - wholeExceptionsBeforeDomainValue
0595:                                    * this .segmentSize;
0596:                            // - partialTimeInException;
0597:                        }
0598:                    }
0599:                }
0600:
0601:                return result;
0602:            }
0603:
0604:            /**
0605:             * Translates a date into a value relative to the segmented timeline. The 
0606:             * values relative to the segmented timeline are all consecutives starting 
0607:             * at zero at the startTime.
0608:             *
0609:             * @param date  date relative to the domain.
0610:             * 
0611:             * @return The timeline value (in milliseconds).
0612:             */
0613:            public long toTimelineValue(Date date) {
0614:                return toTimelineValue(getTime(date));
0615:                //return toTimelineValue(dateDomainValue.getTime());
0616:            }
0617:
0618:            /**
0619:             * Translates a value relative to the timeline into a millisecond.
0620:             *
0621:             * @param timelineValue  the timeline value (in milliseconds).
0622:             * 
0623:             * @return The domain value (in milliseconds).
0624:             */
0625:            public long toMillisecond(long timelineValue) {
0626:
0627:                // calculate the result as if no exceptions
0628:                Segment result = new Segment(this .startTime + timelineValue
0629:                        + (timelineValue / this .segmentsIncludedSize)
0630:                        * this .segmentsExcludedSize);
0631:
0632:                long lastIndex = this .startTime;
0633:
0634:                // adjust result for any exceptions in the result calculated
0635:                while (lastIndex <= result.segmentStart) {
0636:
0637:                    // skip all whole exception segments in the range
0638:                    long exceptionSegmentCount;
0639:                    while ((exceptionSegmentCount = getExceptionSegmentCount(
0640:                            lastIndex, (result.millisecond / this .segmentSize)
0641:                                    * this .segmentSize - 1)) > 0) {
0642:                        lastIndex = result.segmentStart;
0643:                        // move forward exceptionSegmentCount segments skipping 
0644:                        // excluded segments
0645:                        for (int i = 0; i < exceptionSegmentCount; i++) {
0646:                            do {
0647:                                result.inc();
0648:                            } while (result.inExcludeSegments());
0649:                        }
0650:                    }
0651:                    lastIndex = result.segmentStart;
0652:
0653:                    // skip exception or excluded segments we may fall on
0654:                    while (result.inExceptionSegments()
0655:                            || result.inExcludeSegments()) {
0656:                        result.inc();
0657:                        lastIndex += this .segmentSize;
0658:                    }
0659:
0660:                    lastIndex++;
0661:                }
0662:
0663:                return getTimeFromLong(result.millisecond);
0664:            }
0665:
0666:            /**
0667:             * Converts a date/time value to take account of daylight savings time.
0668:             * 
0669:             * @param date  the milliseconds.
0670:             * 
0671:             * @return The milliseconds.
0672:             */
0673:            public long getTimeFromLong(long date) {
0674:                long result = date;
0675:                if (this .adjustForDaylightSaving) {
0676:                    this .workingCalendarNoDST.setTime(new Date(date));
0677:                    this .workingCalendar.set(this .workingCalendarNoDST
0678:                            .get(Calendar.YEAR), this .workingCalendarNoDST
0679:                            .get(Calendar.MONTH), this .workingCalendarNoDST
0680:                            .get(Calendar.DATE), this .workingCalendarNoDST
0681:                            .get(Calendar.HOUR_OF_DAY),
0682:                            this .workingCalendarNoDST.get(Calendar.MINUTE),
0683:                            this .workingCalendarNoDST.get(Calendar.SECOND));
0684:                    this .workingCalendar
0685:                            .set(Calendar.MILLISECOND,
0686:                                    this .workingCalendarNoDST
0687:                                            .get(Calendar.MILLISECOND));
0688:                    // result = this.workingCalendar.getTimeInMillis();  
0689:                    // preceding code won't work with JDK 1.3
0690:                    result = this .workingCalendar.getTime().getTime();
0691:                }
0692:                return result;
0693:            }
0694:
0695:            /**
0696:             * Returns <code>true</code> if a value is contained in the timeline.
0697:             * 
0698:             * @param millisecond  the value to verify.
0699:             * 
0700:             * @return <code>true</code> if value is contained in the timeline.
0701:             */
0702:            public boolean containsDomainValue(long millisecond) {
0703:                Segment segment = getSegment(millisecond);
0704:                return segment.inIncludeSegments();
0705:            }
0706:
0707:            /**
0708:             * Returns <code>true</code> if a value is contained in the timeline.
0709:             * 
0710:             * @param date  date to verify
0711:             * 
0712:             * @return <code>true</code> if value is contained in the timeline
0713:             */
0714:            public boolean containsDomainValue(Date date) {
0715:                return containsDomainValue(getTime(date));
0716:            }
0717:
0718:            /**
0719:             * Returns <code>true</code> if a range of values are contained in the 
0720:             * timeline. This is implemented verifying that all segments are in the 
0721:             * range.
0722:             *
0723:             * @param domainValueStart start of the range to verify
0724:             * @param domainValueEnd end of the range to verify
0725:             * 
0726:             * @return <code>true</code> if the range is contained in the timeline
0727:             */
0728:            public boolean containsDomainRange(long domainValueStart,
0729:                    long domainValueEnd) {
0730:                if (domainValueEnd < domainValueStart) {
0731:                    throw new IllegalArgumentException("domainValueEnd ("
0732:                            + domainValueEnd + ") < domainValueStart ("
0733:                            + domainValueStart + ")");
0734:                }
0735:                Segment segment = getSegment(domainValueStart);
0736:                boolean contains = true;
0737:                do {
0738:                    contains = (segment.inIncludeSegments());
0739:                    if (segment.contains(domainValueEnd)) {
0740:                        break;
0741:                    } else {
0742:                        segment.inc();
0743:                    }
0744:                } while (contains);
0745:                return (contains);
0746:            }
0747:
0748:            /**
0749:             * Returns <code>true</code> if a range of values are contained in the 
0750:             * timeline. This is implemented verifying that all segments are in the 
0751:             * range.
0752:             *
0753:             * @param dateDomainValueStart start of the range to verify
0754:             * @param dateDomainValueEnd end of the range to verify
0755:             * 
0756:             * @return <code>true</code> if the range is contained in the timeline
0757:             */
0758:            public boolean containsDomainRange(Date dateDomainValueStart,
0759:                    Date dateDomainValueEnd) {
0760:                return containsDomainRange(getTime(dateDomainValueStart),
0761:                        getTime(dateDomainValueEnd));
0762:            }
0763:
0764:            /**
0765:             * Adds a segment as an exception. An exception segment is defined as a 
0766:             * segment to exclude from what would otherwise be considered a valid 
0767:             * segment of the timeline.  An exception segment can not be contained 
0768:             * inside an already excluded segment.  If so, no action will occur (the 
0769:             * proposed exception segment will be discarded).
0770:             * <p>
0771:             * The segment is identified by a domainValue into any part of the segment.
0772:             * Therefore the segmentStart <= domainValue <= segmentEnd.
0773:             *
0774:             * @param millisecond  domain value to treat as an exception
0775:             */
0776:            public void addException(long millisecond) {
0777:                addException(new Segment(millisecond));
0778:            }
0779:
0780:            /**
0781:             * Adds a segment range as an exception. An exception segment is defined as
0782:             * a segment to exclude from what would otherwise be considered a valid 
0783:             * segment of the timeline.  An exception segment can not be contained 
0784:             * inside an already excluded segment.  If so, no action will occur (the 
0785:             * proposed exception segment will be discarded).
0786:             * <p>
0787:             * The segment range is identified by a domainValue that begins a valid 
0788:             * segment and ends with a domainValue that ends a valid segment. 
0789:             * Therefore the range will contain all segments whose segmentStart 
0790:             * <= domainValue and segmentEnd <= toDomainValue.
0791:             *
0792:             * @param fromDomainValue  start of domain range to treat as an exception
0793:             * @param toDomainValue  end of domain range to treat as an exception
0794:             */
0795:            public void addException(long fromDomainValue, long toDomainValue) {
0796:                addException(new SegmentRange(fromDomainValue, toDomainValue));
0797:            }
0798:
0799:            /**
0800:             * Adds a segment as an exception. An exception segment is defined as a 
0801:             * segment to exclude from what would otherwise be considered a valid 
0802:             * segment of the timeline.  An exception segment can not be contained 
0803:             * inside an already excluded segment.  If so, no action will occur (the 
0804:             * proposed exception segment will be discarded).
0805:             * <p>
0806:             * The segment is identified by a Date into any part of the segment.
0807:             *
0808:             * @param exceptionDate  Date into the segment to exclude.
0809:             */
0810:            public void addException(Date exceptionDate) {
0811:                addException(getTime(exceptionDate));
0812:                //addException(exceptionDate.getTime());
0813:            }
0814:
0815:            /**
0816:             * Adds a list of dates as segment exceptions. Each exception segment is 
0817:             * defined as a segment to exclude from what would otherwise be considered 
0818:             * a valid segment of the timeline.  An exception segment can not be 
0819:             * contained inside an already excluded segment.  If so, no action will 
0820:             * occur (the proposed exception segment will be discarded).
0821:             * <p>
0822:             * The segment is identified by a Date into any part of the segment.
0823:             *
0824:             * @param exceptionList  List of Date objects that identify the segments to
0825:             *                       exclude.
0826:             */
0827:            public void addExceptions(List exceptionList) {
0828:                for (Iterator iter = exceptionList.iterator(); iter.hasNext();) {
0829:                    addException((Date) iter.next());
0830:                }
0831:            }
0832:
0833:            /**
0834:             * Adds a segment as an exception. An exception segment is defined as a 
0835:             * segment to exclude from what would otherwise be considered a valid 
0836:             * segment of the timeline.  An exception segment can not be contained 
0837:             * inside an already excluded segment.  This is verified inside this 
0838:             * method, and if so, no action will occur (the proposed exception segment 
0839:             * will be discarded).
0840:             *
0841:             * @param segment  the segment to exclude.
0842:             */
0843:            private void addException(Segment segment) {
0844:                if (segment.inIncludeSegments()) {
0845:                    int p = binarySearchExceptionSegments(segment);
0846:                    this .exceptionSegments.add(-(p + 1), segment);
0847:                }
0848:            }
0849:
0850:            /**
0851:             * Adds a segment relative to the baseTimeline as an exception. Because a 
0852:             * base segment is normally larger than our segments, this may add one or 
0853:             * more segment ranges to the exception list.
0854:             * <p>
0855:             * An exception segment is defined as a segment
0856:             * to exclude from what would otherwise be considered a valid segment of 
0857:             * the timeline.  An exception segment can not be contained inside an 
0858:             * already excluded segment.  If so, no action will occur (the proposed 
0859:             * exception segment will be discarded).
0860:             * <p>
0861:             * The segment is identified by a domainValue into any part of the 
0862:             * baseTimeline segment.
0863:             *
0864:             * @param domainValue  domain value to teat as a baseTimeline exception.
0865:             */
0866:            public void addBaseTimelineException(long domainValue) {
0867:
0868:                Segment baseSegment = this .baseTimeline.getSegment(domainValue);
0869:                if (baseSegment.inIncludeSegments()) {
0870:
0871:                    // cycle through all the segments contained in the BaseTimeline 
0872:                    // exception segment
0873:                    Segment segment = getSegment(baseSegment.getSegmentStart());
0874:                    while (segment.getSegmentStart() <= baseSegment
0875:                            .getSegmentEnd()) {
0876:                        if (segment.inIncludeSegments()) {
0877:
0878:                            // find all consecutive included segments
0879:                            long fromDomainValue = segment.getSegmentStart();
0880:                            long toDomainValue;
0881:                            do {
0882:                                toDomainValue = segment.getSegmentEnd();
0883:                                segment.inc();
0884:                            } while (segment.inIncludeSegments());
0885:
0886:                            // add the interval as an exception
0887:                            addException(fromDomainValue, toDomainValue);
0888:
0889:                        } else {
0890:                            // this is not one of our included segment, skip it
0891:                            segment.inc();
0892:                        }
0893:                    }
0894:                }
0895:            }
0896:
0897:            /**
0898:             * Adds a segment relative to the baseTimeline as an exception. An 
0899:             * exception segment is defined as a segment to exclude from what would 
0900:             * otherwise be considered a valid segment of the timeline.  An exception 
0901:             * segment can not be contained inside an already excluded segment. If so, 
0902:             * no action will occure (the proposed exception segment will be discarded).
0903:             * <p>
0904:             * The segment is identified by a domainValue into any part of the segment.
0905:             * Therefore the segmentStart <= domainValue <= segmentEnd.
0906:             *
0907:             * @param date  date domain value to treat as a baseTimeline exception
0908:             */
0909:            public void addBaseTimelineException(Date date) {
0910:                addBaseTimelineException(getTime(date));
0911:            }
0912:
0913:            /**
0914:             * Adds all excluded segments from the BaseTimeline as exceptions to our 
0915:             * timeline. This allows us to combine two timelines for more complex 
0916:             * calculations.
0917:             *
0918:             * @param fromBaseDomainValue Start of the range where exclusions will be 
0919:             *                            extracted.
0920:             * @param toBaseDomainValue End of the range to process.
0921:             */
0922:            public void addBaseTimelineExclusions(long fromBaseDomainValue,
0923:                    long toBaseDomainValue) {
0924:
0925:                // find first excluded base segment starting fromDomainValue
0926:                Segment baseSegment = this .baseTimeline
0927:                        .getSegment(fromBaseDomainValue);
0928:                while (baseSegment.getSegmentStart() <= toBaseDomainValue
0929:                        && !baseSegment.inExcludeSegments()) {
0930:
0931:                    baseSegment.inc();
0932:
0933:                }
0934:
0935:                // cycle over all the base segments groups in the range
0936:                while (baseSegment.getSegmentStart() <= toBaseDomainValue) {
0937:
0938:                    long baseExclusionRangeEnd = baseSegment.getSegmentStart()
0939:                            + this .baseTimeline.getSegmentsExcluded()
0940:                            * this .baseTimeline.getSegmentSize() - 1;
0941:
0942:                    // cycle through all the segments contained in the base exclusion 
0943:                    // area
0944:                    Segment segment = getSegment(baseSegment.getSegmentStart());
0945:                    while (segment.getSegmentStart() <= baseExclusionRangeEnd) {
0946:                        if (segment.inIncludeSegments()) {
0947:
0948:                            // find all consecutive included segments
0949:                            long fromDomainValue = segment.getSegmentStart();
0950:                            long toDomainValue;
0951:                            do {
0952:                                toDomainValue = segment.getSegmentEnd();
0953:                                segment.inc();
0954:                            } while (segment.inIncludeSegments());
0955:
0956:                            // add the interval as an exception
0957:                            addException(new BaseTimelineSegmentRange(
0958:                                    fromDomainValue, toDomainValue));
0959:                        } else {
0960:                            // this is not one of our included segment, skip it
0961:                            segment.inc();
0962:                        }
0963:                    }
0964:
0965:                    // go to next base segment group
0966:                    baseSegment.inc(this .baseTimeline.getGroupSegmentCount());
0967:                }
0968:            }
0969:
0970:            /**
0971:             * Returns the number of exception segments wholly contained in the
0972:             * (fromDomainValue, toDomainValue) interval.
0973:             *
0974:             * @param fromMillisecond  the beginning of the interval.
0975:             * @param toMillisecond  the end of the interval.
0976:             * 
0977:             * @return Number of exception segments contained in the interval.
0978:             */
0979:            public long getExceptionSegmentCount(long fromMillisecond,
0980:                    long toMillisecond) {
0981:                if (toMillisecond < fromMillisecond) {
0982:                    return (0);
0983:                }
0984:
0985:                int n = 0;
0986:                for (Iterator iter = this .exceptionSegments.iterator(); iter
0987:                        .hasNext();) {
0988:                    Segment segment = (Segment) iter.next();
0989:                    Segment intersection = segment.intersect(fromMillisecond,
0990:                            toMillisecond);
0991:                    if (intersection != null) {
0992:                        n += intersection.getSegmentCount();
0993:                    }
0994:                }
0995:
0996:                return (n);
0997:            }
0998:
0999:            /**
1000:             * Returns a segment that contains a domainValue. If the domainValue is 
1001:             * not contained in the timeline (because it is not contained in the 
1002:             * baseTimeline), a Segment that contains 
1003:             * <code>index + segmentSize*m</code> will be returned for the smallest
1004:             * <code>m</code> possible.
1005:             *
1006:             * @param millisecond  index into the segment
1007:             * 
1008:             * @return A Segment that contains index, or the next possible Segment.
1009:             */
1010:            public Segment getSegment(long millisecond) {
1011:                return new Segment(millisecond);
1012:            }
1013:
1014:            /**
1015:             * Returns a segment that contains a date. For accurate calculations,
1016:             * the calendar should use TIME_ZONE for its calculation (or any other 
1017:             * similar time zone).
1018:             *
1019:             * If the date is not contained in the timeline (because it is not 
1020:             * contained in the baseTimeline), a Segment that contains 
1021:             * <code>date + segmentSize*m</code> will be returned for the smallest 
1022:             * <code>m</code> possible.
1023:             *
1024:             * @param date date into the segment
1025:             * 
1026:             * @return A Segment that contains date, or the next possible Segment.
1027:             */
1028:            public Segment getSegment(Date date) {
1029:                return (getSegment(getTime(date)));
1030:            }
1031:
1032:            /**
1033:             * Convenient method to test equality in two objects, taking into account 
1034:             * nulls.
1035:             * 
1036:             * @param o first object to compare
1037:             * @param p second object to compare
1038:             * 
1039:             * @return <code>true</code> if both objects are equal or both 
1040:             *         <code>null</code>, <code>false</code> otherwise.
1041:             */
1042:            private boolean equals(Object o, Object p) {
1043:                return (o == p || ((o != null) && o.equals(p)));
1044:            }
1045:
1046:            /**
1047:             * Returns true if we are equal to the parameter
1048:             * 
1049:             * @param o Object to verify with us
1050:             * 
1051:             * @return <code>true</code> or <code>false</code>
1052:             */
1053:            public boolean equals(Object o) {
1054:                if (o instanceof  SegmentedTimeline) {
1055:                    SegmentedTimeline other = (SegmentedTimeline) o;
1056:
1057:                    boolean b0 = (this .segmentSize == other.getSegmentSize());
1058:                    boolean b1 = (this .segmentsIncluded == other
1059:                            .getSegmentsIncluded());
1060:                    boolean b2 = (this .segmentsExcluded == other
1061:                            .getSegmentsExcluded());
1062:                    boolean b3 = (this .startTime == other.getStartTime());
1063:                    boolean b4 = equals(this .exceptionSegments, other
1064:                            .getExceptionSegments());
1065:                    return b0 && b1 && b2 && b3 && b4;
1066:                } else {
1067:                    return (false);
1068:                }
1069:            }
1070:
1071:            /**
1072:             * Returns a hash code for this object.
1073:             * 
1074:             * @return A hash code.
1075:             */
1076:            public int hashCode() {
1077:                int result = 19;
1078:                result = 37 * result
1079:                        + (int) (this .segmentSize ^ (this .segmentSize >>> 32));
1080:                result = 37 * result
1081:                        + (int) (this .startTime ^ (this .startTime >>> 32));
1082:                return result;
1083:            }
1084:
1085:            /**
1086:             * Preforms a binary serach in the exceptionSegments sorted array. This 
1087:             * array can contain Segments or SegmentRange objects.
1088:             *
1089:             * @param  segment the key to be searched for.
1090:             * 
1091:             * @return index of the search segment, if it is contained in the list;
1092:             *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
1093:             *         <i>insertion point</i> is defined as the point at which the
1094:             *         segment would be inserted into the list: the index of the first
1095:             *         element greater than the key, or <tt>list.size()</tt>, if all
1096:             *         elements in the list are less than the specified segment.  Note
1097:             *         that this guarantees that the return value will be &gt;= 0 if
1098:             *         and only if the key is found.
1099:             */
1100:            private int binarySearchExceptionSegments(Segment segment) {
1101:                int low = 0;
1102:                int high = this .exceptionSegments.size() - 1;
1103:
1104:                while (low <= high) {
1105:                    int mid = (low + high) / 2;
1106:                    Segment midSegment = (Segment) this .exceptionSegments
1107:                            .get(mid);
1108:
1109:                    // first test for equality (contains or contained)
1110:                    if (segment.contains(midSegment)
1111:                            || midSegment.contains(segment)) {
1112:                        return mid;
1113:                    }
1114:
1115:                    if (midSegment.before(segment)) {
1116:                        low = mid + 1;
1117:                    } else if (midSegment.after(segment)) {
1118:                        high = mid - 1;
1119:                    } else {
1120:                        throw new IllegalStateException("Invalid condition.");
1121:                    }
1122:                }
1123:                return -(low + 1); // key not found
1124:            }
1125:
1126:            /**
1127:             * Special method that handles conversion between the Default Time Zone and
1128:             * a UTC time zone with no DST. This is needed so all days have the same 
1129:             * size. This method is the prefered way of converting a Data into 
1130:             * milliseconds for usage in this class.
1131:             *
1132:             * @param date Date to convert to long.
1133:             * 
1134:             * @return The milliseconds.
1135:             */
1136:            public long getTime(Date date) {
1137:                long result = date.getTime();
1138:                if (this .adjustForDaylightSaving) {
1139:                    this .workingCalendar.setTime(date);
1140:                    this .workingCalendarNoDST.set(this .workingCalendar
1141:                            .get(Calendar.YEAR), this .workingCalendar
1142:                            .get(Calendar.MONTH), this .workingCalendar
1143:                            .get(Calendar.DATE), this .workingCalendar
1144:                            .get(Calendar.HOUR_OF_DAY), this .workingCalendar
1145:                            .get(Calendar.MINUTE), this .workingCalendar
1146:                            .get(Calendar.SECOND));
1147:                    this .workingCalendarNoDST.set(Calendar.MILLISECOND,
1148:                            this .workingCalendar.get(Calendar.MILLISECOND));
1149:                    Date revisedDate = this .workingCalendarNoDST.getTime();
1150:                    result = revisedDate.getTime();
1151:                }
1152:
1153:                return result;
1154:            }
1155:
1156:            /** 
1157:             * Converts a millisecond value into a {@link Date} object.
1158:             * 
1159:             * @param value  the millisecond value.
1160:             * 
1161:             * @return The date.
1162:             */
1163:            public Date getDate(long value) {
1164:                this .workingCalendarNoDST.setTime(new Date(value));
1165:                return (this .workingCalendarNoDST.getTime());
1166:            }
1167:
1168:            /**
1169:             * Returns a clone of the timeline.
1170:             * 
1171:             * @return A clone.
1172:             * 
1173:             * @throws CloneNotSupportedException ??.
1174:             */
1175:            public Object clone() throws CloneNotSupportedException {
1176:                SegmentedTimeline clone = (SegmentedTimeline) super .clone();
1177:                return clone;
1178:            }
1179:
1180:            /**
1181:             * Internal class to represent a valid segment for this timeline. A segment
1182:             * is valid on a timeline if it is part of its included, excluded or 
1183:             * exception segments.
1184:             * <p>
1185:             * Each segment will know its segment number, segmentStart, segmentEnd and
1186:             * index inside the segment.
1187:             */
1188:            public class Segment implements  Comparable, Cloneable, Serializable {
1189:
1190:                /** The segment number. */
1191:                protected long segmentNumber;
1192:
1193:                /** The segment start. */
1194:                protected long segmentStart;
1195:
1196:                /** The segment end. */
1197:                protected long segmentEnd;
1198:
1199:                /** A reference point within the segment. */
1200:                protected long millisecond;
1201:
1202:                /**
1203:                 * Protected constructor only used by sub-classes.
1204:                 */
1205:                protected Segment() {
1206:                    // empty
1207:                }
1208:
1209:                /**
1210:                 * Creates a segment for a given point in time.
1211:                 * 
1212:                 * @param millisecond  the millisecond (as encoded by java.util.Date).
1213:                 */
1214:                protected Segment(long millisecond) {
1215:                    this .segmentNumber = calculateSegmentNumber(millisecond);
1216:                    this .segmentStart = SegmentedTimeline.this .startTime
1217:                            + this .segmentNumber
1218:                            * SegmentedTimeline.this .segmentSize;
1219:                    this .segmentEnd = this .segmentStart
1220:                            + SegmentedTimeline.this .segmentSize - 1;
1221:                    this .millisecond = millisecond;
1222:                }
1223:
1224:                /**
1225:                 * Calculates the segment number for a given millisecond.
1226:                 * 
1227:                 * @param millis  the millisecond (as encoded by java.util.Date).
1228:                 *  
1229:                 * @return The segment number.
1230:                 */
1231:                public long calculateSegmentNumber(long millis) {
1232:                    if (millis >= SegmentedTimeline.this .startTime) {
1233:                        return (millis - SegmentedTimeline.this .startTime)
1234:                                / SegmentedTimeline.this .segmentSize;
1235:                    } else {
1236:                        return ((millis - SegmentedTimeline.this .startTime) / SegmentedTimeline.this .segmentSize) - 1;
1237:                    }
1238:                }
1239:
1240:                /**
1241:                 * Returns the segment number of this segment. Segments start at 0.
1242:                 * 
1243:                 * @return The segment number.
1244:                 */
1245:                public long getSegmentNumber() {
1246:                    return this .segmentNumber;
1247:                }
1248:
1249:                /**
1250:                 * Returns always one (the number of segments contained in this 
1251:                 * segment).
1252:                 * 
1253:                 * @return The segment count (always 1 for this class).
1254:                 */
1255:                public long getSegmentCount() {
1256:                    return 1;
1257:                }
1258:
1259:                /**
1260:                 * Gets the start of this segment in ms.
1261:                 * 
1262:                 * @return The segment start.
1263:                 */
1264:                public long getSegmentStart() {
1265:                    return this .segmentStart;
1266:                }
1267:
1268:                /**
1269:                 * Gets the end of this segment in ms.
1270:                 * 
1271:                 * @return The segment end.
1272:                 */
1273:                public long getSegmentEnd() {
1274:                    return this .segmentEnd;
1275:                }
1276:
1277:                /**
1278:                 * Returns the millisecond used to reference this segment (always 
1279:                 * between the segmentStart and segmentEnd).
1280:                 * 
1281:                 * @return The millisecond.
1282:                 */
1283:                public long getMillisecond() {
1284:                    return this .millisecond;
1285:                }
1286:
1287:                /**
1288:                 * Returns a {@link java.util.Date} that represents the reference point
1289:                 * for this segment.
1290:                 * 
1291:                 * @return The date.
1292:                 */
1293:                public Date getDate() {
1294:                    return SegmentedTimeline.this .getDate(this .millisecond);
1295:                }
1296:
1297:                /**
1298:                 * Returns true if a particular millisecond is contained in this 
1299:                 * segment.
1300:                 * 
1301:                 * @param millis  the millisecond to verify.
1302:                 * 
1303:                 * @return <code>true</code> if the millisecond is contained in the 
1304:                 *         segment.
1305:                 */
1306:                public boolean contains(long millis) {
1307:                    return (this .segmentStart <= millis && millis <= this .segmentEnd);
1308:                }
1309:
1310:                /**
1311:                 * Returns <code>true</code> if an interval is contained in this 
1312:                 * segment.
1313:                 * 
1314:                 * @param from  the start of the interval.
1315:                 * @param to  the end of the interval.
1316:                 * 
1317:                 * @return <code>true</code> if the interval is contained in the 
1318:                 *         segment.
1319:                 */
1320:                public boolean contains(long from, long to) {
1321:                    return (this .segmentStart <= from && to <= this .segmentEnd);
1322:                }
1323:
1324:                /**
1325:                 * Returns <code>true</code> if a segment is contained in this segment.
1326:                 * 
1327:                 * @param segment  the segment to test for inclusion
1328:                 * 
1329:                 * @return <code>true</code> if the segment is contained in this 
1330:                 *         segment.
1331:                 */
1332:                public boolean contains(Segment segment) {
1333:                    return contains(segment.getSegmentStart(), segment
1334:                            .getSegmentEnd());
1335:                }
1336:
1337:                /**
1338:                 * Returns <code>true</code> if this segment is contained in an 
1339:                 * interval.
1340:                 * 
1341:                 * @param from  the start of the interval.
1342:                 * @param to  the end of the interval.
1343:                 * 
1344:                 * @return <code>true</code> if this segment is contained in the 
1345:                 *         interval.
1346:                 */
1347:                public boolean contained(long from, long to) {
1348:                    return (from <= this .segmentStart && this .segmentEnd <= to);
1349:                }
1350:
1351:                /**
1352:                 * Returns a segment that is the intersection of this segment and the 
1353:                 * interval.
1354:                 * 
1355:                 * @param from  the start of the interval.
1356:                 * @param to  the end of the interval.
1357:                 * 
1358:                 * @return A segment.
1359:                 */
1360:                public Segment intersect(long from, long to) {
1361:                    if (from <= this .segmentStart && this .segmentEnd <= to) {
1362:                        return this ;
1363:                    } else {
1364:                        return null;
1365:                    }
1366:                }
1367:
1368:                /**
1369:                 * Returns <code>true</code> if this segment is wholly before another 
1370:                 * segment.
1371:                 * 
1372:                 * @param other  the other segment.
1373:                 * 
1374:                 * @return A boolean.
1375:                 */
1376:                public boolean before(Segment other) {
1377:                    return (this .segmentEnd < other.getSegmentStart());
1378:                }
1379:
1380:                /**
1381:                 * Returns <code>true</code> if this segment is wholly after another 
1382:                 * segment.
1383:                 * 
1384:                 * @param other  the other segment.
1385:                 * 
1386:                 * @return A boolean.
1387:                 */
1388:                public boolean after(Segment other) {
1389:                    return (this .segmentStart > other.getSegmentEnd());
1390:                }
1391:
1392:                /**
1393:                 * Tests an object (usually another <code>Segment</code>) for equality
1394:                 * with this segment.
1395:                 * 
1396:                 * @param object The other segment to compare with us
1397:                 * 
1398:                 * @return <code>true</code> if we are the same segment
1399:                 */
1400:                public boolean equals(Object object) {
1401:                    if (object instanceof  Segment) {
1402:                        Segment other = (Segment) object;
1403:                        return (this .segmentNumber == other.getSegmentNumber()
1404:                                && this .segmentStart == other.getSegmentStart()
1405:                                && this .segmentEnd == other.getSegmentEnd() && this .millisecond == other
1406:                                .getMillisecond());
1407:                    } else {
1408:                        return false;
1409:                    }
1410:                }
1411:
1412:                /**
1413:                 * Returns a copy of ourselves or <code>null</code> if there was an 
1414:                 * exception during cloning.
1415:                 * 
1416:                 * @return A copy of this segment.
1417:                 */
1418:                public Segment copy() {
1419:                    try {
1420:                        return (Segment) this .clone();
1421:                    } catch (CloneNotSupportedException e) {
1422:                        return null;
1423:                    }
1424:                }
1425:
1426:                /**
1427:                 * Will compare this Segment with another Segment (from Comparable 
1428:                 * interface).
1429:                 *
1430:                 * @param object The other Segment to compare with
1431:                 * 
1432:                 * @return -1: this < object, 0: this.equal(object) and 
1433:                 *         +1: this > object 
1434:                 */
1435:                public int compareTo(Object object) {
1436:                    Segment other = (Segment) object;
1437:                    if (this .before(other)) {
1438:                        return -1;
1439:                    } else if (this .after(other)) {
1440:                        return +1;
1441:                    } else {
1442:                        return 0;
1443:                    }
1444:                }
1445:
1446:                /**
1447:                 * Returns true if we are an included segment and we are not an 
1448:                 * exception.
1449:                 * 
1450:                 * @return <code>true</code> or <code>false</code>.
1451:                 */
1452:                public boolean inIncludeSegments() {
1453:                    if (getSegmentNumberRelativeToGroup() < SegmentedTimeline.this .segmentsIncluded) {
1454:                        return !inExceptionSegments();
1455:                    } else {
1456:                        return false;
1457:                    }
1458:                }
1459:
1460:                /**
1461:                 * Returns true if we are an excluded segment.
1462:                 * 
1463:                 * @return <code>true</code> or <code>false</code>.
1464:                 */
1465:                public boolean inExcludeSegments() {
1466:                    return getSegmentNumberRelativeToGroup() >= SegmentedTimeline.this .segmentsIncluded;
1467:                }
1468:
1469:                /**
1470:                 * Calculate the segment number relative to the segment group. This 
1471:                 * will be a number between 0 and segmentsGroup-1. This value is 
1472:                 * calculated from the segmentNumber. Special care is taken for 
1473:                 * negative segmentNumbers.
1474:                 * 
1475:                 * @return The segment number.
1476:                 */
1477:                private long getSegmentNumberRelativeToGroup() {
1478:                    long p = (this .segmentNumber % SegmentedTimeline.this .groupSegmentCount);
1479:                    if (p < 0) {
1480:                        p += SegmentedTimeline.this .groupSegmentCount;
1481:                    }
1482:                    return p;
1483:                }
1484:
1485:                /**
1486:                 * Returns true if we are an exception segment. This is implemented via
1487:                 * a binary search on the exceptionSegments sorted list.
1488:                 *
1489:                 * If the segment is not listed as an exception in our list and we have
1490:                 * a baseTimeline, a check is performed to see if the segment is inside
1491:                 * an excluded segment from our base. If so, it is also considered an
1492:                 * exception.
1493:                 *
1494:                 * @return <code>true</code> if we are an exception segment.
1495:                 */
1496:                public boolean inExceptionSegments() {
1497:                    return binarySearchExceptionSegments(this ) >= 0;
1498:                }
1499:
1500:                /**
1501:                 * Increments the internal attributes of this segment by a number of
1502:                 * segments.
1503:                 *
1504:                 * @param n Number of segments to increment.
1505:                 */
1506:                public void inc(long n) {
1507:                    this .segmentNumber += n;
1508:                    long m = n * SegmentedTimeline.this .segmentSize;
1509:                    this .segmentStart += m;
1510:                    this .segmentEnd += m;
1511:                    this .millisecond += m;
1512:                }
1513:
1514:                /**
1515:                 * Increments the internal attributes of this segment by one segment.
1516:                 * The exact time incremented is segmentSize.
1517:                 */
1518:                public void inc() {
1519:                    inc(1);
1520:                }
1521:
1522:                /**
1523:                 * Decrements the internal attributes of this segment by a number of
1524:                 * segments.
1525:                 *
1526:                 * @param n Number of segments to decrement.
1527:                 */
1528:                public void dec(long n) {
1529:                    this .segmentNumber -= n;
1530:                    long m = n * SegmentedTimeline.this .segmentSize;
1531:                    this .segmentStart -= m;
1532:                    this .segmentEnd -= m;
1533:                    this .millisecond -= m;
1534:                }
1535:
1536:                /**
1537:                 * Decrements the internal attributes of this segment by one segment.
1538:                 * The exact time decremented is segmentSize.
1539:                 */
1540:                public void dec() {
1541:                    dec(1);
1542:                }
1543:
1544:                /**
1545:                 * Moves the index of this segment to the beginning if the segment.
1546:                 */
1547:                public void moveIndexToStart() {
1548:                    this .millisecond = this .segmentStart;
1549:                }
1550:
1551:                /**
1552:                 * Moves the index of this segment to the end of the segment.
1553:                 */
1554:                public void moveIndexToEnd() {
1555:                    this .millisecond = this .segmentEnd;
1556:                }
1557:
1558:            }
1559:
1560:            /**
1561:             * Private internal class to represent a range of segments. This class is 
1562:             * mainly used to store in one object a range of exception segments. This 
1563:             * optimizes certain timelines that use a small segment size (like an 
1564:             * intraday timeline) allowing them to express a day exception as one 
1565:             * SegmentRange instead of multi Segments.
1566:             */
1567:            protected class SegmentRange extends Segment {
1568:
1569:                /** The number of segments in the range. */
1570:                private long segmentCount;
1571:
1572:                /**
1573:                 * Creates a SegmentRange between a start and end domain values.
1574:                 * 
1575:                 * @param fromMillisecond  start of the range
1576:                 * @param toMillisecond  end of the range
1577:                 */
1578:                public SegmentRange(long fromMillisecond, long toMillisecond) {
1579:
1580:                    Segment start = getSegment(fromMillisecond);
1581:                    Segment end = getSegment(toMillisecond);
1582:                    //            if (start.getSegmentStart() != fromMillisecond 
1583:                    //                || end.getSegmentEnd() != toMillisecond) {
1584:                    //                throw new IllegalArgumentException("Invalid Segment Range ["
1585:                    //                    + fromMillisecond + "," + toMillisecond + "]");
1586:                    //            }
1587:
1588:                    this .millisecond = fromMillisecond;
1589:                    this .segmentNumber = calculateSegmentNumber(fromMillisecond);
1590:                    this .segmentStart = start.segmentStart;
1591:                    this .segmentEnd = end.segmentEnd;
1592:                    this .segmentCount = (end.getSegmentNumber()
1593:                            - start.getSegmentNumber() + 1);
1594:                }
1595:
1596:                /**
1597:                 * Returns the number of segments contained in this range.
1598:                 * 
1599:                 * @return The segment count.
1600:                 */
1601:                public long getSegmentCount() {
1602:                    return this .segmentCount;
1603:                }
1604:
1605:                /**
1606:                 * Returns a segment that is the intersection of this segment and the 
1607:                 * interval.
1608:                 * 
1609:                 * @param from  the start of the interval.
1610:                 * @param to  the end of the interval.
1611:                 * 
1612:                 * @return The intersection.
1613:                 */
1614:                public Segment intersect(long from, long to) {
1615:
1616:                    // Segment fromSegment = getSegment(from);
1617:                    // fromSegment.inc();
1618:                    // Segment toSegment = getSegment(to);
1619:                    // toSegment.dec();
1620:                    long start = Math.max(from, this .segmentStart);
1621:                    long end = Math.min(to, this .segmentEnd);
1622:                    // long start = Math.max(
1623:                    //     fromSegment.getSegmentStart(), this.segmentStart
1624:                    // );
1625:                    // long end = Math.min(toSegment.getSegmentEnd(), this.segmentEnd);
1626:                    if (start <= end) {
1627:                        return new SegmentRange(start, end);
1628:                    } else {
1629:                        return null;
1630:                    }
1631:                }
1632:
1633:                /**
1634:                 * Returns true if all Segments of this SegmentRenge are an included 
1635:                 * segment and are not an exception.
1636:                 * 
1637:                 * @return <code>true</code> or </code>false</code>.
1638:                 */
1639:                public boolean inIncludeSegments() {
1640:                    for (Segment segment = getSegment(this .segmentStart); segment
1641:                            .getSegmentStart() < this .segmentEnd; segment.inc()) {
1642:                        if (!segment.inIncludeSegments()) {
1643:                            return (false);
1644:                        }
1645:                    }
1646:                    return true;
1647:                }
1648:
1649:                /**
1650:                 * Returns true if we are an excluded segment.
1651:                 * 
1652:                 * @return <code>true</code> or </code>false</code>.
1653:                 */
1654:                public boolean inExcludeSegments() {
1655:                    for (Segment segment = getSegment(this .segmentStart); segment
1656:                            .getSegmentStart() < this .segmentEnd; segment.inc()) {
1657:                        if (!segment.inExceptionSegments()) {
1658:                            return (false);
1659:                        }
1660:                    }
1661:                    return true;
1662:                }
1663:
1664:                /**
1665:                 * Not implemented for SegmentRange. Always throws 
1666:                 * IllegalArgumentException.
1667:                 *
1668:                 * @param n Number of segments to increment.
1669:                 */
1670:                public void inc(long n) {
1671:                    throw new IllegalArgumentException(
1672:                            "Not implemented in SegmentRange");
1673:                }
1674:
1675:            }
1676:
1677:            /**
1678:             * Special <code>SegmentRange</code> that came from the BaseTimeline.
1679:             */
1680:            protected class BaseTimelineSegmentRange extends SegmentRange {
1681:
1682:                /**
1683:                 * Constructor.
1684:                 * 
1685:                 * @param fromDomainValue  the start value.
1686:                 * @param toDomainValue  the end value.
1687:                 */
1688:                public BaseTimelineSegmentRange(long fromDomainValue,
1689:                        long toDomainValue) {
1690:                    super(fromDomainValue, toDomainValue);
1691:                }
1692:
1693:            }
1694:
1695:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.