/**
* Main project class file.
*/
package gederedem;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import gederedem.data.DataSet;
import gederedem.data.Job;
import gederedem.ihm.MainWindow;
import gederedem.storage.HsqldbStorage;
/**
* Main software class.
*
* @author chrelaix
* @version 0.1
*/
/**
* @author chrelaix
*
*/
public class Gederedem {
// Constants
//--------------------------
private static final String EQUAL = "=";
private static final String NL = "\n";
// keywords for settings
/**
* key name to configure the position of setting file.
*/
private static final String CONFFILE = "configFile";
/**
* key name to configure the log level in setting file.
*/
private static final String LOGLEVEL = "loglevel";
/**
* key name to configure the language in setting file.
*/
private static final String LANG = "lang";
/**
* key name to configure the X position of GUI in setting file at launch.
*/
private static final String IHM_XPOS = "xPos";
/**
* key name to configure the Y position of GUI in setting file at launch.
*/
private static final String IHM_YPOS = "yPos";
/**
* key name to configure the width of GUI in setting file at launch.
*/
private static final String IHM_WIDTH = "width";
/**
* key name to configure the height of GUI in setting file at launch.
*/
private static final String IHM_HEIGHT = "height";
/**
* key name to configure the height of GUI in setting file at launch.
*/
private static final String PROFILE = "profile";
/**
* key name to configure the height of GUI in setting file at launch.
*/
private static final String STORAGE_TYPE = "dbType";
public static final String BASENAME = "gederedem.gederedem";
/**
* Where to save the data by default.
*/
public static final String DEFAULT_DATA_DIR = "datas";
/**
* The version number
*/
// TODO : find a way to change it automatically in factory
public static final String VERSION = "0.2.3";
// Internal objects
//--------------------------
/**
* call settings (prior to file settings), parameters passed to program at launch.
*/
private Hashtable<String, String> callSettings = new Hashtable<String, String>();
/**
* Where to save the config file (by default in calling app directory).
*/
private String confFilename = "Gederedem.config";
/**
* String used to store log messages before setting of log level.
*/
private String preConfigLogMsg;
/**
* String used to store debug messages before setting of log level.
*/
private String preConfigDbgMsg;
/**
* Program settings.
*/
private Properties settings;
/**
* Datas.
*/
private DataSet profile;
/**
* Program GUI.
*/
private MainWindow ihm;
// Localization
/**
* Application language.
*/
private static Locale lang = Locale.getDefault();
/**
* Localized messages.
*/
public ResourceBundle labels = ResourceBundle.getBundle(BASENAME, lang);
/**
* Logger object.
*/
public Trace log = new Trace(Gederedem.class.getName(),BASENAME);
/**
* Main method, to launch the app.
*
* @param args : Launch options
*/
public static void main(final String[] args) {
// Create the app
Gederedem app = new Gederedem();
// TODO : Try to display only after choice of language (with an array ?)
app.preConfigLogMsg = MessageFormat.format(app.labels.getString("logs.main.start"),
app.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(),
Gederedem.VERSION);
app.preConfigDbgMsg = "";
// Set default log (to none)
app.log.setLevel(Level.OFF);
// Handle args passed in command line
app.analyzeArgs(args);
// Now we know the log level, we can try to log
app.log.info(app.preConfigLogMsg);
app.log.debug(app.preConfigDbgMsg);
// Do not forget to destroy objects to avoid further use
app.preConfigLogMsg = null;
app.preConfigDbgMsg = null;
// Initialize the app
app.init();
// Select & load the DataSet
app.getProfile();
}
/**
* Analyze the options added in the command line call.
*
* @param argsArray the options
*/
private void analyzeArgs(final String[] argsArray) {
// Get the optional args while calling program.
String msg = "";
String callMsg = "";
int i = 0;
while (i < argsArray.length) {
i++;
callMsg += " " + argsArray[i - 1];
if (argsArray[i - 1].startsWith(LOGLEVEL + EQUAL, 0)){
// To set the log level
String logLevel = argsArray[i - 1].substring(LOGLEVEL.length() + 1);
setLogType(logLevel);
callSettings.put(LOGLEVEL, logLevel);
continue;
}
if (argsArray[i - 1].startsWith(LANG + EQUAL, 0)) {
// To set a specific language
String customLang = argsArray[i - 1].substring(LANG.length() + 1);
setLang(customLang);
callSettings.put(LANG, customLang);
continue;
}
if (argsArray[i - 1].startsWith(CONFFILE + EQUAL, 0)) {
// To set a specific configuration file
setConfFilename(argsArray[i - 1].substring(CONFFILE.length() + 1));
callSettings.put(CONFFILE, getConfFilename());
continue;
}
if (argsArray[i - 1].startsWith(PROFILE + EQUAL,0)) {
// To set a specific DataSet profile
setConfFilename(argsArray[i - 1].substring(PROFILE.length() + 1));
callSettings.put(PROFILE, getConfFilename());
continue;
}
if (argsArray[i - 1].startsWith(STORAGE_TYPE + EQUAL, 0)) {
// To set a specific DataSet profile
setConfFilename(argsArray[i - 1].substring(STORAGE_TYPE.length() + 1));
callSettings.put(STORAGE_TYPE, getConfFilename());
continue;
}
// Unknown parameter.
if (msg.compareTo("") == 0) {
msg += NL;
}
msg += MessageFormat.format(labels.getString("logs.main.unknownArg"),
argsArray[i - 1]);
}
if (msg.compareTo("") != 0) {
// There are some unknown parameters : we must display usage
msg += NL + NL + labels.getString("labels.info.main.usage");
log.warning(msg);
// send always a popup even if the loglevel is set too low
DisplayErrorMsg(msg);
}
preConfigDbgMsg = MessageFormat.format(labels.getString("logs.main.callInfos"),
getClass().getProtectionDomain().getCodeSource().getLocation().getPath()) +
callMsg;
}
/**
* Program initialization. Get the saved parameters
*/
private void init() {
log.entering(this.getClass().getName(), "init()");
settings = new Properties();
// Get the previous parameters
getProgramSettings();
// Create the display
ihm = new MainWindow(this);
// Set loglevel
if (callSettings.containsKey(LOGLEVEL)) {
setLogType(callSettings.get(LOGLEVEL));
} else {
setLogType(settings.getProperty(LOGLEVEL));
}
// Display the main windows
log.debug(labels.getString("logs.main.displayIhm"));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Set properties
ihm.setLocation(Integer.valueOf(settings.getProperty(IHM_XPOS)).intValue(),
Integer.valueOf(settings.getProperty(IHM_YPOS)).intValue());
ihm.setSize(Integer.valueOf(settings.getProperty(IHM_WIDTH)).intValue(),
Integer.valueOf(settings.getProperty(IHM_HEIGHT)).intValue());
// Make it visible.
ihm.setVisible(true);
}
});
log.exiting(this.getClass().getName(), "init()");
}
/**
* Set the GUI position to have the same placement in next start.
*/
private void updateIHMSettings() {
log.entering(this.getClass().getName(), "updateIHMSettings()");
settings.setProperty(IHM_XPOS, Integer.valueOf(ihm.getX() ).toString());
settings.setProperty(IHM_YPOS, Integer.valueOf(ihm.getY()).toString());
settings.setProperty(IHM_WIDTH, Integer.valueOf(ihm.getWidth()).toString());
settings.setProperty(IHM_HEIGHT, Integer.valueOf(ihm.getHeight()).toString());
log.exiting(this.getClass().getName(), "updateIHMSettings()");
}
/**
* Get the previously saved program settings.
*/
private void getProgramSettings() {
log.entering(this.getClass().getName(), "getProgramSettings()");
try {
final File confFile = new File(confFilename);
if (confFile.exists() == false) {
String msg = MessageFormat.format(labels.getString("logs.main.confFileMissing"),
confFile.getAbsolutePath());
DisplayInfoMsg(msg);
// Set Default values
settings.setProperty(IHM_XPOS, "0");
settings.setProperty(IHM_YPOS, "0");
settings.setProperty(IHM_WIDTH, "640");
settings.setProperty(IHM_HEIGHT, "480");
settings.setProperty(LANG, lang.toString());
settings.setProperty(LOGLEVEL, Level.OFF.toString());
settings.setProperty(STORAGE_TYPE, HsqldbStorage.TYPE);
} else {
if (confFile.canRead() == false) {
String msg = MessageFormat.format(labels.getString("logs.main.confFileUnreadable"),
confFile.getAbsolutePath());
DisplayErrorMsg(msg);
} else {
// Get values from propertiesFile
settings.load(new FileInputStream(confFilename));
String msg = MessageFormat.format(labels.getString("logs.main.numSettingsRead"),
settings.size());
log.info(msg);
}
}
} catch (IOException ex) {
ex.printStackTrace();
DisplayErrorMsg(ex.toString());
System.exit(1);
}
log.exiting(this.getClass().getName(), "getProgramSettings()");
}
/**
* Save the program settings to have the same in next start.
*/
private void saveProgramSettings() {
log.entering(this.getClass().getName(), "saveProgramSettings()");
try {
final File confFile = new File(confFilename);
if ((confFile.exists() == true) && (confFile.canWrite() == false)) {
String msg = MessageFormat.format(labels.getString("logs.main.confFileUnwritable"),
confFile.getAbsolutePath());
DisplayErrorMsg(msg);
Exit(1);
}
String msg = MessageFormat.format(labels.getString("logs.main.saveParams"), settings.size(),
confFile.getAbsolutePath(), settings.toString());
log.info(msg);
// Set values to propertiesFile in project root folder
settings.store(new FileOutputStream(confFilename),
labels.getString("labels.info.storage.confFileTitle"));
} catch (IOException ex) {
ex.printStackTrace();
DisplayErrorMsg(ex.toString());
System.exit(-1);
}
log.exiting(this.getClass().getName(), "saveProgramSettings()");
}
/**
* Set the displayed labels language.
* @param newLang : new language
*/
private void setLang(String newLang) {
log.entering(this.getClass().getName(), "setLang(" + newLang + ")");
final Locale[] availableLangs = Locale.getAvailableLocales();
final int numLocals = availableLangs.length;
Locale tmpLocal;
int i = 0;
boolean found = false;
// if the lang is not found we always have the default one
while (i < numLocals) {
tmpLocal = availableLangs[i];
if (tmpLocal.toString().equalsIgnoreCase(newLang)) {
Locale.setDefault(tmpLocal);
lang = tmpLocal;
// Update the messages lang too
setLabels();
found = true;
break;
}
i++;
}
if (found == false) {
String logMsg = MessageFormat.format(labels.getString("logs.main.langNotFound"), newLang);
if (preConfigLogMsg != null) {
preConfigLogMsg += NL + logMsg;
} else {
log.warning(logMsg);
}
}
log.exiting(this.getClass().getName(), "setLang()");
}
/**
* Set the Labels in function of previously defined language.
*/
private void setLabels() {
log.entering(this.getClass().getName(), "setLabels()");
labels = ResourceBundle.getBundle(BASENAME, lang);
String logMsg = MessageFormat.format(labels.getString("logs.main.setLabel"), lang);
if (preConfigLogMsg != null) {
preConfigLogMsg += NL + logMsg;
} else {
log.info(logMsg);
}
log.exiting(this.getClass().getName(), "setLabels()");
}
/**
* Set the name of configuration file.
* @param confFilename the confFilename to set
*/
private void setConfFilename(String confFile) {
log.entering(this.getClass().getName(), "setConfFilename(" + confFile + ")");
String logMsg = MessageFormat.format(labels.getString("logs.main.setConfigFile"), confFile);
if (preConfigLogMsg != null) {
preConfigLogMsg += NL + logMsg;
} else {
log.info(logMsg);
}
this.confFilename = confFile;
log.exiting(this.getClass().getName(), "setConfFilename()");
}
/**
* Retrieve the name of the configuration file.
* @return the confFilename
*/
private String getConfFilename() {
log.entering(this.getClass().getName(), "getConfFilename()");
log.exiting(this.getClass().getName(), "getConfFilename()", confFilename);
return confFilename;
}
/**
* Set the log level.
*
* @param logName the logType to set :
* SEVERE (highest value), WARNING, INFO, CONFIG, FINE, FINER, FINEST (lowest value)
*/
private void setLogType(String logName) {
log.entering(this.getClass().getName(), "setLogType(" + logName + ")");
if (logName != null) {
try {
log.setLevel(Level.parse(logName));
} catch (IllegalArgumentException e) {
String msg = MessageFormat.format(labels.getString("logs.main.loglevelUnknown"), logName);
if (preConfigLogMsg != null) {
// log not set, begin of program
// We need to display anyway, so send a box
msg += e.getLocalizedMessage();
DisplayErrorMsg(msg);
} else {
log.severe(msg);
log.severe(e.getLocalizedMessage());
}
}
} else {
String logMsg = MessageFormat.format(labels.getString("logs.main.loglevelNull"), logName);;
log.severe(logMsg);
}
String logMsg = MessageFormat.format(labels.getString("logs.main.loglevelSettled"), logName);
if (preConfigDbgMsg != null) {
preConfigDbgMsg += NL + logMsg;
} else {
log.debug(logMsg);
}
log.exiting(this.getClass().getName(), "setLogType()");
}
/**
* Search previous configuration and if it is a valid configuration load it.
*/
private void getProfile() {
log.entering(this.getClass().getName(), "getProfile()");
// Search previous configuration and
// if valid configuration load it
if (callSettings.containsKey(PROFILE)) {
String msg = MessageFormat.format(labels.getString("logs.main.profileInCall"),
callSettings.get(PROFILE));
log.bigDebug(msg);
// TODO : add a test to find if the function got good (boolean return) ?
loadProfile(callSettings.get(PROFILE));
} else if (settings.containsKey(PROFILE)) {
String msg = MessageFormat.format(labels.getString("logs.main.profileInConf"),
settings.getProperty(PROFILE));
log.bigDebug(msg);
loadProfile(settings.getProperty(PROFILE));
}
// if no active configuration (no settled or not valid) choose a new profile
// TODO : add a test to find if the function got good (boolean return) ?
chooseProfile();
if (profile == null) {
log.severe(labels.getString("logs.main.noProfile"));
// TODO : see if the popup is not more useful in Trace object
DisplayErrorMsg(labels.getString("logs.main.noProfile"));
ToDoBeforeExit();
}
log.exiting(this.getClass().getName(), "getProfile()");
}
/**
* Load a found Data set.
* @param name
*/
private void loadProfile(final String name) {
log.entering(this.getClass().getName(), "loadProfile(" + name + ")");
// Create
profile = new DataSet(name, log);
// Update
profile.load();
// Display
ihm.loadTable(profile);
log.exiting(this.getClass().getName(), "loadProfile()");
}
/**
* Choose a selected profile.
*/
private void chooseProfile() {
log.entering(this.getClass().getName(), "chooseProfile()");
String choosenProfile = null;
String newProfileLabel = labels.getString("labels.info.data.new");
// list the existing profiles
ArrayList<String> availableProfiles = new ArrayList<String>();
// Add "new" choice
availableProfiles.add(0, newProfileLabel);
String msg = MessageFormat.format(labels.getString("logs.gui.profileDialog.addChoice"), newProfileLabel);
log.bigDebug(msg);
// define where to save the profiles
// TODO : add a configuration stuff to choose a different name and save it in configuration
final File folder = new File(Gederedem.DEFAULT_DATA_DIR);
if (folder.exists()) {
msg = MessageFormat.format(labels.getString("logs.main.dataDir.exist"), folder);
log.debug(msg);
final File[] listOfFiles = folder.listFiles();
if (listOfFiles != null) {
msg = MessageFormat.format(labels.getString("logs.main.dataDir.count"), listOfFiles.length);
log.debug(msg);
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isDirectory()) {
String tmpName = listOfFiles[i].getName();
availableProfiles.add(tmpName);
msg = MessageFormat.format(labels.getString("logs.gui.profileDialog.addChoice"), tmpName);
log.bigDebug(msg);
}
}
}
} else {
log.debug("logs.main.dataDir.notExist");
}
final String[] profileNames = new String[availableProfiles.size()];
for (int i = 0; i < availableProfiles.size(); i++) {
profileNames[i] = availableProfiles.get(i);
}
if (profileNames.length > 1) {
log.bigDebug("logs.gui.profileDialog.moreThanOneChoice");
// display the choice between the existing profile
// TODO : set default choice
choosenProfile = JOptionPane.showInputDialog(null,
labels.getString("labels.info.main.chooseProfile.msg"),
labels.getString("labels.info.main.chooseProfile.title"),
JOptionPane.QUESTION_MESSAGE,
null,
profileNames,
profileNames[0]).toString();
}
if ((choosenProfile == null) || (choosenProfile.equals(newProfileLabel))) {
log.bigDebug("logs.gui.profileDialog.needNew");
choosenProfile = JOptionPane.showInputDialog(null,
labels.getString("labels.info.main.newProfile.msg"),
labels.getString("labels.info.main.newProfile.title"),
JOptionPane.QUESTION_MESSAGE);
if (choosenProfile == null) {
log.bigDebug("logs.gui.profileDialog.noProfile");
// TODO : handle error exit
ihm.exit();
// Exit(-1);
} else {
msg = MessageFormat.format(labels.getString("logs.gui.profileDialog.gotAProfile"),
choosenProfile);
log.bigDebug(msg);
profile = new DataSet(choosenProfile, log);
profile.CreateProfile();
}
} else {
msg = MessageFormat.format(labels.getString("logs.gui.profileDialog.gotAProfile"),
choosenProfile);
log.bigDebug(msg);
// TODO : Handle errors in load of profile
loadProfile(choosenProfile);
}
log.exiting(this.getClass().getName(), "chooseProfile()");
}
/**
* Add a new entry in datas.
* @param newJob The datas to add
*/
public void addJobInProfile(final Job newJob) {
log.entering(this.getClass().getName(), "addJobInProfile(" + newJob.getTitle() + ")");
String msg = MessageFormat.format(labels.getString("logs.data.dataset.addJob"),
newJob.getTitle());
log.debug(msg);
profile.addJob(newJob);
log.exiting(this.getClass().getName(), "addJobInProfile()");
}
/**
* Add a new line in datas.
* @param newJob The datas to add
*/
public void removeJobInProfile(int indexOfJob) {
log.entering(this.getClass().getName(), "removeJobInProfile(" + indexOfJob + ")");
String msg = MessageFormat.format(labels.getString("logs.data.dataset.removeJob"),
indexOfJob);
log.debug(msg);
profile.removeJob(indexOfJob);
log.exiting(this.getClass().getName(), "removeJobInProfile()");
}
/**
* test if there are some changes in job table.
*/
private void testChangeInJobTable() {
log.entering(this.getClass().getName(), "testChangeInJobTable()");
final int nbRow = ihm.jobTable.getRowCount();
int selectedRow = 0;
while (selectedRow < nbRow) {
String msg = MessageFormat.format(labels.getString("logs.gui.jobTable.testRow"),
selectedRow);
log.debug(msg);
// TODO�: change cast to variabilize the getters
String id = (String) ihm.jobTable.getValueAt(selectedRow, 0);
String title = (String) ihm.jobTable.getValueAt(selectedRow, 1);
String enterprise = (String) ihm.jobTable.getValueAt(selectedRow, 2);
Date publishedDate = null;
String visiblePublication = (String) ihm.jobTable.getValueAt(selectedRow, 3);
try {
publishedDate = ihm.df.parse(visiblePublication);
} catch (ParseException e) {
e.printStackTrace();
log.warning(e.getLocalizedMessage());
}
msg = labels.getString("logs.gui.jobTable.jobToTest") +
labels.getString("labels.info.data.id") + " : " + id + ", " +
labels.getString("labels.info.data.title") + " [" + title + "], " +
labels.getString("labels.info.data.enterprise") + " [" + enterprise + "], " +
labels.getString("labels.info.data.publication") + " : " + visiblePublication;
log.debug(msg);
Job tmpJob = profile.getJob(Integer.parseInt(id));
if (tmpJob == null) {
// The job was deleted
msg = MessageFormat.format(labels.getString("logs.gui.jobTable.rowDeleted"),
id);
log.debug(msg);
} else if ((tmpJob.getTitle().compareTo(title) != 0)
|| (tmpJob.getEnterprise().compareTo(enterprise) != 0)
|| (publishedDate.compareTo(tmpJob.getPublication()) != 0)) {
tmpJob.setTitle(title);
tmpJob.setEnterprise(enterprise);
tmpJob.setPublication(publishedDate);
log.debug("logs.gui.jobTable.rowChanged");
profile.changeJob(tmpJob);
}
selectedRow++;
}
// TODO : Test all the needed information to do the saving
log.exiting(this.getClass().getName(), "testChangeInJobTable()");
}
/**
* Save the datas.
*/
private void saveProfile() {
log.entering(this.getClass().getName(), "saveProfile()");
// TODO : Test all the needed information to do the saving
// If all the informations are OK, then save with the correct connection type.
if ((profile != null) && (profile.isUnsaved)) {
log.info("logs.data.dataset.saveRequired");
profile.Save();
}
log.exiting(this.getClass().getName(), "saveProfile()");
}
/**
* Last things to do before closing the application.
*/
public void ToDoBeforeExit() {
log.entering(this.getClass().getName(), "ToDoBeforeExit()");
log.debug("logs.main.exitCall");
updateIHMSettings();
saveProgramSettings();
if (profile != null) {
testChangeInJobTable();
saveProfile();
}
log.exiting(this.getClass().getName(), "ToDoBeforeExit()");
Exit(0);
}
/**
* Exit the app.
* @param returnedCode
*/
private void Exit(int returnedCode) {
log.entering(this.getClass().getName(), "Exit(" + returnedCode + ")");
ihm.dispose();
log.fine("logs.main.exit");
System.exit(returnedCode);
}
/**
* Display an error popup.
* @param msg The text to display
*/
public static void DisplayErrorMsg(String msg) {
JOptionPane.showMessageDialog(null, msg, "Error", JOptionPane.ERROR_MESSAGE);
}
// TODO : try to avoid the next 3 methods
public void DisplayWarningMsg(String msg) {
log.entering(this.getClass().getName(), "DisplayWarningMsg(" + msg + ")");
if (log.getLevel().intValue() > Level.WARNING.intValue()) {
JOptionPane.showMessageDialog(null, msg, "Warning", JOptionPane.WARNING_MESSAGE);
}
log.exiting(this.getClass().getName(), "DisplayWarningMsg()");
}
public void DisplayInfoMsg(String msg) {
log.entering(this.getClass().getName(), "DisplayInfoMsg(" + msg + ")");
if (log.getLevel().intValue() > Level.INFO.intValue()) {
JOptionPane.showMessageDialog(null, msg, "Information", JOptionPane.INFORMATION_MESSAGE);
}
log.exiting(this.getClass().getName(), "DisplayInfoMsg()");
}
}
// TODO :
/**************************************************************
* Things to do in Gederedem development
***************************************
*
* * Set a file for the log in addition of console output
*
* * Seek if it's possible to fusion DataSet & JobTableModel (and if it's pertinent)
*
* * Change labels name to assemble all displayed labels into labels.xxxx
* and all logs in dbg.yyy, info.xxxx etc...
*
*
* GUI :
* * Set a config page to set configurations options
* * log level (NONE, ERRORS, WARNING, INFO, DEBUG, BIG_DEBUG, ALL)
*
*
*************************************************************/