/*
* Copyright (c) 2010-2011 by Stefan Laubenberger.
*
* Tyr is free software: you can redistribute it and/or modify
* it under the terms of the General Public License v2.0.
*
* Tyr 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:
* <http://www.gnu.org/licenses>
*
* This distribution is available at:
* <http://code.google.com/p/tyr/>
* <http://dev.laubenberger.net/tyr/>
*
* Contact information:
* Stefan Laubenberger
* Bullingerstrasse 53
* CH-8004 Zuerich
*
* <http://www.laubenberger.net>
*
* <laubenberger@gmail.com>
*/
package net.laubenberger.tyr.controller;
import java.awt.TrayIcon.MessageType;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.xml.bind.JAXBException;
import net.laubenberger.bogatyr.controller.application.ControllerApplicationAbstract;
import net.laubenberger.bogatyr.helper.HelperEnvironment;
import net.laubenberger.bogatyr.helper.HelperIO;
import net.laubenberger.bogatyr.helper.HelperImage;
import net.laubenberger.bogatyr.helper.HelperLog;
import net.laubenberger.bogatyr.helper.HelperString;
import net.laubenberger.bogatyr.helper.HelperXml;
import net.laubenberger.bogatyr.misc.exception.RuntimeExceptionIsNull;
import net.laubenberger.bogatyr.model.application.ModelApplication;
import net.laubenberger.bogatyr.model.misc.Language;
import net.laubenberger.bogatyr.service.localizer.Localizer;
import net.laubenberger.bogatyr.service.property.Property;
import net.laubenberger.bogatyr.view.swing.worker.Worker;
import net.laubenberger.tyr.Tyr;
import net.laubenberger.tyr.misc.ScalableIcon;
import net.laubenberger.tyr.model.FileType;
import net.laubenberger.tyr.model.TyrData;
import net.laubenberger.tyr.model.TyrDataImpl;
import net.laubenberger.tyr.view.ViewTyr;
import net.laubenberger.tyr.view.ViewTyrImpl;
import org.jdesktop.swingx.JXErrorPane;
import org.jdesktop.swingx.error.ErrorInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The implementation of the {@link Tyr} controller.
*
* @author Stefan Laubenberger
* @version 0.8.5 (20110214)
* @since 0.2.0
*/
public final class ControllerTyrImpl extends ControllerApplicationAbstract<ModelApplication, ViewTyr> implements
ControllerTyr {
static final Logger log = LoggerFactory.getLogger(ControllerTyrImpl.class);
// private static final String PROPERTY_UPDATE_LOCATION = "update.location"; //$NON-NLS-1$
private static final String PROPERTY_DIRECTORY_MODULE = "directory.module"; //$NON-NLS-1$
// private static final String PROPERTY_DIRECTORY_DATA = "directory.data"; //$NON-NLS-1$
// private static final String PROPERTY_DIRECTORY_DB = "directory.db"; //$NON-NLS-1$
private static final String RES_LOCK = "lock"; //$NON-NLS-1$
private static final String RES_LOCK_TEXT = "lock.text"; //$NON-NLS-1$
private static final String RES_EXCEPTION = "exception"; //$NON-NLS-1$
private static final String RES_EXCEPTION_UNCAUGHT = "exception.uncaught"; //$NON-NLS-1$
final Property property = getModel().getProperty();
final Localizer localizer = getModel().getLocalizer();
private TyrData data;
private final ControllerModule controllerModule;
final File dirData;
public ControllerTyrImpl(final ModelApplication model) {
super(model, null);
if (log.isTraceEnabled()) log.trace(HelperLog.constructor(model));
Thread.setDefaultUncaughtExceptionHandler(new Handler());
// dirData = property.getFile(PROPERTY_DIRECTORY_DATA);
dirData = new File(HelperEnvironment.getUserHomeDirectory(), ".tyr/data/"); //$NON-NLS-1$
if (!dirData.exists()) {
dirData.mkdirs();
}
// dirDb = property.getFile(PROPERTY_DIRECTORY_DB);
final File dirDb = new File(HelperEnvironment.getUserHomeDirectory(), ".tyr/db/"); //$NON-NLS-1$
if (!dirDb.exists()) {
dirDb.mkdirs();
}
// read data for the Tyr application
final File tda = new File(dirData, model.getUUID() + FileType.DATA.getExtension());
data = new TyrDataImpl();
try {
if (tda.exists()) {
data = HelperXml.deserialize(tda, TyrDataImpl.class);
}
} catch (JAXBException ex) {
log.error("Could not process Tyr data file " + HelperString.quote(tda.getAbsolutePath()), ex); //$NON-NLS-1$
exit(21);
}
setView(new ViewTyrImpl(this));
// should only be set on the first application start
if (null == data.getDirectoryModule()) data.setDirectoryModule(property.getFile(PROPERTY_DIRECTORY_MODULE));
if (null == data.getDirectoryData()) data.setDirectoryData(dirData);
if (null == data.getDirectoryDB()) data.setDirectoryDB(dirDb);
if (null == data.getLanguage()) data.setLanguage(Language.ENGLISH);
if (null == data.getLookAndFeel()) data.setLookAndFeel(UIManager.getLookAndFeel().getClass().getCanonicalName());
// directory check
if (!data.getDirectoryData().exists()) {
data.getDirectoryData().mkdir();
}
if (!data.getDirectoryDB().exists()) {
data.getDirectoryDB().mkdir();
}
controllerModule = new ControllerModuleImpl(this);
}
/*
* Private methods
*/
private void lock() {
if (log.isTraceEnabled()) log.trace(HelperLog.methodStart());
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(final File file) {
return file.getName().contains(getModel().getUUID().toString())
&& file.getName().contains(FileType.LOCK.getExtension());
}
};
if (!HelperIO.getFiles(HelperEnvironment.getOsTempDirectory(), filter).isEmpty()) {
if (log.isInfoEnabled()) log.info("Instance already running"); //$NON-NLS-1$
if (JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(getView().getOwner(),
localizer.getValue(RES_LOCK_TEXT),
getModel().getName() + " - " + localizer.getValue(RES_LOCK), JOptionPane.YES_NO_OPTION)) { //$NON-NLS-1$
exit(50);
}
}
try {
HelperIO.writeFile(HelperIO.getTemporaryFile(getModel().getUUID().toString(), FileType.LOCK.getExtension()),
getModel().getName());
} catch (IOException ex) {
if (log.isWarnEnabled()) {
log.warn("Could not create lock file", ex); //$NON-NLS-1$
}
}
if (log.isTraceEnabled()) log.trace(HelperLog.methodExit());
}
private void setupLanguage() {
if (log.isTraceEnabled()) log.trace(HelperLog.methodStart());
final Language language = data.getLanguage();
if (null != language) {
localizer.setLocale(language.getLocale());
}
if (log.isTraceEnabled()) log.trace(HelperLog.methodExit());
}
/*
* Implemented methods
*/
@Override
public void run() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
super.run();
setupLanguage();
lock();
controllerModule.load();
getView().display();
controllerModule.run();
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void exit(final int returnCode) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(returnCode));
controllerModule.exit(returnCode);
// Store data from the Tyr application
final File tda = new File(dirData, getModel().getUUID() + FileType.DATA.getExtension());
try {
HelperXml.serialize(tda, data);
} catch (JAXBException ex) {
log.error("Could not save the Tyr data file " + HelperString.quote(tda.getAbsolutePath()), ex); //$NON-NLS-1$
}
super.exit(returnCode);
}
@Override
public void addWorker(final Worker worker, final String title, final String text, final ScalableIcon icon) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(worker, title, text, icon));
getView().addWorker(worker, title, text, icon);
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void removeWorker(final Worker worker) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(worker));
getView().removeWorker(worker);
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void displayMessage(final String title, final String message, final MessageType messageType) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(title, message, messageType));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getView().getTrayIcon().displayMessage(title, message, messageType);
}
});
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void setPopupEnabled(final boolean isEnabled) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(isEnabled));
controllerModule.setPopupEnabled(isEnabled);
if (isEnabled) {
getView().getTrayIcon().setJPopupMenu(getView().getPopup());
} else {
getView().getTrayIcon().setJPopupMenu(null);
}
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public ImageIcon getScaledIcon(final ScalableIcon icon) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(icon));
if (null == icon) {
throw new RuntimeExceptionIsNull("icon"); //$NON-NLS-1$
}
ImageIcon result = null;
if (0 != data.getIconSize().width) {
result = 48 < data.getIconSize().width ? new ImageIcon(HelperImage.getScaledImage(icon.getIconLarge(),
data.getIconSize())) : new ImageIcon(HelperImage.getScaledImage(icon.getIcon(), data.getIconSize()));
}
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(result));
return result;
}
@Override
public ControllerModule getControllerModule() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(controllerModule));
return controllerModule;
}
@Override
public TyrData getData() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(data));
return data;
}
/*
* Inner classes
*/
final class Handler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(final Thread t, final Throwable ex) {
log.error("Uncaught exception occurred", ex); //$NON-NLS-1$
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setPopupEnabled(false);
final ErrorInfo info = new ErrorInfo(
getModel().getName() + " - " + localizer.getValue(RES_EXCEPTION), localizer.getValue(RES_EXCEPTION_UNCAUGHT), null, null, ex, Level.SEVERE, null); //$NON-NLS-1$
JXErrorPane.showDialog(getView().getOwner(), info);
setPopupEnabled(true);
System.exit(255);
}
});
if (log.isWarnEnabled()) log.warn(HelperLog.applicationExit(255));
}
}
}