/*******************************************************************************
* Copyright (c) 2007, 2016 Borland Software Corporation and others.
*
* 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:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.ui.wizards.project;
import java.io.IOException;
import java.util.IdentityHashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.StatusDialog;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.m2m.internal.qvt.oml.common.ui.launch.UriChooserListener;
import org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil;
import org.eclipse.m2m.internal.qvt.oml.emf.util.mmregistry.WorkspaceMetamodelProvider;
import org.eclipse.m2m.internal.qvt.oml.emf.util.ui.choosers.ISourceChooser;
import org.eclipse.m2m.internal.qvt.oml.emf.util.ui.choosers.ResourceSourceChooser;
import org.eclipse.m2m.internal.qvt.oml.emf.util.urimap.MModelURIMapFactory;
import org.eclipse.m2m.internal.qvt.oml.emf.util.urimap.MModelURIMapPackage;
import org.eclipse.m2m.internal.qvt.oml.emf.util.urimap.MappingContainer;
import org.eclipse.m2m.internal.qvt.oml.emf.util.urimap.MetamodelURIMappingHelper;
import org.eclipse.m2m.internal.qvt.oml.emf.util.urimap.URIMapping;
import org.eclipse.m2m.internal.qvt.oml.ui.QVTUIPlugin;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
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.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PropertyPage;
public class QvtMetamodelMappingPage extends PropertyPage {
private TableViewer myTableViewer;
private IProject myProject;
private final ResourceSet rs;
private Button addButton;
private Button removeButton;
private Button editButton;
private Button removeAllButton;
private MappingContainer uriMap;
private Map<URIMapping, ProblemInfo> uriProblems;
private IStatus status = Status.OK_STATUS;
public QvtMetamodelMappingPage() {
super();
rs = new ResourceSetImpl();
}
@Override
public boolean performOk() {
performApply();
return status.isOK();
}
@Override
protected void performApply() {
if(uriMap != null) {
try {
status = Status.OK_STATUS;
Resource res = uriMap.eResource();
res.save(null);
doValidatePage();
} catch (IOException e) {
setErrorMessage(Messages.QvtMetamodelMappingPage_saveIOError);
status = new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, getErrorMessage(), e);
QVTUIPlugin.log(status);
return;
}
}
if(myProject != null) {
try {
IFile mappingFile = MetamodelURIMappingHelper.getMappingFileHandle(myProject);
mappingFile.refreshLocal(IResource.DEPTH_ZERO, null);
} catch (CoreException e) {
QVTUIPlugin.log(e.getStatus());
}
}
}
@Override
protected void performDefaults() {
if(uriMap != null) {
load();
}
}
@Override
public void dispose() {
super.dispose();
EmfUtil.cleanupResourceSet(rs);
}
protected boolean doValidatePage() {
setMessage(null);
setErrorMessage(null);
boolean valid = uriProblems == null || uriProblems.isEmpty();
if(!valid) {
setErrorMessage(Messages.QvtMetamodelMappingPage_invalidItemsInMappingTable);
}
return valid;
}
@Override
protected Control createContents(Composite parent) {
IProject project = getSelectedProject();
if(project == null) {
setValid(false);
setErrorMessage(Messages.QvtMetamodelMappingPage_no_QVT_projectSelected);
return new Composite(parent, SWT.BORDER);
}
Composite containerGroup = new Composite(parent, SWT.BORDER);
GridLayout containerLayout = new GridLayout(1, true);
containerLayout.marginHeight = containerLayout.marginWidth = 5;
containerGroup.setLayout(containerLayout);
int style = SWT.SINGLE | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION;
myTableViewer = new TableViewer(containerGroup, style);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
myTableViewer.getControl().setLayoutData(gridData);
myTableViewer.setContentProvider(new Contents());
myTableViewer.setLabelProvider(new Labels());
Table table = myTableViewer.getTable();
TableLayout layout = new TableLayout();
table.setLayout(layout);
table.setHeaderVisible(true);
table.setLinesVisible(true);
createColumn(table, layout, Messages.QvtMetamodelMappingPage_sourceURILabel);
createColumn(table, layout, Messages.QvtMetamodelMappingPage_targetURILabel);
createButtons(containerGroup);
myTableViewer.addSelectionChangedListener(
new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
boolean hasSelection = !event.getSelection().isEmpty();
removeButton.setEnabled(hasSelection);
editButton.setEnabled(hasSelection);
ProblemInfo problemInfo = getProblemInfo(getSelectedElement());
if(hasSelection && problemInfo != null && !problemInfo.isOK()) {
setErrorMessage(problemInfo.summaryMessage());
} else {
doValidatePage();
}
}
}
);
myTableViewer.getTable().addMouseTrackListener(new MouseTracker());
load();
return containerGroup;
}
private void createButtons(final Composite composite) {
Composite buttonArea = new Composite(composite, SWT.NONE);
buttonArea.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END | GridData.VERTICAL_ALIGN_CENTER));
GridLayout layout = new GridLayout(4, true);
layout.marginHeight = layout.marginWidth = 0;
buttonArea.setLayout(layout);
GridData data = new GridData(GridData.VERTICAL_ALIGN_END);
buttonArea.setLayoutData(data);
addButton = new Button(buttonArea, SWT.PUSH);
addButton.setFont(composite.getFont());
setButtonLayoutData(addButton);
addButton.setText(Messages.QvtMetamodelMappingPage_addBtnLabel);
addButton.addSelectionListener(new AddListener());
editButton = new Button(buttonArea, SWT.PUSH);
editButton.setEnabled(false);
editButton.setFont(composite.getFont());
editButton.setText(Messages.QvtMetamodelMappingPage_editBtnLabel);
editButton.addSelectionListener(new EditListener());
setButtonLayoutData(editButton);
removeButton = new Button(buttonArea, SWT.PUSH);
removeButton.setEnabled(false);
removeButton.setFont(composite.getFont());
removeButton.setText(Messages.QvtMetamodelMappingPage_removeBtnLabel);
removeButton.addSelectionListener(new RemoveListener());
setButtonLayoutData(removeButton);
removeAllButton = new Button(buttonArea, SWT.PUSH);
removeAllButton.setFont(composite.getFont());
removeAllButton.setText(Messages.QvtMetamodelMappingPage_removeAllBtnLabel);
removeAllButton.addSelectionListener(new RemoveAllListener());
setButtonLayoutData(editButton);
}
private IProject getSelectedProject() {
Object element = getElement();
if(element instanceof IProject) {
return (IProject) element;
} else if(element instanceof IAdaptable) {
return (IProject) ((IAdaptable) element).getAdapter(IProject.class);
} else {
return null;
}
}
private EPackage loadEPackage(String uriStr) {
try {
return WorkspaceMetamodelProvider.loadResourceMetamodel(uriStr, rs);
} catch (RuntimeException exc) {
// do nothing here
}
return null;
}
private void createColumn(Table table, TableLayout layout, String name) {
TableColumn nameColumn = new TableColumn(table, SWT.NONE);
layout.addColumnData(new ColumnWeightData(3, true));
nameColumn.setText(name);
}
private void loadMappings() {
assert myTableViewer != null;
// ensure mapping package gets into registry before loading
MModelURIMapPackage.eINSTANCE.getNsURI();
Resource res = MetamodelURIMappingHelper.createMappingResource(getSelectedProject());
try {
status = Status.OK_STATUS;
if(MetamodelURIMappingHelper.hasMappingResource(getSelectedProject())) {
res.load(null);
}
} catch (IOException e) {
setErrorMessage(Messages.QvtMetamodelMappingPage_loadIOError);
status = new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, getErrorMessage(), e);
QVTUIPlugin.log(status);
}
if(res.getContents().isEmpty()) {
uriMap = MetamodelURIMappingHelper.createNewMappings(res);
if(!super.isValid()) {
return;
}
} else {
uriMap = MetamodelURIMappingHelper.getMappings(res);
EObject root = res.getContents().get(0);
if(root instanceof MappingContainer) {
uriMap = (MappingContainer) root;
}
}
this.uriProblems = null;
if(status.isOK()) {
int pos = 0;
for (URIMapping nextMapping : uriMap.getMapping()) {
IStatus sourceStatus = validateURI(nextMapping.getSourceURI(), Messages.QvtMetamodelMappingPage_sourceURILabel);
IStatus targetStatus = validateTargetMappingURI(nextMapping.getTargetURI());
if(sourceStatus.isOK()) {
sourceStatus = getSourceUriAlreadyExistsStatus(nextMapping.getSourceURI(), pos++);
}
if(!sourceStatus.isOK() || !targetStatus.isOK()) {
if(uriProblems == null) {
uriProblems = new IdentityHashMap<URIMapping, ProblemInfo>();
}
uriProblems.put(nextMapping, new ProblemInfo(sourceStatus, targetStatus));
}
}
doValidatePage();
}
}
private IStatus getSourceUriAlreadyExistsStatus(String uri, int upToIndex) {
if(uri != null && uriMap != null) {
int pos = 0;
for (URIMapping mapping : uriMap.getMapping()) {
if(upToIndex >= 0 && pos++ == upToIndex) break;
if(uri.equals(mapping.getSourceURI())) {
String message = Messages.QvtMetamodelMappingPage_URIAlreadyMapped;
return new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, NLS.bind(message, uri));
}
}
}
return Status.OK_STATUS;
}
private static IStatus validateURI(String uriStr, String uriLabel) {
if(uriStr == null || uriStr.trim().length() == 0) {
String message = NLS.bind(Messages.QvtMetamodelMappingPage_noURISpecified, uriLabel);
return new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, message);
}
try {
URI.createURI(uriStr);
} catch (IllegalArgumentException e) {
String message = NLS.bind(Messages.QvtMetamodelMappingPage_invalidModelURI, uriLabel);
return new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, message + ":" + e.getLocalizedMessage()); //$NON-NLS-1$
}
return Status.OK_STATUS;
}
private IStatus validateTargetMappingURI(String uriStr) {
IStatus status = validateURI(uriStr, Messages.QvtMetamodelMappingPage_targetURILabel);
if(status.isOK()) {
URI targetUri = URI.createURI(uriStr);
if(!targetUri.isPlatformResource()) {
String message = NLS.bind(Messages.QvtMetamodelMappingPage_platformResourceURIexpected,
Messages.QvtMetamodelMappingPage_targetURILabel);
status = new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, message);
}
}
if(status.isOK()) {
EPackage loadEPackage = loadEPackage(uriStr);
if(loadEPackage == null || loadEPackage.getNsURI() == null) {
String message = NLS.bind(Messages.QvtMetamodelMappingPage_loadResourceMetamodelError, uriStr);
status = new Status(IStatus.ERROR, QVTUIPlugin.PLUGIN_ID, message);
}
}
return status;
}
private class Labels extends LabelProvider implements ITableLabelProvider {
public String getColumnText(Object element, int columnIndex) {
if(element instanceof URIMapping) {
URIMapping mapping = (URIMapping) element;
return columnIndex == 0 ? mapping.getSourceURI() : mapping.getTargetURI();
}
return null;
}
public Image getColumnImage(Object element, int columnIndex) {
if (!(element instanceof URIMapping)) {
return null;
}
URIMapping mapping = (URIMapping)element;
ProblemInfo problemInfo = getProblemInfo(mapping);
if(problemInfo != null && !problemInfo.isOK()) {
IStatus status = columnIndex == 0 ? problemInfo.sourceUriStatus : problemInfo.targetUriStatus;
String imageName = status.isOK() ? null : ISharedImages.IMG_OBJS_INFO_TSK;
switch (status.getSeverity()) {
case IStatus.ERROR:
imageName = ISharedImages.IMG_OBJS_ERROR_TSK;
break;
case IStatus.WARNING:
imageName = ISharedImages.IMG_OBJS_ERROR_TSK;
break;
}
if(imageName != null) {
return PlatformUI.getWorkbench().getSharedImages().getImage(imageName);
}
}
return null;
}
}
private static class Contents implements IStructuredContentProvider {
public Object[] getElements(Object inputElement) {
if(inputElement instanceof MappingContainer) {
return ((MappingContainer)inputElement).getMapping().toArray();
}
return new Object[0];
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
private void load() {
myProject = getSelectedProject();
if(myProject != null) {
loadMappings();
if(uriMap != null && myTableViewer != null) {
myTableViewer.setInput(uriMap);
}
}
}
private ProblemInfo getProblemInfo(URIMapping mapping) {
return uriProblems != null ? uriProblems.get(mapping) : null;
}
private class MouseTracker implements MouseTrackListener {
public void mouseExit(MouseEvent e) {
resetToolTip();
}
public void mouseEnter(MouseEvent e) {
resetToolTip();
}
public void mouseHover(MouseEvent e) {
TableItem item = myTableViewer.getTable().getItem(new Point(e.x, e.y));
if(item != null) {
ProblemInfo problemInfo = getProblemInfo((URIMapping)item.getData());
if(problemInfo != null) {
String tooltip = buildTooltipText(problemInfo);
myTableViewer.getControl().setToolTipText(tooltip);
}
} else {
resetToolTip();
}
}
void resetToolTip() {
myTableViewer.getControl().setToolTipText(null);
}
String buildTooltipText(ProblemInfo problemInfo) {
StringBuilder buf = new StringBuilder();
if(!problemInfo.sourceUriStatus.isOK()) {
buf.append("<b>Source</b> URI:").append('\n'); //$NON-NLS-1$
buf.append('-').append(problemInfo.sourceUriStatus.getMessage());
}
if(!problemInfo.targetUriStatus.isOK()) {
if(buf.length() > 0) buf.append('\n');
buf.append("Target URI:").append('\n'); //$NON-NLS-1$
buf.append('-').append(problemInfo.targetUriStatus.getMessage());
}
return buf.toString();
}
}
private URIMapping getSelectedElement() {
ISelection sel = myTableViewer.getSelection();
if(!sel.isEmpty()) {
return (URIMapping)((IStructuredSelection)sel).getFirstElement();
}
return null;
}
private class AddListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
URIMappingDialog dlg = new URIMappingDialog(getShell());
if(IDialogConstants.OK_ID == dlg.open()) {
URIMapping mapping = dlg.data;
if(mapping != null) {
uriMap.getMapping().add(mapping);
myTableViewer.add(mapping);
doValidatePage();
}
}
}
}
private class RemoveListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
URIMapping mapping = getSelectedElement();
if(mapping != null) {
myTableViewer.remove(mapping);
if(uriProblems != null) {
uriProblems.remove(mapping);
}
uriMap.getMapping().remove(mapping);
}
doValidatePage();
}
}
private class EditListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
URIMapping mapping = getSelectedElement();
if(mapping != null) {
if(IDialogConstants.OK_ID == new URIMappingDialog(getShell(), mapping).open()) {
if(uriProblems != null) uriProblems.remove(mapping);
myTableViewer.refresh();
doValidatePage();
}
}
}
}
private class RemoveAllListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
myTableViewer.setItemCount(0);
uriMap.getMapping().clear();
if(uriProblems != null) {
uriProblems.clear();
}
doValidatePage();
}
}
private static class ProblemInfo {
final IStatus sourceUriStatus;
final IStatus targetUriStatus;
ProblemInfo(IStatus sourceUriStatus, IStatus targetUriStatus) {
if(sourceUriStatus == null || targetUriStatus == null) {
throw new NullPointerException();
}
this.sourceUriStatus = sourceUriStatus;
this.targetUriStatus = targetUriStatus;
}
boolean isOK() {
return sourceUriStatus.isOK() && targetUriStatus.isOK();
}
String summaryMessage() {
return targetUriStatus.isOK() ? sourceUriStatus.getMessage() : targetUriStatus.getMessage();
}
}
private class URIMappingDialog extends StatusDialog {
private URIMapping data;
private Text modelURIText;
private Text targetURIText;
public URIMappingDialog(Shell parent) {
this(parent, null);
}
public URIMappingDialog(Shell parent, URIMapping mappingData) {
super(parent);
this.data = mappingData;
String title = Messages.QvtMetamodelMappingPage_addNewMappingTitle;
if(data != null) {
title = Messages.QvtMetamodelMappingPage_editMappingTitle;
} else {
// Add new mode
data = MModelURIMapFactory.eINSTANCE.createURIMapping();
}
setTitle(title);
setHelpAvailable(false);
setStatusLineAboveButtons(true);
}
@Override
protected void buttonPressed(int buttonId) {
if(buttonId == IDialogConstants.OK_ID) {
data.setSourceURI(modelURIText.getText());
data.setTargetURI(targetURIText.getText());
}
super.buttonPressed(buttonId);
}
@Override
protected Control createContents(Composite parent) {
Control control = super.createContents(parent);
updateStatus(getStatus());
return control;
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite)super.createDialogArea(parent);
createResourceURIControls(parent, 2);
if(data.eContainer() == null) {
// when adding new one, intially empty, take as invalid
updateStatus(validateURI(modelURIText.getText(), Messages.QvtMetamodelMappingPage_sourceURILabel));
modelURIText.setFocus();
targetURIText.setText("platform:/resource/*"); //$NON-NLS-1$
} else {
modelURIText.setText(safeURIValue(data.getSourceURI()));
targetURIText.setText(safeURIValue(data.getTargetURI()));
}
return composite;
}
protected void createResourceURIControls(Composite parent, int columnsPerLine) {
Composite inner= new Composite(parent, SWT.NONE);
GridLayout layout= new GridLayout();
layout.marginHeight= 5;
layout.marginWidth= 5;
layout.numColumns= 4;
inner.setLayout(layout);
Label sourceURILabel = new Label(inner, SWT.NONE);
sourceURILabel.setText(Messages.QvtMetamodelMappingPage_sourceURILabel);
modelURIText = new Text(inner, SWT.BORDER);
GridData gd = new GridData(GridData.FILL, GridData.CENTER, true, false);
gd.widthHint = convertWidthInCharsToPixels(40);
gd.horizontalSpan = 3;
modelURIText.setLayoutData(gd);
modelURIText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
IStatus status = validateSourceURI();
if(status.isOK()) {
status = validateTargetMappingURI(targetURIText.getText());
}
updateStatus(status);
}
});
Label targetURILabel = new Label(inner, SWT.NONE);
targetURILabel.setText(Messages.QvtMetamodelMappingPage_targetURILabel);
targetURIText = new Text(inner, SWT.BORDER);
GridData gd2 = new GridData(GridData.FILL, GridData.CENTER, true, false);
gd2.widthHint = convertWidthInCharsToPixels(40);
gd2.horizontalSpan = 2;
targetURIText.setLayoutData(gd2);
targetURIText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
IStatus status = validateTargetMappingURI(targetURIText.getText());
if(status.isOK()) {
if(modelURIText.getText() == null || modelURIText.getText().length() == 0) {
modelURIText.setText(loadEPackage(targetURIText.getText()).getNsURI());
}
status = validateSourceURI();
}
else {
modelURIText.setText(""); //$NON-NLS-1$
}
updateStatus(status);
}
});
Button browse = new Button(inner, SWT.PUSH);
browse.setText(Messages.QvtMetamodelMappingPage_browseWorkspaceBtnLabel);
GridData bgd = new GridData(GridData.END, GridData.CENTER, true, false);
bgd.horizontalSpan = 1;
browse.setLayoutData(bgd);
browse.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ISourceChooser sourceChooser = new ResourceSourceChooser(false, rs);
new UriChooserListener(targetURIText, sourceChooser, getShell()).widgetSelected(e);
}
});
}
private IStatus validateSourceURI() {
String uri = modelURIText.getText();
IStatus status = validateURI(uri, Messages.QvtMetamodelMappingPage_sourceURILabel);
if(status.isOK()) {
status = getSourceUriAlreadyExistsStatus(uri, uriMap.getMapping().indexOf(data));
}
return status;
}
private String safeURIValue(String uriStr) {
return uriStr != null ? uriStr : ""; //$NON-NLS-1$
}
}
}