hw1: complete

This commit is contained in:
Yuri Tatishchev 2025-02-19 23:54:50 -08:00
commit 25f13105f2
Signed by: CaZzzer
GPG Key ID: 28BE602058C08557
14 changed files with 851 additions and 0 deletions

29
hw1/.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
### IntelliJ IDEA ###
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

8
hw1/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

6
hw1/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
hw1/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/hw1.iml" filepath="$PROJECT_DIR$/hw1.iml" />
</modules>
</component>
</project>

6
hw1/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

20
hw1/events.txt Normal file
View File

@ -0,0 +1,20 @@
CS146 Lecture
MW 12:00 13:15 1/27/2025 5/12/2025
CS151 Lecture
TR 9:00 10:15 1/23/2025 5/8/2025
CS157C Lecture
MW 10:30 11:45 1/27/2025 5/12/2025
Interview at BigCorp
4/18/2025 9:30 11:30
Dentist appt
6/3/2025 16:15 17:00
Bakery appt
3/3/2025 13:30 14:00
Grocery appt
2/8/2025 11:15 12:00
Battery appt
3/9/2025 19:30 20:30
Course Committee Meeting
F 18:30 20:30 1/24/2025 5/9/2025
Procrastination Committee Meeting
F 20:30 23:00 1/24/2025 5/9/2025

1
hw1/googledoc.txt Normal file
View File

@ -0,0 +1 @@
https://docs.google.com/document/d/14LadH5F3WpchLzI4l7Q6pQ4kT4jXz2yGlmln-7SywNM/edit?usp=sharing

11
hw1/hw1.iml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

18
hw1/output.txt Normal file
View File

@ -0,0 +1,18 @@
Hello Event
1/15/2025 14:00 16:00
CS151 Lecture
TR 09:00 10:15 1/23/2025 5/8/2025
Course Committee Meeting
F 18:30 20:30 1/24/2025 5/9/2025
Procrastination Committee Meeting
F 20:30 23:00 1/24/2025 5/9/2025
CS157C Lecture
MW 10:30 11:45 1/27/2025 5/12/2025
CS146 Lecture
MW 12:00 13:15 1/27/2025 5/12/2025
Battery appt
3/9/2025 19:30 20:30
Interview at BigCorp
4/18/2025 09:30 11:30
Dentist appt
6/3/2025 16:15 17:00

217
hw1/src/Event.java Normal file
View File

