/**
* DataCleaner (community edition)
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.datacleaner.actions;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.inject.Inject;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import org.apache.commons.vfs2.FileObject;
import org.apache.metamodel.util.FileHelper;
import org.datacleaner.Version;
import org.datacleaner.bootstrap.WindowContext;
import org.datacleaner.configuration.DataCleanerConfiguration;
import org.datacleaner.extensions.ExtensionPackage;
import org.datacleaner.guice.DCModule;
import org.datacleaner.guice.DCModuleImpl;
import org.datacleaner.job.AnalysisJobMetadata;
import org.datacleaner.job.ComponentConfigurationException;
import org.datacleaner.job.JaxbJobReader;
import org.datacleaner.job.NoSuchComponentException;
import org.datacleaner.job.NoSuchDatastoreException;
import org.datacleaner.job.builder.AnalysisJobBuilder;
import org.datacleaner.result.AnalysisResult;
import org.datacleaner.user.UserPreferences;
import org.datacleaner.util.ChangeAwareObjectInputStream;
import org.datacleaner.util.FileFilters;
import org.datacleaner.util.VFSUtils;
import org.datacleaner.util.WidgetUtils;
import org.datacleaner.widgets.DCFileChooser;
import org.datacleaner.widgets.OpenAnalysisJobFileChooserAccessory;
import org.datacleaner.windows.AnalysisJobBuilderWindow;
import org.datacleaner.windows.OpenAnalysisJobAsTemplateDialog;
import org.datacleaner.windows.ResultWindow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.util.Providers;
/**
* ActionListener that will display an "Open file" dialog which allows the user
* to select a job file.
*
* The class also contains a few reusable static methods for opening job files
* without showing the dialog.
*/
public class OpenAnalysisJobActionListener implements ActionListener {
private static final Logger logger = LoggerFactory.getLogger(OpenAnalysisJobActionListener.class);
private final DataCleanerConfiguration _configuration;
private final AnalysisJobBuilderWindow _parentWindow;
private final WindowContext _windowContext;
private final DCModule _parentModule;
private final UserPreferences _userPreferences;
@Inject
public OpenAnalysisJobActionListener(final AnalysisJobBuilderWindow parentWindow,
final DataCleanerConfiguration configuration, final WindowContext windowContext,
final DCModule parentModule, final UserPreferences userPreferences) {
_parentWindow = parentWindow;
_configuration = configuration;
_windowContext = windowContext;
_parentModule = parentModule;
_userPreferences = userPreferences;
}
public static Injector open(final FileObject file, final DataCleanerConfiguration configuration,
final Injector injector) {
final UserPreferences userPreferences = injector.getInstance(UserPreferences.class);
final OpenAnalysisJobActionListener openAnalysisJobActionListener =
new OpenAnalysisJobActionListener(null, configuration, null, injector.getInstance(DCModule.class),
userPreferences);
return openAnalysisJobActionListener.openAnalysisJob(file);
}
@Override
public void actionPerformed(final ActionEvent event) {
final DCFileChooser fileChooser = new DCFileChooser(_userPreferences.getAnalysisJobDirectory());
final OpenAnalysisJobFileChooserAccessory accessory =
new OpenAnalysisJobFileChooserAccessory(_windowContext, _configuration, fileChooser,
Providers.of(this));
fileChooser.setAccessory(accessory);
fileChooser.addChoosableFileFilter(FileFilters.ANALYSIS_XML);
fileChooser.addChoosableFileFilter(FileFilters.ANALYSIS_RESULT_SER);
fileChooser.setFileFilter(FileFilters
.combined("DataCleaner analysis files", FileFilters.ANALYSIS_XML, FileFilters.ANALYSIS_RESULT_SER));
final int openFileResult = fileChooser.showOpenDialog(_parentWindow.toComponent());
if (openFileResult == JFileChooser.APPROVE_OPTION) {
final FileObject file = fileChooser.getSelectedFileObject();
openFile(file);
}
}
public void openFile(final FileObject file) {
if (file.getName().getBaseName().endsWith(FileFilters.ANALYSIS_RESULT_SER.getExtension())) {
openAnalysisResult(file, _parentModule);
} else {
final Injector injector = openAnalysisJob(file);
if (injector == null) {
// this may happen, in which case the error was signalled to the
// user already
return;
}
final AnalysisJobBuilderWindow window = injector.getInstance(AnalysisJobBuilderWindow.class);
window.open();
if (_parentWindow != null && !_parentWindow.isDatastoreSet()) {
_parentWindow.close();
}
}
}
public ResultWindow openAnalysisResult(final FileObject fileObject, final DCModule parentModule) {
final AnalysisResult analysisResult;
try {
final ChangeAwareObjectInputStream is =
new ChangeAwareObjectInputStream(fileObject.getContent().getInputStream());
try {
is.addClassLoader(ExtensionPackage.getExtensionClassLoader());
analysisResult = (AnalysisResult) is.readObject();
} finally {
FileHelper.safeClose(is);
}
} catch (final Exception e) {
throw new IllegalStateException(e);
}
final File file = VFSUtils.toFile(fileObject);
if (file != null) {
_userPreferences.setAnalysisJobDirectory(file.getParentFile());
_userPreferences.addRecentJobFile(fileObject);
}
final Injector injector = Guice.createInjector(new DCModuleImpl(parentModule, null) {
public FileObject getJobFilename() {
return fileObject;
}
@Override
public AnalysisResult getAnalysisResult() {
return analysisResult;
}
@Override
public AnalysisJobBuilder getAnalysisJobBuilder(final DataCleanerConfiguration configuration) {
return null;
}
});
final ResultWindow resultWindow = injector.getInstance(ResultWindow.class);
resultWindow.open();
return resultWindow;
}
/**
* Opens a job file
*
* @param file
* @return
*/
public Injector openAnalysisJob(final FileObject file) {
final JaxbJobReader reader = new JaxbJobReader(_configuration);
try {
final AnalysisJobBuilder ajb = reader.create(file);
return openAnalysisJob(file, ajb);
} catch (final NoSuchComponentException e) {
final String message;
if (Version.EDITION_COMMUNITY.equals(Version.getEdition())) {
message = "<html><p>Failed to open job because of a missing component:</p><pre>" + e.getMessage()
+ "</pre>"
+ "<p>This may happen if the job requires a <a href=\"https://datacleaner.org/editions\">Commercial Edition of DataCleaner</a>, or an extension that you do not have installed.</p></html>";
} else {
message = "<html>Failed to open job because of a missing component: " + e.getMessage() + "<br/><br/>"
+ "This may happen if the job requires an extension that you do not have installed.</html>";
}
WidgetUtils.showErrorMessage("Cannot open job", message);
return null;
} catch (final NoSuchDatastoreException e) {
if (_windowContext == null) {
// This can happen in case of single-datastore + job file
// bootstrapping of DC
throw e;
}
final AnalysisJobMetadata metadata = reader.readMetadata(file);
final int result = JOptionPane
.showConfirmDialog(null, e.getMessage() + "\n\nDo you wish to open this job as a template?",
"Error: " + e.getMessage(), JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE);
if (result == JOptionPane.OK_OPTION) {
final OpenAnalysisJobAsTemplateDialog dialog =
new OpenAnalysisJobAsTemplateDialog(_windowContext, _configuration, file, metadata,
Providers.of(this));
dialog.setVisible(true);
}
return null;
} catch (final ComponentConfigurationException e) {
final String message;
final Throwable cause = e.getCause();
if (cause != null) {
// check for causes of the mis-configuration. If there's a cause
// with a message, then show the message first and foremost
// (usually a validation error).
if (!Strings.isNullOrEmpty(cause.getMessage())) {
message = cause.getMessage();
} else {
message = e.getMessage();
}
} else {
message = e.getMessage();
}
WidgetUtils.showErrorMessage("Failed to validate job configuration", message, e);
return null;
} catch (final RuntimeException e) {
logger.error("Unexpected failure when opening job: {}", file, e);
throw e;
}
}
/**
* Opens a job builder
*
* @param fileObject
* @param ajb
* @return
*/
public Injector openAnalysisJob(final FileObject fileObject, final AnalysisJobBuilder ajb) {
final File file = VFSUtils.toFile(fileObject);
if (file != null) {
_userPreferences.setAnalysisJobDirectory(file.getParentFile());
_userPreferences.addRecentJobFile(fileObject);
}
return Guice.createInjector(new DCModuleImpl(_parentModule, ajb) {
public FileObject getJobFilename() {
return fileObject;
}
});
}
}