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.pm.calendar;
051:
052: import java.util.ArrayList;
053: import java.util.HashSet;
054: import java.util.Iterator;
055: import java.util.List;
056: import java.util.Set;
057:
058: import com.projity.configuration.Settings;
059: import com.projity.document.Document;
060: import com.projity.pm.time.HasStartAndEnd;
061: import com.projity.strings.Messages;
062: import com.projity.timescale.CalendarUtil;
063:
064: /**
065: * Facade for manipulating calendars via a dialog or web interface
066: */
067: public class CalendarService {
068: private static CalendarService instance = null;
069: ArrayList baseCalendars = new ArrayList();
070: ArrayList derivedCalendars = new ArrayList();
071: ArrayList assignmentCalendars = new ArrayList();
072:
073: public static CalendarService getInstance() {
074: if (instance == null)
075: instance = new CalendarService();
076: return instance;
077: }
078:
079: /**
080: * Gets the name of a calendar
081: *
082: * @param cal
083: * Calendar whose name to return
084: * @return name of calendar or "" if cal is null
085: */
086: public String getCalendarName(WorkingCalendar cal) {
087: if (cal == null)
088: return "";
089: return cal.getName();
090: }
091:
092: /**
093: * Get the base calendar for a given calendar
094: *
095: * @param cal
096: * whose based to get
097: * @return base calendar or null if cal has no base
098: */
099: public WorkingCalendar getBaseCalendar(WorkingCalendar cal) {
100: return (WorkingCalendar) cal.getBaseCalendar();
101: }
102:
103: /**
104: * Get a descriptor for the day (its hours and modified status). The
105: * priority order what day is return is: 1. Derived calendar month day
106: * exception 2. Derived calendar week day exception 3. Base calendar special
107: * month day 4. Base calendar week day
108: *
109: * @param workingCalendar
110: * The calendar to use
111: * @param date
112: * Midnight on the day in question (see DateTime.midnightToday())
113: * @return DayDescriptor with modified info and work day
114: */
115: public DayDescriptor getDay(WorkingCalendar workingCalendar,
116: long date) {
117: return workingCalendar.getMonthDayDescriptor(date);
118: }
119:
120: /**
121: *
122: * @param workingCalendar
123: * @param intervals
124: * @return Common day or null
125: */
126: public DayDescriptor getDay(WorkingCalendar workingCalendar,
127: Set intervals, boolean selectedWeekDays[]) {
128: CalendarUtil.DayIterator days = new CalendarUtil.DayIterator();
129: DayDescriptor common = null;
130: DayDescriptor current;
131:
132: for (int i = 0; i < 7; i++) {
133: if (selectedWeekDays[i]) {
134: current = workingCalendar.getWeekDayDescriptor(i + 1);
135: common = mergeWithCommon(current, common);
136: if (common == null)
137: return null;
138: }
139: }
140:
141: for (Iterator i = intervals.iterator(); i.hasNext();) {
142: days.setInterval((HasStartAndEnd) i.next());
143: while (days.hasMoreDays()) {
144: current = workingCalendar.getMonthDayDescriptor(days
145: .nextDay());
146: common = mergeWithCommon(current, common);
147: if (common == null)
148: return null;
149: }
150: }
151: return common;
152: }
153:
154: private DayDescriptor mergeWithCommon(DayDescriptor current,
155: DayDescriptor common) {
156: if (common == null) {
157: common = current;
158: return common;
159: }
160: if (current.isModified() != common.isModified()
161: || current.isWorking() != common.isWorking())
162: return null;
163: // compare working hours
164: if (current.isModified() && current.isWorking()) {
165: // to avoid breaking code with redefining equals in DayDescriptor...
166: if (current.getWorkingHours() == null
167: && common.getWorkingHours() == null)
168: return null;
169: if ((current.getWorkingHours() == null && common
170: .getWorkingHours() != null)
171: || (current.getWorkingHours() != null && common
172: .getWorkingHours() == null))
173: return null;
174: List currentIntervals = current.getWorkingHours()
175: .getIntervals();
176: List commonIntervals = common.getWorkingHours()
177: .getIntervals();
178: if (commonIntervals.size() != currentIntervals.size())
179: return null;
180: Iterator com = commonIntervals.iterator();
181: Iterator cur = currentIntervals.iterator();
182: HasStartAndEnd comInterval, curInterval;
183: while (com.hasNext()) {
184: comInterval = (HasStartAndEnd) com.next();
185: curInterval = (HasStartAndEnd) cur.next();
186: if (curInterval == null && comInterval == null)
187: continue;
188: if (curInterval == null && comInterval != null)
189: return null;
190: if (curInterval != null && comInterval == null)
191: return null;
192: if (comInterval.getStart() != curInterval.getStart()
193: || comInterval.getEnd() != curInterval.getEnd())
194: return null;
195: }
196: }
197: return common;
198: }
199:
200: /**
201: * Get a descriptor for the week day (its hours and modified status). If the
202: * derived calendar does not have a special weekday defined, then the base
203: * calendar weekday is used
204: *
205: * @param workingCalendar
206: * The calendar to use
207: * @param dayNum
208: * Corresponds to constants in java.util.Calendar SUNDAY=1,
209: * MONDAY=2... SATURDAY=7
210: * @return DayDescriptor with modified info and work day
211: */
212: public DayDescriptor getWeekDay(WorkingCalendar workingCalendar,
213: int dayNum) {
214: return workingCalendar.getWeekDayDescriptor(dayNum);
215: }
216:
217: /**
218: * Clear out any month day exceptions for the given date
219: *
220: * @param workingCalendar
221: * @param date
222: */
223: public void makeDefaultDay(WorkingCalendar workingCalendar,
224: long date) {
225: workingCalendar.makeDefaultDay(date);
226: }
227:
228: /**
229: *
230: * @param workingCalendar
231: * @param intervals
232: * with no overlap
233: * @return
234: */
235: public void makeDefaultDays(WorkingCalendar workingCalendar,
236: Set intervals, boolean[] selectedDays) {
237: for (int i = 0; i < 7; i++) {
238: if (selectedDays[i]) {
239: makeDefaultWeekDay(workingCalendar, i + 1);
240: }
241: }
242:
243: CalendarUtil.DayIterator days = new CalendarUtil.DayIterator();
244: for (Iterator i = intervals.iterator(); i.hasNext();) {
245: days.setInterval((HasStartAndEnd) i.next());
246: while (days.hasMoreDays())
247: workingCalendar.makeDefaultDay(days.nextDay());
248: }
249: }
250:
251: /**
252: * Clear out any week day exceptions for the week day
253: *
254: * @param workingCalendar
255: * @param dayNum
256: * Corresponds to constants in java.util.Calendar SUNDAY=1,
257: * MONDAY=2... SATURDAY=7
258: */
259: public void makeDefaultWeekDay(WorkingCalendar workingCalendar,
260: int dayNum) {
261: workingCalendar.makeDefaultWeekDay(dayNum);
262: }
263:
264: /**
265: * Set the working hours for a day. Working hours should be filled as
266: * follows: An existing WorkingHours should be cloned before modification in
267: * case the modification causes an exception. WorkingHours is modified by
268: * calls to WorkingHours.setInterval(int number, long start, long end) See
269: * comments in this method as to what values to use (must be on the date
270: * 1/1/70 or -1 if null)
271: *
272: * @param workingCalendar -
273: * Calendar whose day to set
274: * @param date -
275: * date of the exception
276: * @param workingHours -
277: * contains the work ranges
278: * @throws WorkRangeException -
279: * If invalid ranges, an exception is thrown
280: */
281: public void setDayWorkingHours(WorkingCalendar workingCalendar,
282: long date, WorkingHours workingHours)
283: throws WorkRangeException {
284: if (workingHours.getDuration() == 0)
285: setDayNonWorking(workingCalendar, date);
286: else
287: workingCalendar.setDayWorkingHours(date, workingHours);
288: }
289:
290: public void setDaysWorkingHours(WorkingCalendar workingCalendar,
291: Set intervals, boolean selectedDays[],
292: WorkingHours workingHours) throws WorkRangeException,
293: InvalidCalendarException {
294: for (int i = 0; i < 7; i++) {
295: if (selectedDays[i]) {
296: setWeekDayWorkingHours(workingCalendar, i + 1,
297: workingHours);
298: }
299: }
300:
301: CalendarUtil.DayIterator days = new CalendarUtil.DayIterator();
302: for (Iterator i = intervals.iterator(); i.hasNext();) {
303: days.setInterval((HasStartAndEnd) i.next());
304: while (days.hasMoreDays())
305: workingCalendar.setDayWorkingHours(days.nextDay(),
306: workingHours);
307: }
308: }
309:
310: /**
311: * Set the working hours for a week day
312: *
313: * @param workingCalendar -
314: * Calendar whose week day to set
315: * @param dayNum
316: * Corresponds to constants in java.util.Calendar SUNDAY=1,
317: * MONDAY=2... SATURDAY=7
318: * @param workingHours -
319: * contains the work ranges
320: * @throws WorkRangeException -
321: * If invalid ranges, an exception is thrown
322: * @throws InvalidCalendarException
323: */
324: public void setWeekDayWorkingHours(WorkingCalendar workingCalendar,
325: int dayNum, WorkingHours workingHours)
326: throws WorkRangeException, InvalidCalendarException {
327: if (workingHours.getDuration() == 0)
328: setWeekDayNonWorking(workingCalendar, dayNum);
329: else
330: workingCalendar
331: .setWeekDayWorkingHours(dayNum, workingHours);
332: }
333:
334: /**
335: * Set a month day as a non working day
336: *
337: * @param workingCalendar
338: * @param date
339: * what date is not working
340: */
341: public void setDayNonWorking(WorkingCalendar workingCalendar,
342: long date) {
343: workingCalendar.setDayNonWorking(date);
344:
345: }
346:
347: public void setDaysNonWorking(WorkingCalendar workingCalendar,
348: Set intervals, boolean[] selectedDays)
349: throws InvalidCalendarException {
350: for (int i = 0; i < 7; i++) {
351: if (selectedDays[i]) {
352: setWeekDayNonWorking(workingCalendar, i + 1);
353: }
354: }
355: CalendarUtil.DayIterator days = new CalendarUtil.DayIterator();
356: for (Iterator i = intervals.iterator(); i.hasNext();) {
357: days.setInterval((HasStartAndEnd) i.next());
358: while (days.hasMoreDays())
359: workingCalendar.setDayNonWorking(days.nextDay());
360: }
361: }
362:
363: /**
364: * Sets a week day as a non working day
365: *
366: * @param workingCalendar
367: * @param dayNum
368: * Corresponds to constants in java.util.Calendar SUNDAY=1,
369: * MONDAY=2... SATURDAY=7
370: */
371: public void setWeekDayNonWorking(WorkingCalendar workingCalendar,
372: int dayNum) throws InvalidCalendarException {
373: int nonWorkingDays = 0;
374: for (int i = 0; i < 7; i++) {
375: if (!getWeekDay(workingCalendar, i + 1).isWorking())
376: nonWorkingDays++;
377: }
378: if (nonWorkingDays >= 6)
379: throw new InvalidCalendarException(
380: Messages
381: .getString("Message.errorCalendarMustHaveWorkingDay"));
382: workingCalendar.setWeekDayNonWorking(dayNum);
383: }
384:
385: /**
386: * Assigns an existing calendar with the contents of another. Used when
387: * validating the calendar dialog to copy the days back into the non-scratch
388: * calendar
389: *
390: * @param destination
391: * Where to copy data
392: * @param source
393: * Where to copy from
394: */
395: public void assignCalendar(WorkingCalendar destination,
396: WorkingCalendar source) {
397: destination.assignFrom(source);
398: }
399:
400: /**
401: * Make a temporary copy of a calendar. Used in the calendar dialog - A
402: * scratch copy should be modified, and only upon validation should the
403: * calendar data be copied back into the original calendar via a call to
404: * assignCalendar
405: *
406: * @param from
407: * @return
408: */
409: public WorkingCalendar makeScratchCopy(WorkingCalendar from) {
410: return from.makeScratchCopy();
411: }
412:
413: /**
414: * For debugging - returns a toString() for a calendar
415: *
416: * @param cal
417: * @return String representation of calendar for debugging
418: */
419: public String dump(WorkingCalendar cal) {
420: return cal.dump();
421: }
422:
423: public WorkingCalendar getStandardBasedInstance() {
424: WorkingCalendar wc = WorkingCalendar.getStandardBasedInstance();
425: add(wc);
426: return wc;
427: }
428:
429: public WorkingCalendar getStandardInstance() {
430: WorkingCalendar wc = WorkingCalendar.getStandardInstance();
431: return wc;
432: }
433:
434: public WorkingCalendar getDefaultInstance() {
435: WorkingCalendar wc = WorkingCalendar.getDefaultInstance();
436: return wc;
437: }
438:
439: public void saveAndUpdate(WorkingCalendar workingCalendar) {
440: invalidate(workingCalendar);
441: }
442:
443: public void invalidate(WorkingCalendar cal) {
444: Iterator i = cal.getObjectsUsing().iterator();
445: HasCalendar hasCal;
446: HashSet documents = new HashSet();
447: while (i.hasNext()) {
448: hasCal = (HasCalendar) i.next();
449: documents.add(hasCal.invalidateCalendar());
450: }
451: Iterator d = documents.iterator();
452: while (d.hasNext()) {
453: Document doc = (Document) d.next();
454: doc.fireUpdateEvent(this , doc);
455: }
456: }
457:
458: // HashMap importedCalendarMap = new HashMap();
459: // public void addImportedCalendar(WorkingCalendar cal, MPXCalendar mpxCal) {
460: // importedCalendarMap.put(mpxCal,cal);
461: // if (cal.isBaseCalendar()) {
462: // if (findBaseCalendar(cal.getName(),true) != null)
463: // return;
464: //// cal.setName(cal.getName() + PLACE_HOLDER_NAME);
465: // }
466: // add(cal);
467: // }
468: //
469: // public WorkCalendar findImportedCalendar(MPXCalendar mpxCal) {
470: // return (WorkCalendar) importedCalendarMap.get(mpxCal);
471: // }
472: // public MPXCalendar findImportedMPXCalendar(String name) {
473: // Iterator i = importedCalendarMap.keySet().iterator();
474: // MPXCalendar cal;
475: // while (i.hasNext()) {
476: // cal = (MPXCalendar)i.next();
477: // if (cal.getName().equals(name))
478: // return cal;
479: // }
480: // return null;
481: // }
482: //
483: // HashMap exportedCalendarMap = new HashMap();
484: // public void addExportedCalendar(MPXCalendar mpxCal,WorkingCalendar cal) {
485: // exportedCalendarMap.put(cal,mpxCal);
486: // }
487: //
488: // public MPXCalendar findExportedCalendar(WorkCalendar cal) {
489: // return (MPXCalendar) exportedCalendarMap.get(cal);
490: // }
491:
492: public void add(WorkingCalendar cal) {
493: if (cal.isBaseCalendar()) {
494: if (!baseCalendars.contains(cal)) {
495: boolean found = findBaseCalendar(cal.getName()) != null;
496: if (found)
497: return;
498: baseCalendars.add(cal);
499: }
500: } else {
501: if (!baseCalendars.contains(cal))
502: derivedCalendars.add(cal);
503: }
504: }
505:
506: //
507: //
508: // public void invalidate(WorkingCalendar cal) {
509: // HashSet set = new HashSet();
510: // cal.invalidate();
511: // if (cal.isBaseCalendar()) {
512: // Iterator i = derivedCalendars.iterator();
513: // WorkingCalendar cur;
514: // while (i.hasNext()) {
515: // cur = (WorkingCalendar)i.next();
516: // if (cur.getBaseCalendar() == cal) {
517: // cur.invalidate();
518: // set.add(cur.getDocument());
519: // }
520: // }
521: // } else {
522: // set.add(cal.getDocument());
523: // }
524: //
525: // // update all documents with modified calendars
526: // Iterator i = set.iterator();
527: // Document document;
528: // while (i.hasNext()) {
529: // document = (Document)i.next();
530: // if (document == null)
531: // continue;
532: // document.fireUpdateEvent(null,cal); // TODO should use correct cal
533: // // but for now docs update on
534: // // all
535: // }
536: // }
537: //
538: public final ArrayList getBaseCalendars() {
539: return baseCalendars;
540: }
541:
542: public final ArrayList getDerivedCalendars() {
543: return derivedCalendars;
544: }
545:
546: public static final Object[] allBaseCalendars() {
547: return getInstance().getBaseCalendars().toArray();
548: }
549:
550: public static final WorkCalendar findBaseCalendar(String name) {
551: return getInstance().findBaseCalendar(name, false);
552: }
553:
554: public WorkCalendar findBaseCalendar(String name, boolean importing) {
555: if (name == null)
556: return null;
557: importing = false; // don't do the treatment - if a calendar is found
558: // with same name, use it
559: // previously, I would create a new calendar for the project one, but
560: // this led to an explosion of calendars on repeated imports and exports
561: if (importing)
562: name = name + PLACE_HOLDER_NAME;
563: Iterator i = getInstance().getBaseCalendars().iterator();
564: WorkingCalendar current;
565: while (i.hasNext()) {
566: current = (WorkingCalendar) i.next();
567: if (current.getName().equals(name))
568: return current;
569: }
570: return null;
571: }
572:
573: public WorkCalendar findBaseCalendar(long id) {
574: Iterator i = getInstance().getBaseCalendars().iterator();
575: WorkingCalendar current;
576: while (i.hasNext()) {
577: current = (WorkingCalendar) i.next();
578: if (current.getUniqueId() == id)
579: return current;
580: }
581: return null;
582: }
583:
584: // public WorkCalendar findDocumentCalendar(String name, Document document) {
585: // if (name == null)
586: // return null;
587: // Iterator i = getInstance().getDerivedCalendars().iterator();
588: // WorkingCalendar current;
589: // while (i.hasNext()) {
590: // current = (WorkingCalendar)i.next();
591: // if (document == current.getDocument() && current.getName().equals(name))
592: // return current;
593: // }
594: // return null;
595: // }
596:
597: private static final String PLACE_HOLDER_NAME = "____~";
598:
599: public void renameImportedBaseCalendars(String documentName) {
600: Iterator i = getInstance().getBaseCalendars().iterator();
601: WorkingCalendar current;
602: String currentName;
603: while (i.hasNext()) {
604: current = (WorkingCalendar) i.next();
605: currentName = current.getName();
606: int spot = currentName.indexOf(PLACE_HOLDER_NAME);
607: if (spot == -1)
608: continue;
609: currentName = toImportedName(
610: currentName.substring(0, spot), documentName);
611: current.setName(currentName);
612: }
613:
614: }
615:
616: private static String toImportedName(String calName,
617: String documentName) {
618: return calName + Settings.LEFT_BRACKET + documentName
619: + Settings.RIGHT_BRACKET;
620: }
621:
622: public void reassignCalendar(HasCalendar object,
623: WorkCalendar oldCal, WorkCalendar newCal) {
624: if (oldCal == newCal)
625: return;
626: if (oldCal != null && oldCal instanceof WorkingCalendar)
627: ((WorkingCalendar) oldCal).removeObjectUsing(object);
628: if (newCal != null && newCal instanceof WorkingCalendar)
629: ((WorkingCalendar) newCal).addObjectUsing(object);
630: }
631:
632: }
|