001    package ttsolver;
002    
003    import ifs.model.*;
004    import ifs.util.*;
005    import ifs.solution.*;
006    
007    import java.io.*;
008    import java.util.*;
009    import java.text.*;
010    import org.dom4j.*;
011    import org.dom4j.io.*;
012    import java.sql.Statement;
013    
014    import ttsolver.constraint.*;
015    import ttsolver.model.*;
016    import edu.purdue.smas.timetable.data.*;
017    import edu.purdue.smas.timetable.data.pattern.*;
018    import edu.purdue.smas.timetable.util.Constants;
019    import edu.purdue.smas.timetable.util.Database;
020    
021    /**
022     * This class saves the resultant solution in the XML format.
023     * <br><br>
024     * Parameters:
025     * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
026     * <tr><td>General.Output</td><td>{@link String}</td><td>Folder with the output solution in XML format (solution.xml)</td></tr>
027     * <tr><td>General.ConvertIds</td><td>{@link Boolean}</td><td>If true, ids are converted (to be able to make input data public)</td></tr>
028     * </table>
029     *
030     * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
031     * @version 1.0
032     */
033    
034    public class TimetableXMLSaver extends TimetableSaver {
035        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableXMLSaver.class);
036        private static DecimalFormat[] sDF = {new DecimalFormat(""),new DecimalFormat("0"),new DecimalFormat("00"),new DecimalFormat("000"),new DecimalFormat("0000"),new DecimalFormat("00000"),new DecimalFormat("000000"),new DecimalFormat("0000000")};
037        public static boolean DEBUG = false;
038        
039        private TimetableModel iModel = null;
040        private Solution iSolution = null;
041        private boolean iConvertIds = false;
042        private File iOutputFolder = null;
043        
044        public TimetableXMLSaver(Solution solution) {
045            super(solution);
046            iConvertIds = getModel().getProperties().getPropertyBoolean("General.ConvertIds",false);
047            iOutputFolder = new File(getModel().getProperties().getProperty("General.Output"));
048            iSolution = solution; iModel = (TimetableModel)iSolution.getModel();
049        }
050        
051        public void setSolution(Solution solution) { iSolution = solution; iModel = (TimetableModel)iSolution.getModel(); }
052        
053        private static String getId(Hashtable conversion, boolean convert, Object id) {
054            String newId = (String)conversion.get(id);
055            if (newId==null) {
056                newId = String.valueOf(conversion.size()+1);
057                conversion.put(id, newId);
058            }
059            return (convert?newId:id.toString());
060        }
061        
062        public void save(PrintWriter out) throws Exception {
063            iOutputFolder.mkdirs();
064            File outFile = new File(iOutputFolder,"solution.xml");
065            sLogger.debug("Writting XML data to:"+outFile);
066            
067            Document document = DocumentHelper.createDocument();
068            document.addComment("Large Lecture Room Timetabling");
069            
070            if (!iModel.assignedVariables().isEmpty()) {
071                StringBuffer comments = new StringBuffer("Solution Info:\n");
072                Dictionary solutionInfo=(iSolution==null?iModel.getInfo():iSolution.getInfo());
073                for (Enumeration e=edu.purdue.smas.timetable.util.ToolBox.sortEnumeration(solutionInfo.keys());e.hasMoreElements();) {
074                    String key = (String)e.nextElement();
075                    Object value = solutionInfo.get(key);
076                    comments.append("    "+key+": "+value+"\n");
077                }
078                document.addComment(comments.toString());
079            }
080            
081            Element root = document.addElement("llrt");
082            root.addAttribute("semester", iModel.getProperties().getProperty("Data.Semester"));
083            root.addAttribute("year", iModel.getProperties().getProperty("Data.Year"));
084            root.addAttribute("version", iModel.getProperties().getProperty("Data.Version"));
085            root.addAttribute("created", String.valueOf(new Date()));
086            root.addAttribute("nrDays", String.valueOf(Constants.DAY_CODES.length));
087            root.addAttribute("halfHoursPerDay", String.valueOf(Constants.SLOTS_PER_DAY));
088                
089            Element instructorsEl = root.addElement("instructors").addAttribute("count", String.valueOf(iModel.getInstructorConstraints().size()));
090            Element departmentsEl = root.addElement("departments");
091            HashSet depts = new HashSet();
092            
093            Element roomsEl = root.addElement("rooms").addAttribute("count", String.valueOf(iModel.getRoomConstraints().size()));
094            Hashtable roomConv = new Hashtable();
095            Hashtable roomElements = new Hashtable();
096            for (Enumeration e=iModel.getRoomConstraints().elements();e.hasMoreElements();) {
097                RoomConstraint rc = (RoomConstraint)e.nextElement();
098                Element roomEl = roomsEl.addElement("room").addAttribute("id", getId(roomConv, iConvertIds, rc.getResourceId()));
099                roomElements.put(getId(roomConv, iConvertIds, rc.getResourceId()), roomEl);
100            }
101            
102            Element classesEl = root.addElement("classes").addAttribute("count", String.valueOf(iModel.variables().size()));
103            Hashtable classConv = new Hashtable();
104            Hashtable courseConv = new Hashtable();
105            Hashtable deptConv = new Hashtable();
106            Hashtable instructorConv = new Hashtable();
107            HashSet filledRooms = new HashSet();
108            iModel.variables().addAll(iModel.ignoredClasses());
109            for (Enumeration e1=iModel.variables().elements();e1.hasMoreElements();) {
110                Lecture lecture = (Lecture)e1.nextElement();
111                Placement placement = (Placement)lecture.getAssignment();
112                Element classEl = classesEl.addElement("class").addAttribute("id", getId(classConv, iConvertIds, String.valueOf(lecture.getClassId())));
113                classEl.addAttribute("course", getId(courseConv, true, lecture.sameLectures()));
114                classEl.addAttribute("expectedCapacity", String.valueOf(lecture.countStudents()));
115                if (lecture.getDepartment()!=null) {
116                    classEl.addAttribute("department", getId(deptConv, iConvertIds, lecture.getDepartment()));
117                    depts.add(lecture.getDepartment());
118                }
119                if (lecture.getInstructorConstraint()!=null) {
120                    Element instrEl = classEl.addElement("instructor").addAttribute("id", getId(instructorConv, iConvertIds, lecture.getInstructorConstraint().getResourceId()));
121                    if (placement!=null) instrEl.addAttribute("solution","true");
122                }
123                for (Enumeration e2=lecture.roomLocations().elements();e2.hasMoreElements();) {
124                    RoomLocation rl = (RoomLocation)e2.nextElement();
125                    Element roomLocationEl = (Element)classEl.addElement("room");
126                    roomLocationEl.addAttribute("id",  getId(roomConv, iConvertIds, rl.getId()));
127                    roomLocationEl.addAttribute("pref", String.valueOf(rl.getPreference()));
128                    if (placement!=null && placement.getRoomLocation().equals(rl)) roomLocationEl.addAttribute("solution", "true");
129                    if (filledRooms.add(rl)) {
130                        Element roomEl = (Element)roomElements.get(rl.getId());
131                        roomEl.addAttribute("capacity", String.valueOf(rl.getRoomSize()));
132                        if (rl.getPosX()>0 || rl.getPosY()>0)
133                            roomEl.addAttribute("location", rl.getPosX()+","+rl.getPosY());
134                    }
135                }
136                for (Enumeration e2=lecture.timeLocations().elements();e2.hasMoreElements();) {
137                    TimeLocation tl = (TimeLocation)e2.nextElement();
138                    Element timeLocationEl = (Element)classEl.addElement("time");
139                    timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))));
140                    timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
141                    timeLocationEl.addAttribute("length", String.valueOf(tl.getLength()));
142                    timeLocationEl.addAttribute("pref", String.valueOf((int)tl.getNormalizedPreference()));
143                    if (placement!=null && placement.getTimeLocation().equals(tl)) timeLocationEl.addAttribute("solution", "true");
144                }
145            }
146            classesEl.addAttribute("courses",String.valueOf(courseConv.size()));
147            
148            Element grConstraintsEl = root.addElement("groupConstraints").addAttribute("count", String.valueOf(iModel.getGroupConstraints().size()));
149            Hashtable grConv = new Hashtable();
150            for (Enumeration e1=iModel.getGroupConstraints().elements();e1.hasMoreElements();) {
151                ttsolver.constraint.GroupConstraint gc = (ttsolver.constraint.GroupConstraint)e1.nextElement();
152                Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId(grConv, iConvertIds, String.valueOf(gc.getId())));
153                grEl.addAttribute("type", gc.getTypeStr());
154                grEl.addAttribute("pref", gc.getPrologPreference());
155                for (Enumeration e2=gc.variables().elements(); e2.hasMoreElements();) {
156                    Lecture l = (Lecture)e2.nextElement();
157                    grEl.addElement("class").addAttribute("id",getId(classConv, iConvertIds, String.valueOf(l.getId())));
158                }
159            }
160            
161            Hashtable students = new Hashtable();
162            for (Enumeration e1=iModel.variables().elements();e1.hasMoreElements();) {
163                Lecture lecture = (Lecture)e1.nextElement();
164                for (Enumeration e2=lecture.students().elements();e2.hasMoreElements();) {
165                    String student = (String)e2.nextElement();
166                    Vector enrls = (Vector)students.get(student);
167                    if (enrls==null) {
168                        enrls = new Vector();
169                        students.put(student, enrls);
170                    }
171                    enrls.add(getId(classConv, iConvertIds, String.valueOf(lecture.getId())));
172                }
173            }
174            
175            Element studentsEl = root.addElement("students").addAttribute("count", String.valueOf(students.size()));
176            Hashtable studentConv = new Hashtable();
177            for (Enumeration e1=ToolBox.sortEnumeration(students.keys(), new IdComparator());e1.hasMoreElements();) {
178                String student = (String)e1.nextElement();
179                Element stEl = studentsEl.addElement("student").addAttribute("id", getId(studentConv, iConvertIds, student));
180                Vector lectures = (Vector)students.get(student);
181                Collections.sort(lectures);
182                for (Enumeration e2=lectures.elements();e2.hasMoreElements();) {
183                    stEl.addElement("class").addAttribute("id", (String)e2.nextElement());
184                }
185            }
186            
187            departmentsEl.addAttribute("count", String.valueOf(depts.size()));
188    
189            FileOutputStream fos = new FileOutputStream(outFile);
190            (new XMLWriter(fos,OutputFormat.createPrettyPrint())).write(document);
191            fos.flush();fos.close();
192        }
193        
194        private static class IdComparator implements Comparator {
195            public int compare(Object a, Object b) {
196                return Long.valueOf((String)a).compareTo(Long.valueOf((String)b));
197            }
198        }
199    }