Scheduler.java Source code

Java tutorial

Introduction

Here is the source code for Scheduler.java

Source

/*
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;
    }

}