@ -0,0 +1,217 @@
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
/**
* Represents an event that occurs on a specific date or a range of dates.
* <p>
* The event has a time interval and a description.
* <p>
* The event can be recurring on specific days of the week.
*
* @author Yuri Tatishchev
* @version 0.1 2025-02-08
*/
public class Event implements Comparable<Event> {
private final LocalDate startDate;
private final LocalDate endDate;
private final TimeInterval timeInterval;
private final String name;
private final SortedSet<DayOfWeek> recurs;
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("M/d/yyyy");
private static final DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("H:mm");
private static final Map<Character, DayOfWeek> charToDayOfWeek = Map.of(
'M', DayOfWeek.MONDAY,
'T', DayOfWeek.TUESDAY,
'W', DayOfWeek.WEDNESDAY,
'R', DayOfWeek.THURSDAY,
'F', DayOfWeek.FRIDAY,
'S', DayOfWeek.SATURDAY,
'U', DayOfWeek.SUNDAY
);
private static final Map<DayOfWeek, Character> dayOfWeekToChar = charToDayOfWeek.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
/**
* Constructs a one-time event with the specified name, date, and time interval.
*/
public Event(String name, LocalDate date, TimeInterval timeInterval) {
this.name = name;
this.startDate = date;
this.endDate = date;
this.timeInterval = timeInterval;
this.recurs = null;
}
/**
* Constructs a recurring event with the specified name, start date, time interval, and recurring days.
*
* @throws IllegalArgumentException if the start date is after the end date
*/
public Event(String name, LocalDate startDate, LocalDate endDate, TimeInterval timeInterval, SortedSet<DayOfWeek> recurs) {
if (startDate.isAfter(endDate)) {
throw new IllegalArgumentException("Start date must be before or equal to end date");
}
this.name = name;
this.startDate = startDate;
this.endDate = endDate;
this.timeInterval = timeInterval;
this.recurs = recurs;
}
public LocalDate getStartDate() {
return startDate;
}
public LocalDate getEndDate() {
return endDate;
}
public TimeInterval getTimeInterval() {
return timeInterval;
}
public String getName() {
return name;
}
public boolean isRecurring() {
return recurs != null;
}
public SortedSet<DayOfWeek> getRecurs() {
return recurs;
}
/**
* Checks if the event occurs on a specific date.
*
* @param date the date to check
* @return true if the event occurs on the date, false otherwise
*/
public boolean occursOnDate(LocalDate date) {
if (date.isBefore(startDate) || date.isAfter(endDate)) {
return false;
}
if (recurs == null) {
return true;
}
for (DayOfWeek day : recurs) {
if (date.getDayOfWeek() == day) {
return true;
}
}
return false;
}
/**
* Checks if this event overlaps with another event.
* <p>
* Two events overlap if their dates overlap and their time intervals overlap.
*
* @param other the other event
* @return true if the events overlap, false otherwise
*/
public boolean overlapsWith(Event other) {
// Dates do not overlap
if (this.endDate.isBefore(other.startDate) || this.startDate.isAfter(other.endDate)) {
return false;
// Dates overlap, but time intervals do not
} else if (!this.timeInterval.overlapsWith(other.timeInterval)) {
return false;
// Dates and time intervals overlap, but possibly not on the same day
} else if (this.isRecurring()) {
// this is recurring, but other is not
if (!other.isRecurring()) {
return this.recurs.contains(other.getStartDate().getDayOfWeek());
}
// Both are recurring, check if any of the recurring days overlap
// TODO: check if this is correct.
// I suspect there are edge cases where the days that overlap are not in the overlapping date range.
return this.recurs.stream().anyMatch(other.recurs::contains);
// other is recurring, but this is not
} else if (other.isRecurring()) {
return other.recurs.contains(this.startDate.getDayOfWeek());
}
return true;
}
public int compareTo(Event other) {
if (this.startDate.isBefore(other.startDate)) {
return -1;
} else if (this.startDate.isAfter(other.startDate)) {
return 1;
} else {
return this.timeInterval.getStart().compareTo(other.timeInterval.getStart());
}
}
/**
* Returns a string representation of the event.
*
* @return a string representation of the event
* <p>
* Example output: <br>
* <br> One event name
* <br> 1/23/2025 9:00 10:15
* <br>
* <br> Recurring event name
* <br> TR 10:30 11:45 1/27/2025 5/12/2025
*/
public String toString() {
return String.format("%s\n%s\n", name, scheduleString());
}
/**
* Returns a string representation of the event's schedule.
*
* @return a string representation of the event's schedule
* <p>
* Example output: <br>
* <br> 1/23/2025 9:00 10:15
* <br> TR 10:30 11:45 1/27/2025 5/12/2025
*/
public String scheduleString() {
if (recurs == null) return String.format("%s %s", dateFormat.format(startDate), timeInterval);
String recursString = recurs.stream().map(dayOfWeekToChar::get).map(String::valueOf).collect(Collectors.joining());
return String.format("%s %s %s %s", recursString, timeInterval, dateFormat.format(startDate), dateFormat.format(endDate));
}
/**
* Parses a schedule string and returns an event.
*
* @param name the name of the event
* @param schedule the schedule string
* @return an event
* <p>
* Example schedule strings:
* <br> TR 9:00 10:15 1/23/2025 5/8/2025
* <br> MW 10:30 11:45 1/27/2025 5/12/2025
* <br> 4/18/2025 9:30 11:30
* <br> 6/3/2025 16:15 17:00
* <br> F 18:30 20:30 1/24/2025 5/9/2025
*/
public static Event fromScheduleString(String name, String schedule) {
String[] parts = schedule.split("\\s+");
if (parts.length == 3) {
LocalDate date = LocalDate.parse(parts[0], dateFormat);
LocalTime start = LocalTime.parse(parts[1], timeFormat);
LocalTime end = LocalTime.parse(parts[2], timeFormat);
TimeInterval timeInterval = new TimeInterval(start, end);
return new Event(name, date, timeInterval);
} else {
LocalTime start = LocalTime.parse(parts[1], timeFormat);
LocalTime end = LocalTime.parse(parts[2], timeFormat);
LocalDate startDate = LocalDate.parse(parts[3], dateFormat);
LocalDate endDate = LocalDate.parse(parts[4], dateFormat);
TimeInterval timeInterval = new TimeInterval(start, end);
SortedSet<DayOfWeek> recurs = parts[0].chars().mapToObj(c -> charToDayOfWeek.get((char) c)).collect(Collectors.toCollection(TreeSet::new));
return new Event(name, startDate, endDate, timeInterval, recurs);
}
}
}

