package caltool.model.schedule; import caltool.model.caldb.*; import mvp.*; /**** * * Class Schedule is the top-level model class in the schedule package. It * provides methods to schedule the four types of calendar item. It also * contains a Categories data field, which is the sub-model for editing * scheduled item categories. * * @author Gene Fisher (gfisher@calpoly.edu) * @version 25nov14 * */ public class Schedule extends Model { /** * Construct this with the given companion view and the parent CalendarDB * model. The CalendarDB is provided to access to its service methods that * store items in the current user calendar. Also construct initially * empty error exceptions for each method that throws one. *
      post:
        this'.view == view &&
        this'.calDB == calDB &&
        this'.scheduleEventPrecondViolation != null &&
        !this'.scheduleEventPrecondViolation.anyErrors();
           // 3 other checks omitted temporarily for simplicity
     */
    public Schedule(View view, CalendarDB calDB) {
        super(view);
        this.calDB = calDB;
        scheduleEventPrecondViolation = new ScheduleEventPrecondViolation();
        scheduleAppointmentPrecondViolation = 
            new ScheduleAppointmentPrecondViolation();
        scheduleMeetingPrecondViolation = 
            new ScheduleMeetingPrecondViolation();
        scheduleTaskPrecondViolation = new ScheduleTaskPrecondViolation();
    }

    /**
     * ScheduleAppointment adds the given Appointment to the current Calendar
     * if an appointment of the same time, duration, and title is not already
     * scheduled.
     *                                                                     
     normal_behavior
      pre:
        //
        // The Title field is not empty.
        //
        (appt.title != null && appt.title.length() >= 1)

            &&

        //
        // The startOrDueDate field is a valid date value.
        //
        ((appt.startOrDueDate != null) && appt.startOrDueDate.isValid())

            &&

        //
        // If non-empty, the EndDate field is a valid date value.
        //
        ((appt.endDate != null) || appt.endDate.isValid())

            &&

        //
        // The current workspace is not null.
        //
        (calDB.getCurrentCalendar() != null)

            &&

        //
        // No appt of same title and start date is in the current workspace
        // calendar.  The UserCalendar.getItem method does the work.
        //
        calDB.getCurrentCalendar().getItem(appt.getKey()) == null;

      post:
        //
        // If preconds met, a scheduled item is in the output calendar if
        // and only if it is the new appt to be added or it is in the 
        // input calendar.
        //
        forall (ScheduledItem item;
            calDB'.getCurrentCalendar().getItem(item.getKey()) != null iff
                (item.equals(appt) ||
                 calDB.getCurrentCalendar().getItem(item.getKey()) != null))

            &&

        //
        // Also, requiresSaving and hasChanged are both true in the output
        // calendar.
        //
        calDB'.getCurrentCalendar().requiresSaving()
            &&
        calDB'.getCurrentCalendar().hasChanged();

     */
    public void scheduleAppointment(Appointment appt) {
        System.out.println("In Schedule.scheduleAppointment.");
    }

    /**
     * ScheduleMeeting adds a Meeting to the current calendar, based on the the
     * given MeetingRequest.  The work is done by the three suboperations,
     * which determine a list of possible meetings times, set
     * meeting-scheduling options, and confirm the scheduling of a specific
     * meeting selected from the possibles list.
     */
    public void scheduleMeeting(MeetingRequest meeting_req) {
        System.out.println("In Schedule.scheduleMeeting.");
    }

    /**
     * Produce the list of possible meeting times that satisfy the given
     * MeetingRequest.
     */
    public PossibleMeetingTimes listMeetingTimes(MeetingRequest request) {
        System.out.println("In schedule.listMeetingTimes.");
        return null;
    }

    /**
     * Set the meeting options in the CalendarDB to those given.
     * 
     */
    public void setMeetingOptions(MeetingSchedulingOptions options) {
        System.out.println("In schedule.setMeetingOptions.");
    }

    /**
     * ConfirmMeeting takes a CalendarDB, MeetingRequest, list of
     * PossibleMeetingTimes, and a selected time from the list.  It outputs a
     * new CalendarDB with the given request scheduled at the selected time.
     */
    public void confirmMeeting(MeetingRequest meeting_req,
            PossibleMeetingTimes possible_times, int selected_time) {
        System.out.println("In Schedule.confirmMeeting");
    }

    /**
     * ScheduleTask adds the given Task to the given CalendarDB, if a task of
     * the same start date, title, and priority is not already scheduled.
     */
    public void scheduleTask(Task task) {
        System.out.println("In Schedule.scheduleTask.");
    }

    /**
     * ScheduleEvent adds the given Event to the given CalendarDB, if an event
     * of the same start date and title is not already scheduled.
     *                                                                     
     normal_behavior
      pre:
        //
        // The Title field is not empty.
        //
        (event.title != null && event.title.length() >= 1)

            &&

        //
        // The startOrDueDate field is a valid date value.
        //
        (event.startOrDueDate != null) && event.startOrDueDate.isValid()

            &&

        //
        // If non-empty, the EndDate field is a valid date value.
        //
        if (event.endDate != null) (event.endDate.isValid())

            &&

        //
        // The current workspace is not null.
        //
        (calDB.getCurrentCalendar() != null)

            &&

        //
        // No event of same title and start date is in the current workspace
        // calendar.  The UserCalendar.getItem method does the work.
        //
        calDB.getCurrentCalendar().getItem(event.getKey()) == null;

      post:
        //
        // If preconds met, a scheduled item is in the output calendar if
        // and only if it is the new appt to be added or it is in the 
        // input calendar.  Note that this is a refined version of the original
        // more abstract spec, to make this spec more implementable.
        //
        forall (ScheduledItem item;
            calDB'.getCurrentCalendar().getItems().contains(item);
                item.equals(event) ||
                   calDB.getCurrentCalendar().getItems().contains(item))

            &&

        //
        // Check that the size of the number of items in the output calendar is
        // one greater than the input calendar.  This conjoin is new here
        // compared to the original abstract spec.  Its addition, together with
        // the uniqueness condition being met ensure no junk and no confusion.
        // The reader should convince her or himself that this is the case.
        //
        (calDB'.getCurrentCalendar().getItems().size() ==
            calDB.getCurrentCalendar().getItems().size() + 1)

            &&

        //
        // Also, requiresSaving and hasChanged are both true in the output
        // calendar.
        //
        calDB'.getCurrentCalendar().requiresSaving()
            &&
        calDB'.getCurrentCalendar().hasChanged();

     */
    public void scheduleEvent(Event event)
            throws ScheduleEventPrecondViolation {

        /*
         * Clear out the error fields in precond violation exception object.
         */
        scheduleEventPrecondViolation.clear();

        /*
         * Throw a precond violation if the validity check fails on the title,
         * start date, or end date.
         */
        if (validateInputs(event).anyErrors()) {
            throw scheduleEventPrecondViolation;
        }

        /*
         * Throw a precond violation if an event of the same start date and
         * title is already scheduled.
         */
        if (alreadyScheduled(event)) {
            scheduleEventPrecondViolation.setAlreadyScheduledError();
            throw scheduleEventPrecondViolation;
        }

        /*
         * Throw a precond violation if there is no currently active calendar.
         * Note that this condition will not be violated when interacting
         * through the view, since the 'Schedule Event' menu item is disabled
         * whenever the there is no active calendar.
         */
        if (calDB.getCurrentCalendar() == null) {
            scheduleEventPrecondViolation.setNoActiveCalendarError();
            throw scheduleEventPrecondViolation;
        }

        /*
         * If preconditions are met, add the given event to the currently
         * active calendar.
         */
        calDB.getCurrentCalendar().add(event);

    }

    /**
     * Change the given old appointment to the given new one in the
     * current calendar.
     */
    public void changeAppointment(Appointment oldAppt, Appointment newAppt) {
        System.out.println("In Schedule.changeAppointment.");
    }

    /**
     * Delete the given appointment from the current calendar.
     */
    public void deleteAppointment(Appointment appt) {
        System.out.println("In Schedule.deleteAppointment.");
    }


    /*-*
     * Access methods
     */

    /**
     * Return the categories component.
     *                                                                     
      pre: // no precondition ;
      post: return == this.categories ;
     */    
    public Categories getCategories() {
        return categories;
    }

    /*-*
     * Set methods.
     */

    /**
     * Set the priority of the given task to the given priority value.  The
     * value must be between 0 and 10, inclusive.  Throw an exception if it's
     * not in this range.
     *                                                                     
      pre:
        priority >= 0 && priority <= 10;

      post:
        task.priority == priority;
     */
    public void setTaskPriority(Task task, int priority)
            throws ScheduleTaskPrecondViolation {
        /*
         * Throw a precond violation if priority is out of range.
         */
        if ((priority < 0) || (priority > 10)) {
            scheduleTaskPrecondViolation.clear();
            scheduleTaskPrecondViolation.setPriorityOutOfRangeError();
            throw scheduleTaskPrecondViolation;
        }

        /*
         * Otherwise set the priority in the given task.
         */
        task.priority = priority;
    }

    /**
     * Convert this to a printable string.  Note that the categories field is
     * only converted shallow since no methods of this change the contents of
     * categories.  The deep string conversion is of calDB.getCurrentCalendar,
     * since it's the object to which the scheduling methods effect change.
     */
    public String toString() {
        return
            "Categories: " + categories + "\n" +
            "caldDB.currentCalendar:\n" /* +
            (calDB != null && calDB.getCurrentCalendar() != null ?
                calDB.getCurrentCalendar().toString() : "" */;
    }

    /*-*
     * Protected methods
     */

    /**
     * Return true if there is an already scheduled event of the same title on
     * any of the same dates as the given event.
     */
    protected boolean alreadyScheduled(ScheduledItem item) {

        /*
         * Implementation forthcoming.
         */
        return false;

        /*
         * The following won't fully work, since we must check all dates.
         *
        return calDB.getCurrentCalendar().getItem(
            new ItemKey(e.startDate, null, null, e.title)) == null;
         *
         */

    }

    /**
     * Validate the  ScheduleEvent
     *  precondition.  Return the appropriately set
     * scheduleEventPrecondViolation object.  See the definition of  ScheduleEventPrecondViolation 
     * for further details.
     */
    protected ScheduleEventPrecondViolation validateInputs(Event event) {

        if ((event.title == null) || (event.title.length() == 0)) {
            scheduleEventPrecondViolation.setEmptyTitleError();
        }

        if (! event.startOrDueDate.isValid()) {
            scheduleEventPrecondViolation.setInvalidStartDateError();
        }

        if ((event.endDate != null) && (! event.endDate.isValid())) {
            scheduleEventPrecondViolation.setInvalidEndDateError();
        }

        return scheduleEventPrecondViolation;

    }

    /**
     * Validate the  ScheduleAppointment
     *  precondition.  Return the appropriately set
     * scheduleAppointmentPrecondViolation object.  See the definition of  ScheduleAppointmentPrecondViolation 
     * for further details.
     */
    protected ScheduleAppointmentPrecondViolation validateInputs(
            Appointment appt) {

        if ((appt.title == null) || (appt.title.length() == 0)) {
            scheduleAppointmentPrecondViolation.setEmptyTitleError();
        }

        if (! appt.startOrDueDate.isValid()) {
            scheduleAppointmentPrecondViolation.setInvalidStartDateError();
        }

        if ((appt.endDate != null) && (! appt.endDate.isValid())) {
            scheduleAppointmentPrecondViolation.setInvalidEndDateError();
        }

        return scheduleAppointmentPrecondViolation;

    }

    protected ScheduleTaskPrecondViolation validateInputs(Task task) {
        // Implementation forthcoming
        return scheduleTaskPrecondViolation;
    }

    /*-*
     * Derived data fields
     */

    /** Category list in which scheduled item categories are defined */
    protected Categories categories;


    /*-*
     * Process data fields
     */

    /** Calendar database that contains the current calendar in which scheduled
     * items are stored */
    protected CalendarDB calDB;

    /** Precond violation exception objects */
    protected ScheduleAppointmentPrecondViolation
        scheduleAppointmentPrecondViolation;
    protected ScheduleMeetingPrecondViolation scheduleMeetingPrecondViolation;
    protected ScheduleTaskPrecondViolation scheduleTaskPrecondViolation;    
    protected ScheduleEventPrecondViolation scheduleEventPrecondViolation;

}