/* * 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.wizards; import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.WizardDataTransferPage; import org.eclipse.ui.progress.IProgressConstants; import org.teiid.designer.core.ModelerCore; 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.tools.textimport.ui.wizards.AbstractObjectProcessor; import org.teiid.designer.tools.textimport.ui.wizards.ITextImportMainPage; import org.teiid.designer.transformation.ui.UiConstants; import org.teiid.designer.transformation.ui.UiPlugin; import org.teiid.designer.transformation.ui.textimport.VirtualModelSelectorDialog; import org.teiid.designer.transformation.ui.textimport.VirtualTablelLocationSelectionValidator; import org.teiid.designer.transformation.util.SqlMappingRootCache; import org.teiid.designer.transformation.util.TransformationHelper; import org.teiid.designer.transformation.util.TransformationMappingHelper; import org.teiid.designer.ui.common.util.UiUtil; import org.teiid.designer.ui.common.util.WidgetUtil; import org.teiid.designer.ui.common.widget.IListPanelController; import org.teiid.designer.ui.editors.ModelEditorManager; import org.teiid.designer.ui.viewsupport.ModelObjectUtilities; import org.teiid.designer.ui.viewsupport.ModelUtilities; /** * @since 8.0 */ public class ImportVirtualTablesMainPage extends WizardDataTransferPage implements IListPanelController, ITextImportMainPage { public static final String IMPORT_ID = getString("textImport.comboText"); //$NON-NLS-1$ public static final String IMPORT_DESC = getString("textImport.descriptionText"); //$NON-NLS-1$ public static final String IMPORT_DATA = getString("textImport.sampleData"); //$NON-NLS-1$ // widgets protected Combo sourceNameField; protected Button sourceBrowseButton; private Text modelFolderNameField; private Button modelFolderBrowseButton; private ListViewer listViewer; ModelResource targetResource; private Object targetLocation; private Collection rows = Collections.EMPTY_LIST; VirtualRelationalObjectProcessor virtualRelationalObjectProcessor = new VirtualRelationalObjectProcessor(); // A boolean to indicate if the user has typed anything boolean entryChanged = false; private boolean initializing = false; /** * Mode flag to open a zip file for reading. */ public static final int OPEN_READ = 0x1; private final static String BROWSE_SHORTHAND = "Browse..."; //$NON-NLS-1$ private static final String FILE_IMPORT_MASK = "*.csv;*.txt";//$NON-NLS-1$ // dialog store id constants private static final String I18N_PREFIX = "ImportVirtualTablesMainPage"; //$NON-NLS-1$ private static final String SEPARATOR = "."; //$NON-NLS-1$ private static final String INITIAL_MESSAGE = getString("initialMessage"); //$NON-NLS-1$ private static final String PAGE_TITLE = getString("pageTitle"); //$NON-NLS-1$ private final static String STORE_SOURCE_NAMES_ID = getString("storeSourceNamesId");//$NON-NLS-1$ private static final int LARGE_ROWS = 100; static String getString( final String id ) { return UiConstants.Util.getString(I18N_PREFIX + SEPARATOR + id); } /** * Creates an instance of this class */ protected ImportVirtualTablesMainPage( String title, IStructuredSelection selection ) { super(title); setTitle(title); } /** * Creates an instance of this class * * @param aWorkbench IWorkbench * @param selection IStructuredSelection */ public ImportVirtualTablesMainPage( IStructuredSelection selection ) { this(PAGE_TITLE, selection); setTitle(PAGE_TITLE); } public ImportVirtualTablesMainPage() { this(null); } /** * The <code>WizardResourceImportPage</code> implementation of this <code>WizardDataTransferPage</code> method returns * <code>true</code>. Subclasses may override this method. */ @Override protected boolean allowNewContainerName() { return true; } /** * Handle all events and enablements for widgets in this dialog * * @param event Event */ @Override public void handleEvent( Event event ) { if (!initializing) { boolean validate = false; if (event.widget == sourceBrowseButton) { handleSourceBrowseButtonPressed(); validate = true; } if (event.widget == modelFolderBrowseButton) { handleModelFolderBrowseButtonPressed(); validate = true; } if (event.widget == sourceNameField || event.widget == modelFolderNameField) { validate = true; } if (validate) setCompletionStatus(); updateWidgetEnablements(); } } /** * Creates a new button with the given id. * <p> * The <code>Dialog</code> implementation of this framework method creates a standard push button, registers for selection * events including button presses and registers default buttons with its shell. The button id is stored as the buttons client * data. Note that the parent's layout is assumed to be a GridLayout and the number of columns in this layout is incremented. * Subclasses may override. * </p> * * @param parent the parent composite * @param id the id of the button (see <code>IDialogConstants.*_ID</code> constants for standard dialog button ids) * @param label the label from the button * @param defaultButton <code>true</code> if the button is to be the default button, and <code>false</code> otherwise */ protected Button createButton( Composite parent, int id, String label, boolean defaultButton ) { // increment the number of columns in the button bar ((GridLayout)parent.getLayout()).numColumns++; Button button = new Button(parent, SWT.PUSH); button.setFont(parent.getFont()); GridData buttonData = new GridData(GridData.FILL_HORIZONTAL); button.setLayoutData(buttonData); button.setData(new Integer(id)); button.setText(label); if (defaultButton) { Shell shell = parent.getShell(); if (shell != null) { shell.setDefaultButton(button); } button.setFocus(); } return button; } /** * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) * @since 4.2 */ @Override public void createControl( Composite parent ) { initializeDialogUnits(parent); Composite composite = new Composite(parent, SWT.NULL); composite.setLayout(new GridLayout()); composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL)); composite.setSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); composite.setFont(parent.getFont()); createSourceGroup(composite); createDestinationGroup(composite); createWorkspaceListGroup(composite); // createOptionsGroup(composite); restoreWidgetValues(); updateWidgetEnablements(); setPageComplete(false); setMessage(INITIAL_MESSAGE); setControl(composite); } /** * Create the group for creating the root directory */ protected void createSourceGroup( Composite parent ) { Composite sourceContainerGroup = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.numColumns = 3; sourceContainerGroup.setLayout(layout); sourceContainerGroup.setFont(parent.getFont()); sourceContainerGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); Label groupLabel = new Label(sourceContainerGroup, SWT.NONE); groupLabel.setText(getString("groupLabel")); //$NON-NLS-1$ groupLabel.setFont(parent.getFont()); // source name entry field sourceNameField = new Combo(sourceContainerGroup, SWT.BORDER); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL); data.widthHint = SIZING_TEXT_FIELD_WIDTH; sourceNameField.setLayoutData(data); sourceNameField.setFont(parent.getFont()); sourceNameField.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( SelectionEvent e ) { updateFromSourceField(); setCompletionStatus(); } }); sourceNameField.addKeyListener(new KeyListener() { /* * @see KeyListener.keyPressed */ @Override public void keyPressed( KeyEvent e ) { // If there has been a key pressed then mark as dirty entryChanged = true; } /* * @see KeyListener.keyReleased */ @Override public void keyReleased( KeyEvent e ) { } }); sourceNameField.addFocusListener(new FocusListener() { /* * @see FocusListener.focusGained(FocusEvent) */ @Override public void focusGained( FocusEvent e ) { // Do nothing when getting focus } /* * @see FocusListener.focusLost(FocusEvent) */ @Override public void focusLost( FocusEvent e ) { // Clear the flag to prevent constant update if (entryChanged) { entryChanged = false; } } }); // source browse button sourceBrowseButton = new Button(sourceContainerGroup, SWT.PUSH); sourceBrowseButton.setText(getString("browse_1")); //$NON-NLS-1$ sourceBrowseButton.addListener(SWT.Selection, this); sourceBrowseButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); sourceBrowseButton.setFont(parent.getFont()); setButtonLayoutData(sourceBrowseButton); } /** * Method to create List box control group for displaying current zip file project list. * * @param parent * @since 4.2 */ private void createWorkspaceListGroup( Composite parent ) { Label messageLabel = new Label(parent, SWT.NONE); messageLabel.setText(getString("modelListMessage")); //$NON-NLS-1$ messageLabel.setFont(parent.getFont()); listViewer = new ListViewer(parent); GridData data = new GridData(GridData.FILL_BOTH); listViewer.getControl().setLayoutData(data); } /** * Creates the import destination specification controls. * * @param parent the parent control */ protected void createDestinationGroup( Composite parent ) { // container specification group Composite containerGroup = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.numColumns = 3; containerGroup.setLayout(layout); containerGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); containerGroup.setFont(parent.getFont()); // container label Label resourcesLabel = new Label(containerGroup, SWT.NONE); resourcesLabel.setText(getString("targetLocation")); //$NON-NLS-1$ resourcesLabel.setFont(parent.getFont()); // container name entry field modelFolderNameField = new Text(containerGroup, SWT.SINGLE | SWT.BORDER); modelFolderNameField.addListener(SWT.Modify, this); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL); data.widthHint = SIZING_TEXT_FIELD_WIDTH; modelFolderNameField.setLayoutData(data); modelFolderNameField.setFont(parent.getFont()); modelFolderNameField.setEditable(false); // container browse button modelFolderBrowseButton = new Button(containerGroup, SWT.PUSH); modelFolderBrowseButton.setText(BROWSE_SHORTHAND); modelFolderBrowseButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); modelFolderBrowseButton.addListener(SWT.Selection, this); modelFolderBrowseButton.setFont(parent.getFont()); setButtonLayoutData(modelFolderBrowseButton); } /** * Open an appropriate source browser so that the user can specify a source to import from */ protected void handleSourceBrowseButtonPressed() { String selectedFile = queryFileToImport(); clearListViewer(); if (selectedFile != null) { if (!selectedFile.equals(sourceNameField.getText())) { sourceNameField.setText(selectedFile); // Need to call the method to update the project list because source (zip file) may have changed. this.rows = this.virtualRelationalObjectProcessor.loadLinesFromFile(sourceNameField.getText()); loadListViewer(this.rows); } } } protected void updateFromSourceField() { clearListViewer(); this.rows = this.virtualRelationalObjectProcessor.loadLinesFromFile(sourceNameField.getText()); loadListViewer(this.rows); } /** * Opens a file selection dialog and returns a string representing the selected file, or <code>null</code> if the dialog was * canceled. */ protected String queryFileToImport() { FileDialog dialog = new FileDialog(sourceNameField.getShell(), SWT.OPEN); dialog.setFilterExtensions(new String[] {FILE_IMPORT_MASK}); String currentSourceString = sourceNameField.getText(); int lastSeparatorIndex = currentSourceString.lastIndexOf(File.separator); if (lastSeparatorIndex != -1) dialog.setFilterPath(currentSourceString.substring(0, lastSeparatorIndex)); return dialog.open(); } /** * Opens a container selection dialog and displays the user's subsequent container resource selection in this page's container * name field. */ protected void handleModelFolderBrowseButtonPressed() { // ================================== // launch Location chooser // ================================== VirtualModelSelectorDialog mwdDialog = new VirtualModelSelectorDialog( UiPlugin.getDefault().getCurrentWorkbenchWindow().getShell()); mwdDialog.setValidator(new VirtualTablelLocationSelectionValidator()); mwdDialog.setAllowMultiple(false); mwdDialog.open(); if (mwdDialog.getReturnCode() == Window.OK) { Object[] oSelectedObjects = mwdDialog.getResult(); // add the selected location to this Relationship if (oSelectedObjects.length > 0) { setTableLocation(oSelectedObjects[0]); } } } private void setTableLocation( Object oLocation ) { if (oLocation instanceof IFile) { // Let's get the model resource and work from there... this.modelFolderNameField.setText(((IFile)oLocation).getName()); try { targetResource = ModelUtil.getModelResource((IFile)oLocation, false); targetLocation = targetResource; } catch (ModelWorkspaceException err) { } } else if (oLocation instanceof EObject) { EObject eObj = (EObject)oLocation; targetLocation = eObj; targetResource = ModelUtilities.getModelResourceForModelObject(eObj); String locationStr = targetResource.getItemName() + '/' + ModelObjectUtilities.getRelativePath(eObj); this.modelFolderNameField.setText(locationStr); } else if (oLocation instanceof ModelResource) { targetResource = (ModelResource)oLocation; targetLocation = targetResource; String locationStr = targetResource.getItemName(); this.modelFolderNameField.setText(locationStr); } } /** * @see org.eclipse.jface.dialogs.IDialogPage#dispose() * @since 4.2 */ @Override public void dispose() { super.dispose(); } /** * @see org.eclipse.jface.dialogs.DialogPage#setMessage(java.lang.String) * @since 4.2 */ @Override public void setMessage( String newMessage ) { super.setMessage(newMessage); } boolean setCompletionStatus() { if (validateSource() && validateDestination()) { setErrorMessage(null); setMessage(INITIAL_MESSAGE); setPageComplete(true); return true; } setPageComplete(false); return false; } private boolean validateDestination() { if (targetResource == null) { setErrorMessage(getString("noValidLocationSelectedMessage")); //$NON-NLS-1$ return false; } return true; } private boolean validateSource() { if (sourceNameField == null) { setErrorMessage(getString("noValidSourceSelectedMessage")); //$NON-NLS-1$ return false; } return true; } /** * The Finish button was pressed. Try to do the required work now and answer a boolean indicating success. If false is * returned then the wizard will not close. * * @return boolean */ @Override public boolean finish() { saveWidgetValues(); // Generate RowObjects from raw RowStrings Collection tableRows = this.virtualRelationalObjectProcessor.createRowObjsFromStrings(this.rows); if (!tableRows.isEmpty() && virtualRelationalObjectProcessor.confirmLargeImport(this.getShell(), tableRows.size(), LARGE_ROWS)) { generateWithJob(targetResource, targetLocation, tableRows); } return true; } boolean execute( final ModelResource resource, final Object location, final Collection tableRows, IProgressMonitor monitor ) { boolean requiredStart = ModelerCore.startTxn(false, false, getString("undoTitle"), this); //$NON-NLS-1$ boolean succeeded = false; try { this.virtualRelationalObjectProcessor.generateObjsFromRowObjs(targetResource, targetLocation, tableRows); succeeded = true; } finally { // if we started the txn, commit it. if (requiredStart) { if (succeeded && !monitor.isCanceled()) { ModelerCore.commitTxn(); } else { ModelerCore.rollbackTxn(); } } } // Validate Transformations requiredStart = ModelerCore.startTxn(false, false, getString("undoTitle"), this); //$NON-NLS-1$ succeeded = false; try { List mappingRoots = Collections.EMPTY_LIST; try { mappingRoots = TransformationHelper.getAllTransformations(resource.getEmfResource()); } catch (ModelWorkspaceException e) { // Error getting mapping roots } if (mappingRoots != null) { Iterator iter = mappingRoots.iterator(); while (iter.hasNext()) { EObject mRoot = (EObject)iter.next(); EObject transTarget = TransformationHelper.getTransformationLinkTarget(mRoot); if (TransformationHelper.isSqlProcedure(transTarget)) { SqlMappingRootCache.invalidateSelectStatus(mRoot, false, null); TransformationMappingHelper.reconcileSources(mRoot, null); TransformationMappingHelper.reconcileTargetAttributes(mRoot, null); } } } succeeded = true; } finally { // if we started the txn, commit it. if (requiredStart) { if (succeeded && !monitor.isCanceled()) { ModelerCore.commitTxn(); } else { ModelerCore.rollbackTxn(); } } } // Changed to use method that insures Object editor mode is on UiUtil.runInSwtThread(new Runnable() { @Override public void run() { ModelEditorManager.openInEditMode(targetResource, true, org.teiid.designer.ui.UiConstants.ObjectEditor.IGNORE_OPEN_EDITOR); } }, true); return succeeded; } private boolean generateWithJob( final ModelResource resource, final Object location, final Collection tableRows ) { final String message = getString("progressTitle"); //$NON-NLS-1$ final Job job = new Job(message) { @Override protected IStatus run( IProgressMonitor monitor ) { try { monitor.beginTask(message, tableRows.size()); if (!monitor.isCanceled()) { virtualRelationalObjectProcessor.setProgressMonitor(monitor); execute(resource, location, tableRows, monitor); } monitor.done(); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } return new Status(IStatus.OK, UiConstants.PLUGIN_ID, IStatus.OK, AbstractObjectProcessor.FINISHED, null); } catch (Exception e) { UiConstants.Util.log(e); return new Status(IStatus.ERROR, UiConstants.PLUGIN_ID, IStatus.ERROR, getString("createError"), e); //$NON-NLS-1$ } finally { } } }; job.setSystem(false); job.setUser(true); job.setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); // start as soon as possible job.schedule(); return true; } private void clearListViewer() { org.eclipse.swt.widgets.List contents = listViewer.getList(); String[] items = contents.getItems(); listViewer.remove(items); } private void loadListViewer( Collection rows ) { Iterator iter = rows.iterator(); while (iter.hasNext()) { String rowStr = (String)iter.next(); listViewer.add(rowStr); } } @Override public Object[] addButtonSelected() { return null; } @Override public void downButtonSelected( IStructuredSelection selection ) { } @Override public Object editButtonSelected( IStructuredSelection selection ) { return null; } @Override public void itemsSelected( IStructuredSelection selection ) { } @Override public Object[] removeButtonSelected( IStructuredSelection selection ) { return null; } @Override public void upButtonSelected( IStructuredSelection selection ) { } /** * Use the dialog store to restore widget values to the values that they held last time this wizard was used to completion */ @Override protected void restoreWidgetValues() { IDialogSettings settings = getDialogSettings(); WidgetUtil.removeMissingResources(settings, STORE_SOURCE_NAMES_ID); if (settings != null) { String[] sourceNames = settings.getArray(STORE_SOURCE_NAMES_ID); if (sourceNames == null) return; // ie.- no values stored, so stop // set filenames history for (int i = 0; i < sourceNames.length; i++) sourceNameField.add(sourceNames[i]); } } /** * Since Finish was pressed, write widget values to the dialog store so that they will persist into the next invocation of * this wizard page */ @Override public void saveWidgetValues() { IDialogSettings settings = getDialogSettings(); if (settings != null) { // update source names history String[] sourceNames = settings.getArray(STORE_SOURCE_NAMES_ID); if (sourceNames == null) sourceNames = new String[0]; sourceNames = addToHistory(sourceNames, sourceNameField.getText()); settings.put(STORE_SOURCE_NAMES_ID, sourceNames); } } @Override public String getComboText() { return IMPORT_ID; } @Override public String getDescriptionText() { return IMPORT_DESC; } @Override public String getSampleDataText() { return IMPORT_DATA; } @Override public String getType() { return IMPORT_ID; } }