/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.module;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.openflexo.ApplicationContext;
import org.openflexo.GeneralPreferences;
import org.openflexo.action.SubmitDocumentationAction;
import org.openflexo.ch.FCH;
import org.openflexo.components.ProgressWindow;
import org.openflexo.components.SaveProjectsDialog;
import org.openflexo.drm.DocResourceManager;
import org.openflexo.foundation.FlexoEditor;
import org.openflexo.foundation.rm.SaveResourceExceptionList;
import org.openflexo.foundation.utils.OperationCancelledException;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.module.external.ExternalCEDModule;
import org.openflexo.module.external.ExternalDMModule;
import org.openflexo.module.external.ExternalIEModule;
import org.openflexo.module.external.ExternalOEModule;
import org.openflexo.module.external.ExternalWKFModule;
import org.openflexo.module.external.IModuleLoader;
import org.openflexo.prefs.FlexoPreferences;
import org.openflexo.swing.FlexoSwingUtils;
import org.openflexo.toolbox.HasPropertyChangeSupport;
import org.openflexo.view.controller.FlexoController;
import org.openflexo.view.controller.model.ControllerModel;
import org.openflexo.view.menu.ToolsMenu;
/**
* This class handles computation of available modules and modules loading. Only one instance of this class is instancied, and available all
* over Flexo Application Suite. This is the ONLY ONE WAY to access external modules from a given module.
*
* @author sguerin
*/
public class ModuleLoader implements IModuleLoader, HasPropertyChangeSupport {
private static final Logger logger = Logger.getLogger(ModuleLoader.class.getPackage().getName());
public static final String ACTIVE_MODULE = "activeModule";
public static final String MODULE_LOADED = "moduleLoaded";
public static final String MODULE_UNLOADED = "moduleUnloaded";
public static final String MODULE_ACTIVATED = "moduleActivated";
private boolean allowsDocSubmission;
private FlexoModule activeModule = null;
private Module activatingModule;
private WeakReference<FlexoEditor> lastActiveEditor;
private class ActiveEditorListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(ControllerModel.CURRENT_EDITOR)) {
FlexoEditor newEditor = (FlexoEditor) evt.getNewValue();
if (newEditor != null) {
lastActiveEditor = new WeakReference<FlexoEditor>(newEditor);
}
}
}
}
private ActiveEditorListener activeEditorListener = new ActiveEditorListener();
/**
* Hashtable where are stored Module instances (instance of FlexoModule associated to a Module instance key.
*/
private Map<Module, FlexoModule> _modules = new Hashtable<Module, FlexoModule>();
private final ApplicationContext applicationContext;
private PropertyChangeSupport propertyChangeSupport;
public ModuleLoader(ApplicationContext applicationContext) {
super();
this.applicationContext = applicationContext;
this.propertyChangeSupport = new PropertyChangeSupport(this);
}
@Override
public PropertyChangeSupport getPropertyChangeSupport() {
return propertyChangeSupport;
}
@Override
public String getDeletedProperty() {
return null;
}
public boolean allowsDocSubmission() {
return allowsDocSubmission;
}
public void setAllowsDocSubmission(boolean allowsDocSubmission) {
this.allowsDocSubmission = allowsDocSubmission;
SubmitDocumentationAction.actionType.setAllowsDocSubmission(allowsDocSubmission);
}
public FlexoEditor getLastActiveEditor() {
if (lastActiveEditor != null) {
return lastActiveEditor.get();
}
return null;
}
@Override
public FlexoModule getActiveModule() {
return activeModule;
}
/**
* Return all loaded modules as an Enumeration of FlexoModule instances
*
* @return Enumeration
*/
public Enumeration<FlexoModule> loadedModules() {
return new Vector<FlexoModule>(_modules.values()).elements();
}
public int getLoadedModuleCount() {
return _modules.size();
}
/**
* Return all unloaded modules but available modules as a Vector of Module instances
*
* @return Vector
*/
public List<Module> unloadedButAvailableModules() {
List<Module> returned = new ArrayList<Module>(getAvailableModules());
for (Enumeration<FlexoModule> e = loadedModules(); e.hasMoreElements();) {
returned.remove(e.nextElement().getModule());
}
return returned;
}
public void unloadModule(Module module) {
if (isLoaded(module)) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Unloading module " + module.getName());
}
FlexoModule flexoModule = _modules.remove(module);
if (activeModule == flexoModule) {
activeModule = null;
}
getPropertyChangeSupport().firePropertyChange(MODULE_UNLOADED, module, null);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Unable to unload unloaded module " + module.getName());
}
}
}
private FlexoModule loadModule(Module module) throws Exception {
boolean createProgress = !ProgressWindow.hasInstance();
if (createProgress) {
ProgressWindow.showProgressWindow(FlexoLocalization.localizedForKey("loading_module") + " " + module.getLocalizedName(), 8);
}
ProgressWindow.setProgressInstance(FlexoLocalization.localizedForKey("loading_module") + " " + module.getLocalizedName());
FlexoModule flexoModule = module.getConstructor().newInstance(new Object[] { applicationContext });
_modules.put(module, flexoModule);
if (activatingModule == module) {
activeModule = flexoModule;
}
doInternalLoadModule(flexoModule);
if (createProgress) {
ProgressWindow.hideProgressWindow();
}
propertyChangeSupport.firePropertyChange(MODULE_LOADED, null, module);
return flexoModule;
}
private class ModuleLoaderCallable implements Callable<FlexoModule> {
private final FlexoModule module;
public ModuleLoaderCallable(FlexoModule module) {
this.module = module;
}
@Override
public FlexoModule call() throws Exception {
if (logger.isLoggable(Level.INFO)) {
logger.info("Loading module " + module.getName());
}
module.initModule();
FCH.ensureHelpEntryForModuleHaveBeenCreated(module);
return module;
}
}
private FlexoModule doInternalLoadModule(FlexoModule module) throws Exception {
ModuleLoaderCallable loader = new ModuleLoaderCallable(module);
return FlexoSwingUtils.syncRunInEDT(loader);
}
public boolean isAvailable(Module module) {
return getAvailableModules().contains(module);
}
public List<Module> getAvailableModules() {
return Modules.getInstance().getAvailableModules();
}
public List<Module> getLoadedModules() {
List<Module> list = new ArrayList<Module>();
for (Module module : getAvailableModules()) {
if (isLoaded(module)) {
list.add(module);
}
}
return list;
}
public List<Module> getUnloadedModules() {
List<Module> list = new ArrayList<Module>();
for (Module module : getAvailableModules()) {
if (!isLoaded(module)) {
list.add(module);
}
}
return list;
}
public boolean isLoaded(Module module) {
return _modules.get(module) != null;
}
public boolean isActive(Module module) {
return getActiveModule() != null && getActiveModule().getModule() == module;
}
public boolean isActive(FlexoModule module) {
return getActiveModule() == module;
}
public FlexoModule getModuleInstance(Module module) throws ModuleLoadingException {
if (module == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Trying to get module instance for module null");
}
return null;
}
if (isAvailable(module)) {
if (isLoaded(module)) {
return _modules.get(module);
} else {
try {
return loadModule(module);
} catch (Exception e) {
ProgressWindow.hideProgressWindow();
e.printStackTrace();
throw new ModuleLoadingException(module);
}
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Sorry, module " + module.getName() + " not available.");
}
return null;
}
}
public ExternalWKFModule getWKFModule() throws ModuleLoadingException {
return (ExternalWKFModule) getModuleInstance(Module.WKF_MODULE);
}
public ExternalIEModule getIEModule() throws ModuleLoadingException {
return (ExternalIEModule) getModuleInstance(Module.IE_MODULE);
}
public ExternalDMModule getDMModule() throws ModuleLoadingException {
return (ExternalDMModule) getModuleInstance(Module.DM_MODULE);
}
public ExternalCEDModule getCEDModule() throws ModuleLoadingException {
return (ExternalCEDModule) getModuleInstance(Module.VPM_MODULE);
}
public ExternalOEModule getOEModule() throws ModuleLoadingException {
return (ExternalOEModule) getModuleInstance(Module.VE_MODULE);
}
private boolean ignoreSwitch = false;
public FlexoModule switchToModule(final Module module) throws ModuleLoadingException {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
switchToModule(module);
} catch (ModuleLoadingException e) {
e.printStackTrace();
}
}
});
return null;
}
if (ignoreSwitch || activeModule != null && activeModule.getModule() == module) {
return activeModule;
}
ignoreSwitch = true;
activatingModule = module;
try {
if (logger.isLoggable(Level.INFO)) {
logger.info("Switch to module " + module.getName());
}
FlexoModule moduleInstance = getModuleInstance(module);
if (moduleInstance != null) {
FlexoModule old = activeModule;
if (activeModule != null) {
activeModule.getController().getControllerModel().getPropertyChangeSupport()
.removePropertyChangeListener(ControllerModel.CURRENT_EDITOR, activeEditorListener);
activeModule.setAsInactive();
}
activeModule = moduleInstance;
moduleInstance.setAsActiveModule();
if (activeModule.getModule().requireProject()) {
activeModule.getController().getControllerModel().getPropertyChangeSupport()
.addPropertyChangeListener(ControllerModel.CURRENT_EDITOR, activeEditorListener);
}
getPropertyChangeSupport().firePropertyChange(ACTIVE_MODULE, old, activeModule);
return moduleInstance;
}
throw new ModuleLoadingException(module);
} finally {
activatingModule = null;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isLoaded(module)) {
_modules.get(module).getFlexoFrame().toFront();
}
ModuleLoader.this.ignoreSwitch = false;
}
});
}
}
/**
* Called for quitting. Ask if saving must be performed, and exit on request.
*
* @param askConfirmation
* if flexo must ask confirmation to the user
* @throws OperationCancelledException
* whenever user decide to not quit
*/
public void quit(boolean askConfirmation) throws OperationCancelledException {
if (askConfirmation) {
proceedQuit();
} else {
proceedQuitWithoutConfirmation();
}
}
private void proceedQuit() throws OperationCancelledException {
if (logger.isLoggable(Level.INFO)) {
logger.info("Exiting FLEXO Application Suite...");
}
if (applicationContext.getProjectLoader().someProjectsAreModified()) {
try {
saveModifiedProjects();
} catch (SaveResourceExceptionList e) {
e.printStackTrace();
if (FlexoController.confirm(FlexoLocalization.localizedForKey("error_during_saving") + "\n"
+ FlexoLocalization.localizedForKey("would_you_like_to_exit_anyway"))) {
proceedQuitWithoutConfirmation();
}
}
proceedQuitWithoutConfirmation();
} else {
if (FlexoController.confirm(FlexoLocalization.localizedForKey("really_quit"))) {
proceedQuitWithoutConfirmation();
} else {
throw new OperationCancelledException();
}
}
}
public void saveModifiedProjects() throws OperationCancelledException, SaveResourceExceptionList {
SaveProjectsDialog dialog = new SaveProjectsDialog(getActiveModule() != null ? getActiveModule().getController() : null,
applicationContext.getProjectLoader().getModifiedProjects());
if (dialog.isOk()) {
applicationContext.getProjectLoader().saveProjects(dialog.getSelectedProject());
} else { // CANCEL
if (logger.isLoggable(Level.INFO)) {
logger.info("Exiting FLEXO Application Suite... CANCELLED");
}
throw new OperationCancelledException();
}
}
private void proceedQuitWithoutConfirmation() {
if (activeModule != null) {
GeneralPreferences.setFavoriteModuleName(activeModule.getModule().getName());
FlexoPreferences.savePreferences(true);
}
for (Enumeration<FlexoModule> en = loadedModules(); en.hasMoreElements();) {
en.nextElement().closeWithoutConfirmation(false);
}
if (allowsDocSubmission() && !isAvailable(Module.DRE_MODULE) && DocResourceManager.instance().getSessionSubmissions().size() > 0) {
if (FlexoController.confirm(FlexoLocalization.localizedForKey("you_have_submitted_documentation_without_having_saved_report")
+ "\n" + FlexoLocalization.localizedForKey("would_you_like_to_save_your_submissions"))) {
new ToolsMenu.SaveDocSubmissionAction().actionPerformed(null);
}
}
if (isAvailable(Module.DRE_MODULE)) {
if (DocResourceManager.instance().needSaving()) {
if (FlexoController.confirm(FlexoLocalization.localizedForKey("documentation_resource_center_not_saved") + "\n"
+ FlexoLocalization.localizedForKey("would_you_like_to_save_documenation_resource_center"))) {
DocResourceManager.instance().save();
}
}
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Exiting FLEXO Application Suite... DONE");
}
System.exit(0);
}
@Override
public ExternalIEModule getIEModuleInstance() throws ModuleLoadingException {
return getIEModule();
}
@Override
public ExternalDMModule getDMModuleInstance() throws ModuleLoadingException {
return getDMModule();
}
@Override
public ExternalWKFModule getWKFModuleInstance() throws ModuleLoadingException {
return getWKFModule();
}
@Override
public ExternalCEDModule getVPMModuleInstance() throws ModuleLoadingException {
return getCEDModule();
}
@Override
public ExternalOEModule getVEModuleInstance() throws ModuleLoadingException {
return getOEModule();
}
@Override
public boolean isWKFLoaded() {
return isLoaded(Module.WKF_MODULE);
}
public void closeAllModulesWithoutConfirmation() {
for (FlexoModule module : new ArrayList<FlexoModule>(_modules.values())) {
module.closeWithoutConfirmation(false);
}
}
public void closeModule(FlexoModule module) {
module.close();
}
public boolean isLastLoadedModule(Module module) {
return _modules.size() == 1 && _modules.containsKey(module);
}
}