Skip to content

3.27 Tutorial 4

practice_guide_4.pdf

To examine whether two instance are same

  1. check whether it is null.

  2. check whether it is an instance of that class

  3. convert the class type and check whether they are equal.

  4. check each field

image

To check the invariant

image

.... 真是作业造火箭 什么鬼......Calendar.zip

Task 2:

image

Date.java

package DateAndMonthCalendar;

public class Date {

    private int day;
    private int month;
    private int year;

    //!!! Noticing
    //The minimum valid date constants.
    public static final int MIN_DAY = 15;
    public static final int MIN_MONTH = 10;
    public static final int MIN_YEAR = 1582;

    /**
     * Sets the Date to the minimum date 15/10/1582.
     */
    //!!! Noticing
    public Date() {
        day = MIN_DAY;
        month = MIN_MONTH;
        year = MIN_YEAR;
    }

    /**
     * Sets the Date with a specific day, month, and year
     * this date should be greater than minimum date
     */
    public Date(int newDay, int newMonth, int newYear) {
        day = newDay;
        month = newMonth;
        year = newYear;
        //!!! Noticing
        if (!repOk()) {
            throw new IllegalArgumentException("Invalid date. Date must be greater or equal than 15/10/1582 and valid.");
        }
    }

    /**
     * The algorithm of calculating days in the month
     */
    public int daysInMonthAlgorithm(int month) {
        //!!! Noticing
        /*
        if(month == 1 || month == 3 || month == 5 || month == 7
            || month == 8 || month == 10 || month == 12) {
            return 31;
        }
        else if(month == 4 || month == 6 || month == 9 || month == 11) {
            return 30;
        }
        else if(isLeapYear(year)) {
            return 29;
        }
        else {
            return 28;
        }
        */
        switch (month) {
            case 1: case 3: case 5: case 7: 
            case 8: case 10: case 12:
                return 31;
            case 4: case 6: case 9: case 11:
                return 30;
            case 2:
                return isLeapYear(year) ? 29 : 28;
            default:
                throw new IllegalArgumentException("Invalid month: " + month);
        }
    }   

    /**
     * return how many days the month of a Date has.
     */
    public int daysInMonth() {
        return daysInMonthAlgorithm(month);
    }

    private int daysInMonth(int month) {
        return daysInMonthAlgorithm(month);
    }

    /**
     *  determine whether the year is leap year
     */
    public boolean isLeapYear() {
        return isLeapYear(year);
    }

    private boolean isLeapYear(int year) {
        return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
    }

    /**
     * Methods to get the day, month, and year of a Date.
     */
    public int getDay() {
        return day;
    }

    public int getMonth() {
        return month;
    }

    public int getYear() {
        return year;
    }

    /**
     * Change the day, month, and year of a Date
     */
    public void changeDate(int newDay, int newMonth, int newYear) {
        /*day = newDay;
        month = newMonth;
        year = newYear;
        */
        //!!! Noticing
        int oldDay = day, oldMonth = month, oldYear = year;
        day = newDay;
        month = newMonth;
        year = newYear;
        if (!repOk()) {
            // Revert changes if new values are invalid.
            day = oldDay;
            month = oldMonth;
            year = oldYear;
            throw new IllegalArgumentException("Invalid date change. Date must be greater or equal than 15/10/1582 and valid.");
        }
    }

     /** if this date is less (-1), equal
      * (0), or greater (1) than the other Date
      */
    public int compareTo(Date other) {
        if(this.getYear() > other.getYear()) {
            return 1;
        }
        else if(this.getYear() < other.getYear()) {
            return -1;
        }
        else {
            if(this.getMonth() > other.getMonth()) {
                return 1;
            }
            else if(this.getMonth() < other.getMonth()) {
                return -1;
            }
            else {
                if(this.getDay() > other.getDay()) {
                    return 1;
                }
                else if(this.getDay() < other.getDay()) {
                    return -1;
                }
                else {
                    return 0;
                }
            }
        }
    }

     /** The algorithm to calculate the difference days from 1/1/1 to this Date
      */
    public int countDays() {
        int total = 0;
        int i = 1;
        while(i < this.getYear()) {
            total += isLeapYear(i) ? 366 : 365;
            i++;
        }

        i = 1;
        while(i < this.getMonth()) {
            total += daysInMonth(i);
            i++;
        }

        total += this.getDay();
        return total;
    }


    /** to calculate the difference in days between another Date 
     *  which must be less or equal than the current one.
     */
    //!!! Noticing
    public int differenceInDays(Date other) {
        //other < this
        if (this.compareTo(other) < 0) {
            throw new IllegalArgumentException("Other date must be less than or equal to the current date.");
        }
        return this.countDays() - other.countDays();
    }

