blob: 159ee0d120ca3e4dfad54f7d0dddc836b712eef5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.ui.internal.wizards.conversion.java;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jpt.common.core.internal.utility.WorkspaceRunnableAdapter;
import org.eclipse.jpt.common.core.resource.ProjectResourceLocator;
import org.eclipse.jpt.common.ui.internal.util.SWTUtil;
import org.eclipse.jpt.common.ui.internal.utility.SynchronousUiCommandExecutor;
import org.eclipse.jpt.common.ui.internal.utility.swt.SWTTools;
import org.eclipse.jpt.common.utility.command.Command;
import org.eclipse.jpt.common.utility.internal.StringTools;
import org.eclipse.jpt.common.utility.internal.Tools;
import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel;
import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.jpa.core.JpaProject;
import org.eclipse.jpt.jpa.core.JpaProjectManager;
import org.eclipse.jpt.jpa.core.JpaStructureNode;
import org.eclipse.jpt.jpa.core.JptJpaCorePlugin;
import org.eclipse.jpt.jpa.core.context.MappingFile;
import org.eclipse.jpt.jpa.core.context.orm.EntityMappings;
import org.eclipse.jpt.jpa.core.context.persistence.MappingFileRef;
import org.eclipse.jpt.jpa.core.context.persistence.Persistence;
import org.eclipse.jpt.jpa.core.context.persistence.PersistenceUnit;
import org.eclipse.jpt.jpa.core.context.persistence.PersistenceXml;
import org.eclipse.jpt.jpa.core.resource.xml.JpaXmlResource;
import org.eclipse.jpt.jpa.ui.JptJpaUiPlugin;
import org.eclipse.jpt.jpa.ui.internal.JptUiMessages;
import org.eclipse.jpt.jpa.ui.internal.jface.XmlMappingFileViewerFilter;
import org.eclipse.jpt.jpa.ui.internal.wizards.SelectMappingFileDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
public abstract class JavaMetadataConversionWizardPage
extends WizardPage
{
/**
* The JPA project can<em>not</em> be <code>null</code>.
*/
protected final JpaProject jpaProject;
/**
* The persistence unit can be <code>null</code>.
*/
protected final PersistenceUnit persistenceUnit;
protected final boolean noConvertibleJavaMetadata;
private final SimplePropertyValueModel<String> mappingFileNameModel = new SimplePropertyValueModel<String>();
private static final String PAGE_NAME = "JavaMetadataConversion"; //$NON-NLS-1$
public static final String HELP_CONTEXT_ID = JptJpaUiPlugin.PLUGIN_ID_ + PAGE_NAME;
protected JavaMetadataConversionWizardPage(JpaProject jpaProject, String title, String description) {
super(PAGE_NAME);
if (jpaProject == null) {
throw new NullPointerException();
}
this.jpaProject = jpaProject;
this.persistenceUnit = this.buildPersistenceUnit();
this.mappingFileNameModel.setValue(this.buildDefaultMappingFileName());
this.setTitle(title);
this.setDescription(description);
this.noConvertibleJavaMetadata = this.hasNoConvertibleJavaMetadata();
}
/**
* Return the JPA project's <em>first</em> persistence unit.
*/
protected PersistenceUnit buildPersistenceUnit() {
PersistenceXml persistenceXml = this.jpaProject.getRootContextNode().getPersistenceXml();
if (persistenceXml == null) {
return null;
}
Persistence persistence = persistenceXml.getPersistence();
if (persistence == null) {
return null;
}
Iterator<PersistenceUnit> stream = persistence.getPersistenceUnits().iterator();
return stream.hasNext() ? stream.next() : null;
}
protected String buildDefaultMappingFileName() {
IPath path = this.buildDefaultMappingFilePath();
return (path == null) ? null : path.toString();
}
protected IPath buildDefaultMappingFilePath() {
IPath path = this.getFirstMappingFilePath();
return (path == null) ? this.getDefaultMappingFileRuntimPath() : this.convertToRuntimPath(path);
}
protected abstract IPath getDefaultMappingFileRuntimPath();
/**
* Return the JPA project's <em>first</em> mapping file with the
* {@link #getMappingFileContentType() default content type}.
*/
protected IPath getFirstMappingFilePath() {
if (this.persistenceUnit == null) {
return null;
}
for (MappingFileRef ref : this.persistenceUnit.getMappingFileRefs()) {
MappingFile mappingFile = ref.getMappingFile();
if (mappingFile != null) {
IFile file = mappingFile.getXmlResource().getFile();
if (this.jpaProject.getJpaFile(file).getContentType().isKindOf(this.getMappingFileContentType())) {
return file.getFullPath();
}
}
}
return null;
}
protected IContentType getMappingFileContentType() {
return JptJpaCorePlugin.MAPPING_FILE_CONTENT_TYPE;
}
protected IPath convertToRuntimPath(IPath path) {
ProjectResourceLocator locator = (ProjectResourceLocator) this.jpaProject.getProject().getAdapter(ProjectResourceLocator.class);
return locator.getRuntimePath(path);
}
// ********** missing metadata warning message **********
protected final boolean hasNoConvertibleJavaMetadata() {
return ! this.hasConvertibleJavaMetadata();
}
protected final boolean hasConvertibleJavaMetadata() {
return (this.persistenceUnit != null) && this.hasConvertibleJavaMetadata_();
}
/**
* Pre-condition: the {@link #persistenceUnit persistence unit}
* is not <code>null</code>.
*/
protected abstract boolean hasConvertibleJavaMetadata_();
protected abstract String getMissingJavaMetadataWarningMessage();
// ********** UI control **********
public void createControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout());
Label pageLabel = new Label(composite, SWT.NONE);
pageLabel.setText(JptUiMessages.JavaMetadataConversionWizardPage_label);
pageLabel.setBounds(3, 230, 150, 12);
Control mappingFileControl = this.createMappingFileControl(composite);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.heightHint = 58;
data.grabExcessHorizontalSpace = true;
mappingFileControl.setLayoutData(data);
Text noteTextField = new Text(composite, SWT.READ_ONLY | SWT.MULTI);
noteTextField.setText(NLS.bind(JptUiMessages.JavaMetadataConversion_warning, StringTools.CR));
noteTextField.setFont(new Font(composite.getDisplay(), "Arial", 10, SWT.EMBEDDED)); //$NON-NLS-1$
noteTextField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 1, 1));
this.setControl(composite);
// no need to remove this listener, since we build the model ourselves
this.mappingFileNameModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.buildMappingFileNameListener());
this.setPageComplete(this.getOrmXmlResource() != null);
}
protected Control createMappingFileControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout(3, false));
Link link = new Link(composite, SWT.LEFT);
link.setText(JptUiMessages.JavaMetadataConversionWizardPage_newMappingFileLink);
link.setToolTipText(JptUiMessages.JavaMetadataConversionWizardPage_newMappingFileLinkToolTip);
link.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
link.addSelectionListener(this.buildMappingFileLinkListener());
Text nameTextField = new Text(composite, SWT.SEARCH | SWT.SINGLE);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.grabExcessVerticalSpace = true;
gridData.widthHint = 486;
gridData.heightHint = 14;
gridData.horizontalSpan = 1;
gridData.grabExcessHorizontalSpace = true;
nameTextField.setLayoutData(gridData);
SWTTools.bind(this.mappingFileNameModel, nameTextField);
Button browseButton = new Button(composite, SWT.CENTER);
browseButton.setToolTipText(JptUiMessages.JavaMetadataConversionWizardPage_mappingFileBrowseButtonToolTip);
browseButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
browseButton.setText(JptUiMessages.JavaMetadataConversionWizardPage_mappingFileBrowseButton);
browseButton.addSelectionListener(this.buildMappingFileBrowseButtonListener());
return composite;
}
// ********** mapping file name listener **********
protected PropertyChangeListener buildMappingFileNameListener() {
return new MappingFileNameListener();
}
protected class MappingFileNameListener
extends PropertyChangeAdapter
{
@Override
public void propertyChanged(PropertyChangeEvent event) {
JavaMetadataConversionWizardPage.this.validate();
}
}
protected void validate() {
String errorMessage = this.buildErrorMessage();
this.setErrorMessage(errorMessage);
this.setPageComplete(errorMessage == null);
}
protected String buildErrorMessage() {
JpaXmlResource ormXmlResource = this.getOrmXmlResource();
if (ormXmlResource == null) {
return JptUiMessages.JavaMetadataConversion_mappingFileDoesNotExist;
}
String mappingFileVersion = ormXmlResource.getVersion();
String jpaProjectVersion = this.getJpaProjectVersion();
if (Tools.valuesAreDifferent(mappingFileVersion, jpaProjectVersion)) {
return NLS.bind(JptUiMessages.JavaMetadataConversion_mappingFileVersionIsInvalid, mappingFileVersion, jpaProjectVersion);
}
if (this.getEntityMappings(ormXmlResource) == null) {
return JptUiMessages.JavaMetadataConversion_mappingFileNotListedInPersistenceXml;
}
if (this.noConvertibleJavaMetadata) {
this.setMessage(this.getMissingJavaMetadataWarningMessage(), IMessageProvider.WARNING);
}
return null;
}
protected EntityMappings getEntityMappings() {
return this.getEntityMappings(this.getOrmXmlResource());
}
protected EntityMappings getEntityMappings(JpaXmlResource ormXmlResource) {
return (ormXmlResource == null) ? null : this.getEntityMappings_(ormXmlResource);
}
/**
* Pre-condition: the specified <code>orm.xml</code> resource is
* not <code>null</code>.
*/
protected EntityMappings getEntityMappings_(JpaXmlResource ormXmlResource) {
Iterator<JpaStructureNode> nodes = this.jpaProject.getJpaFile(ormXmlResource.getFile()).getRootStructureNodes().iterator();
return nodes.hasNext() ? (EntityMappings) nodes.next() : null;
}
protected JpaXmlResource getOrmXmlResource() {
IPath mappingFilePath = this.getMappingFilePath();
return (mappingFilePath == null) ? null : this.jpaProject.getMappingFileXmlResource(mappingFilePath);
}
protected IPath getMappingFilePath() {
String mappingFileName = this.getMappingFileName();
return StringTools.stringIsEmpty(mappingFileName) ? null : new Path(mappingFileName);
}
protected String getMappingFileName() {
String mappingFileName = this.mappingFileNameModel.getValue();
return StringTools.stringIsEmpty(mappingFileName) ? null : mappingFileName;
}
protected String getJpaProjectVersion(){
return this.jpaProject.getJpaPlatform().getJpaVersion().getVersion();
}
// ********** mapping file link listener **********
protected SelectionListener buildMappingFileLinkListener() {
return new MappingFileLinkListener();
}
protected class MappingFileLinkListener
extends SelectionAdapter
{
@Override
public void widgetSelected(SelectionEvent event) {
JavaMetadataConversionWizardPage.this.mappingFileLinkPressed();
}
}
protected void mappingFileLinkPressed() {
IPath path = this.openNewMappingFileWizard();
if (path != null) {
this.mappingFileNameModel.setValue(path.toString());
// revalidate because the mapping file may have been created
this.validate();
}
}
protected abstract IPath openNewMappingFileWizard();
// ********** mapping file browse button listener **********
protected SelectionListener buildMappingFileBrowseButtonListener() {
return new MappingFileBrowseButtonListener();
}
protected class MappingFileBrowseButtonListener
extends SelectionAdapter
{
@Override
public void widgetSelected(SelectionEvent event) {
JavaMetadataConversionWizardPage.this.mappingFileBrowseButtonPressed();
}
}
protected void mappingFileBrowseButtonPressed() {
IProject project = this.jpaProject.getProject();
SelectMappingFileDialog dialog = this.buildSelectMappingFileDialog();
dialog.setTitle(JptUiMessages.SelectMappingFileDialog_title);
dialog.setMessage(JptUiMessages.SelectMappingFileDialog_message);
dialog.addFilter(this.buildSelectMappingFileDialogViewerFilter());
dialog.setInput(project);
JpaXmlResource resource = this.getOrmXmlResource();
IFile file = (resource == null) ? null : resource.getFile();
if (file != null) {
dialog.setInitialSelection(file);
}
if (dialog.open() == Window.OK) {
this.mappingFileNameModel.setValue(dialog.getChosenName());
// revalidate because the mapping file may have been created
this.validate();
}
}
protected abstract SelectMappingFileDialog buildSelectMappingFileDialog();
protected ViewerFilter buildSelectMappingFileDialogViewerFilter() {
return new XmlMappingFileViewerFilter(this.jpaProject, this.getMappingFileContentType());
}
// ********** finish => conversion **********
protected boolean performFinish() {
return this.isOKToConvert() && this.performFinish_();
}
protected boolean isOKToConvert() {
return this.hasConvertibleJavaMetadata();
}
protected boolean performFinish_() {
// true=fork; true=cancellable
try {
this.buildProgressMonitorDialog().run(true, true, this.buildConversionRunnable());
} catch (InvocationTargetException ex) {
JptJpaUiPlugin.log(ex);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // runnable cancelled
}
return true;
}
protected ProgressMonitorDialog buildProgressMonitorDialog() {
return new ProgressMonitorDialog(this.getShell());
}
protected IRunnableWithProgress buildConversionRunnable() {
return new ConversionRunnable(this.getConversionCommandStrategy(), this.getEntityMappings());
}
protected abstract ConversionCommand.Strategy getConversionCommandStrategy();
/**
* UI runnable
*/
protected static class ConversionRunnable
implements IRunnableWithProgress
{
protected final ConversionCommand.Strategy conversionCommandStrategy;
protected final EntityMappings entityMappings;
protected ConversionRunnable(ConversionCommand.Strategy conversionCommandStrategy, EntityMappings entityMappings) {
super();
if ((conversionCommandStrategy == null) || (entityMappings == null)) {
throw new NullPointerException();
}
this.conversionCommandStrategy = conversionCommandStrategy;
this.entityMappings = entityMappings;
}
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
IWorkspace ws = ResourcesPlugin.getWorkspace();
ISchedulingRule rule = ws.getRuleFactory().modifyRule(ws.getRoot());
try {
ws.run(this.buildWorkspaceRunnable(), rule, IWorkspace.AVOID_UPDATE, monitor);
} catch (CoreException ex) {
if (ex.getStatus().getSeverity() == IStatus.CANCEL) {
throw new InterruptedException(); // ???
}
throw new InvocationTargetException(ex);
}
}
protected void run_(IProgressMonitor monitor) {
Command conversionCommand = new ConversionCommand(this.conversionCommandStrategy, this.entityMappings, monitor);
try {
this.getJpaProjectManager().execute(conversionCommand, SynchronousUiCommandExecutor.instance());
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new OperationCanceledException();
}
}
protected IWorkspaceRunnable buildWorkspaceRunnable() {
return new WorkspaceRunnable();
}
protected JpaProjectManager getJpaProjectManager() {
return (JpaProjectManager) this.getWorkspace().getAdapter(JpaProjectManager.class);
}
protected IWorkspace getWorkspace() {
return this.entityMappings.getParent().getXmlResource().getFile().getWorkspace();
}
@Override
public String toString() {
return StringTools.buildToStringFor(this);
}
/**
* Workspace runnable
*/
protected class WorkspaceRunnable
extends WorkspaceRunnableAdapter
{
@Override
public void run(IProgressMonitor monitor) throws CoreException {
ConversionRunnable.this.run_(monitor);
}
}
}
/**
* Conversion command.
* This is dispatched to the JPA project manager.
*/
protected static class ConversionCommand
implements Command
{
protected final Strategy strategy;
protected final EntityMappings entityMappings;
protected final IProgressMonitor monitor;
protected ConversionCommand(Strategy strategy, EntityMappings entityMappings, IProgressMonitor monitor) {
super();
if ((strategy == null) || (entityMappings == null) || (monitor == null)) {
throw new NullPointerException();
}
this.strategy = strategy;
this.entityMappings = entityMappings;
this.monitor = monitor;
}
public void execute() {
this.strategy.execute(this.entityMappings, this.monitor);
this.getOrmXmlResource().save();
this.saveAllFiles();
this.openEditorOnMappingFile();
}
protected JpaXmlResource getOrmXmlResource() {
return this.entityMappings.getParent().getXmlResource();
}
protected void saveAllFiles() {
SWTUtil.asyncExec(this.buildSaveAllFilesRunnable());
}
protected Runnable buildSaveAllFilesRunnable() {
return new SaveFilesRunnable(this.getProject());
}
protected IProject getProject() {
return this.entityMappings.getJpaProject().getProject();
}
protected void openEditorOnMappingFile() {
SWTUtil.asyncExec(this.buildOpenEditorOnMappingFileRunnable());
}
protected Runnable buildOpenEditorOnMappingFileRunnable() {
return new OpenEditorRunnable(this.getOrmXmlResource().getFile());
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.entityMappings.getPersistenceUnit());
}
public interface Strategy {
void execute(EntityMappings entityMappings, IProgressMonitor monitor);
}
}
// ********** save files **********
protected static class SaveFilesRunnable
implements Runnable
{
protected final IResource[] resourceRoots;
protected SaveFilesRunnable(IProject project) {
super();
if (project == null) {
throw new NullPointerException();
}
this.resourceRoots = new IResource[] { project };
}
public void run() {
IDE.saveAllEditors(this.resourceRoots, false); // false=no confirm
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.resourceRoots[0]);
}
}
// ********** open editor **********
protected static class OpenEditorRunnable
implements Runnable
{
protected final IFile file;
protected OpenEditorRunnable(IFile file) {
super();
if (file == null) {
throw new NullPointerException();
}
this.file = file;
}
public void run() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
IDE.openEditor(page, this.file);
} catch (PartInitException ex) {
JptJpaUiPlugin.log(ex);
}
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.file);
}
}
// ********** misc **********
@Override
public final void performHelp() {
PlatformUI.getWorkbench().getHelpSystem().displayHelp(HELP_CONTEXT_ID);
}
}