Merge branch 'dev'

This commit is contained in:
Václav Přibík
2025-10-23 21:20:59 +02:00
9 changed files with 156 additions and 29 deletions

View File

@@ -28,8 +28,7 @@ public class APFactory implements AbstractAPFactory {
@Override
public LocalDayPlan createLocalDayPlan(LocalDay day, Instant start, Instant end) {
// TODO Return an instance of your class that implements LocalDayPlan
return null;
return new LocalDayPlanImpl(day, start, end);
}
@Override

View File

@@ -12,6 +12,13 @@ public class AppointmentImpl implements Appointment {
private AppointmentRequest request;
public AppointmentImpl(Instant start, AppointmentRequest request) {
this.start = start;
this.request = request;
this.stop = start.plus(request.duration());
}
@Override
public Instant start() {
return start;

View File

@@ -3,6 +3,7 @@ package appointmentplanner;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
@@ -15,11 +16,12 @@ import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimePreference;
import appointmentplanner.api.TimeSlot;
import appointmentplanner.customlist.CustomLinkedListImpl;
import appointmentplanner.customlist.CustomListToJavaBinding;
import appointmentplanner.customlist.api.CustomLinkedList;
public class LocalDayPlanImpl implements LocalDayPlan {
private CustomLinkedList<AppointmentImpl> timeline = new CustomLinkedListImpl<>();
private CustomLinkedList<Appointment> timeline = new CustomLinkedListImpl<>();
public LocalDayPlanImpl(LocalDay day, Instant start, Instant end) {
this.day = day;
@@ -55,8 +57,7 @@ public class LocalDayPlanImpl implements LocalDayPlan {
@Override
public Optional<Appointment> addAppointment(AppointmentData appointmentData, LocalTime startTime) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAppointment'");
throw new UnsupportedOperationException();
}
@Override
@@ -67,20 +68,26 @@ public class LocalDayPlanImpl implements LocalDayPlan {
@Override
public AppointmentRequest removeAppointment(Appointment appointment) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAppointment'");
Appointment removedItem = timeline.remove(appointment);
return removedItem == null ? null : removedItem.request();
}
@Override
public List<AppointmentRequest> removeAppointments(Predicate<Appointment> filter) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAppointments'");
CustomLinkedList<AppointmentRequest> removedRequests = new CustomLinkedListImpl<>();
for (Appointment appointment : timeline.removeFound(filter)) {
removedRequests.add(appointment.request());
}
return new CustomListToJavaBinding<>(removedRequests);
}
@Override
public List<Appointment> appointments() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'appointments'");
return new CustomListToJavaBinding<Appointment>(timeline);
}
@Override
@@ -89,28 +96,47 @@ public class LocalDayPlanImpl implements LocalDayPlan {
throw new UnsupportedOperationException("Unimplemented method 'findMatchingFreeSlotsOfDuration'");
}
private CustomLinkedList<TimeSlot> traverseGapsFitting(Duration toFind, CustomLinkedList<TimeSlot> goodSlots,
Instant startOfBefore, Iterator<Appointment> iterator) {
boolean hasNext = iterator.hasNext();
Appointment nextAppointment = hasNext ? iterator.next() : null;
TimeSlot possibleFittingSlot = new TimeSlotImpl(hasNext ? nextAppointment.end() : startOfDay(),
startOfBefore == null ? endOfDay() : startOfBefore);
if (possibleFittingSlot.fits(toFind)) {
goodSlots.add(possibleFittingSlot);
}
if (!hasNext) {
return goodSlots;
}
return traverseGapsFitting(toFind, goodSlots, nextAppointment.start(), iterator);
}
@Override
public List<TimeSlot> findGapsFitting(Duration duration) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findGapsFitting'");
return new CustomListToJavaBinding<>(
traverseGapsFitting(duration, new CustomLinkedListImpl<>(), null, timeline.iterator()));
}
@Override
public List<Appointment> findAppointments(Predicate<Appointment> filter) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findAppointments'");
return new CustomListToJavaBinding<>(timeline.find(filter));
}
@Override
public boolean contains(Appointment appointment) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'contains'");
return timeline.contains(appointment);
}
@Override
public int nrOfAppointments() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'nrOfAppointments'");
return timeline.size();
}
@Override

View File

@@ -0,0 +1,27 @@
package appointmentplanner;
import java.time.Instant;
import appointmentplanner.api.TimeSlot;
public class TimeSlotImpl implements TimeSlot {
private Instant start;
private Instant end;
public TimeSlotImpl(Instant start, Instant end) {
this.start = start;
this.end = end;
}
@Override
public Instant start() {
return start;
}
@Override
public Instant end() {
return end;
}
}

View File