    /**
     * Returns true if this Date meets the class invariants:
     * - The date is not before 15/10/1582.
     * - The month is between 1 and 12.
     * - The day is between 1 and the maximum days in that month.
     */
    public boolean repOk() {
        /*
        if(year < 1582) {
            return false;
        }
        else if(month < 10) {
            return false;
        }
        else if(day < 15) {
            return false;
        }
        */
        if (year < MIN_YEAR) {
            return false;
        }
        if (year == MIN_YEAR) {
            if (month < MIN_MONTH) {
                return false;
            }
            if (month == MIN_MONTH && day < MIN_DAY) {
                return false;
            }
        }
        if (month < 1 || month > 12) {
            return false;
        }
        if (day < 1 || day > daysInMonth(month)) {
            return false;
        }
        return true;
    }


    /**
    * Checks if this Date is equal to another.
    * Two dates are equal if they have the same day, month, and year.
    */
    @Override
    public boolean equals(Object other) {
        // Check if it's the same object reference
        if (this == other) {
            return true;
        }

        // Check if other is null or not a Date object
        if (other == null || !(other instanceof Date)) {
            return false;
        }

        // Cast other to Date
        Date otherDate = (Date) other;

        // Compare day, month, and year
        return this.day == otherDate.day && 
            this.month == otherDate.month && 
            this.year == otherDate.year;
    }
    /**
     *  return a string representation of a Date
     */
    @Override
    public String toString() {
        if(month < 10) {
            return day + "/0" + month + "/" + year;
        }
        else {
            return day + "/" + month + "/" + year;
        }   
    }
}

MonthCalendar.java

package DateAndMonthCalendar;

import java.util.ArrayList;

public class MonthCalendar {
    private int year;
    private int month;
    private ArrayList<Date> freeDates;
    private ArrayList<Date> busyDates;

    /**
     * Constructor for MonthCalendar.
     * Preconditions:
     * - year must be >= 1582.
     * - month must be between 1 and 12, and if year is 1582, month must be >= 10.
     * Postcondition: All valid Dates in the month are initially marked as free.
     */
    public MonthCalendar(int year, int month) {
        if (year < Date.MIN_YEAR) {
            throw new IllegalArgumentException("Year must be at least " + Date.MIN_YEAR);
        }
        if (month < 1 || month > 12) {
            throw new IllegalArgumentException("Month must be between 1 and 12");
        }
        if (year == Date.MIN_YEAR && month < Date.MIN_MONTH) {
            throw new IllegalArgumentException("For year " + Date.MIN_YEAR + ", month must be at least " + Date.MIN_MONTH);
        }
        this.year = year;
        this.month = month;
        freeDates = new ArrayList<Date>();
        busyDates = new ArrayList<Date>();

        int startDay = 1;
        if (year == Date.MIN_YEAR && month == Date.MIN_MONTH) {
            startDay = Date.MIN_DAY;
        }
        int numDays;
        if (year == Date.MIN_YEAR && month == Date.MIN_MONTH) {
            Date dummy = new Date(Date.MIN_DAY, month, year);
            numDays = dummy.daysInMonth();
        } else {
            Date dummy = new Date(1, month, year);
            numDays = dummy.daysInMonth();
        }

        for (int d = startDay; d <= numDays; d++) {
            Date current = new Date(d, month, year);
            freeDates.add(current);
        }

        if (!repOk()) {
            throw new IllegalStateException("Invariant violation in MonthCalendar constructor.");
        }
    }

    /**
     * Marks a valid, free Date as busy.
     * Precondition: The Date is in this month/year and is currently free.
     * Postcondition: The Date is moved from freeDates to busyDates.
     */
    public void markAsBusy(Date date) {
        if (date.getYear() != this.year || date.getMonth() != this.month) {
            throw new IllegalArgumentException("Date is not in the calendar month.");
        }
        if (!freeDates.contains(date)) {
            throw new IllegalArgumentException("Date is not free or does not exist.");
        }
        freeDates.remove(date);
        busyDates.add(date);

        if (!repOk()) {
            throw new IllegalStateException("Invariant violation after marking date as busy.");
        }
    }

    /**
     * Marks a valid, busy Date as free.
     * Precondition: The Date is in this month/year and is currently busy.
     * Postcondition: The Date is moved from busyDates to freeDates.
     */
    public void freeDate(Date date) {
        if (date.getYear() != this.year || date.getMonth() != this.month) {
            throw new IllegalArgumentException("Date is not in the calendar month.");
        }
        if (!busyDates.contains(date)) {
            throw new IllegalArgumentException("Date is not busy.");
        }
        busyDates.remove(date);
        freeDates.add(date);

        if (!repOk()) {
            throw new IllegalStateException("Invariant violation after freeing date.");
        }
    }

