5252import java .time .format .DateTimeParseException ;
5353import java .util .ArrayList ;
5454import java .util .Arrays ;
55+ import java .util .Collection ;
5556import java .util .Comparator ;
56- import java .util .HashMap ;
57- import java .util .HashSet ;
57+ import java .util .LinkedHashMap ;
5858import java .util .LinkedHashSet ;
5959import java .util .List ;
6060import java .util .Map ;
6161import java .util .Objects ;
6262import java .util .Set ;
63+ import java .util .SortedMap ;
64+ import java .util .TreeMap ;
65+ import java .util .TreeSet ;
6366import java .util .function .Function ;
6467
6568import ai .timefold .solver .benchmarks .examples .common .persistence .AbstractXlsxSolutionFileIO ;
@@ -113,11 +116,6 @@ public class ConferenceSchedulingXlsxFileIO extends
113116 private static final String TALK_PROHIBITED_ROOM_TAGS_DESCRIPTION =
114117 "Penalty per prohibited tag in a talk's room, per minute" ;
115118
116- private static final String PUBLISHED_TIMESLOT_DESCRIPTION =
117- "Penalty per published talk with a different timeslot than its published timeslot, per match" ;
118-
119- private static final String PUBLISHED_ROOM_DESCRIPTION =
120- "Penalty per published talk with a different room than its published room, per match" ;
121119 private static final String THEME_TRACK_CONFLICT_DESCRIPTION =
122120 "Penalty per common theme track of 2 talks with overlapping timeslots, per overlapping minute" ;
123121 private static final String THEME_TRACK_ROOM_STABILITY_DESCRIPTION =
@@ -157,10 +155,8 @@ public class ConferenceSchedulingXlsxFileIO extends
157155 "Penalty per missing preferred tag in a talk's room, per minute" ;
158156 private static final String TALK_UNDESIRED_ROOM_TAGS_DESCRIPTION = "Penalty per undesired tag in a talk's room, per minute" ;
159157
160- private static final Comparator <Timeslot > COMPARATOR =
161- comparing (Timeslot ::getStartDateTime )
162- .thenComparing (reverseOrder (comparing (
163- Timeslot ::getEndDateTime )));
158+ private static final Comparator <Timeslot > COMPARATOR = comparing (Timeslot ::getStartDateTime )
159+ .thenComparing (reverseOrder (comparing (Timeslot ::getEndDateTime )));
164160
165161 private final boolean strict ;
166162
@@ -199,10 +195,10 @@ public ConferenceSchedulingXlsxReader(XSSFWorkbook workbook) {
199195 @ Override
200196 public ConferenceSolution read () {
201197 solution = new ConferenceSolution ();
202- totalTalkTypeMap = new HashMap <>();
203- totalTimeslotTagSet = new HashSet <>();
204- totalRoomTagSet = new HashSet <>();
205- totalTalkCodeMap = new HashMap <>();
198+ totalTalkTypeMap = new TreeMap <>();
199+ totalTimeslotTagSet = new TreeSet <>();
200+ totalRoomTagSet = new TreeSet <>();
201+ totalTalkCodeMap = new TreeMap <>();
206202 readConfiguration ();
207203 readTimeslotList ();
208204 readRoomList ();
@@ -286,17 +282,15 @@ private void readTimeslotList() {
286282 readHeaderCell ("Talk types" );
287283 readHeaderCell ("Tags" );
288284 List <TalkType > talkTypeList = new ArrayList <>();
289- List <Timeslot > timeslotList =
290- new ArrayList <>(currentSheet .getLastRowNum () - 1 );
285+ List <Timeslot > timeslotList = new ArrayList <>(currentSheet .getLastRowNum () - 1 );
291286 var id = 0L ;
292287 var talkTypeId = 0L ;
293288 while (nextRow ()) {
294- var timeslot =
295- new Timeslot (id ++);
289+ var timeslot = new Timeslot (id ++);
296290 var day = LocalDate .parse (nextStringCell ().getStringCellValue (), DAY_FORMATTER );
297291 var startTime = LocalTime .parse (nextStringCell ().getStringCellValue (), TIME_FORMATTER );
298292 var endTime = LocalTime .parse (nextStringCell ().getStringCellValue (), TIME_FORMATTER );
299- if (startTime .compareTo (endTime ) >= 0 ) {
293+ if (! startTime .isBefore (endTime )) {
300294 throw new IllegalStateException (currentPosition () + ": The startTime (" + startTime
301295 + ") must be less than the endTime (" + endTime + ")." );
302296 }
@@ -359,12 +353,10 @@ private void readRoomList() {
359353 readHeaderCell ("Talk types" );
360354 readHeaderCell ("Tags" );
361355 readTimeslotHoursHeaders ();
362- List <Room > roomList =
363- new ArrayList <>(currentSheet .getLastRowNum () - 1 );
356+ List <Room > roomList = new ArrayList <>(currentSheet .getLastRowNum () - 1 );
364357 var id = 0L ;
365358 while (nextRow ()) {
366- var room =
367- new Room (id ++);
359+ var room = new Room (id ++);
368360 room .setName (nextStringCell ().getStringCellValue ());
369361 if (strict && !VALID_NAME_PATTERN .matcher (room .getName ()).matches ()) {
370362 throw new IllegalStateException (currentPosition () + ": The room name (" + room .getName ()
@@ -402,10 +394,8 @@ private void readRoomList() {
402394 }
403395 }
404396 totalRoomTagSet .addAll (room .getTagSet ());
405- Set <Timeslot > unavailableTimeslotSet =
406- new LinkedHashSet <>();
407- for (var timeslot : solution
408- .getTimeslotList ()) {
397+ Set <Timeslot > unavailableTimeslotSet = new LinkedHashSet <>();
398+ for (var timeslot : solution .getTimeslotList ()) {
409399 var cell = nextStringCell ();
410400 if (Objects .equals (extractColor (cell , UNAVAILABLE_COLOR ), UNAVAILABLE_COLOR )) {
411401 unavailableTimeslotSet .add (timeslot );
@@ -446,12 +436,10 @@ private void readSpeakerList() {
446436 readHeaderCell ("Undesired room tags" );
447437
448438 readTimeslotHoursHeaders ();
449- List <Speaker > speakerList =
450- new ArrayList <>(currentSheet .getLastRowNum () - 1 );
439+ List <Speaker > speakerList = new ArrayList <>(currentSheet .getLastRowNum () - 1 );
451440 var id = 0L ;
452441 while (nextRow ()) {
453- var speaker =
454- new Speaker (id ++);
442+ var speaker = new Speaker (id ++);
455443 speaker .setName (nextStringCell ().getStringCellValue ());
456444 if (strict && !VALID_NAME_PATTERN .matcher (speaker .getName ()).matches ()) {
457445 throw new IllegalStateException (currentPosition () + ": The speaker name (" + speaker .getName ()
@@ -481,10 +469,8 @@ private void readSpeakerList() {
481469 speaker .setUndesiredRoomTagSet (Arrays .stream (nextStringCell ().getStringCellValue ().split (", " ))
482470 .filter (tag -> !tag .isEmpty ()).collect (toCollection (LinkedHashSet ::new )));
483471 verifyRoomTags (speaker .getUndesiredRoomTagSet ());
484- Set <Timeslot > unavailableTimeslotSet =
485- new LinkedHashSet <>();
486- for (var timeslot : solution
487- .getTimeslotList ()) {
472+ Set <Timeslot > unavailableTimeslotSet = new LinkedHashSet <>();
473+ for (var timeslot : solution .getTimeslotList ()) {
488474 var cell = nextStringCell ();
489475 if (Objects .equals (extractColor (cell , UNAVAILABLE_COLOR ), UNAVAILABLE_COLOR )) {
490476 unavailableTimeslotSet .add (timeslot );
@@ -501,10 +487,7 @@ private void readSpeakerList() {
501487 }
502488
503489 private void readTalkList () {
504- var speakerMap =
505- solution .getSpeakerList ().stream ().collect (
506- toMap (Speaker ::getName ,
507- speaker -> speaker ));
490+ var speakerMap = toSortedMap (solution .getSpeakerList (), Speaker ::getName , speaker -> speaker );
508491 nextSheet ("Talks" );
509492 nextRow (false );
510493 readHeaderCell ("Code" );
@@ -538,25 +521,17 @@ private void readTalkList() {
538521 readHeaderCell ("Published Start" );
539522 readHeaderCell ("Published End" );
540523 readHeaderCell ("Published Room" );
541- List <Talk > talkList =
542- new ArrayList <>(currentSheet .getLastRowNum () - 1 );
524+ List <Talk > talkList = new ArrayList <>(currentSheet .getLastRowNum () - 1 );
543525 var id = 0L ;
544- var timeslotMap =
545- solution .getTimeslotList ().stream ().collect (
546- toMap (timeslot -> {
547- var key = timeslot .getStartDateTime ();
548- return new Pair <>(key , timeslot .getEndDateTime ());
549- },
550- Function .identity ()));
551- var roomMap =
552- solution .getRoomList ().stream ().collect (
553- toMap (Room ::getName ,
554- Function .identity ()));
555- Map <Talk , Set <String >> talkToPrerequisiteTalkSetMap =
556- new HashMap <>();
526+ var timeslotMap = solution .getTimeslotList ().stream ().collect (
527+ toMap (timeslot -> {
528+ var key = timeslot .getStartDateTime ();
529+ return new Pair <>(key , timeslot .getEndDateTime ());
530+ }, Function .identity (), (a , b ) -> a , LinkedHashMap ::new ));
531+ var roomMap = toSortedMap (solution .getRoomList (), Room ::getName , Function .identity ());
532+ Map <Talk , Set <String >> talkToPrerequisiteTalkSetMap = new LinkedHashMap <>();
557533 while (nextRow ()) {
558- var talk =
559- new Talk (id ++);
534+ var talk = new Talk (id ++);
560535 talk .setCode (nextStringCell ().getStringCellValue ());
561536 totalTalkCodeMap .put (talk .getCode (), talk );
562537 if (strict && !VALID_CODE_PATTERN .matcher (talk .getCode ()).matches ()) {
@@ -662,9 +637,13 @@ private void readTalkList() {
662637 solution .setTalkList (talkList );
663638 }
664639
665- private Timeslot extractTimeslot (
666- Map <Pair <LocalDateTime , LocalDateTime >, Timeslot > timeslotMap ,
667- Talk talk ) {
640+ private static <A , K extends Comparable <K >, K2 extends K , V > SortedMap <K2 , V > toSortedMap (Collection <A > collection ,
641+ Function <A , K2 > keyFunction , Function <A , V > valueFunction ) {
642+ return collection .stream ()
643+ .collect (toMap (keyFunction , valueFunction , (a , b ) -> a , TreeMap ::new ));
644+ }
645+
646+ private Timeslot extractTimeslot (Map <Pair <LocalDateTime , LocalDateTime >, Timeslot > timeslotMap , Talk talk ) {
668647 Timeslot assignedTimeslot ;
669648 var dateString = nextStringCell ().getStringCellValue ();
670649 var startTimeString = nextStringCell ().getStringCellValue ();
@@ -698,9 +677,7 @@ private Timeslot extractTimeslot(
698677 return null ;
699678 }
700679
701- private Room extractRoom (
702- Map <String , Room > roomMap ,
703- Talk talk ) {
680+ private Room extractRoom (Map <String , Room > roomMap , Talk talk ) {
704681 var roomName = nextStringCell ().getStringCellValue ();
705682 if (!roomName .isEmpty ()) {
706683 var room = roomMap .get (roomName );
@@ -753,13 +730,10 @@ private void verifyRoomTags(Set<String> roomTagSet) {
753730 }
754731 }
755732
756- private void setPrerequisiteTalkSets (
757- Map <Talk , Set <String >> talkToPrerequisiteTalkSetMap ) {
758- for (var entry : talkToPrerequisiteTalkSetMap
759- .entrySet ()) {
733+ private void setPrerequisiteTalkSets (Map <Talk , Set <String >> talkToPrerequisiteTalkSetMap ) {
734+ for (var entry : talkToPrerequisiteTalkSetMap .entrySet ()) {
760735 var currentTalk = entry .getKey ();
761- Set <Talk > prerequisiteTalkSet =
762- new HashSet <>();
736+ Set <Talk > prerequisiteTalkSet = new LinkedHashSet <>();
763737 for (var prerequisiteTalkCode : entry .getValue ()) {
764738 var prerequisiteTalk = totalTalkCodeMap .get (prerequisiteTalkCode );
765739 if (prerequisiteTalk == null ) {
@@ -775,8 +749,7 @@ private void setPrerequisiteTalkSets(
775749
776750 private void readTimeslotDaysHeaders () {
777751 LocalDate previousTimeslotDay = null ;
778- for (var timeslot : solution
779- .getTimeslotList ()) {
752+ for (var timeslot : solution .getTimeslotList ()) {
780753 var timeslotDay = timeslot .getDate ();
781754 if (timeslotDay .equals (previousTimeslotDay )) {
782755 readHeaderCell ("" );
0 commit comments