@@ -1,6 +1,7 @@
package appointmentplanner.customlist;
import java.util.Iterator;
import java.util.function.Predicate;
import appointmentplanner.customlist.api.CustomLinkedList;
@@ -84,31 +85,32 @@ public class CustomLinkedListImpl<T> implements CustomLinkedList<T> {
}
@Override
public void remove(T item) {
public T remove(T item) {
if (head == null) {
return;
return null;
}
if (head.getItem().equals(item)) {
head = head.getNext();
return;
return item;
}
CustomLinkedListNode<T> beforeNode = traverseFind(head, item, ItemPosition.BEFORE);
if (beforeNode == null) {
return;
return item;
}
CustomLinkedListNode<T> nodeToRemove = beforeNode.getNext();
if (beforeNode.getNext() == null) {
beforeNode.setNext(null);
return;
return item;
}
beforeNode.setNext(nodeToRemove.getNext());
return item;
}
@Override
@@ -172,4 +174,34 @@ public class CustomLinkedListImpl<T> implements CustomLinkedList<T> {
return recursiveSizeCalc(this.head, 1);
}
private CustomLinkedList<T> traverseFilter(Predicate<T> filter, CustomLinkedListNode<T> node,
CustomLinkedList<T> found) {
if (node == null) {
return found;
}
T item = node.getItem();
if (filter.test(item)) {
found.add(item);
}
return traverseFilter(filter, node.getNext(), found);
}
@Override
public CustomLinkedList<T> find(Predicate<T> filter) {
return traverseFilter(filter, head, new CustomLinkedListImpl<>());
}
@Override
public CustomLinkedList<T> removeFound(Predicate<T> filter) {
CustomLinkedList<T> items = find(filter);
for (T item : items) {
remove(item);
}
return items;
}
}

View File

@@ -17,6 +17,9 @@ public class CustomLinkedListIterator<T> implements Iterator<T> {
@Override
public boolean hasNext() {
if (lastNode == null) {
return false;
}
return lastNode.getNext() != null;
}

View File

@@ -1,11 +1,15 @@
package appointmentplanner.customlist.api;
import java.util.function.Predicate;
public interface CustomLinkedList<T> extends Iterable<T> {
void add(T item);
void remove(T item);
T remove(T item);
CustomLinkedList<T> removeFound(Predicate<T> filter);
void insertAfter(T reference, T item);
@@ -18,4 +22,6 @@ public interface CustomLinkedList<T> extends Iterable<T> {
boolean contains(T item);
int size();
CustomLinkedList<T> find(Predicate<T> filter);
}

View File

@@ -2,15 +2,19 @@ package appointmentplanner;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import appointmentplanner.api.LocalDay;
import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimeSlot;
public class LocalDayPlanTest {
@@ -23,14 +27,14 @@ public class LocalDayPlanTest {
@ParameterizedTest
@MethodSource("provideLocalDPDataset")
void ldPlanInit_shouldBeSuccessful(LocalDay day, Instant start, Instant end) {
void ldpInit_shouldBeSuccessful(LocalDay day, Instant start, Instant end) {
LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan).isNotNull();
}
@ParameterizedTest
@MethodSource("provideLocalDPDataset")
void ldPlanGetters_shouldReturnSetValues(LocalDay day, Instant start, Instant end) {
void ldpGetters_shouldReturnSetValues(LocalDay day, Instant start, Instant end) {
LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan.day()).isEqualTo(day);
@@ -40,9 +44,18 @@ public class LocalDayPlanTest {
@ParameterizedTest
@MethodSource("provideLocalDPDataset")
void ldToString_shouldReturnStringContainingLocalDateAndTimeZone(LocalDay day, Instant start, Instant end) {
void ldpToString_shouldReturnStringContainingLocalDateAndTimeZone(LocalDay day, Instant start, Instant end) {
LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan.toString()).contains(day.zone().toString(), day.date().toString());
}
@Test
void ldpFindMatchingFreeSlotsOnEmptyDay() {
LocalDayPlan plan = TestData.emptyWorkingDay();
List<TimeSlot> freeSlots = plan.findGapsFitting(Duration.ofHours(2));
assertThat(freeSlots.size()).isEqualTo(1);
}
}

View File

@@ -160,7 +160,21 @@ public class CustomLinkedListTest {
}
private CustomLinkedList<String> initPopulatedList(String[] initData) {
@Test
void cllFind_shouldFindCorrectItem() {
CustomLinkedList<String> list = initPopulatedList("Ahoj", "ja", "jsem", "kkt");
CustomLinkedList<String> foundItems = list.find(i -> i.length() == 4);
assertThat(foundItems.size()).isEqualTo(2);
assertThat(foundItems.contains("Ahoj")).isTrue();
assertThat(foundItems.contains("jsem")).isTrue();
assertThat(foundItems.contains("kkt")).isFalse();
}
private CustomLinkedList<String> initPopulatedList(String... initData) {
CustomLinkedList<String> list = new CustomLinkedListImpl<>();
Stream.of(initData).forEach(list::add);
return list;