Java tutorial
/* This file is part of KU-Scheduler. KU-Scheduler is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. KU-Scheduler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with KU-Scheduler. If not, see <http://www.gnu.org/licenses/>. */ import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.jsoup.Jsoup; import org.jsoup.select.Elements; /******************************************************************** * Class: Scheduler * Purpose: holds dynamic courses and related functions /*******************************************************************/ public class Scheduler implements Serializable { private static final long serialVersionUID = 1L; // serialization version private Map<String, List<Course>> catalog; // full mapping of all courses private List<String> courseIDs; // course ID list private List<String> currentCourseList; // current selected course private boolean loaded; // currently loaded latest information private boolean exception; // whether or not an exception occurred private String termIn; // term currently in /******************************************************************** * Constructor: Scheduler * Purpose: default constructor for scheduler object /*******************************************************************/ public Scheduler() { // Defaults this.catalog = new HashMap<String, List<Course>>(); this.courseIDs = new ArrayList<String>(); this.currentCourseList = new ArrayList<String>(); MainApplication.courseChecks = new HashMap<String, Boolean>(); this.termIn = ""; } /******************************************************************** * Method: resetCourses * Purpose: resets all courses to be blank for loading of new terms /*******************************************************************/ public void resetCourses() { // Defaults this.catalog = new HashMap<String, List<Course>>(); this.courseIDs = new ArrayList<String>(); this.currentCourseList = new ArrayList<String>(); MainApplication.courseChecks = new HashMap<String, Boolean>(); this.termIn = ""; } /******************************************************************** * Method: storeDynamicCourses() * Purpose: store all dynamic courses to memory /*******************************************************************/ public boolean storeDynamicCourse(String coursePrefix, DefaultHttpClient client, String term) { try { // Debug message //System.out.println("Storing " + coursePrefix); this.termIn = term; // Parameters for GET call String params = "?rsts=dummy&crn=dummy&term_in=" + term + "&sel_subj=dummy&sel_day=dummy&sel_schd=dummy&sel_insm=dummy&sel_camp=dummy" + "&sel_levl=dummy&sel_sess=dummy&sel_instr=dummy&sel_ptrm=dummy&sel_attr=dummy" + "&sel_subj=" + coursePrefix + "&sel_crse=&sel_title=&sel_schd=%25&sel_from_cred=&sel_to_cred=" + "&sel_camp=%25&sel_levl=%25&sel_ptrm=%25&sel_instr=%25&sel_attr=%25&begin_hh=0&" + "begin_mi=0&begin_ap=a&end_hh=0&end_mi=0&end_ap=a&SUB_BTN=Section+Search&path=1"; // Create GET call with correct referer HttpGet courseSections = new HttpGet( "http://jweb.kettering.edu/cku1/bwskfcls.P_GetCrse_Advanced" + params); courseSections.setHeader("Referer", "http://jweb.kettering.edu/cku1/bwskfcls.P_GetCrse"); // Execute HttpResponse response = client.execute(courseSections); // Get main container of courses Elements mainContainer = Jsoup .parse(response.getEntity().getContent(), "UTF-8", "http://jweb.kettering.edu") .getElementsByClass("datadisplaytable"); // Valid container ? if (mainContainer.size() > 0 && mainContainer.get(0).getElementsByTag("tbody").size() > 0) { // Get courses offered for subject Elements coursesOffered = mainContainer.get(0).getElementsByTag("tbody").get(0).children(); // Remove first two rows (headers) if (coursesOffered.size() > 0) coursesOffered.remove(0); if (coursesOffered.size() > 0) coursesOffered.remove(0); // Properties String courseName = "", courseID = "", instructor = "", sec = ""; String location = "", time = "", days = ""; boolean closed = false, nr = false, sr = false; double credits = 0; int crn = 0; Course currentCourse = new Course(); String room = ""; // Go through all courses offered for (int j = 0; j < coursesOffered.size(); j++) { boolean courseFailed = false; // Get all sections Elements e = coursesOffered.get(j).getElementsByTag("td"); // Correctly formatted line ? if (e.size() == 17 && !e.get(1).text().replace("\u00a0", "").equals("") && !e.get(2).text().replace("\u00a0", "").equals("") && !e.get(3).text().replace("\u00a0", "").equals("") && !e.get(4).text().replace("\u00a0", "").equals("") && !e.get(5).text().replace("\u00a0", "").equals("") && !e.get(6).text().replace("\u00a0", "").equals("") && !e.get(7).text().replace("\u00a0", "").equals("") && !e.get(8).text().replace("\u00a0", "").equals("") && !e.get(9).text().replace("\u00a0", "").equals("") && !e.get(13).text().replace("\u00a0", "").equals("") && !e.get(15).text().replace("\u00a0", "").equals("") && (e.get(8).text().contains("M") || e.get(8).text().contains("T") || e.get(8).text().contains("W") || e.get(8).text().contains("R") || e.get(8).text().contains("F")) && TimeBlock.isConvertable(e.get(9).text())) { // Store information (numbers are based on HTML table) courseID = e.get(2).text() + " " + e.get(3).text(); courseName = e.get(7).text(); days = e.get(8).text(); time = e.get(9).text(); instructor = e.get(13).text(); room = e.get(15).text(); closed = e.get(0).text().contains("C"); nr = e.get(0).text().contains("NR"); sr = e.get(0).text().contains("SR"); location = e.get(5).text(); sec = e.get(4).text(); try { // Convert CRN and credits crn = Integer.parseInt(e.get(1).text()); credits = Double.parseDouble(e.get(6).text()); } catch (Exception ex) { // Set invalid values crn = 0; credits = -1; courseFailed = true; } // Special case for lab if (credits == 0.0) courseID += (sec.charAt(sec.length() - 1)); if (!courseFailed) { // Create course currentCourse = new Course(courseName, courseID, crn, credits, closed, nr, sr); // Create meeting Meeting currentMeeting = new Meeting(currentCourse, time, days, location, instructor, room, 0); currentCourse.getMeetings().add(currentMeeting); if (this.catalog.get(courseID) == null) { // Brand new course set List<Course> courseSet = new ArrayList<Course>(); courseSet.add(currentCourse); this.catalog.put(courseID, courseSet); this.courseIDs.add(courseID); } else { // Simply add the course this.catalog.get(courseID).add(currentCourse); } } } // Only meeting information (missing column info) else if (e.size() == 17 && (e.get(8).text().contains("M") || e.get(8).text().contains("T") || e.get(8).text().contains("W") || e.get(8).text().contains("R") || e.get(8).text().contains("F")) && TimeBlock.isConvertable(e.get(9).text()) && currentCourse.getMeetings().size() > 0) { // Update subcourse info days = e.get(8).text(); time = e.get(9).text(); instructor = e.get(13).text(); room = e.get(15).text(); Meeting currentSub = new Meeting(currentCourse, time, days, location, instructor, room, currentCourse.getMeetings().size()); currentCourse.getMeetings().add(currentSub); } } } // Updated loaded status this.loaded = true; return true; } // Print all exceptions catch (Exception e) { e.printStackTrace(); this.loaded = false; return false; } } /******************************************************************** * Method: generatePermutations * Purpose: generates all working permutations for current list (3rd Implementation) /*******************************************************************/ public List<List<Course>> generatePermutations(List<List<Course>> workingSet) { // Try to persuade GC to run System.gc(); Runtime.getRuntime().gc(); try { // Reset exception exception = false; // Initialize three sets // One as a current iteration working area and another as iteration results // and last as memory for checking course sets //List<List<Course>> workingSet = new ArrayList<List<Course>>(); workingSet.clear(); List<List<Course>> newSet = new ArrayList<List<Course>>(); List<Course> cloneSet = new ArrayList<Course>(); // Memory for meeting lists List<Meeting> courseSubs = new ArrayList<Meeting>(); // For each course for (String courseID : this.currentCourseList) { // For each section in the course for (Course course : this.catalog.get(courseID)) { if (workingSet.size() == 0) { testClasses(new ArrayList<Course>(), course, cloneSet, courseSubs); newSet.add(cloneSet); cloneSet = new ArrayList<Course>(); } else { // Check all current workingSets for (List<Course> courseSet : workingSet) { // If fits into working set then add to newSet if (testClasses(courseSet, course, cloneSet, courseSubs)) { newSet.add(cloneSet); cloneSet = new ArrayList<Course>(); } else { cloneSet.clear(); } } } if (exception) break; } if (exception) break; // Update working set List<List<Course>> tempSet = workingSet; workingSet = newSet; newSet = tempSet; newSet.clear(); } // Return working set this.exception = false; return workingSet; } catch (java.lang.OutOfMemoryError e) { this.exception = true; System.out.println("Out of memory."); } return null; } /******************************************************************** * Method: testClasses * Purpose: tests if classes work together /*******************************************************************/ public boolean testClasses(List<Course> coursesOrig, Course courseCmpOrig, List<Course> clone, List<Meeting> courseSubs) { try { // Add course clone.add(courseCmpOrig); // Generate a complete list of sub courses courseSubs.clear(); for (Course courselist : coursesOrig) { // Add course to cloned list clone.add(courselist); // Add all sub courses for (Meeting course : courselist.getMeetings()) courseSubs.add(course); } // Add sub courses from compare course (except for first one) for (Meeting courseCmp : courseCmpOrig.getMeetings()) { // Get time TimeBlock cmpBlock = courseCmp.getTime(); if (cmpBlock == null) return false; // Get time info to minutes int startC = cmpBlock.getStartHours() * 60 + cmpBlock.getStartMin(); int endC = cmpBlock.getEndHours() * 60 + cmpBlock.getEndMin(); int uniqueCrn = courseCmp.getCourseReference().getCRN() * 1000 + courseCmp.getMeetingNumber(); String crnS = ""; // Check all sub courses against the compare course for (Meeting course : courseSubs) { int currentUniqueCRN = course.getCourseReference().getCRN() * 1000 + course.getMeetingNumber(); // Sort crn string as smallest crn first (for hashing value) if (uniqueCrn < currentUniqueCRN) { // Correct order crnS = uniqueCrn + (currentUniqueCRN + ""); } else crnS = (currentUniqueCRN + "") + uniqueCrn; // Check if the comparison has already been done if (MainApplication.courseChecks.containsKey(crnS)) { // If conflict, then simply return false if (!MainApplication.courseChecks.get(crnS)) return false; } // Courses have never been compared else { // Get time information for comparison block TimeBlock block = course.getTime(); if (block == null) return false; // Start for block B int startB = block.getStartHours() * 60 + block.getStartMin(); int endB = block.getEndHours() * 60 + block.getEndMin(); // Times overlap? if (startB < endC && endB > startC) { // Days also over lap? if ((course.M && courseCmp.M) || (course.T && courseCmp.T) || (course.W && courseCmp.W) || (course.R && courseCmp.R) || (course.F && courseCmp.F)) { // Store comparison and return false MainApplication.courseChecks.put(crnS, false); return false; } else { // Store comparison as successful MainApplication.courseChecks.put(crnS, true); } } else { // Store comparison as successful MainApplication.courseChecks.put(crnS, true); } } } } // At least one course must exist for schedule to be successful return coursesOrig.size() > 0; } catch (java.lang.OutOfMemoryError e) { exception = true; return false; } } /******************************************************************** * Method: getPreInfo * Purpose: gets the prequisite information for a course /*******************************************************************/ public String getPreInfo(String courseID) { String html = "Could not get prerequisite information."; String courseSubject = ""; String courseNumber = ""; try { courseSubject = courseID.split(" ")[0]; courseNumber = courseID.split(" ")[1].replaceAll("L", ""); // Create client DefaultHttpClient client = new DefaultHttpClient(); String url = "http://jweb.kettering.edu/cku1/bwckctlg.p_display_courses?term_in=" + termIn + "&one_subj=" + courseSubject + "&sel_crse_strt=" + courseNumber + "&sel_crse_end=" + courseNumber + "&sel_subj=&sel_levl=&sel_schd=&sel_coll=&sel_divs=&sel_dept=&sel_attr="; HttpGet termPage = new HttpGet(url); termPage.setHeader("Referer", "http://jweb.kettering.edu/cku1/twbkwbis.P_WWWLogin"); HttpResponse response = client.execute(termPage); Elements mainContainer = Jsoup .parse(response.getEntity().getContent(), "UTF-8", "https://jweb.kettering.edu") .getElementsByClass("datadisplaytable"); if (mainContainer.size() > 0 && mainContainer.get(0).getElementsByClass("ntdefault").size() > 0 && mainContainer.get(0).getElementsByClass("ntdefault").get(0).textNodes().size() > 0) { // Return first text node (prereq info) String infoBlock = mainContainer.get(0).getElementsByClass("ntdefault").get(0).textNodes().get(0) .text(); return infoBlock; } } catch (Exception e) { e.printStackTrace(); } return html; } /******************************************************************** * Accessors * Purpose: get the corresponding data /*******************************************************************/ public Map<String, List<Course>> getCatalog() { return this.catalog; } public List<String> getCourseIDs() { return this.courseIDs; } public boolean getLoaded() { return this.loaded; } public List<String> getCurrentCourseList() { return this.currentCourseList; } }