/******************************************************** * Copyright (C) 2008 Course Scheduler Team * * This program 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. * * This program 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 this program; * if not, write to: * Free Software Foundation, Inc. * 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA ********************************************************/ /********************************************************* * Course Scheduler * File: ParseAssistThread.java * * Contains class: * * ParseAssistThread: * * Purpose: To provide a thread for assisting in downloading * professor ratings from Rate-My-Professor.com * * @author Mike Reinhold ********************************************************/ package Scheduler; //declare as member of scheduler package /********************************************************* * The Following imports are necessary for the functioning of this class ********************************************************/ import java.io.InputStream; //input streams import java.io.IOException; //input/output exceptions import java.net.URL; //url class import java.util.Scanner; //scanner utility import javax.swing.SwingWorker; //swing worker abstract class import org.slf4j.ext.XLogger; import org.slf4j.ext.XLoggerFactory; /********************************************************* * Class: ParseAssistThread * * @purpose Provide a helper thread for downloading and managing * rate-my-professor ratings for professors * * @see SwingWorker ********************************************************/ public class ParseAssistThread extends SwingWorker<Void, Void> { /** * Static logger */ private static XLogger logger = XLoggerFactory.getXLogger(ParseAssistThread.class); /******************************************************** * UPDATE SERIAL VERSION IN VERSION WHEN THIS FILE CHANGES ********************************************************/ protected final static long versionID = 2008071400041L;//class version /******************************************************** * The following are private static constants in the * class for parsing rate my professor ratings *********************************************************/ private static final String ratePrefix = new String("<td class=\"icon_col\">");//parsing prefix private static final String namePrefix1 = new String("<td><a href=\"ShowRatings.jsp?tid=");//parsing prefix private static final String namePrefix2 = new String("<td><a href=\"AddRating.jsp?tid=");//parsing prefix private static final String rateMyProf = new String("http://www.ratemyprofessors.com/SelectTeacher.jsp?the_dept=All&sid=");//posting url private static final String query = new String("&orderby=TLName&letter=");//posting url portion private static final int name = 0; //iteration for the prof name private static final int numRate = 1; //iteration for the numerical rating private static final int ratingItem = 2; //iteration for the rating item private static final int easeRate = 3; //iteration for the professor's ease rating private static final int startIndex = 1; //the starting index private static final int scale = 10; //scaling factor used to scale the ratings private static final int maxLen = 85; //the max length of a valid line private static final int min = 0; //minimum value for the iteration private static final int reset = -1; //value to reset to /********************************************************* * The following are the private dynamic values used within a thread ********************************************************/ private Char letter; //the character for this thread private boolean exception = false; //if this thread sourced an exception private ThreadSynch sync; //thread sync object private ProfDatabase profs; //the prof database to add to private String sid; //the school id to use /********************************************************* * @purpose provides the necessary method for running the thread, * this is the task completed by the thread, parsing the profs * * @see Override, SwingWorker<T,V> ********************************************************/ @Override //signal override protected Void doInBackground() throws Exception { int time = min; //create pass counter Prof next = new Prof(); //create new professor double rating = min; //create double for the rating try{ InputStream data = new URL( //new url for rate my prof rateMyProf + sid + query + letter).openStream(); Scanner test = new Scanner(data); //create scanner on the stream String val = new String(); //create new string for line while(test.hasNext()){ //while more lines to read if(sync.isCanceled()){ //check if cancelled sync.allowUpdate = false; //disallow update sync.removeHelper(this); //remove this return null; //return null } val = test.nextLine().trim(); //get line and trim if (val.length() < maxLen && (val.startsWith(ratePrefix) || val.startsWith(namePrefix1) || val.startsWith(namePrefix2))){ //validate length, and prefixes switch(time){ //switch to correct parameter case name:{ //current param is name Scanner reader = new Scanner(val);//line reader reader.useDelimiter(">");//parse by delimiter ">" reader.next(); //parse first time reader.next(); //parse again reader.useDelimiter("<");//parse by delimiter "<" String nameStr = reader.next().substring(startIndex);//get last name reader.useDelimiter(">");//parse by delimiter ">" reader.next(); //parse once reader.useDelimiter("<");//parse by delimiter "<" nameStr += reader.next().substring(startIndex);//get first name next = new Prof(); //create new prof next.setName(nameStr); //set prof name reader.close(); break; //break case } case numRate:{break;} //break the case since we don't care about num of ratings case ratingItem:{ //current param is rating Scanner reader = new Scanner(val);//get line reader reader.useDelimiter(">");//parse by delimiter ">" reader.next(); //parse next item reader.useDelimiter("<");//parse by delimiter "<" String temp = reader.next().substring(startIndex);//get rating as string try{ rating = new Scanner(temp).nextDouble();//try reading double } catch(Exception ex){ //catch conversion failure rating = min; //set rating to 0 } reader.close(); break; //break the case } case easeRate: { //current parameter is ease rating Scanner reader = new Scanner(val);//get line reader reader.useDelimiter(">");//parse by delimiter ">" reader.next(); //parse next item reader.useDelimiter("<");String temp = reader.next().substring(1);//get rating as string try{ rating += new Scanner(temp).nextDouble();//try reading double } catch(Exception ex){ //catch conversion failure rating += min; //add to rating } next.setRating(rating * scale); //scale rating profs.addIfNew(next); //add if new prof time = reset; //reset current parameter reader.close(); break; //break the case } } time++; //increment the current parameter } } test.close(); } catch(IOException ex){ logger.error("Unable to parse professor and rating", ex); //print out error message exception = true; //set exception to true } return null; //return Void type } /********************************************************* * @purpose return the letter for this thread * * @return Char: the letter this thread was dispatched for ********************************************************/ public Char getLetter() { return letter; //return the char } /********************************************************* * @purpose set the letter for this thread to download and parse * * @param Char letter: the letter this thread is dispatched for ********************************************************/ public void setLetter(Char letter) { this.letter = letter; //set the letter } /********************************************************* * @purpose return the Rate-My-Professor school id for this thread * * @return String: the SID for this thread ********************************************************/ public String getSid() { return sid; //return the School id } /********************************************************* * @purpose set the school id for this thread * * @param String sid: the Rate-My-Professor school id for this thread ********************************************************/ public void setSid(String sid) { this.sid = sid; //set the sid } /********************************************************* * @purpose return the prof database from this thread * * @return ProfDatabase: the ProfDatabase used by this thread ********************************************************/ public ProfDatabase getProfs() { return profs; //return the database } /********************************************************* * @purpose set the ProfDatabase for this thread * * @param ProfDatabase profs: the ProfDatabase this thread should * add to ********************************************************/ public void setProfs(ProfDatabase profs) { this.profs = profs; //set the prof database } /********************************************************* * @purpose to clean up after the thread is done * * @see Override, SwingWorker<T,V> ********************************************************/ @Override public void done(){ sync.removeHelper(this); //remove this thread from the thread list if(!sync.isCanceled() && !this.isCancelled() && sync.allowUpdate){//check if cancelled if(sync.hasLivingHelpers()){ //check if no more threads helping if(exception){ //check for exceptions caught sync.updateWatch("Failed rating download: " + letter, -1);//set note for character } else{ //no problems sync.updateWatch("Finished rating download: " + letter, -1);//set note for character } sync.incrementProgress(); //increment the progress } } } /********************************************************* * @purpose return the thread synchronization object * * @return ThreadSynch: this thread's synchronization object ********************************************************/ public ThreadSynch getSync() { return sync; //return the object } /********************************************************* * @purpose set the thread synchronization object for this thread * * @param ThreadSynch sync: the synchronization object ********************************************************/ public void setSync(ThreadSynch sync) { this.sync = sync; //set the object } }