/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.transformation.ui.actions;
import java.util.EventObject;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPart;
import org.teiid.core.designer.event.EventObjectListener;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.query.QueryValidationResult;
import org.teiid.designer.core.query.QueryValidator;
import org.teiid.designer.metamodels.diagram.Diagram;
import org.teiid.designer.metamodels.transformation.SqlTransformationMappingRoot;
import org.teiid.designer.query.sql.lang.ICommand;
import org.teiid.designer.query.sql.lang.IQuery;
import org.teiid.designer.transformation.ui.PluginConstants;
import org.teiid.designer.transformation.ui.UiConstants;
import org.teiid.designer.transformation.ui.UiPlugin;
import org.teiid.designer.transformation.ui.editors.QueryEditorStatusEvent;
import org.teiid.designer.transformation.ui.editors.TransformationObjectEditorPage;
import org.teiid.designer.transformation.ui.editors.sqleditor.SqlEditorPanel;
import org.teiid.designer.transformation.ui.reconciler.ReconcilerDialog;
import org.teiid.designer.transformation.util.SqlMappingRootCache;
import org.teiid.designer.transformation.util.SqlTransformationStatusChangeEvent;
import org.teiid.designer.transformation.util.TransformationHelper;
import org.teiid.designer.transformation.util.TransformationMappingHelper;
import org.teiid.designer.transformation.validation.SqlTransformationResult;
import org.teiid.designer.transformation.validation.TransformationValidator;
import org.teiid.designer.ui.common.util.UiUtil;
import org.teiid.designer.ui.common.viewsupport.UiBusyIndicator;
import org.teiid.designer.ui.editors.ModelEditorManager;
import org.teiid.designer.ui.viewsupport.ModelObjectUtilities;
import org.teiid.query.ui.sqleditor.component.QueryDisplayNode;
/**
* The <code>ReconcileSql</code> class is the action that handles the global copy.
*
* @since 8.0
*/
public class ReconcileTransformationAction extends TransformationAction implements EventObjectListener {
private static final String DIALOG_TITLE_TEXT = UiConstants.Util.getString("ReconcilerDialog.title.text"); //$NON-NLS-1$
private static final String SUBQUERY_RECONCILE_TITLE_TEXT = UiConstants.Util.getString("ReconcilerDialog.subQueryReconcile.title.text"); //$NON-NLS-1$
private static final String SUBQUERY_RECONCILE_MESSAGE_TEXT = UiConstants.Util.getString("ReconcilerDialog.subQueryMessage.text"); //$NON-NLS-1$
TransformationObjectEditorPage tObjEditorPage = null;
boolean txnOK = true;
public ReconcileTransformationAction( EObject transformationEObject,
Diagram diagram ) {
super(transformationEObject, diagram);
setImageDescriptor(UiPlugin.getDefault().getImageDescriptor(PluginConstants.Images.RECONCILER_ICON));
SqlMappingRootCache.addEventListener(this);
}
public void setTransObjectEditorPage( TransformationObjectEditorPage toePage ) {
if (toePage != null) {
this.tObjEditorPage = toePage;
this.tObjEditorPage.addEventListener(this);
setEnabled(shouldEnable(toePage.getCurrentMappingRoot()));
}
}
/**
* Override default method - enable based on the state of the SQL
*
* @param transformationEObject the transformation object
*/
@Override
public void setTransformation( EObject transformationEObject ) {
super.setTransformation(transformationEObject);
boolean enable = false;
if (!isDependencyDiagram()) {
// Check the current transformation
EObject transMappingRoot = getTransformation();
if (transMappingRoot != null && TransformationHelper.isTransformationMappingRoot(transMappingRoot)) {
enable = shouldEnable(transMappingRoot);
}
}
setEnabled(enable);
}
@Override
public void selectionChanged(IWorkbenchPart thePart,
ISelection theSelection) {
super.selectionChanged(thePart, theSelection);
if( this.tObjEditorPage != null ) {
setEnabled(shouldEnable(this.tObjEditorPage.getCurrentMappingRoot()));
}
}
/**
* handler for SqlTransformationStatusChangeEvents
*
* @param e the eventObject
*/
@Override
public void processEvent( EventObject e ) {
// ----------------------------------------------------------------------
// respond to SqlTransformationStatusChangeEvent for the current
// Transformation. This event is fired whenever the SQL for a
// Transformation changes.
// ----------------------------------------------------------------------
if (e instanceof SqlTransformationStatusChangeEvent) {
boolean enable = false;
// MappingRoot on which the sql changed
Object eventMappingRoot = ((SqlTransformationStatusChangeEvent)e).getMappingRoot();
// If sql has changed on actions transformation, see if we should enable
if (eventMappingRoot != null && eventMappingRoot.equals(getTransformation())) {
enable = shouldEnable(getTransformation());
setEnabled(enable);
}
} else if (e instanceof QueryEditorStatusEvent) {
setEnabled(shouldEnable(getTransformation()));
}
}
/*
* determine whether the ReconcileTransformationAction should be enabled
* @param transMappingRoot the transformation mappingRoot
* @return 'true' if the action should be enabled, 'false' if not.
*/
private boolean shouldEnable( EObject transMappingRoot ) {
boolean shouldEnable = false;
// Determine based on the current transformation if we can enable
if (rootIsValid(transMappingRoot)) {
boolean canUseReconciler = true; //TransformationHelper.canUseReconciler(transMappingRoot);
boolean isReadOnly = ModelObjectUtilities.isReadOnly(transMappingRoot);
if (canUseReconciler && !isReadOnly) {
shouldEnable = true;
}
}
// If transformation can be reconciled, check Editor - is there any other reason we should disable
if (shouldEnable) {
// Assume current tab is select, check editor if set
boolean currentTabIsSelect = true;
boolean hasPendingChanges = false;
if (this.tObjEditorPage != null) {
if (!tObjEditorPage.isCurrentTabSelect()) {
currentTabIsSelect = false;
} else {
hasPendingChanges = tObjEditorPage.hasPendingChanges();
}
}
// Disable if different tab is selected
if (!currentTabIsSelect || hasPendingChanges) {
shouldEnable = false;
// do further checking if this is a union query
} else {
if (this.tObjEditorPage != null) {
SqlEditorPanel sePanel = tObjEditorPage.getCurrentSqlEditor();
boolean isUnion = sePanel.isCommandUnion();
if (isUnion) {
ICommand command = sePanel.getCommand();
int uIndex = sePanel.getCurrentUnionCommandSegmentIndex();
// index -1 : consider the entire command, make sure its resolvable
if (uIndex == -1) {
if (!command.isResolved()) {
shouldEnable = false;
}
} else {
QueryDisplayNode qdn = (QueryDisplayNode)sePanel.getCurrentCommandDisplayNode();
command = (IQuery)qdn.getLanguageObject();
// If command is not resolved, attempt to resolve it.
if (!command.isResolved()) {
QueryValidator validator = new TransformationValidator(
(SqlTransformationMappingRoot)transMappingRoot);
QueryValidationResult result = validator.validateSql(command.toString(),
QueryValidator.SELECT_TRNS,
false);
if (!result.isResolvable()) {
shouldEnable = false;
}
}
}
} else {
ICommand command = sePanel.getCommand();
if (command != null && !command.isResolved()) {
shouldEnable = false;
}
}
}
}
}
return shouldEnable;
}
/**
* @see org.eclipse.jface.action.Action#run()
*/
@Override
protected void doRun() {
Shell shell = super.getPlugin().getWorkbench().getActiveWorkbenchWindow().getShell();
// Check for Dirty Model and if can use reconciler
if( this.tObjEditorPage == null ) return;
if( this.tObjEditorPage.getParentModelEditor().isResourceDirty() ) {
MessageDialog.openWarning(shell,
UiConstants.Util.getString("ReconcileTransformationAction.pendingChangesTitle"), //$NON-NLS-1$
UiConstants.Util.getString("ReconcileTransformationAction.pendingChangesMessage")); //$NON-NLS-1$
return;
}
int subIndex = -1;
List builderGroups = null;
boolean isUnion = false;
SqlEditorPanel sePanel = null;
// If T-editor is closed, this action may still be on the diagram toolbar
// So we need to check if the mapping root was set to NULL on deactivate()
// as well as if the page has not been created yet.
if (tObjEditorPage == null || tObjEditorPage.getCurrentMappingRoot() == null) {
// Need to open editor page so we can get the builder groups from the basic query
ModelEditorManager.edit(getTransformation());
}
if (tObjEditorPage != null) {
// Check for null or EMPTY SQL
if( this.tObjEditorPage.getCurrentSqlEditor().getText() == null || this.tObjEditorPage.getCurrentSqlEditor().getText().trim().length() == 0 ) {
MessageDialog.openWarning(shell,
UiConstants.Util.getString("ReconcileTransformationAction.emptySQLTitle"), //$NON-NLS-1$
UiConstants.Util.getString("ReconcileTransformationAction.emptySQLMessage")); //$NON-NLS-1$
return;
}
// Check Parsable Status
SqlTransformationResult existingStatus = SqlMappingRootCache.getSqlTransformationStatus(this.tObjEditorPage.getCurrentMappingRoot(), QueryValidator.SELECT_TRNS, true, null);
if( existingStatus != null ) {
if( !existingStatus.isParsable()) {
MessageDialog.openWarning(shell,
UiConstants.Util.getString("ReconcileTransformationAction.sqlNotParsableTitle"), //$NON-NLS-1$
UiConstants.Util.getString("ReconcileTransformationAction.sqlNotParsableMessage")); //$NON-NLS-1$
return;
}
}
sePanel = tObjEditorPage.getCurrentSqlEditor();
isUnion = sePanel.isCommandUnion();
boolean isSubQuerySelected = sePanel.isSubQuerySelected();
if (isSubQuerySelected) {
// User is trying to reconcile a subquery and we don't want to go there... so pop up a Warning dialog
// to tell the user to put cursor in primary query and reconcile.
MessageDialog.openWarning(shell, SUBQUERY_RECONCILE_TITLE_TEXT, SUBQUERY_RECONCILE_MESSAGE_TEXT);
return;
}
if (isUnion) {
subIndex = sePanel.getCurrentUnionCommandSegmentIndex();
}
builderGroups = sePanel.getGroupsForBuilderTree(true);
}
// --------------------------------------------------
// Create the Reconciler Dialog and Open it
// --------------------------------------------------
ReconcilerDialog reconcilerDialog = new ReconcilerDialog(shell, getTransformation(), DIALOG_TITLE_TEXT, isUnion,
subIndex, builderGroups);
int returnStatus = reconcilerDialog.open();
// -------------------------------------------------------------------------
// If the Dialog is OK'd and it has Modifications, do the updates
// -------------------------------------------------------------------------
if ((returnStatus == Window.OK) && reconcilerDialog.hasValidModifications()) {
final boolean canUndo = ITransformationDiagramActionConstants.DiagramActions.UNDO_RECONCILE_TRANSFORMATION;
final int index = subIndex;
final ReconcilerDialog dialog = reconcilerDialog;
txnOK = true;
if (reconcilerDialog.hasPreModifications()) {
final boolean requiredStartPre = ModelerCore.startTxn(true, canUndo, "Reconcile Transformation", this); //$NON-NLS-1$
UiBusyIndicator.showWhile(UiUtil.getWorkbenchShellOnlyIfUiThread().getDisplay(), new Runnable() {
@Override
public void run() {
boolean succeeded = false;
try {
// Apply all of the Modifications
dialog.applyPreModifications(ModelerCore.getCurrentUoW().getSource());
succeeded = true;
} finally {
// If we start txn, commit it
if (requiredStartPre) {
if (succeeded) {
ModelerCore.commitTxn();
tObjEditorPage.refreshEditorContent();
} else {
ModelerCore.rollbackTxn();
txnOK = false;
}
}
TransformationMappingHelper.setCreateTargetAttributes(true);
}
}
});
}
if (txnOK) {
final boolean requiredStart = ModelerCore.startTxn(true, canUndo, "Reconcile Transformation", this); //$NON-NLS-1$
// show busy cursor while applying modifications and committing transaction
UiBusyIndicator.showWhile(UiUtil.getWorkbenchShellOnlyIfUiThread().getDisplay(), new Runnable() {
@Override
public void run() {
boolean succeeded = false;
try {
// Apply all of the Modifications
dialog.applyAllModifications(index, ModelerCore.getCurrentUoW().getSource());
succeeded = true;
} finally {
// If we start txn, commit it
if (requiredStart) {
if (succeeded) {
ModelerCore.commitTxn();
tObjEditorPage.refreshEditorContent();
} else {
ModelerCore.rollbackTxn();
}
}
TransformationMappingHelper.setCreateTargetAttributes(true);
}
}
});
}
}
}
private boolean rootIsValid( EObject transMappingRoot ) {
if (transMappingRoot != null && transMappingRoot.eResource() != null) return true;
return false;
}
@Override
public void dispose() {
SqlMappingRootCache.removeEventListener(this);
super.dispose();
}
}