/*
* DataPool.java
*
* Created on 21.09.2007, 22:23:54
*
* This file is part of the TimeFinder project.
* Visit http://www.timefinder.de for more information.
* Copyright 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.timefinder.algo.io;
import de.timefinder.data.algo.DataPoolSettings;
import de.timefinder.algo.MyStatusBar;
import de.timefinder.algo.constraint.EventOrderConstraint;
import de.timefinder.algo.constraint.PersonITCRasterConstraint;
import de.timefinder.algo.constraint.RasterConstraint;
import de.timefinder.data.DataPool;
import de.timefinder.data.DataPoolImpl;
import de.timefinder.data.Event;
import de.timefinder.data.Feature;
import de.timefinder.data.Location;
import de.timefinder.data.Person;
import de.timefinder.data.access.Dao;
import de.timefinder.data.set.RasterEnum;
import de.timefinder.data.set.WeekRaster;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class DataPool4Track2 {
private final Log logger = LogFactory.getLog(getClass());
private DataPoolSettings poolSettings;
private String fileName;
private Reader reader;
private DataPool dataPool;
private MyStatusBar statusBar;
public DataPool4Track2() {
dataPool = new DataPoolImpl();
}
/**
* This constructor creates a new instance of DataPool.
* Now you can call startOptimization. See the class description for more
* information.
*/
public void setSettings(DataPoolSettings poolSettings) {
this.poolSettings = poolSettings;
}
public DataPoolSettings getSettings() {
return poolSettings;
}
public void setDataPool(DataPool dataPool) {
this.dataPool = dataPool;
}
public DataPool getDataPool() {
return dataPool;
}
public Dao<Event> getEventDao() {
return getDataPool().getDao(Event.class);
}
public Dao<Feature> getFeatureDao() {
return getDataPool().getDao(Feature.class);
}
public Dao<Person> getPersonDao() {
return getDataPool().getDao(Person.class);
}
public Dao<Location> getLocationDao() {
return getDataPool().getDao(Location.class);
}
public String getFileName() {
if (fileName == null) {
fileName = "ncp-solution";
}
return fileName;
}
public void setSource(Object file) {
if (file instanceof String) {
fileName = (String) file;
} else if (file instanceof File) {
fileName = ((File) file).getAbsolutePath();
} else {
reader = (Reader) file;
}
}
public Reader getReader() throws FileNotFoundException {
if (reader == null) {
// return a fresh reader on every get, because multiple reads should be possible:
return new FileReader(fileName);
}
return reader;
}
public void doWork() {
try {
parse(getReader());
} catch (FileNotFoundException ex) {
logger.error("Cannot find file to load timetabling data.", ex);
throw new RuntimeException("File not found! " + ex.getMessage());
} catch (IOException ex) {
logger.error("Cannot load timetabling data.", ex);
throw new RuntimeException("Cannot load file! " + ex.getMessage());
}
}
public void save() {
try {
writeToFile(getEventDao().getAll(), fileName);
} catch (IOException ex) {
logger.error("Couldn't save timetabling data.", ex);
}
}
public void setStatusBar(MyStatusBar bar) {
statusBar = bar;
}
public void updateStatus(int percentage) {
if (statusBar != null)
statusBar.getMyProgressMonitor().worked(percentage);
}
/**
* This method writes the solution to a file.
*/
public void writeToFile(Collection<? extends Event> optTIs, String fileName)
throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(fileName + ".sln"));
logger.debug("Write to file: " + fileName + ".sln");
try {
for (Event ti : optTIs) {
Location r = ti.getLocation();
assert ti.getStart() == -1 ? r == null : true;
// Hint: If a Event was not assigned to the timetable the
// starttime is null and the room is null, so this is fine:
// writer.write("-1 -1");
writer.write(ti.getStart() + " " + indexOfLocation(r) + "\n");
}
} finally {
writer.close();
}
}
/**
* This method parses an input file with a special timetabling format.
* Download those special files here:
* http://downloads.sourceforge.net/timefinder/track2.zip
*
* @see http://www.cs.qub.ac.uk/eventmap/postenrolcourse/course_post_index_files/Inputformat.htm
*/
public void parse(Reader reader) throws FileNotFoundException, IOException {
if (reader == null)
throw new NullPointerException("Reader cannot be null!");
if (fileName != null)
logger.info("Parse file:" + fileName);
// first of all remove existent objects
getPersonDao().detachAll();
getFeatureDao().detachAll();
getLocationDao().detachAll();
getEventDao().detachAll();
BufferedReader bReader = new BufferedReader(reader);
updateStatus(10);
try {
//1. The first line defines the number of some data:
String numberOf[] = bReader.readLine().split(" ");
int noOfEvents = Integer.parseInt(numberOf[0]);
int noOfLocations = Integer.parseInt(numberOf[1]);
int noOfFeatures = Integer.parseInt(numberOf[2]);
int noOfPersons = Integer.parseInt(numberOf[3]);
// 2. Every line contains the capacity of one room
for (int i = 0; i < noOfLocations; i++) {
Location location = createLocation(Integer.parseInt(bReader.readLine()));
location.putConstraint(newRasterConstraint());
// EventSetConstraint esc = (EventSetConstraint) appCtx.getBean(ES_CONSTRAINT);
// location.putConstraint(esc);
}
// initialize events
for (int i = 0; i < noOfEvents; i++) {
createEvent();
}
// initialize features
for (int i = 0; i < noOfFeatures; i++) {
createFeature();
}
updateStatus(30);
double tmp = 10.0 / (noOfPersons + 1);
// 3. 'noOfSubjects'-lines describes the choice of one person (student/event)
Person currentPerson;
for (int p = 0; p < noOfPersons; p++) {
updateStatus(30 + (int) (p * tmp));
currentPerson = createPerson();
currentPerson.putConstraint(newRasterConstraint());
for (Event event : getEventDao().getAll()) {
if ((Integer.parseInt(bReader.readLine()) > 0)) {
currentPerson.addEvent(event, true);
}
}
}
updateStatus(40);
// 4. Now 'noOfFeatures'-lines describes the features of one room (room/feature)
for (Location currentLocation : getLocationDao().getAll()) {
for (Feature feature : getFeatureDao().getAll()) {
if ((Integer.parseInt(bReader.readLine()) > 0)) {
currentLocation.addFeature(feature);
}
}
}
updateStatus(50);
// 5. 'noOfFeatures'-lines describes the features-requirements of one subject (event/feature)
for (Event currentSubject : getEventDao().getAll()) {
for (Feature feature : getFeatureDao().getAll()) {
if ((Integer.parseInt(bReader.readLine()) != 0)) {
currentSubject.addFeature(feature);
}
}
}
updateStatus(60);
// 6. 'noOfTimeSlots'-lines describes the valid assignments of one subject (event/timeslot)
int noOfTimeSlots = poolSettings.getTimeslotsPerWeek();
for (Event currentEvent : getEventDao().getAll()) {
RasterConstraint rc = newRasterConstraint();
currentEvent.putConstraint(rc);
WeekRaster raster = rc.getRaster();
for (int ts = 0; ts < noOfTimeSlots; ts++) {
//String s = reader.readLine(); sb.append(s);
if (Integer.parseInt(bReader.readLine()) != 0) {
raster.set(ts, RasterEnum.ALLOWED);
}
}
}
updateStatus(70);
// 7. 'noOfSubjects'-lines describes the 'follow/before'-matrix of one subject (event/event)
for (Event firstEvent : getEventDao().getAll()) {
for (Event secEvent : getEventDao().getAll()) {
int i = Integer.parseInt(bReader.readLine());
if (i == 1) {
// add the constraint to the both event
EventOrderConstraint firstConstraint = firstEvent.getConstraint(EventOrderConstraint.class);
if (firstConstraint == null) {
firstConstraint = new EventOrderConstraint(firstEvent);
firstEvent.putConstraint(firstConstraint);
}
firstConstraint.addFollow(secEvent);
EventOrderConstraint secConstraint = secEvent.getConstraint(EventOrderConstraint.class);
if (secConstraint == null) {
secConstraint = new EventOrderConstraint(secEvent);
secEvent.putConstraint(secConstraint);
}
secConstraint.addBefore(firstEvent);
} else if (i == -1) {
// We could check now the priviously added followers and beforers,
// but this is not possible because '-1' could occur the
// first time (instead of expected '1')
}
}
}
updateStatus(80);
// getMagicNumbersOfDataPool();
String line = bReader.readLine();
if (line != null) {
logger.fatal("End of file should be reached. But content of line was:" + line);
int counter = 0;
while ((line = bReader.readLine()) != null) {
counter++;
}
logger.fatal("Further " + counter + " unprocessed lines detected");
}
} finally {
//log.info(sb.toString());
reader.close();
}
}
// private void getMagicNumbersOfDataPool() {
// int sFeatures = 0;
// int lFeatures = 0;
// int l2Features = 0;
// int sRaster = 0;
// int lRaster = 0;
// int pRaster = 0;
// int lCap = 0;
// int visitors = 0;
// int visitors2 = 0;
// int events = 0;
//
// for (Event sub : getEventDao().getAll()) {
// visitors += sub.getPersons().size();
// visitors2 += sub.getPersons().size();
// sFeatures += sub.getFeatures().size();
// sRaster += sub.getConstraint(RasterConstraint.class).getRaster().getForbidden().getAssignments();
// if (sub.getFeatures().size() > 4) {
// log.info(sub + " features:" + sub.getFeatures());
// }
// }
// for (Person p : getPersonDao().getAll()) {
// pRaster += p.getConstraint(RasterConstraint.class).getRaster().getForbidden().getAssignments();
// events += p.getEvents().size();
// }
// for (Location r : getLocationDao().getAll()) {
// lRaster += r.getConstraint(RasterConstraint.class).getRaster().getForbidden().getAssignments();
// lCap += r.getCapacity();
// lFeatures += r.getFeatures().size();
// l2Features += r.getFeatureSet().getAssignments();
// log.info(r + " features=" + r.getFeatures());
// }
//
// log.info("sFeatures:" + sFeatures);
// log.info("lFeatures:" + lFeatures);
// log.info("l2Features:" + l2Features);
// log.info("sRaster:" + sRaster);
// log.info("lRaster:" + lRaster);
// log.info("pRaster:" + pRaster);
// log.info("lCap:" + lCap);
// log.info("visitors:" + visitors);
// log.info("visitors2:" + visitors2);
// log.info("#events:" + events);
// }
private int indexOfLocation(Location r) {
int index = 0;
for (Location room : getLocationDao().getAll()) {
if (room.equals(r)) {
return index;
}
index++;
}
return -1;
}
public Location createLocation(int capacity) {
Location loc = new Location();
loc.setCapacity(capacity);
getLocationDao().attach(loc);
loc.setName("Room " + loc.getId());
return loc;
}
public Event createEvent() {
Event event = new Event();
//startTime == [0, 45); duration == 1
event.setDuration(1);
getEventDao().attach(event);
event.setName("Event " + event.getId());
return event;
}
public Person createPerson() {
Person person = new Person();
person.putConstraint(new PersonITCRasterConstraint(person, poolSettings));
getPersonDao().attach(person);
person.setName("Person " + person.getId());
return person;
}
public void createFeature() {
Feature feature = new Feature();
getFeatureDao().attach(feature);
feature.setName("Feature " + feature.getId());
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("#Persons:");
sb.append(getPersonDao().getAll().size());
sb.append("\n#Rooms:");
sb.append(getLocationDao().getAll().size());
sb.append("\n#Events:");
sb.append(getEventDao().getAll().size());
sb.append("\nPersons:");
sb.append(getPersonDao().getAll());
sb.append("\nRooms:");
sb.append(getLocationDao().getAll());
sb.append("\nEvents:");
sb.append(getEventDao().getAll());
return sb.toString();
}
private RasterConstraint newRasterConstraint() {
return new RasterConstraint(poolSettings.createWeekRaster());
}
public void changeSettings() {
poolSettings.setNumberOfDays(5);
poolSettings.setTimeslotsPerDay(9);
}
}