73
hw1/src/MyCalendar.java Normal file
View File

@ -0,0 +1,73 @@
import java.time.LocalDate;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Stream;
/**
* Represents a calendar that stores events in sorted order.
* <p>
* The calendar can add and remove events, and filter events by date.
* <p>
* The calendar does not allow overlapping events.
*
* @see Event
* @implNote The calendar uses a {@link TreeSet} to store events in sorted order.
* @author Yuri Tatishchev
* @version 0.1 2025-02-08
*/
public class MyCalendar {
private final SortedSet<Event> events;
public MyCalendar() {
events = new TreeSet<>();
}
public SortedSet<Event> getEvents() {
return events;
}
/**
* Adds an event to the calendar.
*
* @param newEvent the event to add
* @throws IllegalArgumentException if the event overlaps with an existing event
*/
public void addEvent(Event newEvent) {
// Check if the event overlaps with any existing event
for (Event e : events) {
if (newEvent.overlapsWith(e)) {
String message = String.format("Event '%s' overlaps with event '%s'", newEvent.getName(), e.getName());
throw new IllegalArgumentException(message);
}
}
events.add(newEvent);
}
/**
* Removes an event from the calendar.
*
* @param event the event to remove
*/
public void removeEvent(Event event) {
events.remove(event);
}
/**
* Removes all one-time events that occur on the specified date.
*
* @param date the date to remove events on
*/
public void removeOneTimeEventsOnDate(LocalDate date) {
events.removeIf(e -> !e.isRecurring() && e.occursOnDate(date));
}
/**
* Returns a stream of events that occur on the specified date.
*
* @param date the date to filter events by
* @return a stream of events that occur on the specified date
*/
public Stream<Event> getEventsOnDate(LocalDate date) {
return events.stream().filter(e -> e.occursOnDate(date));
}
}

View File