    /**
     * Returns a string representation of the MonthCalendar.
     * Format:
     *   Month calendar: ¡month name¿/¡year¿ 
     *   Busy dates:
     *     list of busy dates (one per line)
     *   Free dates:
     *     list of free dates (one per line)
     */
    public String toString() {
        String s = "Month calendar: " + getMonthName(month) + "/" + year + "\n";
        s = s + "Busy dates:\n";
        if (busyDates.size() == 0) {
            s = s + "None\n";
        } else {
            for (int i = 0; i < busyDates.size(); i++) {
                s = s + busyDates.get(i).toString() + "\n";
            }
        }
        s = s + "Free dates:\n";
        if (freeDates.size() == 0) {
            s = s + "None\n";
        } else {
            for (int i = 0; i < freeDates.size(); i++) {
                s = s + freeDates.get(i).toString() + "\n";
            }
        }
        return s;
    }

    /**
     * Helper method to return the month name.
     */
    private String getMonthName(int month) {
        String[] months = {"January", "February", "March", "April", "May", "June", 
                           "July", "August", "September", "October", "November", "December"};
        return months[month - 1];
    }

    /**
     * Class invariant check.
     * Returns true if the state of this MonthCalendar is valid.
     */
    public boolean repOk() {
        // Check year and month invariants.
        if (year < Date.MIN_YEAR) {
            return false;
        }
        if (year == Date.MIN_YEAR && month < Date.MIN_MONTH) {
            return false;
        }
        if (month < 1 || month > 12) {
            return false;
        }
        // Check that every Date in freeDates and busyDates belongs to this calendar and is valid.
        for (int i = 0; i < freeDates.size(); i++) {
            Date d = freeDates.get(i);
            if (d.getYear() != year || d.getMonth() != month || !d.repOk()) {
                return false;
            }
        }
        for (int i = 0; i < busyDates.size(); i++) {
            Date d = busyDates.get(i);
            if (d.getYear() != year || d.getMonth() != month || !d.repOk()) {
                return false;
            }
        }
        // Ensure no Date is both free and busy.
        for (int i = 0; i < freeDates.size(); i++) {
            Date d = freeDates.get(i);
            if (busyDates.contains(d)) {
                return false;
            }
        }
        return true;
    }
}

MonthOriganizer.java

import DateAndMonthCalendar.MonthCalendar;
import DateAndMonthCalendar.Date;
import java.util.Scanner;

public class MonthOrganizer {
    /**
     * Main method that allows the user to interact with a MonthCalendar.
     * It lets the user mark dates as busy, free busy dates, and display the calendar.
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Welcome to Month Organizer");

        System.out.print("Enter year (>=1582): ");
        int year = scanner.nextInt();
        System.out.print("Enter month (1-12): ");
        int month = scanner.nextInt();

        MonthCalendar calendar;
        try {
            calendar = new MonthCalendar(year, month);
        } catch (IllegalArgumentException e) {
            System.out.println("Error creating calendar: " + e.getMessage());
            scanner.close();
            return;
        }

        boolean exit = false;
        while (!exit) {
            System.out.println("\nSelect an option:");
            System.out.println("1. Mark a date as busy");
            System.out.println("2. Free a busy date");
            System.out.println("3. Display calendar");
            System.out.println("4. Exit");
            System.out.print("Choice: ");
            int choice = scanner.nextInt();

            switch (choice) {
                case 1:
                    System.out.print("Enter day to mark as busy: ");
                    int busyDay = scanner.nextInt();
                    try {
                        Date busyDate = new Date(busyDay, month, year);
                        calendar.markAsBusy(busyDate);
                        System.out.println("Date marked as busy.");
                    } catch (Exception e) {
                        System.out.println("Error: " + e.getMessage());
                    }
                    break;
                case 2:
                    System.out.print("Enter day to free: ");
                    int freeDay = scanner.nextInt();
                    try {
                        Date freeDate = new Date(freeDay, month, year);
                        calendar.freeDate(freeDate);
                        System.out.println("Date freed.");
                    } catch (Exception e) {
                        System.out.println("Error: " + e.getMessage());
                    }
                    break;
                case 3:
                    System.out.println(calendar.toString());
                    break;
                case 4:
                    exit = true;
                    break;
                default:
                    System.out.println("Invalid option.");
            }
        }
        scanner.close();
        System.out.println("Goodbye!");
    }
}