package Scheduler;
import io.devyse.scheduler.analytics.keen.KeenEngine;
import io.devyse.scheduler.logging.Logging;
import io.devyse.scheduler.security.Encryption;
import io.devyse.scheduler.startup.Parameters;
import io.devyse.scheduler.startup.SingleInstanceController;
import io.devyse.scheduler.swing.handlers.DefaultBrowserHyperlinkListener;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import javax.swing.ImageIcon;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.XTabComponent;
import javax.swing.UIManager.LookAndFeelInfo;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;
import com.beust.jcommander.JCommander;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.ArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.Properties;
public class Main {
/**
* Initialize logging as early as possible
*/
static {
Logging.initialize();
}
/**
* Static logger
*/
private static XLogger logger = XLoggerFactory.getXLogger(Main.class);
/********************************************************
* UPDATE SERIAL VERSION IN VERSION WHEN THIS FILE CHANGES
********************************************************/
protected static final long versionID = 2015072022400L;//object id
protected static final long buildNumber = 1591L;//build number
protected static final String version = new String("4.12.9");
protected static final int policyVersion = 1;
protected static final String author = new String("Mike Reinhold");
protected static final String maintain = new String("Mike Reinhold");
protected static final String email = new String("contact@coursescheduler.io");
protected static final String contributers = new String(
"Aaron Simmons, Phil DeMonaco, Alex Thomson, Ryan Murphy, Garrick Brazil");
protected static final String folderName = new String(System.getProperty("user.home") + "/Scheduler");
protected static final String dataName = new String("Data");
protected static final String dataPath = new String(folderName + "/" + dataName);
protected static final String dataFolder = new String(dataPath + "/");
protected static final String databaseExt = new String(".sdb");
protected static final String scheduleExt = new String(".ssf");
protected static final String preferencesExt = new String(".spf");
protected static final String smLogo = new String("Images/logo-small.png");
protected static final String logo = new String("Images/logo.png");
protected static final String xIconStr = new String("Images/xIcon.png");
protected static final String xIconPStr = new String("Images/xIconP.png");
protected static final String xIconRStr = new String("Images/xIconR.png");
protected static final String rmp = new String("Profs.txt");
protected static final String fixRMP = new String(dataFolder + rmp);
protected static final String jarFixRMP = new String(dataName + "/" + rmp);
protected static InputStream fixRMPFile;
private static final int buffers = 1;
protected static String defURL = new String("https://jweb.kettering.edu/cku1/xhwschedule.P_SelectSubject");
protected static String defSID = new String("366");
protected static ClassLoader loader;
protected static Preferences prefs;
protected static MainFrame master;
protected static ScheduledThreadPoolExecutor threadExec;
protected static TreeMap<String, Database> terms;
protected static boolean termChanged = false;
protected static ImageIcon icon;// = new ImageIcon(smLogo);
protected static ImageIcon xlIcon;// = new ImageIcon(logo);
protected static ImageIcon xIcon;
protected static ImageIcon xIconP;
protected static ImageIcon xIconR;
protected static int availProcs = 0;
protected static String windowSystem;
protected static String os;
protected static String jvm;
protected static boolean nimbus = false;
protected static boolean conflictDebugEnabled = false;
public static final String KEEN_STARTUP = "start";
public static final String KEEN_DOWNLOAD = "download";
public static final String KEEN_SCHEDULE = "compute";
public static final String KEEN_CONFIG = "config";
public static void main(String[] args) throws Exception{
//Register a SingleInstanceListener to handle secondary invocation
SingleInstanceController.register();
//process the command line arguments
Parameters parameters = new Parameters();
new JCommander(parameters, args);
//make sure that the required SSL/TLS protocols are enabled for use in HTTPS
Encryption.configureHttpsProtocols(parameters.getHttpsProtocols());
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
nimbus = true;
break;
}
}
}
catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
logger.warn("Unable to set look and feel", e);
}
Properties systemProps = System.getProperties();
windowSystem = systemProps.getProperty("sun.desktop");
os = systemProps.getProperty("os.name");
availProcs = Runtime.getRuntime().availableProcessors();
jvm = System.getProperty("java.vendor") + " " + System.getProperty("java.version");
threadExec = new ScheduledThreadPoolExecutor(32 * availProcs);
threadExec.setKeepAliveTime(1500, TimeUnit.MILLISECONDS);
threadExec.allowCoreThreadTimeOut(true);
loader = Main.class.getClassLoader();
Main.prefs = Preferences.load();
if (Main.prefs == null){
new File(folderName).mkdir();
new File(dataPath).mkdir();
Main.prefs = new Preferences();
}
prefs.setCurrentTerm(Term.nextTerm());
String current = prefs.getCurrentTerm();
terms = new TreeMap<String, Database>();
terms.put(current, Database.load(current));
registerStartupEvent();
try {
fixRMPFile = loader.getResourceAsStream(jarFixRMP);
icon = new ImageIcon(loader.getResource(smLogo));
xlIcon = new ImageIcon(loader.getResource(logo));
xIcon = new ImageIcon(loader.getResource(xIconStr));
xIconP = new ImageIcon(loader.getResource(xIconPStr));
xIconR = new ImageIcon(loader.getResource(xIconRStr));
if(icon == null){
icon = new ImageIcon(smLogo);
}
if(xlIcon == null){
xlIcon = new ImageIcon(logo);
}
if(xIcon == null){
xIcon = new ImageIcon(xIconStr);
}
if(xIconP == null){
xIconP = new ImageIcon(xIconPStr);
}
if(xIconR == null){
xIconR = new ImageIcon(xIconRStr);
}
}
catch (Exception e){
icon = new ImageIcon(smLogo);
xlIcon = new ImageIcon(logo);
xIcon = new ImageIcon(xIconStr);
xIconP = new ImageIcon(xIconPStr);
xIconR = new ImageIcon(xIconRStr);
}
XTabComponent.setIcons(xIcon, xIconP, xIconR);
master = new MainFrame();
Database database = terms.get(current);
master.setVisible(true);
master.createBufferStrategy(buffers);
//check if new privacy policy to display
if(prefs.getPolicyVersion() < policyVersion){
//display the policy
master.mainMenu.aboutHelpFrame.setVisible(true);
master.mainMenu.aboutHelpFrame.helpTabMain.setSelectedIndex(2);
JOptionPane.showMessageDialog(master.mainMenu.aboutHelpFrame, "The Course Scheduler has an updated privacy policy! You can view the policy in the About section of the Help menu.", "Updated Privacy Policy", JOptionPane.INFORMATION_MESSAGE);
//update the policy version
prefs.setPolicyVersion(policyVersion);
prefs.save();
}
//check if user is running an older Java version, in this case 1.7
logger.info("java.version: {}", System.getProperty("java.version"));
logger.info("java.vm.specification.version: {}", System.getProperty("java.vm.specification.version"));
logger.info("java.vm.version: {}", System.getProperty("java.vm.version"));
logger.info("java.specification.version: {}", System.getProperty("java.specification.version"));
logger.info("java.class.version: {}", System.getProperty("java.class.version"));
if(System.getProperty("java.version").startsWith("1.7")){
showJavaRuntimeWarning();
}
if (database == null){
int hresult = JOptionPane.showConfirmDialog(Main.master,
"You have not yet downloaded course information for " +
Term.getTermString(current) + ". Do you want to do so now?", "Download Course Information",
JOptionPane.YES_NO_OPTION);
if (hresult == JOptionPane.YES_OPTION){
updateDatabase(current);
}
else{
master.mainMenu.newScheduleMenu.setEnabled(false);
}
}
//open any schedule files specified at start up
openScheduleFiles(parameters.getOpenFiles());
}
private static void registerStartupEvent(){
if(!Main.prefs.isAnalyticsOptOut()){
Map<String, Object> startupEvent = new HashMap<>();
startupEvent.put("startup.type", "end user");
KeenEngine.getDefaultKeenEngine().registerEvent(KEEN_STARTUP, startupEvent);
}
}
public static void printZeroRatedProfs(){
ArrayList<Prof> profs = new ArrayList<Prof>();
Database current = terms.get(prefs.getCurrentTerm());
for(String key: current.getCourseList(CourseType.all)){
Course course = current.getCourse(key);
for(Section item: course.getSectionsLl()){
Prof prof = item.getInstructor();
if(!profs.contains(prof) && prof.getRating() == 0.0){
profs.add(prof);
}
}
}
//only print out the professors without ratings if debug enabled
if(logger.isDebugEnabled()){
for(Prof prof: profs){
logger.debug("{}: {}", prof.getName(), prof.getRating());
}
}
}
private static void showJavaRuntimeWarning(){
// for copying style
JLabel label = new JLabel();
Font font = label.getFont();
// create some css from the label's font
StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
style.append("font-size:" + font.getSize() + "pt;");
//build the message
JEditorPane jreWarning = new JEditorPane("text/html", "<html><body style=\"" + style + "\">"
+ "You are currently running an older version of the Java Runtime Environment (" + System.getProperty("java.version") + "). <br/><br/>"
+ "Oracle has announced the end of life for JRE 7 in April of 2015, more information can be found at "
+ "<a href=\"http://www.oracle.com/technetwork/java/javase/downloads/eol-135779.html\">http://www.oracle.com/technetwork/java/javase/downloads/eol-135779.html</a>. <br/><br/>"
+ "As such, the Course Scheduler will be migrating to Java 8 in an upcoming release. All users are encouraged to "
+ "upgrade to Java 8 as soon as possible. You can download the appropriate installer for your system from "
+ "<a href=\"http://java.com/en/download/\">Oracle</a>."
+ "</body></html>");
jreWarning.setEditable(false);
//Nimbus will override manually setting the background color via setBackground(Color) unless told otherwise
Color bgColor = label.getBackground();
UIDefaults defaults = new UIDefaults();
defaults.put("EditorPane[Enabled].backgroundPainter", bgColor);
jreWarning.putClientProperty("Nimbus.Overrides", defaults);
jreWarning.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
jreWarning.setBackground(bgColor);
//add hyperlink listener to handle opening the link
jreWarning.addHyperlinkListener(new DefaultBrowserHyperlinkListener());
JOptionPane.showMessageDialog(master,
jreWarning,
"Upgrade to Java 8",
JOptionPane.WARNING_MESSAGE);
}
/********************************************************
* @purpose Update the current database
*********************************************************/
public static void updateDatabase(String term){
ParseThread worker = new ParseThread(); //create new parse thread
worker.setEngine(Parser.ku); //set the parsing engine
worker.setTerm(term); //set term to download
worker.setUrl(Main.prefs.getURL()); //set the url to download from
Main.threadExec.execute(worker); //run the thread
}
/********************************************************
* @purpose Update the term display
*********************************************************/
public static void displayTerm(){
Main.master.mainMenu.currentTerm.setText(//set current term text
"Current Term: " + Term.getTermString(
Main.prefs.getCurrentTerm()) + MainMenu.mainMenuPad);
}
/********************************************************
* @purpose Update the downloaded date display
*********************************************************/
public static void displayDate(){
try{
Main.master.mainMenu.downloadDate.setText("Downloaded: " + Main.terms.get(
Main.prefs.getCurrentTerm()).getCreation().getTime().toString());
}
catch(NullPointerException ex){
Main.master.mainMenu.downloadDate.setText("Not Yet Downloaded");
}
}
/********************************************************
* @purpose Update all of the make schedule databases
*********************************************************/
public static void updateAllForRMP(){
boolean anyUpdate = false;
for(String item: terms.keySet()){
if(Integer.parseInt(prefs.getCurrentTerm()) - Integer.parseInt(item) < 4){
if(!terms.get(item).getProfs().hasRatings()){
anyUpdate = true;
updateDatabase(item);
}
}
}
reRateAll();
displayDate();
repairDates();
if(!anyUpdate){
Main.master.setEnabled(true);
}
}
/********************************************************
* @purpose Update all of the make schedule databases
*********************************************************/
public static void updateAll(){
for(String item: terms.keySet()){
if(Integer.parseInt(prefs.getCurrentTerm()) - Integer.parseInt(item) < 4){
updateDatabase(item);
}
}
reRateAll();
displayDate();
repairDates();
}
public static void repairDates(){
for(int pos = 0; pos < Main.master.tabControl.getTabCount(); pos++){//for each tab
Component comp = Main.master.tabControl.getComponentAt(pos);//get the component there
try{
Tab item = (Tab)comp;
item.setDate();
}
catch(ClassCastException ex){}
}
for(int pos = 0; pos < Main.master.detached.size(); pos++){//for each tab
Popup comp = (Popup)Main.master.detached.get(pos);//get the component there
try{
Tab item = (Tab)comp.getComponent();
item.setDate();
}
catch(ClassCastException ex){}
}
}
/********************************************************
* @purpose Rerate all of the stored databases and update schedules
*********************************************************/
public static void reRateAll(){
for(String item: terms.keySet()){
terms.get(item).reRate();
}
for(int pos = 0; pos < Main.master.tabControl.getTabCount(); pos++){//for each tab
Component comp = Main.master.tabControl.getComponentAt(pos);//get the component there
try{
Tab item = (Tab)comp;
item.setDatabase(terms.get(item.getDatabase().getTerm()), true, true);
}
catch(ClassCastException ex){}
}
for(int pos = 0; pos < Main.master.detached.size(); pos++){//for each tab
Popup comp = (Popup)Main.master.detached.get(pos);//get the component there
try{
Tab item = (Tab)comp.getComponent();
item.setDatabase(terms.get(item.getDatabase().getTerm()), true, true);
}
catch(ClassCastException ex){}
}
}
public static Preferences getPreferences(){
return Main.prefs;
}
public static String getApplicationVersion(){
return Main.version;
}
public static String getApplicationDirectory(){
return Main.folderName;
}
public static void openScheduleFiles(List<String> files){
for(String item: files){
if(item.endsWith(Main.scheduleExt)){
ScheduleWrap found = ScheduleWrap.load(item);
Main.master.mainMenu.addMadeSchedule(found, new File(item).getName());
}
}
}
}