    001    package ifs.example.tt;
    002
    003    import ifs.model.*;
    004    import ifs.solution.*;
    005    import java.util.*;
    006    import java.io.*;
    007    import ifs.util.*;
    008
    009    import org.dom4j.*;
    010    import org.dom4j.io.*;
    011
    012    /**
    013     * Simple Timetabling Problem.
    014     * <br><br>
    015     * The problem is modelled in such a way that every lecture was represented by a variable, resource as a constraint
    016     * and every possible location of an activity in the time and space was represented by a single value. It means that a
    017     * value stands for a selection of the time (starting time slot), and one of the available rooms. Binary dependencies
    018     * are of course represented as constraints as well.
    019     *
    020     * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
    021     * @version 1.0
    022     */
    023    public class TimetableModel extends Model {
    024        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableModel.class);
    025        private int iNrDays, iNrHours;
    026
    027        public TimetableModel(int nrDays, int nrHours) {
    028            super();
    029            iNrDays = nrDays; iNrHours = nrHours;
    030        }
    031
    032        public int getNrDays() { return iNrDays; }
    033        public int getNrHours() { return iNrHours; }
    034
    035        public static TimetableModel generate(DataProperties cfg) {
    036            int nrDays = cfg.getPropertyInt("Generator.NrDays",5);
    037            int nrHours = cfg.getPropertyInt("Generator.NrHours",20);
    038            int nrSlots = nrDays * nrHours;
    039            TimetableModel m = new TimetableModel(nrDays, nrHours);
    040
    041            int nrRooms = cfg.getPropertyInt("Generator.NrRooms",20);
    042            int nrInstructors = cfg.getPropertyInt("Generator.NrInstructors",20);
    043            int nrClasses = cfg.getPropertyInt("Generator.NrClasses",20);
    044            int nrGroupsOfRooms = cfg.getPropertyInt("Generator.NrGroupsOfRooms", 20);
    045            int nrRoomsInGroupMin = cfg.getPropertyInt("Generator.NrRoomsInGroupMin",  1);
    046            int nrRoomsInGroupMax = cfg.getPropertyInt("Generator.NrRoomsInGroupMax",  10);
    047            int nrRoomInGroupMin = cfg.getPropertyInt("Generator.NrRoomInGroupMin", 1);
    048            double fillFactor = cfg.getPropertyDouble("Generator.FillFactor",  0.8);
    049            int maxLength = cfg.getPropertyInt("Generator.ActivityLengthMax", 5);
    050            double hardFreeResource = cfg.getPropertyDouble("Generator.HardFreeResource",  0.05);
    051            double softFreeResource = cfg.getPropertyDouble("Generator.SoftFreeResource",  0.3);
    052            double softUsedResource = cfg.getPropertyDouble("Generator.SoftUsedResource",  0.05);
    053            double softUsedActivity = cfg.getPropertyDouble("Generator.SoftUsedActivity",  0.05);
    054            double softFreeActivity = cfg.getPropertyDouble("Generator.SoftFreeActivity",  0.3);
    055            double hardFreeActivity = cfg.getPropertyDouble("Generator.HardFreeActivity",  0.05);
    056            int nrDependencies = cfg.getPropertyInt("Generator.NrDependencies", 50);
    057
    058            Resource rooms[] = new Resource[nrRooms];
    059            Vector groupForRoom[] = new Vector[nrRooms];
    060            for (int i=0;i<nrRooms;i++) {
    061                rooms[i] = new Resource("r"+(i+1),Resource.TYPE_ROOM, "Room "+(i+1));
    062                groupForRoom[i] = new Vector();
    063                m.addConstraint(rooms[i]);
    064            }
    065            Vector groupOfRooms[] = new Vector[nrGroupsOfRooms];
    066            for (int i=0;i<nrGroupsOfRooms;i++) {
    067                groupOfRooms[i] = new FastVector();
    068                for (int j=0;j<ToolBox.random(1+nrRoomsInGroupMax-nrRoomsInGroupMin)+nrRoomsInGroupMin;j++) {
    069                    int r=0;
    070                    do {
    071                        r = ToolBox.random(nrRooms);
    072                    } while (groupOfRooms[i].contains(rooms[r]));
    073                    groupOfRooms[i].add(rooms[r]);
    074                    groupForRoom[r].add(groupOfRooms[i]);
    075                }
    076            }
    077            for (int i=0;i<nrRooms;i++) {
    078                int cnt = 0;
    079                for (int j=0;j<nrGroupsOfRooms;j++)
    080                    if (groupOfRooms[j].contains(rooms[i])) cnt++;
    081                while (cnt < nrRoomInGroupMin) {
    082                    int r = 0;
    083                    do {
    084                        r = ToolBox.random(nrGroupsOfRooms);
    085                    } while (groupOfRooms[r].contains(rooms[i]));
    086                    groupOfRooms[r].add(rooms[i]);
    087                    groupForRoom[i].add(groupOfRooms[r]);
    088                    cnt ++;
    089                }
    090            }
    091            Resource instructors[] = new Resource[nrInstructors];
    092            for (int i=0;i<nrInstructors;i++) {
    093                instructors[i] = new Resource("t"+(i+1), Resource.TYPE_INSTRUCTOR, "Teacher "+(i+1));
    094                m.addConstraint(instructors[i]);
    095            }
    096            Resource classes[] = new Resource[nrClasses];
    097            for (int i=0;i<nrClasses;i++) {
    098                classes[i] = new Resource("c"+(i+1),Resource.TYPE_CLASS ,"Class "+(i+1));
    099                m.addConstraint(classes[i]);
    100            }
    101
    102            int[][] timetable4room = new int[nrRooms][nrSlots];
    103            int[][] timetable4instr = new int[nrInstructors][nrSlots];
    104            int[][] timetable4class = new int[nrClasses][nrSlots];
    105            int act = 0;
    106            for (int i=0;i<timetable4room.length;i++)
    107                for (int j=0;j<timetable4room[i].length;j++)
    108                    timetable4room[i][j]=0;
    109            for (int i=0;i<timetable4instr.length;i++)
    110                for (int j=0;j<timetable4instr[i].length;j++)
    111                    timetable4instr[i][j]=0;
    112            for (int i=0;i<timetable4class.length;i++)
    113                for (int j=0;j<timetable4class[i].length;j++)
    114                    timetable4class[i][j]=0;
    115
    116            int totalSlots = nrRooms * nrSlots;
    117            int usedSlots = 0;
    118            Vector starts = new Vector();
    119            Vector arooms = new Vector();
    120            while ((((double)usedSlots/((double)totalSlots)))<fillFactor) {
    121                int attempt=0;
    122                int slot = ToolBox.random(nrSlots);
    123                int room = ToolBox.random(nrRooms);
    124                while (attempt<500 && timetable4room[room][slot]!=0) {
    125                    slot = ToolBox.random(nrSlots);
    126                    room = ToolBox.random(nrRooms);
    127                }
    128                if (attempt==500) {
    129                    int s = slot; int r = room;
    130                    while (timetable4room[r][s]!=0) {
    131                        r++;
    132                        if (r==nrRooms) r=0;
    133                        if (r==room) s++;
    134                        if (s==nrSlots) s=0;
    135                    }
    136                    slot = s; room = r;
    137                }
    138                int length = maxLength;//ToolBox.random(maxLength)+1;
    139                int aclass = ToolBox.random(nrClasses);
    140                int instr = ToolBox.random(nrInstructors);
    141                attempt=0;
    142                while (attempt<500 && (timetable4class[aclass][slot]!=0 || timetable4instr[instr][slot]!=0)) {
    143                    aclass = ToolBox.random(nrClasses);
    144                    instr = ToolBox.random(nrInstructors);
    145                }
    146                if (attempt==500) continue;
    147                int len = 1;
    148                while (len<length) {
    149                    if ((((slot+len)%nrHours)!=0) && timetable4room[room][slot+len]==0 && timetable4instr[instr][slot+len]==0 && timetable4class[aclass][slot+len]==0)
    150                        len++;
    151                    else break;
    152                }
    153                Vector roomGr = (Vector)ToolBox.random(groupForRoom[room]);
    154                act++;
    155                usedSlots+=len;
    156                Activity a = new Activity(len,"a"+act, "Activity "+act);
    157                a.addResourceGroup(roomGr);
    158                a.addResourceGroup(instructors[instr]);
    159                a.addResourceGroup(classes[aclass]);
    160                m.addVariable(a);
    161                starts.addElement(new Integer(slot));
    162                arooms.addElement(new Integer(room));
    163                for (int i=slot;i<slot+len;i++) {
    164                    timetable4room[room][i]=act;
    165                    timetable4instr[instr][i]=act;
    166                    timetable4class[aclass][i]=act;
    167                }
    168            }
    169            int nrHardFreeRes = 0;
    170            int nrSoftFreeRes = 0;
    171            int nrSoftUsedRes = 0;
    172            for (int slot = 0; slot < nrSlots; slot++) {
    173                for (int room = 0; room < nrRooms; room++) {
    174                    if (timetable4room[room][slot]==0) {
    175                        if (ToolBox.random()<hardFreeResource) {
    176                            nrHardFreeRes++;
    177                            rooms[room].addProhibitedSlot(slot);
    178                        } else if (ToolBox.random()<softFreeResource/(1.0-hardFreeResource)) {
    179                            nrSoftFreeRes++;
    180                            rooms[room].addDiscouragedSlot(slot);
    181                        }
    182                    } else if (ToolBox.random()<softUsedResource) {
    183                        nrSoftUsedRes++;
    184                        rooms[room].addDiscouragedSlot(slot);
    185                    }
    186                }
    187                for (int instr = 0; instr < nrInstructors; instr++) {
    188                    if (timetable4instr[instr][slot]==0) {
    189                        if (ToolBox.random()<hardFreeResource) {
    190                            nrHardFreeRes++;
    191                            instructors[instr].addProhibitedSlot(slot);
    192                        } else if (ToolBox.random()<softFreeResource/(1.0-hardFreeResource)) {
    193                            nrSoftFreeRes++;
    194                            instructors[instr].addDiscouragedSlot(slot);
    195                        }
    196                    } else if (ToolBox.random()<softUsedResource) {
    197                        nrSoftUsedRes++;
    198                        instructors[instr].addDiscouragedSlot(slot);
    199                    }
    200                }
    201                for (int aclass = 0; aclass < nrClasses; aclass++) {
    202                    if (timetable4class[aclass][slot]==0) {
    203                        if (ToolBox.random()<hardFreeResource) {
    204                            nrHardFreeRes++;
    205                            classes[aclass].addProhibitedSlot(slot);
    206                        } else if (ToolBox.random()<softFreeResource/(1.0-hardFreeResource)) {
    207                            nrSoftFreeRes++;
    208                            classes[aclass].addDiscouragedSlot(slot);
    209                        }
    210                    } else if (ToolBox.random()<softUsedResource) {
    211                        nrSoftUsedRes++;
    212                        classes[aclass].addDiscouragedSlot(slot);
    213                    }
    214                }
    215            }
    216            int nrSoftFreeAct = 0;
    217            int nrSoftUsedAct = 0;
    218            int nrHardFreeAct = 0;
    219            for (int i=0;i<m.variables().size();i++) {
    220                Activity activity = (Activity)m.variables().elementAt(i);
    221                for (int slot = 0; slot < nrSlots; slot++) {
    222                    int start = ((Integer)starts.elementAt(i)).intValue();
    223                    if (slot<start || slot>=start+activity.getLength()) {
    224                        if (ToolBox.random()<hardFreeActivity) {
    225                            nrHardFreeAct++;
    226                            activity.addProhibitedSlot(slot);
    227                        } else if (ToolBox.random()<(softFreeActivity/(1.0-hardFreeActivity))) {
    228                            nrSoftFreeAct++;
    229                            activity.addDiscouragedSlot(slot);
    230                        }
    231                    } else {
    232                        if (ToolBox.random()<softUsedActivity) {
    233                            nrSoftUsedAct++;
    234                            activity.addDiscouragedSlot(slot);
    235                        }
    236                    }
    237                }
    238                activity.init();
    239            }
    240            for (int i=0;i<nrDependencies;) {
    241                int ac1 = ToolBox.random(m.variables().size());
    242                int ac2 = ToolBox.random(m.variables().size());
    243                while (ac1==ac2) {
    244                    ac2 = ToolBox.random(m.variables().size());
    245                }
    246                int s1 = ((Integer)starts.elementAt(ac1)).intValue();
    247                int s2 = ((Integer)starts.elementAt(ac2)).intValue();
    248                Activity a1 = (Activity)m.variables().elementAt(ac1);
    249                Activity a2 = (Activity)m.variables().elementAt(ac2);
    250                Dependence dep = null;
    251                if (s1<s2) {
    252                    if (s1+a1.getLength()==s2)
    253                        dep = new Dependence("d"+(i+1),Dependence.TYPE_CLOSELY_BEFORE);
    254                    else if (s1+a1.getLength()<s2)
    255                        dep = new Dependence("d"+(i+1),Dependence.TYPE_BEFORE);
    256                } else {
    257                    if (s2==s1+a1.getLength())
    258                        dep = new Dependence("d"+(i+1),Dependence.TYPE_CLOSELY_AFTER);
    259                    else if (s2>s1+a1.getLength())
    260                        dep = new Dependence("d"+(i+1),Dependence.TYPE_AFTER);
    261                }
    262                if (dep!=null) {
    263                    dep.addVariable(a1);
    264                    dep.addVariable(a2);
    265                    m.addConstraint(dep);
    266                    i++;
    267                }
    268            }
    269            for (int i=0;i<m.variables().size();i++) {
    270                Activity activity = (Activity)m.variables().elementAt(i);
    271                //sLogger.debug("-- processing activity "+activity.getName());
    272                int start = ((Integer)starts.elementAt(i)).intValue();
    273                int room = ((Integer)arooms.elementAt(i)).intValue();
    274                Location location = null;
    275                for (Enumeration e=activity.values().elements();e.hasMoreElements();) {
    276                    Location l = (Location)e.nextElement();
    277                    if (l.getSlot()==start && l.getResource(0).getResourceId().equals("r"+(room+1))) {
    278                        location = l; break;
    279                    }
    280                }
    281                if (location!=null) {
    282                    Set conflicts = m.conflictValues(location);
    283                    if (!conflicts.isEmpty()) {
    284                        sLogger.warn("Unable to assign "+location.getName()+" to "+activity.getName()+", reason:");
    285                        for (Enumeration e=activity.constraints().elements();e.hasMoreElements();) {
    286                            Constraint c = (Constraint)e.nextElement();
    287                            Set cc = new HashSet(); c.computeConflicts(location, cc);
    288                            if (!cc.isEmpty())
    289                                sLogger.warn("  -- Constraint "+c.getName()+" causes conflicts "+cc);
    290                        }
    291                    } else {
    292                        activity.assign(0, location);
    293                        activity.setInitialAssignment(location);
    294                    }
    295                    //sLogger.debug("  -- location "+location.getName()+" found");
    296                    activity.setInitialAssignment(location);
    297                } else {
    298                    sLogger.warn("Unable to assign "+activity.getName()+" -- no location matching slot="+start+" room='R"+(room+1)+"'");
    299                }
    300            }
    301            if (!cfg.getPropertyBoolean("General.InitialAssignment", true)) {
    302                for (int i=0;i<m.variables().size();i++) {
    303                    Activity activity = (Activity)m.variables().elementAt(i);
    304                    activity.unassign(0);
    305                }
    306            }
    307
    308            int forcedPerturbances = cfg.getPropertyInt("General.ForcedPerturbances", 0);
    309            if (forcedPerturbances>0) {
    310                Vector initialVariables = new Vector();
    311                for (Enumeration e=m.variables().elements();e.hasMoreElements();) {
    312                    Variable v = (Variable)e.nextElement();
    313                    if (v.getInitialAssignment()!=null)
    314                        initialVariables.addElement(v);
    315                }
    316                for (int i=0;i<forcedPerturbances;i++) {
    317                    if (initialVariables.isEmpty()) break;
    318                    Variable var = (Variable)ToolBox.random(initialVariables);
    319                    initialVariables.remove(var);
    320                    var.removeInitialValue();
    321                }
    322            }
    323
    324
    325            sLogger.debug("-- Generator Info ---------------------------------------------------------");
    326            sLogger.debug("  Total number of "+m.variables().size()+" activities generated.");
    327            sLogger.debug("  Total number of "+usedSlots+" slots are filled ("+((100.0*usedSlots)/totalSlots)+"% filled).");
    328            sLogger.debug("  Average length of an activity is "+(((double)usedSlots)/m.variables().size()));
    329            sLogger.debug("  Total number of hard constraints posted on free slots on activities: "+nrHardFreeAct);
    330            sLogger.debug("  Total number of soft constraints posted on free slots on activities: "+nrSoftFreeAct);
    331            sLogger.debug("  Total number of soft constraints posted on used slots on activities: "+nrSoftUsedAct);
    332            sLogger.debug("  Total number of hard constraints posted on free slots on resources: "+nrHardFreeRes);
    333            sLogger.debug("  Total number of soft constraints posted on free slots on resources: "+nrSoftFreeRes);
    334            sLogger.debug("  Total number of soft constraints posted on used slots on resources: "+nrSoftUsedRes);
    335            sLogger.debug("  Total number of "+nrDependencies+" dependencies generated.");
    336            sLogger.debug("---------------------------------------------------------------------------");
    337
    338            return m;
    339        }
    340
    341        public static void main(String[] args) {
    342            org.apache.log4j.BasicConfigurator.configure();
    343            TimetableModel model = generate(new DataProperties());
    344            System.out.println(model.getInfo());
    345        }
    346
    347        public void saveAsXML(DataProperties cfg, boolean gen, Solution solution, File outFile) throws IOException {
    348            outFile.getParentFile().mkdirs();
    349            sLogger.debug("Writting XML data to:"+outFile);
    350
    351            Document document = DocumentHelper.createDocument();
    352            document.addComment("Interactive Timetabling - University Timetable Generator (version 2.0)");
    353
    354            if (!assignedVariables().isEmpty()) {
    355                StringBuffer comments = new StringBuffer("Solution Info:\n");
    356                Dictionary solutionInfo=(solution==null?getInfo():solution.getInfo());
    357                for (Enumeration e=ToolBox.sortEnumeration(solutionInfo.keys());e.hasMoreElements();) {
    358                    String key = (String)e.nextElement();
    359                    Object value = solutionInfo.get(key);
    360                    comments.append("    "+key+": "+value+"\n");
    361                }
    362                document.addComment(comments.toString());
    363            }
    364
    365            Element root = document.addElement("Timetable");
    366            if (gen) {
    367                Element generator = root.addElement("Generator");
    368                generator.addAttribute("version", "2.0");
    369                generator.addElement("DaysPerWeek").setText(String.valueOf(iNrDays));
    370                generator.addElement("SlotsPerDay").setText(String.valueOf(iNrHours));
    371                generator.addElement("NrRooms").setText(cfg.getProperty("Generator.NrRooms","20"));
    372                generator.addElement("NrInstructors").setText(cfg.getProperty("Generator.NrInstructors","20"));
    373                generator.addElement("NrClasses").setText(cfg.getProperty("Generator.NrClasses","20"));
    374                generator.addElement("FillFactor").setText(cfg.getProperty("Generator.FillFactor",  "0.8"));
    375                generator.addElement("ActivityLengthMax").setText(cfg.getProperty("Generator.ActivityLengthMax", "5"));
    376                generator.addElement("NrGroupsOfRooms").setText(cfg.getProperty("Generator.NrGroupsOfRooms", "20"));
    377                generator.addElement("NrRoomsInGroupMin").setText(cfg.getProperty("Generator.NrRoomsInGroupMin",  "1"));
    378                generator.addElement("NrRoomsInGroupMax").setText(cfg.getProperty("Generator.NrRoomsInGroupMax",  "10"));
    379                generator.addElement("NrRoomInGroupMin").setText(cfg.getProperty("Generator.NrRoomInGroupMin", "1"));
    380                generator.addElement("HardFreeResource").setText(cfg.getProperty("Generator.HardFreeResource",  "0.05"));
    381                generator.addElement("SoftFreeResource").setText(cfg.getProperty("Generator.SoftFreeResource",  "0.3"));
    382                generator.addElement("SoftUsedResource").setText(cfg.getProperty("Generator.SoftUsedResource",  "0.05"));
    383                generator.addElement("SoftUsedActivity").setText(cfg.getProperty("Generator.SoftUsedActivity",  "0.05"));
    384                generator.addElement("SoftFreeActivity").setText(cfg.getProperty("Generator.SoftFreeActivity",  "0.3"));
    385                generator.addElement("HardFreeActivity").setText(cfg.getProperty("Generator.HardFreeActivity",  "0.05"));
    386                generator.addElement("NrDependencies").setText(cfg.getProperty("Generator.NrDependencies", "50"));
    387            }
    388
    389            Vector rooms = new Vector();
    390            Vector classes = new Vector();
    391            Vector instructors = new Vector();
    392            Vector specials = new Vector();
    393            Vector dependencies = new Vector();
    394
    395            for (Enumeration e=constraints().elements();e.hasMoreElements();) {
    396                Constraint c = (Constraint)e.nextElement();
    397                if (c instanceof Resource) {
    398                    Resource r = (Resource)c;
    399                    switch (r.getType()) {
    400                        case Resource.TYPE_ROOM:
    401                            rooms.addElement(r);
    402                            break;
    403                        case Resource.TYPE_CLASS:
    404                            classes.addElement(r);
    405                            break;
    406                        case Resource.TYPE_INSTRUCTOR:
    407                            instructors.addElement(r);
    408                            break;
    409                        default:
    410                            specials.addElement(r);
    411                    }
    412                } else if (c instanceof Dependence) {
    413                    dependencies.addElement(c);
    414                }
    415            }
    416
    417            Element problem = root.addElement("Problem");
    418            problem.addAttribute("version","2.0");
    419            Element problemGen = problem.addElement("General");
    420            problemGen.addElement("DaysPerWeek").setText(String.valueOf(iNrDays));
    421            problemGen.addElement("SlotsPerDay").setText(String.valueOf(iNrHours));
    422            Element resourceGen = problemGen.addElement("Resources");
    423            resourceGen.addElement("Classrooms").setText(String.valueOf(rooms.size()));
    424            resourceGen.addElement("Teachers").setText(String.valueOf(instructors.size()));
    425            resourceGen.addElement("Classes").setText(String.valueOf(classes.size()));
    426            resourceGen.addElement("Special").setText(String.valueOf(specials.size()));
    427            problemGen.addElement("Activities").setText(String.valueOf(variables().size()));
    428            problemGen.addElement("Dependences").setText(String.valueOf(dependencies.size()));
    429
    430            Element resources = problem.addElement("Resources");
    431
    432            Element resEl = resources.addElement("Classrooms");
    433            for (Enumeration e=rooms.elements();e.hasMoreElements();) {
    434                Resource r = (Resource)e.nextElement();
    435                Element el = resEl.addElement("Resource");
    436                el.addAttribute("id", r.getResourceId());
    437                el.addElement("Name").setText(r.getName());
    438                Element pref = el.addElement("TimePreferences");
    439                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
    440                    pref.addElement("Soft").setText(i.nextElement().toString());
    441                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
    442                    pref.addElement("Hard").setText(i.nextElement().toString());
    443            }
    444
    445            resEl = resources.addElement("Teachers");
    446            for (Enumeration e=instructors.elements();e.hasMoreElements();) {
    447                Resource r = (Resource)e.nextElement();
    448                Element el = resEl.addElement("Resource");
    449                el.addAttribute("id", r.getResourceId());
    450                el.addElement("Name").setText(r.getName());
    451                Element pref = el.addElement("TimePreferences");
    452                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
    453                    pref.addElement("Soft").setText(i.nextElement().toString());
    454                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
    455                    pref.addElement("Hard").setText(i.nextElement().toString());
    456            }
    457
    458            resEl = resources.addElement("Classes");
    459            for (Enumeration e=classes.elements();e.hasMoreElements();) {
    460                Resource r = (Resource)e.nextElement();
    461                Element el = resEl.addElement("Resource");
    462                el.addAttribute("id", r.getResourceId());
    463                el.addElement("Name").setText(r.getName());
    464                Element pref = el.addElement("TimePreferences");
    465                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
    466                    pref.addElement("Soft").setText(i.nextElement().toString());
    467                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
    468                    pref.addElement("Hard").setText(i.nextElement().toString());
    469            }
    470
    471            resEl = resources.addElement("Special");
    472            for (Enumeration e=specials.elements();e.hasMoreElements();) {
    473                Resource r = (Resource)e.nextElement();
    474                Element el = resEl.addElement("Resource");
    475                el.addAttribute("id", r.getResourceId());
    476                el.addElement("Name").setText(r.getName());
    477                Element pref = el.addElement("TimePreferences");
    478                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
    479                    pref.addElement("Soft").setText(i.nextElement().toString());
    480                for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
    481                    pref.addElement("Hard").setText(i.nextElement().toString());
    482            }
    483
    484            boolean hasSolution = false;
    485            Element actEl = problem.addElement("Activities");
    486            for (Enumeration e=variables().elements();e.hasMoreElements();) {
    487                Activity a = (Activity)e.nextElement();
    488                Element el = actEl.addElement("Activity");
    489                el.addAttribute("id", a.getActivityId());
    490                el.addElement("Name").setText(a.getName());
    491                el.addElement("Length").setText(String.valueOf(a.getLength()));
    492                if (a.getAssignment()!=null) hasSolution=true;
    493                Element pref = el.addElement("TimePreferences");
    494                for (Enumeration i=ToolBox.sortEnumeration((new Vector(a.getDiscouragedSlots())).elements());i.hasMoreElements();)
    495                    pref.addElement("Soft").setText(i.nextElement().toString());
    496                for (Enumeration i=ToolBox.sortEnumeration((new Vector(a.getProhibitedSlots())).elements());i.hasMoreElements();)
    497                    pref.addElement("Hard").setText(i.nextElement().toString());
    498                Element reqRes = el.addElement("RequiredResources");
    499                for (Enumeration f=a.getResourceGroups().elements();f.hasMoreElements();) {
    500                    Vector gr = (Vector)f.nextElement();
    501                    if (gr.size()==1) {
    502                        reqRes.addElement("Resource").setText(((Resource)gr.firstElement()).getResourceId());
    503                    } else {
    504                        Element grEl = reqRes.addElement("Group").addAttribute("conjunctive","no");
    505                        for (Enumeration g=gr.elements();g.hasMoreElements();)
    506                            grEl.addElement("Resource").setText(((Resource)g.nextElement()).getResourceId());
    507                    }
    508                }
    509            }
    510
    511            Element depEl = problem.addElement("Dependences");
    512            for (Enumeration e=dependencies.elements();e.hasMoreElements();) {
    513                Dependence d = (Dependence)e.nextElement();
    514                Element el = depEl.addElement("Dependence");
    515                el.addAttribute("id", d.getResourceId());
    516                el.addElement("FirstActivity").setText(((Activity)d.first()).getActivityId());
    517                el.addElement("SecondActivity").setText(((Activity)d.second()).getActivityId());
    518                switch (d.getType()) {
    519                    case Dependence.TYPE_AFTER :
    520                        el.addElement("Operator").setText("After");
    521                        break;
    522                    case Dependence.TYPE_BEFORE :
    523                        el.addElement("Operator").setText("Before");
    524                        break;
    525                    case Dependence.TYPE_CLOSELY_BEFORE :
    526                        el.addElement("Operator").setText("Closely before");
    527                        break;
    528                    case Dependence.TYPE_CLOSELY_AFTER :
    529                        el.addElement("Operator").setText("Closely after");
    530                        break;
    531                    case Dependence.TYPE_CONCURRENCY :
    532                        el.addElement("Operator").setText("Concurrently");
    533                        break;
    534                    default :
    535                        el.addElement("Operator").setText("Unknown");
    536                }
    537            }
    538
    539            if (hasSolution) {
    540                Element solutionEl = root.addElement("Solution");
    541                solutionEl.addAttribute("version","2.0");
    542                for (Enumeration e=variables().elements();e.hasMoreElements();) {
    543                    Activity a = (Activity)e.nextElement();
    544                    Element el = solutionEl.addElement("Activity");
    545                    el.addAttribute("id", a.getActivityId());
    546                    if (a.getAssignment()!=null) {
    547                        Location location = (Location)a.getAssignment();
    548                        el.addElement("StartTime").setText(String.valueOf(location.getSlot()));
    549                        Element res = el.addElement("UsedResources");
    550                        for (int i=0;i<location.getResources().length;i++)
    551                            res.addElement("Resource").setText(location.getResources()[i].getResourceId());
    552                    }
    553                }
    554            }
    555
    556            FileOutputStream fos = new FileOutputStream(outFile);
    557            (new XMLWriter(fos,OutputFormat.createPrettyPrint())).write(document);
    558            fos.flush();fos.close();
    559        }
    560
    561        public static TimetableModel loadFromXML(File inFile, boolean assign) throws IOException, DocumentException {
    562            Document document = (new SAXReader()).read(inFile);
    563            Element root = document.getRootElement();
    564            if (!"Timetable".equals(root.getName())) {
    565                sLogger.error("Given XML file is not interactive timetabling problem.");
    566                return null;
    567            }
    568
    569            Element problem = root.element("Problem");
    570            Element problemGen = problem.element("General");
    571            TimetableModel m = new TimetableModel(
    572                    Integer.parseInt(problemGen.elementText("DaysPerWeek")),
    573                    Integer.parseInt(problemGen.elementText("SlotsPerDay")));
    574
    575            Element resources = problem.element("Resources");
    576
    577            Hashtable resTab = new Hashtable();
    578
    579            Element resEl = resources.element("Classrooms");
    580            for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
    581                Element el = (Element)i.next();
    582                Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_ROOM, el.elementText("Name"));
    583                Element pref = el.element("TimePreferences");
    584                for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
    585                    r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
    586                for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
    587                    r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
    588                m.addConstraint(r);
    589                resTab.put(r.getResourceId(),r);
    590            }
    591
    592            resEl = resources.element("Teachers");
    593            for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
    594                Element el = (Element)i.next();
    595                Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_INSTRUCTOR, el.elementText("Name"));
    596                Element pref = el.element("TimePreferences");
    597                for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
    598                    r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
    599                for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
    600                    r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
    601                m.addConstraint(r);
    602                resTab.put(r.getResourceId(),r);
    603            }
    604
    605            resEl = resources.element("Classes");
    606            for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
    607                Element el = (Element)i.next();
    608                Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_CLASS, el.elementText("Name"));
    609                Element pref = el.element("TimePreferences");
    610                for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
    611                    r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
    612                for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
    613                    r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
    614                m.addConstraint(r);
    615                resTab.put(r.getResourceId(),r);
    616            }
    617
    618            resEl = resources.element("Special");
    619            for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
    620                Element el = (Element)i.next();
    621                Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_OTHER, el.elementText("Name"));
    622                Element pref = el.element("TimePreferences");
    623                for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
    624                    r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
    625                for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
    626                    r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
    627                m.addConstraint(r);
    628                resTab.put(r.getResourceId(),r);
    629            }
    630
    631            Element actEl = problem.element("Activities");
    632            Hashtable actTab = new Hashtable();
    633            for (Iterator i=actEl.elementIterator("Activity");i.hasNext();) {
    634                Element el = (Element)i.next();
    635                Activity a = new Activity(Integer.parseInt(el.elementText("Length")), el.attributeValue("id"), el.elementText("Name"));
    636                Element pref = el.element("TimePreferences");
    637                for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
    638                    a.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
    639                for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
    640                    a.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
    641                Element req = el.element("RequiredResources");
    642                for (Iterator j=req.elementIterator();j.hasNext();) {
    643                    Element rqEl = (Element)j.next();
    644                    if ("Resource".equals(rqEl.getName())) {
    645                        a.addResourceGroup((Resource)resTab.get(rqEl.getText()));
    646                    } else if ("Group".equals(rqEl.getName())) {
    647                        if ("no".equalsIgnoreCase(rqEl.attributeValue("conjunctive")) || "false".equalsIgnoreCase(rqEl.attributeValue("conjunctive"))) {
    648                            Vector gr = new Vector();
    649                            for (Iterator k=rqEl.elementIterator("Resource");k.hasNext();)
    650                                gr.addElement(resTab.get(((Element)k.next()).getText()));
    651                            a.addResourceGroup(gr);
    652                        } else {
    653                            for (Iterator k=rqEl.elementIterator("Resource");k.hasNext();)
    654                                a.addResourceGroup((Resource)resTab.get(((Element)k.next()).getText()));
    655                        }
    656                    }
    657                }
    658                m.addVariable(a);
    659                a.init();
    660                actTab.put(a.getActivityId(),a);
    661            }
    662
    663            Element depEl = problem.element("Dependences");
    664            for (Iterator i=depEl.elementIterator("Dependence");i.hasNext();) {
    665                Element el = (Element)i.next();
    666                int type = Dependence.TYPE_NO_DEPENDENCE;
    667                String typeStr = el.elementText("Operator");
    668                if ("After".equals(typeStr))
    669                    type = Dependence.TYPE_AFTER;
    670                else if ("Before".equals(typeStr))
    671                    type = Dependence.TYPE_BEFORE;
    672                else if ("After".equals(typeStr))
    673                    type = Dependence.TYPE_AFTER;
    674                else if ("Closely before".equals(typeStr))
    675                    type = Dependence.TYPE_CLOSELY_BEFORE;
    676                else if ("Closely after".equals(typeStr))
    677                    type = Dependence.TYPE_CLOSELY_AFTER;
    678                else if ("Concurrently".equals(typeStr))
    679                    type = Dependence.TYPE_CONCURRENCY;
    680                Dependence d = new Dependence(el.attributeValue("id"), type);
    681                d.addVariable((Activity)actTab.get(el.elementText("FirstActivity")));
    682                d.addVariable((Activity)actTab.get(el.elementText("SecondActivity")));
    683                m.addConstraint(d);
    684            }
    685
    686            Element solEl = root.element("Solution");
    687            if (solEl!=null) {
    688                for (Iterator i=solEl.elementIterator("Activity");i.hasNext();) {
    689                    Element el = (Element)i.next();
    690                    Activity a = (Activity)actTab.get(el.attributeValue("id"));
    691                    if (a==null) continue;
    692                    int slot = Integer.parseInt(el.elementText("StartTime"));
    693                    Element usResEl = el.element("UsedResources");
    694                    Vector res = new Vector();
    695                    for (Iterator j=usResEl.elementIterator("Resource");j.hasNext();)
    696                        res.addElement(resTab.get(((Element)j.next()).getText()));
    697                    for (Enumeration e=a.values().elements();e.hasMoreElements();) {
    698                        Location loc = (Location)e.nextElement();
    699                        if (loc.getSlot()!=slot || loc.getResources().length!=res.size()) continue;
    700                        boolean same = true;
    701                        for (int j=0;j<loc.getResources().length && same;j++)
    702                            if (!res.elementAt(j).equals(loc.getResources()[j])) same=false;
    703                        if (!same) continue;
    704                        a.setInitialAssignment(loc);
    705                        if (assign) a.assign(0,loc);
    706                        break;
    707                    }
    708                }
    709            }
    710            return m;
    711        }
    712    }