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 @Override
public LocalDayPlan createLocalDayPlan(LocalDay day, Instant start, Instant end) { public LocalDayPlan createLocalDayPlan(LocalDay day, Instant start, Instant end) {
// TODO Return an instance of your class that implements LocalDayPlan return new LocalDayPlanImpl(day, start, end);
return null;
} }
@Override @Override

View File

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

View File

@@ -3,6 +3,7 @@ package appointmentplanner;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -15,11 +16,12 @@ import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimePreference; import appointmentplanner.api.TimePreference;
import appointmentplanner.api.TimeSlot; import appointmentplanner.api.TimeSlot;
import appointmentplanner.customlist.CustomLinkedListImpl; import appointmentplanner.customlist.CustomLinkedListImpl;
import appointmentplanner.customlist.CustomListToJavaBinding;
import appointmentplanner.customlist.api.CustomLinkedList; import appointmentplanner.customlist.api.CustomLinkedList;
public class LocalDayPlanImpl implements LocalDayPlan { 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) { public LocalDayPlanImpl(LocalDay day, Instant start, Instant end) {
this.day = day; this.day = day;
@@ -55,8 +57,7 @@ public class LocalDayPlanImpl implements LocalDayPlan {
@Override @Override
public Optional<Appointment> addAppointment(AppointmentData appointmentData, LocalTime startTime) { public Optional<Appointment> addAppointment(AppointmentData appointmentData, LocalTime startTime) {
// TODO Auto-generated method stub throw new UnsupportedOperationException();
throw new UnsupportedOperationException("Unimplemented method 'addAppointment'");
} }
@Override @Override
@@ -67,20 +68,26 @@ public class LocalDayPlanImpl implements LocalDayPlan {
@Override @Override
public AppointmentRequest removeAppointment(Appointment appointment) { public AppointmentRequest removeAppointment(Appointment appointment) {
// TODO Auto-generated method stub Appointment removedItem = timeline.remove(appointment);
throw new UnsupportedOperationException("Unimplemented method 'removeAppointment'"); return removedItem == null ? null : removedItem.request();
} }
@Override @Override
public List<AppointmentRequest> removeAppointments(Predicate<Appointment> filter) { 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 @Override
public List<Appointment> appointments() { public List<Appointment> appointments() {
// TODO Auto-generated method stub return new CustomListToJavaBinding<Appointment>(timeline);
throw new UnsupportedOperationException("Unimplemented method 'appointments'");
} }
@Override @Override
@@ -89,28 +96,47 @@ public class LocalDayPlanImpl implements LocalDayPlan {
throw new UnsupportedOperationException("Unimplemented method 'findMatchingFreeSlotsOfDuration'"); 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 @Override
public List<TimeSlot> findGapsFitting(Duration duration) { public List<TimeSlot> findGapsFitting(Duration duration) {
// TODO Auto-generated method stub return new CustomListToJavaBinding<>(
throw new UnsupportedOperationException("Unimplemented method 'findGapsFitting'"); traverseGapsFitting(duration, new CustomLinkedListImpl<>(), null, timeline.iterator()));
} }
@Override @Override
public List<Appointment> findAppointments(Predicate<Appointment> filter) { public List<Appointment> findAppointments(Predicate<Appointment> filter) {
// TODO Auto-generated method stub return new CustomListToJavaBinding<>(timeline.find(filter));
throw new UnsupportedOperationException("Unimplemented method 'findAppointments'");
} }
@Override @Override
public boolean contains(Appointment appointment) { public boolean contains(Appointment appointment) {
// TODO Auto-generated method stub return timeline.contains(appointment);
throw new UnsupportedOperationException("Unimplemented method 'contains'");
} }
@Override @Override
public int nrOfAppointments() { public int nrOfAppointments() {
// TODO Auto-generated method stub return timeline.size();
throw new UnsupportedOperationException("Unimplemented method 'nrOfAppointments'");
} }
@Override @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; package appointmentplanner.customlist;
import java.util.Iterator; import java.util.Iterator;
import java.util.function.Predicate;
import appointmentplanner.customlist.api.CustomLinkedList; import appointmentplanner.customlist.api.CustomLinkedList;
@@ -84,31 +85,32 @@ public class CustomLinkedListImpl<T> implements CustomLinkedList<T> {
} }
@Override @Override
public void remove(T item) { public T remove(T item) {
if (head == null) { if (head == null) {
return; return null;
} }
if (head.getItem().equals(item)) { if (head.getItem().equals(item)) {
head = head.getNext(); head = head.getNext();
return; return item;
} }
CustomLinkedListNode<T> beforeNode = traverseFind(head, item, ItemPosition.BEFORE); CustomLinkedListNode<T> beforeNode = traverseFind(head, item, ItemPosition.BEFORE);
if (beforeNode == null) { if (beforeNode == null) {
return; return item;
} }
CustomLinkedListNode<T> nodeToRemove = beforeNode.getNext(); CustomLinkedListNode<T> nodeToRemove = beforeNode.getNext();
if (beforeNode.getNext() == null) { if (beforeNode.getNext() == null) {
beforeNode.setNext(null); beforeNode.setNext(null);
return; return item;
} }
beforeNode.setNext(nodeToRemove.getNext()); beforeNode.setNext(nodeToRemove.getNext());
return item;
} }
@Override @Override
@@ -172,4 +174,34 @@ public class CustomLinkedListImpl<T> implements CustomLinkedList<T> {
return recursiveSizeCalc(this.head, 1); 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 @Override
public boolean hasNext() { public boolean hasNext() {
if (lastNode == null) {
return false;
}
return lastNode.getNext() != null; return lastNode.getNext() != null;
} }

View File

@@ -1,11 +1,15 @@
package appointmentplanner.customlist.api; package appointmentplanner.customlist.api;
import java.util.function.Predicate;
public interface CustomLinkedList<T> extends Iterable<T> { public interface CustomLinkedList<T> extends Iterable<T> {
void add(T item); void add(T item);
void remove(T item); T remove(T item);
CustomLinkedList<T> removeFound(Predicate<T> filter);
void insertAfter(T reference, T item); void insertAfter(T reference, T item);
@@ -18,4 +22,6 @@ public interface CustomLinkedList<T> extends Iterable<T> {
boolean contains(T item); boolean contains(T item);
int size(); 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 static org.assertj.core.api.Assertions.assertThat;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import appointmentplanner.api.LocalDay; import appointmentplanner.api.LocalDay;
import appointmentplanner.api.LocalDayPlan; import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimeSlot;
public class LocalDayPlanTest { public class LocalDayPlanTest {
@@ -23,14 +27,14 @@ public class LocalDayPlanTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("provideLocalDPDataset") @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); LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan).isNotNull(); assertThat(plan).isNotNull();
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideLocalDPDataset") @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); LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan.day()).isEqualTo(day); assertThat(plan.day()).isEqualTo(day);
@@ -40,9 +44,18 @@ public class LocalDayPlanTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("provideLocalDPDataset") @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); LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan.toString()).contains(day.zone().toString(), day.date().toString()); 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<>(); CustomLinkedList<String> list = new CustomLinkedListImpl<>();
Stream.of(initData).forEach(list::add); Stream.of(initData).forEach(list::add);
return list; return list;