@ -0,0 +1,391 @@
import java.io.*;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Scanner;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* MyCalendarTester is a console interface for the MyCalendar class.
* <p>
* The program reads events from a file, displays a main menu, and allows the user to view, create, go to, list, and delete events.
*
* @see MyCalendar
* @see Event
* @see TimeInterval
* @author Yuri Tatishchev
* @version 0.1 2025-02-08
*/
public class MyCalendarTester {
private static final MyCalendar calendar = new MyCalendar();
private static final String inputFilename = "events.txt";
private static final String outputFilename = "output.txt";
public static void main(String[] args) {
printMonth(LocalDate.now(), true, false);
System.out.println();
readEventsFromFile(inputFilename);
// After loading the events, the program prompts "Loading is done!".
// The program now displays a MAIN MENU with following options: View by, Create, Go to, Event list, Delete, and Quit. After the function of an option is done, the main menu is displayed again for the user to choose the next option.
//
// Select one of the following main menu options:
// [V]iew by [C]reate, [G]o to [E]vent list [D]elete [Q]uit
//
// The user may enter one of the letters highlighted with a pair of the bracket to choose an option. For example,
//
// V
//
// will choose the View by option.
//
// User enters an invalid character or an unrecognized option (e.g., typing X instead of [C]reate). Show an error ("Invalid option. Please choose from the menu.") and re-display the menu.
System.out.println("Select one of the following main menu options:");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("[V]iew by;\t [C]reate;\t [G]o to;\t [E]vent list;\t [D]elete;\t [Q]uit;");
String input = scanner.nextLine();
switch (input.toUpperCase()) {
case "V" -> handleViewBy();
case "C" -> handleCreate();
case "G" -> handleGoTo();
case "E" -> printEventList();
case "D" -> handleDelete();
case "Q" -> {
handleQuit();
return;
}
default -> System.out.println("Invalid option. Please choose from the menu.");
}
}
}
private static void handleQuit() {
System.out.println("Saving events to " + outputFilename);
try (Writer writer = new BufferedWriter(new FileWriter(outputFilename))) {
for (Event event : calendar.getEvents()) {
writer.write(event.toString());
}
} catch (IOException e) {
System.out.println("Error saving events to " + outputFilename);
}
System.out.println("Goodbye!");
}
/**
* [D]elete
* The user can delete an event from the Calendar.
* There are three different ways to delete an event: Selected, All and DeleteRecurring.
* Other types of deletion will not be considered for simplicity.
* <p>
* [S]elected: the user specifies the date and name of an ONE TIME event.
* The specific one time event will be deleted.
* <p>
* [A]ll: the user specifies a date and then all ONE TIME events scheduled on the date will be deleted.
* <p>
* [R]: the user specifies the name of a RECURRING event. The specified recurring event will be deleted.
* This will delete the recurring event throughout the calendar.
* <p>
* [S]elected [A]ll [R]
* <p>
* Here is a scenario of [S]elected as an example.
* You may implement [A]ll and [R] according to the above description.
* If the user enters S, then the calendar asks for the date and displays all the events scheduled on that date.
* The program then asks the name of the event to be deleted and deletes the specified event.
* If there is no such event, the program promotes an error message.
* <p>
* Enter the date [dd/mm/yyyy]
* 06/3/2025
* 16:15 - 17:00 Dentist
* 17:30 - 18:15 Piano Lesson
* <p>
* Enter the name of the event to delete: Dentist
* <p>
* Nonexistent Event: User tries to delete an event that doesnt exist.
* Notify the user ("Error: No event found with the specified name on this date.").
*/
private static void handleDelete() {
Scanner scanner = new Scanner(System.in);
System.out.println("[S]elected\t [A]ll\t [R]");
String input = scanner.nextLine();
switch (input.toUpperCase()) {
case "S" -> deleteSelected();
case "A" -> deleteAll();
case "R" -> deleteRecurring();
default -> System.out.println("Invalid option. Please choose from the menu.");
}
}
private static void deleteRecurring() {
Scanner scanner = new Scanner(System.in);
System.out.println("RECURRING EVENTS");
calendar.getEvents().stream()
.filter(Event::isRecurring)
.forEach(System.out::print);
System.out.println("Enter the name of the recurring event to delete:");
String name = scanner.nextLine();
calendar.getEvents().stream()
.filter(Event::isRecurring)
.filter(e -> e.getName().equals(name))
.findFirst()
.ifPresent(calendar::removeEvent);
System.out.println("Recurring event deleted successfully!");
}
private static void deleteAll() {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the date [MM/DD/YYYY]:");
String dateString = scanner.nextLine();
try {
LocalDate date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("M/d/yyyy"));
printDay(date, e -> !e.isRecurring());
calendar.removeOneTimeEventsOnDate(date);
System.out.println("All events deleted successfully!");
} catch (IllegalArgumentException e) {
System.out.println("Invalid date format. Please use MM/DD/YYYY.");
}
}
private static void deleteSelected() {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the date [MM/DD/YYYY]:");
String dateString = scanner.nextLine();
try {
LocalDate date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("M/d/yyyy"));
printDay(date, e -> !e.isRecurring());
System.out.println("Enter the name of the event to delete:");
String name = scanner.nextLine();
Event event = calendar.getEventsOnDate(date)
.filter(e -> e.getName().equals(name))
.findFirst()
.orElse(null);
if (event != null) {
calendar.removeEvent(event);
System.out.println("Event deleted successfully!");
} else {
System.out.println("Error: No event found with the specified name on this date.");
}
} catch (IllegalArgumentException e) {
System.out.println("Invalid date format. Please use MM/DD/YYYY.");
}
}
/**
* [G]o to
* With this option, the user is asked to enter a date in the form of MM/DD/YYYY
* and then the calendar displays the Day view of the requested date including an
* event scheduled on that day in the order of starting time.
*/
private static void handleGoTo() {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter a date (MM/DD/YYYY):");
String input = scanner.nextLine();
try {
LocalDate date = LocalDate.parse(input, DateTimeFormatter.ofPattern("M/d/yyyy"));
printDay(date, null);
} catch (IllegalArgumentException e) {
System.out.println("Invalid date format. Please use MM/DD/YYYY.");
}
}
/**
* [V]iew by
* The user can choose a Day or a Month view.
* If a Day view is chosen, the program prints today's date.
* If there is an event(s) scheduled on that day, display them in the order of start time of the event.
* <p>
* With a Month view, it displays the current month and highlights day(s) with a pair of brackets {} if any event scheduled on that day.
* After the selected view is displayed, the calendar gives the user three options: P, N, and G, where P, N, and M stand for Previous, Next and Go back to the main menu, respectively.
* The previous and next options allow the user to navigate the current view back and forth.
* If the day view was selected, the view goes back (P) and forth (N) by day.
* If the month view was chosen, the view goes back (P) and forth (N) by month.
* <p>
* Here are sample runs:
* <p>
* [D]ay view or [M]view ?
* <p>
* If the user selects D, then today's date is displayed along with scheduled events.
* <p>
* Thu, January 23, 2025
* CS151 Lecture : 10:30 - 11:45
* <p>
* <p>
* [P]revious or [N]ext or [G]o back to the main menu ? <-- The option menu allows the user to choose navigating the Day view or going back to the main menu
* <p>
* If the user selects M, then
* <p>
* <p>
* January 2025
* <br>Su Mo Tu We Th Fr Sa
* <br>1 2 3 4
* <br>5 6 7 8 9 10 11
* <br>12 13 14 15 16 17 18
* <br>19 20 21 22 {23} {24} 25
* <br>26 {27} {28} {29} {30} {31}
* <p>
* [P]revious or [N]ext or [G]o back to main menu ? Help: The following example code segment shows how to print the given date in a specified format.
* <p>
* LocalDate c = ....
* DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, MMM d yyyy");
* System.out.println(" " + formatter.format(c));
*/
private static void handleViewBy() {
Scanner scanner = new Scanner(System.in);
System.out.println("[D]ay view or [M]view ?");
String input = scanner.nextLine();
switch (input.toUpperCase()) {
case "D" -> viewByDay();
case "M" -> viewByMonth();
default -> System.out.println("Invalid option. Please choose from the menu.");
}
}
private static void viewByDay() {
LocalDate day = LocalDate.now();
Scanner scanner = new Scanner(System.in);
while (true) {
printDay(day, null);
System.out.println("[P]revious or [N]ext or [G]o back to the main menu ?");
String input = scanner.nextLine();
switch (input.toUpperCase()) {
case "P" -> day = day.minusDays(1);
case "N" -> day = day.plusDays(1);
case "G" -> {
return;
}
default -> System.out.println("Invalid option. Please choose from the menu.");
}
}
}
private static void viewByMonth() {
LocalDate month = LocalDate.now();
Scanner scanner = new Scanner(System.in);
while (true) {
printMonth(month, false, true);
System.out.println("[P]revious or [N]ext or [G]o back to the main menu ?");
String input = scanner.nextLine();
switch (input.toUpperCase()) {
case "P" -> month = month.minusMonths(1);
case "N" -> month = month.plusMonths(1);
case "G" -> {
return;
}
default -> System.out.println("Invalid option. Please choose from the menu.");
}
}
}
/**
* [C]reate
* This option allows the user to schedule an event. The calendar asks the user to enter the name, date, starting time, and ending time of an event. For simplicity, we consider one-time event only for the Create function.
* <p>
* Your program should check if a new event is a conflict with existing events including one-time and recurring events. Please stick to the following format to enter data:
* <p>
* Name: a string (doesn't have to be one word)
* Date: MM/DD/YYYY
* Starting time and ending time: 24 hour clock such as 06:00 for 6 AM and 15:30 for 3:30 PM.
* <p>
* Invalid Date Format: User enters a date in the wrong format. Check the date is invalid (e.g., 12/32/2025) and the format is invalid (e.g.2025-12-01 instead of MM/DD/YYYY). Check only these to cases. Validate the date format and show an error ("Invalid date format. Please use MM/DD/YYYY.").
*/
private static void handleCreate() {
Scanner scanner = new Scanner(System.in);
System.out.print("Name: ");
String name = scanner.nextLine();
System.out.print("Date (MM/DD/YYYY): ");
String dateString = scanner.nextLine();
System.out.print("Starting time (HH:MM): ");
String startTimeString = scanner.nextLine();
System.out.print("Ending time (HH:MM): ");
String endTimeString = scanner.nextLine();
try {
LocalDate date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("M/d/yyyy"));
LocalTime startTime = LocalTime.parse(startTimeString);
LocalTime endTime = LocalTime.parse(endTimeString);
Event event = new Event(name, date, new TimeInterval(startTime, endTime));
calendar.addEvent(event);
System.out.println("Event added successfully!");
} catch (DateTimeParseException e) {
System.out.println("Invalid date format. Please use MM/DD/YYYY.");
}
catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
private static void printDay(LocalDate calDate, Predicate<? super Event> filter) {
// Print day of week, month, day, and year
DateTimeFormatter dayFormat = DateTimeFormatter.ofPattern("E, MMM d yyyy");
System.out.println(dayFormat.format(calDate));
// Print events scheduled for the day
Stream<Event> events = calendar.getEventsOnDate(calDate);
if (filter != null) {
events = events.filter(filter);
}
events.forEach(System.out::println);
}
private static void printMonth(LocalDate calDate, boolean highlightDay, boolean showEvents) {
// Print month and year
DateTimeFormatter monthFormat = DateTimeFormatter.ofPattern("MMMM yyyy");
System.out.println(monthFormat.format(calDate));
// Print days of week header
System.out.println("Mo Tu We Th Fr Sa Su");
// Figure out the day of week of the 1st day of the given month
LocalDate day = LocalDate.of(calDate.getYear(), calDate.getMonth(), 1);
// Print leading spaces
int firstOffset = Math.max((day.getDayOfWeek().getValue() - 1) * 3 - 1, 0);
System.out.print(" ".repeat(firstOffset));
// Print the days of the month
int month = calDate.getMonthValue();
for (; day.getMonthValue() == month; day = day.plusDays(1)) {
// New line at the end of the week, otherwise print a space
if (day.getDayOfWeek() == DayOfWeek.MONDAY) {
System.out.println();
} else {
System.out.print(" ");
}
// Highlight today's date
if (highlightDay && day.equals(calDate)) {
System.out.format("[%d]", day.getDayOfMonth());
// Highlight days with events
} else if (showEvents && calendar.getEventsOnDate(day).findAny().isPresent()) {
System.out.format("{%d}", day.getDayOfMonth());
} else {
System.out.format("%2d", day.getDayOfMonth());
}
}
System.out.println();
}
private static void readEventsFromFile(String filename) {
File file = new File(filename);
try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
String name = scanner.nextLine();
String schedule = scanner.nextLine();
Event event = Event.fromScheduleString(name, schedule);
calendar.addEvent(event);
}
System.out.println("Loading is done!");
} catch (FileNotFoundException e) {
System.out.println("File not found: " + filename);
System.out.println("Current working directory: " + System.getProperty("user.dir"));
}
}
private static void printEventList() {
System.out.println("ONE TIME EVENTS");
calendar.getEvents().stream()
.filter(e -> !e.isRecurring())
.forEach(System.out::print);
System.out.println("\nRECURRING EVENTS");
calendar.getEvents().stream()
.filter(Event::isRecurring)
.forEach(System.out::print);
}
}

