/*
* 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.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.query.QueryValidator;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.metamodels.transformation.SqlTransformationMappingRoot;
import org.teiid.designer.query.sql.ISQLConstants;
import org.teiid.designer.transformation.ui.UiConstants;
import org.teiid.designer.transformation.ui.UiPlugin;
import org.teiid.designer.transformation.ui.textimport.VirtualTableRowObject;
import org.teiid.designer.transformation.ui.wizards.VirtualRelationalObjectProcessor;
import org.teiid.designer.transformation.util.TransformationHelper;
import org.teiid.designer.transformation.util.TransformationMappingHelper;
import org.teiid.designer.transformation.validation.TransformationValidator;
import org.teiid.designer.ui.actions.SortableSelectionAction;
import org.teiid.designer.ui.common.eventsupport.SelectionUtilities;
import org.teiid.designer.ui.common.util.WidgetUtil;
import org.teiid.designer.ui.common.widget.ListMessageDialog;
import org.teiid.designer.ui.viewsupport.ModelIdentifier;
import org.teiid.designer.ui.viewsupport.ModelUtilities;
/**
* @since 8.0
*/
public class ImportTransformationSqlFromTextAction extends SortableSelectionAction implements UiConstants {
private static final String IMPORT_PROBLEM_KEY = "ImportTransformationSqlFromTextAction.importProb"; //$NON-NLS-1$
private static final String IMPORT_SQL_DIALOG_TITLE_KEY = "ImportTransformationSqlFromTextAction.importSqlDialog.title"; //$NON-NLS-1$
private static final String IMPORT_SQL_PROBLEM_DIALOG_TITLE_KEY = "ImportTransformationSqlFromTextAction.importSqlProblemDialog.title"; //$NON-NLS-1$
private static final String UNUSED_SQL_DIALOG_TITLE_KEY = "ImportTransformationSqlFromTextAction.unusedSqlDialogTitle"; //$NON-NLS-1$
private static final String UNUSED_SQL_DIALOG_MESSAGE_KEY = "ImportTransformationSqlFromTextAction.unusedSqlDialogMessage"; //$NON-NLS-1$
private static final String MONITOR_MAIN_TASK_NAME_KEY = "ImportTransformationSqlFromTextAction.monitorMainTaskName"; //$NON-NLS-1$
private static final String MONITOR_TASK_SETTING_SQL_KEY = "ImportTransformationSqlFromTextAction.monitorTaskSettingSql";//$NON-NLS-1$
private static final String MONITOR_TASK_RECONCILING_KEY = "ImportTransformationSqlFromTextAction.monitorTaskReconciling";//$NON-NLS-1$
private static final String MONITOR_TASK_VALIDATING_SQL_KEY = "ImportTransformationSqlFromTextAction.monitorTaskValidatingSql";//$NON-NLS-1$
static final String MONITOR_TASK_SAVING_MODEL = UiConstants.Util.getString("ImportTransformationSqlFromTextAction.monitorTaskSavingModel");//$NON-NLS-1$
private static final String DELIMETER = "\\|";//$NON-NLS-1$
private boolean isTestingMode = false;
private Collection anyLeftOverRows;
/**
* @since 5.0
*/
public ImportTransformationSqlFromTextAction() {
super();
}
/**
* @see org.teiid.designer.ui.actions.SortableSelectionAction#isValidSelection(org.eclipse.jface.viewers.ISelection)
* @since 5.0
*/
@Override
public boolean isValidSelection( ISelection selection ) {
// Enable for single/multiple Virtual Tables
return virtualModelSelected(selection);
}
/**
* @see org.eclipse.jface.action.IAction#run()
* @since 5.0
*/
@Override
public void run() {
ISelection cachedSelection = getSelection();
if (cachedSelection != null && !cachedSelection.isEmpty()) {
Object selectedObj = SelectionUtilities.getSelectedObject(cachedSelection);
if (selectedObj != null && selectedObj instanceof IFile) {
ModelResource modelResource = null;
try {
modelResource = ModelUtil.getModelResource(((IFile)selectedObj), false);
if (modelResource != null) {
String fileName = askUserForInputFilename();
if (fileName != null) {
importSqlFromFile(fileName, modelResource);
}
}
} catch (ModelWorkspaceException e) {
UiConstants.Util.log(e);
}
}
}
selectionChanged(null, new StructuredSelection());
}
/**
* @see org.teiid.designer.ui.actions.ISelectionAction#isApplicable(org.eclipse.jface.viewers.ISelection)
* @since 5.0
*/
@Override
public boolean isApplicable( ISelection selection ) {
return virtualModelSelected(selection);
}
private boolean virtualModelSelected( ISelection theSelection ) {
boolean result = false;
List allObjs = SelectionUtilities.getSelectedObjects(theSelection);
if (!allObjs.isEmpty() && allObjs.size() == 1) {
Iterator iter = allObjs.iterator();
result = true;
Object nextObj = null;
while (iter.hasNext() && result) {
nextObj = iter.next();
if (nextObj instanceof IFile) {
result = ModelIdentifier.isRelationalViewModel((IFile)nextObj);
} else {
result = false;
}
}
}
return result;
}
/**
* returns a collection of SqlRow objects retrieved from a Sql Text file
*
* @param fileString
* @return
* @since 5.0
*/
public Collection getSqlRowsFromFile( String fileString ) {
// PERFORM ARG CHECK
// Look for NULL or EMPTY strings
CoreArgCheck.isNotNull(fileString);
CoreArgCheck.isNotEmpty(fileString);
Collection sqlRows = new ArrayList();
FileReader fileReader = null;
BufferedReader bufferReader = null;
try {
fileReader = new FileReader(fileString);
bufferReader = new BufferedReader(fileReader);
String str;
while ((str = bufferReader.readLine()) != null) {
SqlRow newRow = createSqlRow(str);
if (newRow != null) {
sqlRows.add(newRow);
}
}
} catch (Exception e) {
String msg = UiConstants.Util.getString(IMPORT_PROBLEM_KEY);
UiConstants.Util.log(IStatus.ERROR, e, msg);
String dialogMessage = msg + "\n" + e.getMessage(); //$NON-NLS-1$
if (!isTestingMode) {
displayError(getShell(), UiConstants.Util.getString(IMPORT_SQL_PROBLEM_DIALOG_TITLE_KEY), dialogMessage);
}
} finally {
// Clean up readers & buffers
try {
if (fileReader != null) {
fileReader.close();
}
} catch (java.io.IOException e) {
UiConstants.Util.log(IStatus.ERROR, e, UiConstants.Util.getString(IMPORT_PROBLEM_KEY));
}
try {
if (bufferReader != null) {
bufferReader.close();
}
} catch (java.io.IOException e) {
UiConstants.Util.log(IStatus.ERROR, e, UiConstants.Util.getString(IMPORT_PROBLEM_KEY));
}
}
return sqlRows;
}
/**
* Import SQL text from file and set any SQL applicable to the virtual tables within the supplied ModelResource
*
* @param fileString
* @param modelResource
* @since 5.0
*/
public Collection importSqlFromFile( String fileString,
ModelResource modelResource ) {
Collection unusedSqlRows = Collections.EMPTY_LIST;
// PERFORM ARG CHECK
// Look for NULL or EMPTY strings
CoreArgCheck.isNotNull(fileString);
CoreArgCheck.isNotNull(modelResource);
CoreArgCheck.isNotEmpty(fileString);
Collection sqlRows = getSqlRowsFromFile(fileString);
if (!sqlRows.isEmpty()) {
unusedSqlRows = processRows(modelResource, sqlRows);
}
return unusedSqlRows;
}
private String askUserForInputFilename() {
FileDialog dlg = new FileDialog(getShell(), SWT.OPEN);
dlg.setFilterExtensions(new String[] {"*.txt", "*.*"}); //$NON-NLS-1$ //$NON-NLS-2$
dlg.setText(UiConstants.Util.getString(IMPORT_SQL_DIALOG_TITLE_KEY));
return dlg.open();
}
private Collection processRows( final ModelResource modelResource,
final Collection sqlRows ) {
final IRunnableWithProgress op = new IRunnableWithProgress() {
@Override
public void run( final IProgressMonitor monitor ) throws InvocationTargetException {
monitor.beginTask(UiConstants.Util.getString(MONITOR_MAIN_TASK_NAME_KEY, modelResource.getItemName()),
10 * sqlRows.size() + 10);
try {
setTransformationSql(modelResource, sqlRows, monitor);
} catch (ModelWorkspaceException theException) {
UiConstants.Util.log(theException);
}
monitor.subTask(MONITOR_TASK_SAVING_MODEL);
monitor.worked(5);
try {
ModelUtilities.saveModelResource(modelResource, monitor, false, this);
} catch (Exception e) {
throw new InvocationTargetException(e);
}
monitor.worked(5);
}
};
try {
final ProgressMonitorDialog dlg = new ProgressMonitorDialog(getShell());
dlg.run(false, true, op);
if (dlg.getProgressMonitor().isCanceled()) {
// DO NOTHING
}
} catch (final InterruptedException ignored) {
} catch (final Exception err) {
Throwable t = err;
if (err instanceof InvocationTargetException) {
t = err.getCause();
}
WidgetUtil.showError(t);
}
Collection returnedRows = Collections.EMPTY_LIST;
if (anyLeftOverRows != null) {
returnedRows = new ArrayList(anyLeftOverRows);
anyLeftOverRows = null;
}
return returnedRows;
}
private Shell getShell() {
return UiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell();
}
/**
* Opens an error dialog to display the given message.
*
* @param message the error message to show
*/
private void displayError( final Shell shell,
final String dialogTitle,
final String message ) {
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
MessageDialog.openError(shell, dialogTitle, message);
}
});
}
private void notifyUserOfLeftovers( final ModelResource modelResource,
final List leftOverTables,
final List<SqlRow> leftOverRows ) {
if (!isTestingMode && !leftOverTables.isEmpty()) {
final Shell shell = getShell();
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
boolean result = ListMessageDialog.openQuestion(shell,
UiConstants.Util.getString(UNUSED_SQL_DIALOG_TITLE_KEY),
null,
UiConstants.Util.getString(UNUSED_SQL_DIALOG_MESSAGE_KEY),
leftOverTables,
null);
if (result) {
// TODO: we need to create virtual tables
VirtualRelationalObjectProcessor virtualRelationalObjectProcessor = new VirtualRelationalObjectProcessor();
Object location = null;
for (SqlRow row : leftOverRows) {
location = modelResource;
IPath path = new Path(row.getPath());
String tableName = null;
if (path.segmentCount() == 2) {
Collection<VirtualTableRowObject> virtTableRows = new ArrayList<VirtualTableRowObject>();
tableName = path.lastSegment();
// Need to create a schema here
String schemaName = path.segment(0);
location = virtualRelationalObjectProcessor.createSchema(modelResource, schemaName);
virtTableRows.add(new VirtualTableRowObject(tableName, null, row.getSql()));
virtualRelationalObjectProcessor.generateObjsFromRowObjs(modelResource, location, virtTableRows);
} else {
Collection<VirtualTableRowObject> virtTableRows = new ArrayList<VirtualTableRowObject>();
tableName = path.toString();
virtTableRows.add(new VirtualTableRowObject(tableName, null, row.getSql()));
virtualRelationalObjectProcessor.generateObjsFromRowObjs(modelResource, location, virtTableRows);
}
}
}
}
});
}
}
private SqlRow createSqlRow( String rowString ) {
String[] parsedString = rowString.split(DELIMETER);
String sql = parsedString[2];
StringBuilder sb = new StringBuilder();
boolean escaped = false;
for (int i = 0; i < sql.length(); i++) {
char c = sql.charAt(i);
if (escaped) {
escaped = false;
if (c == 'n') {
c = '\n';
} else if ( c == 'r') {
c = '\r';
}
} else if (c == '\\') {
escaped = true;
continue;
}
sb.append(c);
}
if (escaped) {
// probably an error, but just in case
sb.append('\\');
}
return new SqlRow(getSqlTypeFromString(parsedString[1]), parsedString[0], sb.toString());
}
boolean setTransformationSql( ModelResource modelResource,
Collection sqlRows,
IProgressMonitor monitor ) throws ModelWorkspaceException {
boolean sqlChanged = false;
Collection leftOverTables = new HashSet();
Collection<SqlRow> leftOverRows = new HashSet<SqlRow>();
String tableName = null;
for (Iterator iter = sqlRows.iterator(); iter.hasNext();) {
SqlRow nextRow = (SqlRow)iter.next();
EObject existingTable = null;
IPath tablePath = new Path(nextRow.getPath());
tableName = tablePath.lastSegment();
existingTable = ModelerCore.getModelEditor().findObjectByPath(modelResource.getEmfResource(), tablePath);
if (existingTable != null) {
SqlTransformationMappingRoot root = (SqlTransformationMappingRoot)TransformationHelper.getTransformationMappingRoot(existingTable);
if (root != null) {
monitor.subTask(UiConstants.Util.getString(MONITOR_TASK_SETTING_SQL_KEY, nextRow.getPath()));
QueryValidator validator = new TransformationValidator(root);
boolean changed = false;
switch (nextRow.getType()) {
case QueryValidator.SELECT_TRNS: {
changed = TransformationHelper.setSqlString(root,
nextRow.getSql(),
QueryValidator.SELECT_TRNS,
false,
this);
monitor.subTask(UiConstants.Util.getString(MONITOR_TASK_RECONCILING_KEY, tableName));
monitor.worked(3);
TransformationMappingHelper.reconcileMappingsOnSqlChange(root, this);
monitor.subTask(UiConstants.Util.getString(MONITOR_TASK_VALIDATING_SQL_KEY, tableName));
monitor.worked(3);
validator.validateSql(nextRow.getSql(), QueryValidator.SELECT_TRNS, true);
monitor.worked(4);
}
break;
case QueryValidator.INSERT_TRNS: {
changed = TransformationHelper.setSqlString(root,
nextRow.getSql(),
QueryValidator.INSERT_TRNS,
false,
this);
monitor.subTask(UiConstants.Util.getString(MONITOR_TASK_VALIDATING_SQL_KEY, tableName));
monitor.worked(5);
validator.validateSql(nextRow.getSql(), QueryValidator.INSERT_TRNS, true);
monitor.worked(5);
}
break;
case QueryValidator.UPDATE_TRNS: {
changed = TransformationHelper.setSqlString(root,
nextRow.getSql(),
QueryValidator.UPDATE_TRNS,
false,
this);
monitor.subTask(UiConstants.Util.getString(MONITOR_TASK_VALIDATING_SQL_KEY, tableName));
monitor.worked(5);
validator.validateSql(nextRow.getSql(), QueryValidator.UPDATE_TRNS, true);
monitor.worked(5);
}
break;
case QueryValidator.DELETE_TRNS: {
changed = TransformationHelper.setSqlString(root,
nextRow.getSql(),
QueryValidator.DELETE_TRNS,
false,
this);
monitor.worked(5);
monitor.subTask(UiConstants.Util.getString(MONITOR_TASK_VALIDATING_SQL_KEY, tableName));
validator.validateSql(nextRow.getSql(), QueryValidator.DELETE_TRNS, true);
monitor.worked(5);
}
break;
default: {
}
break;
}
if (changed) {
sqlChanged = true;
}
}
} else {
if (!leftOverTables.contains(nextRow.getPath())) {
leftOverTables.add(nextRow.getPath());
leftOverRows.add(nextRow);
}
}
}
anyLeftOverRows = leftOverRows;
notifyUserOfLeftovers(modelResource, new ArrayList(leftOverTables), new ArrayList<SqlRow>(leftOverRows));
return sqlChanged;
}
private int getSqlTypeFromString( String typeString ) {
if (typeString.equalsIgnoreCase(ISQLConstants.SQL_TYPE_SELECT_STRING)) {
return QueryValidator.SELECT_TRNS;
}
if (typeString.equalsIgnoreCase(ISQLConstants.SQL_TYPE_INSERT_STRING)) {
return QueryValidator.INSERT_TRNS;
}
if (typeString.equalsIgnoreCase(ISQLConstants.SQL_TYPE_UPDATE_STRING)) {
return QueryValidator.UPDATE_TRNS;
}
if (typeString.equalsIgnoreCase(ISQLConstants.SQL_TYPE_DELETE_STRING)) {
return QueryValidator.DELETE_TRNS;
}
return QueryValidator.UNKNOWN_TRNS;
}
/**
* @since 5.0
*/
class SqlRow {
private int type;
private String path;
private String sql;
SqlRow( int sqlType,
String relativePathAndName,
String userSqlString ) {
super();
this.type = sqlType;
this.path = relativePathAndName;
this.sql = userSqlString;
}
public String getPath() {
return this.path;
}
public String getSql() {
return this.sql;
}
public int getType() {
return this.type;
}
}
public boolean isTestingMode() {
return this.isTestingMode;
}
public void setIsTestingMode( boolean theIsTestingMode ) {
this.isTestingMode = theIsTestingMode;
}
}