package org.drools.solver.examples.itc2007.examination.persistence;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.drools.solver.examples.common.app.LoggingMain;
import org.drools.solver.examples.common.persistence.XstreamSolutionDaoImpl;
import org.drools.solver.examples.itc2007.examination.domain.Examination;
import org.drools.solver.examples.itc2007.examination.domain.InstitutionalWeighting;
import org.drools.solver.examples.itc2007.examination.domain.Period;
import org.drools.solver.examples.itc2007.examination.domain.PeriodHardConstraint;
import org.drools.solver.examples.itc2007.examination.domain.PeriodHardConstraintType;
import org.drools.solver.examples.itc2007.examination.domain.Room;
import org.drools.solver.examples.itc2007.examination.domain.RoomHardConstraint;
import org.drools.solver.examples.itc2007.examination.domain.RoomHardConstraintType;
import org.drools.solver.examples.itc2007.examination.domain.Student;
import org.drools.solver.examples.itc2007.examination.domain.Topic;
/**
* @author Geoffrey De Smet
*/
public class ExaminationInputConvertor extends LoggingMain {
private static final String INPUT_FILE_SUFFIX = ".exam";
private static final String OUTPUT_FILE_SUFFIX = ".xml";
private static final String SPLIT_REGEX = "\\,\\ ?";
public static void main(String[] args) {
new ExaminationInputConvertor().convert();
}
private final File inputDir = new File("data/itc2007/examination/input/");
private final File outputDir = new File("data/itc2007/examination/unsolved/");
public void convert() {
XstreamSolutionDaoImpl solutionDao = new XstreamSolutionDaoImpl();
File[] inputFiles = inputDir.listFiles();
if (inputFiles == null) {
throw new IllegalArgumentException(
"Your working dir should be drools-solver-examples and contain: " + inputDir);
}
for (File inputFile : inputFiles) {
String inputFileName = inputFile.getName();
if (inputFileName.endsWith(INPUT_FILE_SUFFIX)) {
Examination examination = readExamination(inputFile);
String outputFileName = inputFileName.substring(0, inputFileName.length() - INPUT_FILE_SUFFIX.length())
+ OUTPUT_FILE_SUFFIX;
File outputFile = new File(outputDir, outputFileName);
solutionDao.writeSolution(examination, outputFile);
}
}
}
public Examination readExamination(File inputFile) {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(inputFile));
return readExamination(bufferedReader);
} catch (IOException e) {
throw new IllegalArgumentException(e);
} finally {
IOUtils.closeQuietly(bufferedReader);
}
}
public Examination readExamination(BufferedReader bufferedReader) throws IOException {
Examination examination = new Examination();
examination.setId(0L);
readTopicListAndStudentList(bufferedReader, examination);
readPeriodList(bufferedReader, examination);
readRoomList(bufferedReader, examination);
String line = bufferedReader.readLine();
if (!line.equals("[PeriodHardConstraints]")) {
throw new IllegalStateException("Read line (" + line
+ " is not the expected header ([PeriodHardConstraints])");
}
readPeriodHardConstraintList(bufferedReader, examination);
readRoomHardConstraintList(bufferedReader, examination);
readInstitutionalWeighting(bufferedReader, examination);
tagFrontLoadLargeTopics(examination);
tagFrontLoadLastPeriods(examination);
logger.info("Examination with {} students, {} topics/exams, {} periods, {} rooms, {} period constraints" +
" and {} room constraints.",
new Object[]{examination.getStudentList().size(), examination.getTopicList().size(),
examination.getPeriodList().size(), examination.getRoomList().size(),
examination.getPeriodHardConstraintList().size(),
examination.getRoomHardConstraintList().size()});
// Note: examList stays null, that's work for the StartingSolutionInitializer
return examination;
}
private void readTopicListAndStudentList(BufferedReader bufferedReader, Examination examination) throws IOException {
Map<Integer, Student> studentMap = new HashMap<Integer, Student>();
int examSize = readHeaderWithNumber(bufferedReader, "Exams");
List<Topic> topicList = new ArrayList<Topic>(examSize);
for (int i = 0; i < examSize; i++) {
Topic topic = new Topic();
topic.setId((long) i);
String line = bufferedReader.readLine();
String[] lineTokens = line.split(SPLIT_REGEX);
topic.setDuration(Integer.parseInt(lineTokens[0]));
List<Student> topicStudentList = new ArrayList<Student>(lineTokens.length - 1);
for (int j = 1; j < lineTokens.length; j++) {
topicStudentList.add(findOrCreateStudent(studentMap, Integer.parseInt(lineTokens[j])));
}
topic.setStudentList(topicStudentList);
topic.setFrontLoadLarge(false);
topicList.add(topic);
}
examination.setTopicList(topicList);
List<Student> studentList = new ArrayList<Student>(studentMap.values());
examination.setStudentList(studentList);
}
private Student findOrCreateStudent(Map<Integer, Student> studentMap, int id) {
Student student = studentMap.get(id);
if (student == null) {
student = new Student();
student.setId((long) id);
studentMap.put(id, student);
}
return student;
}
private void readPeriodList(BufferedReader bufferedReader, Examination examination) throws IOException {
int periodSize = readHeaderWithNumber(bufferedReader, "Periods");
List<Period> periodList = new ArrayList<Period>(periodSize);
// Everything is in the default timezone and the default locale.
Calendar calendar = Calendar.getInstance();
final DateFormat DATE_FORMAT = new SimpleDateFormat("dd:MM:yyyy HH:mm:ss");
int referenceDayOfYear = -1;
int referenceYear = -1;
for (int i = 0; i < periodSize; i++) {
Period period = new Period();
period.setId((long) i);
String line = bufferedReader.readLine();
String[] lineTokens = line.split(SPLIT_REGEX);
if (lineTokens.length != 4) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to contain 4 tokens.");
}
String startDateTimeString = lineTokens[0] + " " + lineTokens[1];
period.setStartDateTimeString(startDateTimeString);
period.setPeriodIndex(i);
int dayOfYear;
int year;
try {
calendar.setTime(DATE_FORMAT.parse(startDateTimeString));
calendar.get(Calendar.DAY_OF_YEAR);
dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
year = calendar.get(Calendar.YEAR);
} catch (ParseException e) {
throw new IllegalArgumentException("Illegal startDateTimeString (" + startDateTimeString + ").", e);
}
if (referenceDayOfYear < 0) {
referenceDayOfYear = dayOfYear;
referenceYear = year;
}
if (year != referenceYear) {
// Because the Calendar API in JSE sucks... (java 7 will fix that FINALLY)
throw new IllegalStateException("Not yet implemented to handle periods spread over different years...");
}
int dayIndex = dayOfYear - referenceDayOfYear;
if (dayIndex < 0) {
throw new IllegalStateException("The periods should be in ascending order.");
}
period.setDayIndex(dayIndex);
period.setDuration(Integer.parseInt(lineTokens[2]));
period.setPenalty(Integer.parseInt(lineTokens[3]));
periodList.add(period);
}
examination.setPeriodList(periodList);
}
private void readRoomList(BufferedReader bufferedReader, Examination examination) throws IOException {
int roomSize = readHeaderWithNumber(bufferedReader, "Rooms");
List<Room> roomList = new ArrayList<Room>(roomSize);
for (int i = 0; i < roomSize; i++) {
Room room = new Room();
room.setId((long) i);
String line = bufferedReader.readLine();
String[] lineTokens = line.split(SPLIT_REGEX);
if (lineTokens.length != 2) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to contain 2 tokens.");
}
room.setCapacity(Integer.parseInt(lineTokens[0]));
room.setPenalty(Integer.parseInt(lineTokens[1]));
roomList.add(room);
}
examination.setRoomList(roomList);
}
private void readPeriodHardConstraintList(BufferedReader bufferedReader, Examination examination)
throws IOException {
List<Topic> topicList = examination.getTopicList();
List<PeriodHardConstraint> periodHardConstraintList = new ArrayList<PeriodHardConstraint>();
String line = bufferedReader.readLine();
int id = 0;
while (!line.equals("[RoomHardConstraints]")) {
String[] lineTokens = line.split(SPLIT_REGEX);
PeriodHardConstraint periodHardConstraint = new PeriodHardConstraint();
periodHardConstraint.setId((long) id);
if (lineTokens.length != 3) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to contain 3 tokens.");
}
Topic leftTopic = topicList.get(Integer.parseInt(lineTokens[0]));
periodHardConstraint.setLeftSideTopic(leftTopic);
PeriodHardConstraintType periodHardConstraintType = PeriodHardConstraintType.valueOf(lineTokens[1]);
periodHardConstraint.setPeriodHardConstraintType(periodHardConstraintType);
Topic rightTopic = topicList.get(Integer.parseInt(lineTokens[2]));
periodHardConstraint.setRightSideTopic(rightTopic);
if (periodHardConstraintType == PeriodHardConstraintType.EXAM_COINCIDENCE) {
// It's not specified what happens
// when A coincidences with B and B coincidences with C
// and when A and C share students (but don't directly coincidence)
if (!Collections.disjoint(leftTopic.getStudentList(), rightTopic.getStudentList())) {
logger.warn("Filtering out periodHardConstraint (" + periodHardConstraint
+ ") because the left and right topic share students.");
} else {
periodHardConstraintList.add(periodHardConstraint);
}
} else {
periodHardConstraintList.add(periodHardConstraint);
}
line = bufferedReader.readLine();
id++;
}
examination.setPeriodHardConstraintList(periodHardConstraintList);
}
private void readRoomHardConstraintList(BufferedReader bufferedReader, Examination examination)
throws IOException {
List<Topic> topicList = examination.getTopicList();
List<RoomHardConstraint> roomHardConstraintList = new ArrayList<RoomHardConstraint>();
String line = bufferedReader.readLine();
int id = 0;
while (!line.equals("[InstitutionalWeightings]")) {
String[] lineTokens = line.split(SPLIT_REGEX);
RoomHardConstraint roomHardConstraint = new RoomHardConstraint();
roomHardConstraint.setId((long) id);
if (lineTokens.length != 2) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to contain 3 tokens.");
}
roomHardConstraint.setTopic(topicList.get(Integer.parseInt(lineTokens[0])));
roomHardConstraint.setRoomHardConstraintType(RoomHardConstraintType.valueOf(lineTokens[1]));
roomHardConstraintList.add(roomHardConstraint);
line = bufferedReader.readLine();
id++;
}
examination.setRoomHardConstraintList(roomHardConstraintList);
}
private int readHeaderWithNumber(BufferedReader bufferedReader, String header) throws IOException {
String line = bufferedReader.readLine();
if (!line.startsWith("[" + header + ":") || !line.endsWith("]")) {
throw new IllegalStateException("Read line (" + line + " is not the expected header (["
+ header + ":number])");
}
return Integer.parseInt(line.substring(header.length() + 2, line.length() - 1));
}
private void readInstitutionalWeighting(BufferedReader bufferedReader, Examination examination) throws IOException {
InstitutionalWeighting institutionalWeighting = new InstitutionalWeighting();
institutionalWeighting.setId(0L);
String[] lineTokens;
lineTokens = readInstitutionalWeightingProperty(bufferedReader, "TWOINAROW", 2);
institutionalWeighting.setTwoInARowPenality(Integer.parseInt(lineTokens[1]));
lineTokens = readInstitutionalWeightingProperty(bufferedReader, "TWOINADAY", 2);
institutionalWeighting.setTwoInADayPenality(Integer.parseInt(lineTokens[1]));
lineTokens = readInstitutionalWeightingProperty(bufferedReader, "PERIODSPREAD", 2);
institutionalWeighting.setPeriodSpreadLength(Integer.parseInt(lineTokens[1]));
institutionalWeighting.setPeriodSpreadPenality(1); // constant
lineTokens = readInstitutionalWeightingProperty(bufferedReader, "NONMIXEDDURATIONS", 2);
institutionalWeighting.setMixedDurationPenality(Integer.parseInt(lineTokens[1]));
lineTokens = readInstitutionalWeightingProperty(bufferedReader, "FRONTLOAD", 4);
institutionalWeighting.setFrontLoadLargeTopicSize(Integer.parseInt(lineTokens[1]));
institutionalWeighting.setFrontLoadLastPeriodSize(Integer.parseInt(lineTokens[2]));
institutionalWeighting.setFrontLoadPenality(Integer.parseInt(lineTokens[3]));
examination.setInstitutionalWeighting(institutionalWeighting);
}
private String[] readInstitutionalWeightingProperty(BufferedReader bufferedReader, String property,
int propertySize) throws IOException {
String[] lineTokens;
lineTokens = bufferedReader.readLine().split(SPLIT_REGEX);
if (!lineTokens[0].equals(property) || lineTokens.length != propertySize) {
throw new IllegalArgumentException("Read line (" + Arrays.toString(lineTokens)
+ ") is expected to contain " + propertySize + " tokens and start with " + property + ".");
}
return lineTokens;
}
private void tagFrontLoadLargeTopics(Examination examination) {
List<Topic> sortedTopicList = new ArrayList<Topic>(examination.getTopicList());
Collections.sort(sortedTopicList, new Comparator<Topic>() {
public int compare(Topic a, Topic b) {
return new CompareToBuilder()
.append(a.getStudentSize(), b.getStudentSize()) // Ascending
.append(b.getId(), a.getId()) // Descending (according to spec)
.toComparison();
}
});
int frontLoadLargeTopicSize = examination.getInstitutionalWeighting().getFrontLoadLargeTopicSize();
if (frontLoadLargeTopicSize == 0) {
return;
}
int minimumTopicId = sortedTopicList.size() - frontLoadLargeTopicSize;
if (minimumTopicId < 0) {
logger.warn("The frontLoadLargeTopicSize (" + frontLoadLargeTopicSize + ") is bigger than topicListSize ("
+ sortedTopicList.size() + "). Tagging all topic as frontLoadLarge...");
minimumTopicId = 0;
}
for (Topic topic : sortedTopicList.subList(minimumTopicId, sortedTopicList.size())) {
topic.setFrontLoadLarge(true);
}
}
private void tagFrontLoadLastPeriods(Examination examination) {
List<Period> periodList = examination.getPeriodList();
int frontLoadLastPeriodSize = examination.getInstitutionalWeighting().getFrontLoadLastPeriodSize();
if (frontLoadLastPeriodSize == 0) {
return;
}
int minimumPeriodId = periodList.size() - frontLoadLastPeriodSize;
if (minimumPeriodId < 0) {
logger.warn("The frontLoadLastPeriodSize (" + frontLoadLastPeriodSize + ") is bigger than periodListSize ("
+ periodList.size() + "). Tagging all periods as frontLoadLast...");
minimumPeriodId = 0;
}
for (Period period : periodList.subList(minimumPeriodId, periodList.size())) {
period.setFrontLoadLast(true);
}
}
}
|