001: /*
002: The contents of this file are subject to the Common Public Attribution License
003: Version 1.0 (the "License"); you may not use this file except in compliance with
004: the License. You may obtain a copy of the License at
005: http://www.projity.com/license . The License is based on the Mozilla Public
006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
007: software over a computer network and provide for limited attribution for the
008: Original Developer. In addition, Exhibit A has been modified to be consistent
009: with Exhibit B.
010:
011: Software distributed under the License is distributed on an "AS IS" basis,
012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
013: specific language governing rights and limitations under the License. The
014: Original Code is OpenProj. The Original Developer is the Initial Developer and
015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
017:
018: Alternatively, the contents of this file may be used under the terms of the
019: Projity End-User License Agreeement (the Projity License), in which case the
020: provisions of the Projity License are applicable instead of those above. If you
021: wish to allow use of your version of this file only under the terms of the
022: Projity License and not to allow others to use your version of this file under
023: the CPAL, indicate your decision by deleting the provisions above and replace
024: them with the notice and other provisions required by the Projity License. If
025: you do not delete the provisions above, a recipient may use your version of this
026: file under either the CPAL or the Projity License.
027:
028: [NOTE: The text of this license may differ slightly from the text of the notices
029: in Exhibits A and B of the license at http://www.projity.com/license. You should
030: use the latest text at http://www.projity.com/license for your modifications.
031: You may not remove this license text from the source files.]
032:
033: Attribution Information: Attribution Copyright Notice: Copyright © 2006, 2007
034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
035: an open source solution from Projity. Attribution URL: http://www.projity.com
036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
037: alternatives listed on http://www.projity.com/logo
038:
039: Display of Attribution Information is required in Larger Works which are defined
040: in the CPAL as a work which combines Covered Code or portions thereof with code
041: not governed by the terms of the CPAL. However, in addition to the other notice
042: obligations, all copies of the Covered Code in Executable and Source Code form
043: distributed must, as a form of attribution of the original author, include on
044: each user interface screen the "OpenProj" logo visible to all users. The
045: OpenProj logo should be located horizontally aligned with the menu bar and left
046: justified on the top left of the screen adjacent to the File menu. The logo
047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
048: must direct them back to http://www.projity.com.
049: */
050: package com.projity.script.object;
051:
052: import java.util.Calendar;
053: import java.util.LinkedList;
054: import java.util.ListIterator;
055: import java.util.Locale;
056:
057: import org.apache.commons.lang.time.DateUtils;
058:
059: import com.projity.timescale.CalendarUtil;
060:
061: public class TimeIntervals {
062: public static final int DAY = 1;
063: public static final int WEEK = 2;
064: public static final int MONTH = 3;
065: public static final int QUARTER = 4;
066: public static final int YEAR = 5;
067:
068: public static final int MIN_SCALE = 1;
069: public static final int MAX_SCALE = 2;
070:
071: protected static final int DEFAULT_WINDOW_COUNT = 3;
072: protected static final int WINDOW_INTERVALS = 50;
073:
074: protected LinkedList<TimeWindow> win = new LinkedList<TimeWindow>();
075: protected int scale = MIN_SCALE;
076: protected int translation;
077: protected int winId;
078: protected float center;
079: protected LinkedList<TimeWindow> history = new LinkedList<TimeWindow>();
080: protected long start, end;
081:
082: public int getScale() {
083: return scale;
084: }
085:
086: public void setScale(int scale) {
087: if (scale >= MIN_SCALE && scale <= MAX_SCALE)
088: this .scale = scale;
089: }
090:
091: public LinkedList<TimeWindow> getWin() {
092: return win;
093: }
094:
095: public long getStart() {
096: if (win.size() == 0)
097: return 0;
098: else
099: return win.getFirst().getS();
100: }
101:
102: public long getEnd() {
103: if (win.size() == 0)
104: return Long.MAX_VALUE;
105: else
106: return win.getLast().getE();
107: }
108:
109: public TimeWindow getCenterWin() {
110: int count = win.size();
111: if (count == 0)
112: return null;
113: int i = count / 2;
114: if (count % 2 == 1)
115: return win.get(i);
116: else {
117: TimeWindow t = new TimeWindow();
118: t.setS(win.get(i - 1).calculateCenter());
119: t.setE(win.get(i).calculateCenter());
120: return t;
121: }
122:
123: }
124:
125: public int getTranslation() {
126: return translation;
127: }
128:
129: public void setTranslation(int translation) {
130: this .translation = translation;
131: }
132:
133: public float getCenter() {
134: return center;
135: }
136:
137: public void setCenter(float center) {
138: this .center = center;
139: }
140:
141: public void update() {
142: if (win.size() > 0)
143: update(win.getFirst().getS(), end);
144: }
145:
146: public void update(long start, long end) {
147: update(start, end, DEFAULT_WINDOW_COUNT);
148: }
149:
150: public void update(long start, long end, int winCount) { //winCount positive in this case
151: System.out.println("TimeIntervals.update");
152: this .start = start;
153: this .end = end;
154: win.clear();
155: history.clear();
156: generateWindows(scale, start, 0, Long.MAX_VALUE, winCount, win);
157: indexWindows(winId, win);
158: history.addAll(win);
159: }
160:
161: protected static void indexWindows(int winId,
162: LinkedList<TimeWindow> win) {
163: int i = winId;
164: for (TimeWindow w : win)
165: w.setId(i++);
166: }
167:
168: public TimeIntervals translate(int winCount) { //TODO case winCount<0
169:
170: for (TimeWindow w : history)
171: System.out.println("history0: " + w);
172: for (TimeWindow w : win)
173: System.out.println("win0: " + w);
174:
175: //for (TimeWindow w : history) System.out.println("id="+w.getId());
176: TimeIntervals t = new TimeIntervals();
177: t.setScale(scale);
178: LinkedList<TimeWindow> twin = t.getWin();
179: if (winCount == 0 || win.size() == 0)
180: return t; //or null
181: if (winCount > 0) {
182: t.winId = winId + win.size();
183: int lastId = t.winId - 1 + winCount;
184: int maxHistoryId = Math.min(history.getLast().getId(),
185: lastId);
186: int i = t.winId;
187: if (i <= maxHistoryId) {
188: ListIterator<TimeWindow> it = history.listIterator();
189: TimeWindow w;
190: while (it.hasNext()) {
191: w = it.next();
192: if (w.getId() == t.winId) {
193: it.previous();
194: break;
195: }
196: }
197: for (; i <= maxHistoryId && it.hasNext(); i++) {
198: w = it.next();
199: twin.add(w);
200: System.out.println("Found in history: " + w);
201: }
202: }
203: LinkedList<TimeWindow> newWin = new LinkedList<TimeWindow>();
204: generateWindows(scale, (twin.size() > 0 ? twin : win)
205: .getLast().getE(), start, end, lastId - i + 1,
206: newWin);
207: t.indexWindows(t.winId + t.getWin().size(), newWin);
208: for (TimeWindow w : newWin)
209: System.out.println("New window: " + w);
210: t.getWin().addAll(newWin);
211: history.addAll(newWin);
212: } else {
213: t.winId = winId - 1;
214: int lastId = t.winId + 1 + winCount;
215: int minHistoryId = Math.max(history.getFirst().getId(),
216: lastId);
217: int i = t.winId;
218: if (i >= minHistoryId) {
219: ListIterator<TimeWindow> it = history
220: .listIterator(history.size() - 1);
221: TimeWindow w;
222: while (it.hasPrevious()) {
223: w = it.previous();
224: if (w.getId() == t.winId) {
225: it.next();
226: break;
227: }
228: }
229: for (; i >= minHistoryId; i--) {
230: w = it.previous();
231: twin.addFirst(w);
232: System.out.println("Found in history: " + w);
233: }
234: }
235: System.out.println("winId=" + winId + ", t.winId="
236: + t.winId + ", lastId=" + lastId + ", i=" + i
237: + " minHistoryId=" + minHistoryId);
238: LinkedList<TimeWindow> newWin = new LinkedList<TimeWindow>();
239: generateWindows(scale, (twin.size() > 0 ? twin : win)
240: .getFirst().getS(), start, end, lastId - i - 1,
241: newWin);
242: t.indexWindows(lastId, newWin);
243: for (TimeWindow w : newWin)
244: System.out.println("New window: " + w);
245: t.getWin().addAll(0, newWin);
246: history.addAll(0, newWin);
247: }
248:
249: int translation = 0;
250: for (TimeWindow w : t.getWin()) {
251: if (winCount > 0) {
252: win.removeFirst();
253: win.addLast(w);
254: translation++;
255: } else {
256: win.removeLast();
257: win.addFirst(w);
258: translation--;
259: }
260: }
261: winId = winId + translation;
262: t.setTranslation(translation);
263:
264: for (TimeWindow w : history)
265: System.out.println("history1: " + w);
266: for (TimeWindow w : win)
267: System.out.println("win1: " + w);
268: for (TimeWindow w : twin)
269: System.out.println("t.win1: " + w);
270:
271: return t;
272: }
273:
274: public static int generateWindows(int scale, long ref, long start,
275: long end, int winCount, LinkedList<TimeWindow> windows) {
276: TimeWindow win, lastWin = null;
277: if (winCount > 0) {
278: for (int i = 0; i <= winCount; i++) {
279: win = generateWindow(ref, scale, 1);
280: //if (win.getS()>end) return i;
281: if (lastWin != null) {
282: lastWin.setE(win.getS());
283: windows.add(lastWin);
284: }
285: ref = win.getE();
286: lastWin = win;
287: }
288: } else {
289: for (int i = 0; i >= winCount; i--) {
290: win = generateWindow(ref, scale, -1);
291: //if (win.getE()<start) return i;
292: if (lastWin != null) {
293: lastWin.setS(win.getE());
294: windows.addFirst(lastWin);
295: }
296: ref = win.getS();
297: lastWin = win;
298: }
299: }
300: return winCount;
301: }
302:
303: //not idempotent, need history to undo
304: public static TimeWindow generateWindow(long start, int scale,
305: int sign) {
306: int timeType, timeType2 = 0, number2;
307: int timeIncrement = 1, timeIncrement2 = 1;
308: switch (scale) {
309: case TimeIntervals.DAY:
310: timeType = Calendar.DAY_OF_MONTH;
311: timeType2 = Calendar.WEEK_OF_YEAR;
312: break;
313: case TimeIntervals.WEEK:
314: timeType = Calendar.WEEK_OF_YEAR;
315: timeType2 = Calendar.MONTH;
316: break;
317: case TimeIntervals.MONTH:
318: timeType = Calendar.MONTH;
319: timeType2 = Calendar.MONTH;
320: timeIncrement2 = 3;
321: break;
322: case TimeIntervals.QUARTER:
323: timeType = Calendar.MONTH;
324: timeType2 = Calendar.YEAR;
325: timeIncrement = 3;
326: break;
327: case TimeIntervals.YEAR:
328: timeType = Calendar.YEAR;
329: timeType2 = Calendar.YEAR;
330: break;
331: default:
332: return null;
333: }
334:
335: Calendar c = Calendar.getInstance(DateUtils.UTC_TIME_ZONE,
336: Locale.US);//DateTime.calendarInstance();
337: c.setTimeInMillis(start);
338:
339: //adapt start
340: floorCal(scale, c);
341: long s1 = c.getTimeInMillis();
342: floorCal(scale + 1, c);
343: long s2 = c.getTimeInMillis();
344:
345: c.setTimeInMillis(s1);
346: long s;
347: while ((s = c.getTimeInMillis()) >= s2) { //can occur with week, month scale
348: s1 = s;
349: c.add(timeType, -timeIncrement);
350: }
351:
352: //set approximative end
353: c.setTimeInMillis(s1);
354: c.add(timeType, sign * timeIncrement * WINDOW_INTERVALS);
355: TimeWindow win = new TimeWindow();
356: if (sign > 0)
357: win.setS(s1);
358: else
359: win.setE(s1);
360: if (sign > 0)
361: win.setE(c.getTimeInMillis());
362: else
363: win.setS(c.getTimeInMillis());
364: return win;
365: }
366:
367: private static void floorCal(int scale, Calendar c) {
368: switch (scale) {
369: case TimeIntervals.DAY:
370: CalendarUtil.dayFloor(c);
371: break;
372: case TimeIntervals.WEEK:
373: CalendarUtil.weekFloor(c);
374: break;
375: case TimeIntervals.MONTH:
376: CalendarUtil.monthFloor(c);
377: break;
378: case TimeIntervals.QUARTER:
379: CalendarUtil.monthFloor(c);
380: c.set(Calendar.MONTH, (c.get(Calendar.MONTH) / 3) * 3);
381: break;
382: case TimeIntervals.YEAR:
383: CalendarUtil.yearFloor(c);
384: break;
385: }
386: }
387:
388: }
|