57
hw1/src/TimeInterval.java Normal file
View File

@ -0,0 +1,57 @@
import java.time.LocalTime;
import java.time.Duration;
/**
* Represents a time interval with a start and end time.
* <p>
* The time interval has a duration and can check if it overlaps with another time interval.
* <p>
* The time interval does not allow a start time that is after the end time.
*
* @author Yuri Tatishchev
* @version 0.1 2025-02-08
* @see TimeInterval
*/
public class TimeInterval {
private final LocalTime start;
private final LocalTime end;
private final Duration duration;
/**
* Constructs a time interval with the specified start and end times.
*
* @throws IllegalArgumentException if the start time is after the end time
*/
public TimeInterval(LocalTime start, LocalTime end) {
if (start.isAfter(end)) {
throw new IllegalArgumentException("Start time must be before end time");
}
this.start = start;
this.end = end;
this.duration = Duration.between(start, end);
}
public LocalTime getStart() {
return start;
}
public LocalTime getEnd() {
return end;
}
public Duration getDuration() {
return duration;
}
public boolean overlapsWith(TimeInterval other) {
if (this.start.isBefore(other.start)) {
return this.end.isAfter(other.start);
} else {
return other.end.isAfter(this.start);
}
}
public String toString() {
return start + " " + end;
}
}