(****
 *
 * Module File defines the objects and operations related to file processing
 * in the schedule system.
 *
 *)

module File;
  
  from Edit import all;
  from ScheduleM import all;
  export FileSpace, File;

  object FileSpace is File*
    description: (*
        A FileSpace is an abstract model of a file space in the operating
        environment in which the scheduleTool is run.  The FileSpace is simply
        a collection of zero or more Files, with no other properties modeled
        here.
     *);
  end;

  object File is
    components: name:FileName and permissions:FilePermissions and
        file_type:FileType and data:FileData;
    description: (*
        A File is an abstraction of a file stored in the file space.  It has a
        name, permissions, type, and data.  These are the components sufficient
        to specify the behavior of schedule Tool file operations.
    *);
  end File;

  object FileName is string
    description: (*
        The name of a file.  The string representation here is an abstraction
        of file names used in specific operating environments.  Implementations
        may obey any syntactic or semantic constraints imposed by a particular
        environment.
    *);
  end;

  object FilePermissions is is_readable:IsReadable and is_writable:IsWritable
    description: (*
        FilePermissions indicate whether a file is readable and/or writable.
    *);
  end;

  object IsReadable is boolean
    description: (*
        Flag indicating whether a file is readable, which is required to be
        true by the FileOpen operation.
    *);
  end;

  object IsWritable is boolean
    description: (*
        Flag indicating whether a file is writable, which is required to be
        true by the FileSave operation.
    *);
  end;

  object FileType is schedule_type:ScheduleType or other_type:OtherType
    description: (*
        The type of file data is either ScheduleType data (which we care
        about) or any other type of data (which we don't care about).
    *);
  end FileType;

  object ScheduleType
    description: (*
        File data typing tag indicating that a file contains schedule data
        created by the Scheduler.
    *);
  end ScheduleType;

  object OtherType
    description: (*
        File data typing tag indicating that a file contains data other than
        schedule data created by the scheduler.
    *);
  end OtherType;

  object FileData is Schedule
    description: (*
        The abstract representation of schedule-type FileData is a Schedule
        object.  scheduler implementors may use any concrete file data
        representation that accurately holds all Schedule components.
    *);
  end FileData;

  operation FileNew is
    inputs: uws:UserWorkSpace;
    outputs: uws':UserWorkSpace;

    description: (*
        Add a new empty schedule to the workspace and make it current.
    *);

    precondition: ;

    postcondition:
        (*
         * The output workspace has a new empty schedule and that schedule is
         * current.  The user id of the new schedule is that of the workspace,
         * the file is empty, the options are the given global options input,
         * and the schedule does not require saving.  The schedules in
         * positions 1-last in the the input workspace are in positions
         * 2-last+1 in the output workspace.  The number of unnamed schedules
         * is incremented by on
         *)
        (exists (uc:Schedule)
            (uc = uws'.schedules[1]) and
            (uc.file = nil) and
            (not uc.requires_saving) and
            (#(uws'.schedules) = #(uws.schedules) + 1) and
            (forall (i:integer | (i >= 1) and (i <= #(uws.schedules)))
                uws'.schedules[i+1] = uws.schedules[i]
            )
        );

  end FileNew;

  operation FileOpen is
    inputs: fs:FileSpace, fn:FileName, uws:UserWorkSpace;
    outputs: uws':UserWorkSpace;

    description: (*
        Open an existing schedule file of the given name and put the data from
        that file in the workspace.
    *);

    precondition:
        (*
         * A file of the given name exists in the given file space, the file
         * is readable, and the file's data are of type schedule.
         *)
        exists (file in fs)
            (file.name = fn) and
            file.permissions.is_readable and
            file.file_type?schedule_type;

    postcondition:
        (*
         * The output workspace has a new schedule containing the file data of
         * the input file, and that schedule is current.  The user id of the
         * new schedule is that of the workspace, the options are the given
         * global options input, and the schedule does not require saving.  The
         * schedules in positions 1-last in the the input workspace are in
         * positions 2-last+1 in the output workspace.
         *)
        (exists (uc:Schedule)
            (uc = uws'.schedules[1]) and
            (exists (file in fs)
                (file.name = fn) and
                (uc = file.data)
            ) and
            (not uc.requires_saving) and
            (#(uws'.schedules) = #(uws.schedules) +1) and
            (forall (i:integer | (i >= 2) and (i <= #(uws.schedules)))
                uws'.schedules[i+1] = uws.schedules[i+1]
            )
        );

  end FileOpen;

  operation FileClose is
    inputs: fs:FileSpace, uws:UserWorkSpace;
    outputs: uws':UserWorkSpace;

    description: (*
        Close the current schedule if it does not require saving.
    *);

    precondition:
        (*
         * The schedule does not require saving.
         *)
        not (uws.schedules[1].requires_saving);

    postcondition:
        (*
         * The current schedule is deleted from the workspace.  The remaining
         * schedules, if any, are shifted in position in the list one position
         * earlier.
         *)
        (not (uws.schedules[1] in uws'.schedules)) and
        (#(uws'.schedules) = #(uws.schedules) - 1) and
        (forall (i:integer | (i >= 1) and (i < #(uws.schedules)))
            uws'.schedules[i] = uws.schedules[i+1];
        );

  end FileClose;

operation FileSave is
    inputs: fs:FileSpace, uws:UserWorkSpace;
    outputs: fs':FileSpace, uws':UserWorkSpace;

    description: (*
        If the schedule in the given workspace requires saving, save it in the
        given file space.
    *);

    precondition:
        (*
         * The given workspace requires saving.  Also, there is a writable
         * file of the current workspace filename in the given FileSpace.  Note
         * that the only way the current file could be unwritable is through an
         * external change to the file space since the file was opened by the
         * schedule Tool.  Note further that this precondition disallows the
         * case where the current schedule file has been externally deleted
         * since it was opened by the schedule tool.  That is, the file must
         * both exist and be writable at the time the save is attempted.
         *)
        (uws.schedules[1].requires_saving)

            and

        (exists (file in fs)
            (file.name = uws.schedules[1].file.name) and
            (file.permissions.is_writable));

    postcondition:
        (*
         * There is a schedule-type file in the resulting FileSpace containing
         * the current workspace schedule as its file data.  In the resulting
         * workspace, the requires saving indicator is false.
         *)
        (exists (file in fs')
            (file.name = uws'.schedules[1].file.name) and
            (file.data = uws'.schedules[1]) and
            (file.permissions.is_writable) and
            (file.file_type?schedule_type) and
            (not uws'.schedules[1].requires_saving)
        );
  end FileSave;

end File;