/* * 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.ddl.importer.ui; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TreeExpansionEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.teiid.core.designer.I18n; import org.teiid.designer.ddl.importer.DdlImporter; import org.teiid.designer.metamodels.core.ModelType; import org.teiid.designer.relational.RelationalConstants; import org.teiid.designer.relational.compare.DifferenceReport; import org.teiid.designer.relational.compare.OperationList; import org.teiid.designer.relational.model.RelationalReference; import org.teiid.designer.relational.ui.UiConstants; import org.teiid.designer.relational.ui.UiPlugin; import org.teiid.designer.ui.common.text.StyledTextEditor; import org.teiid.designer.ui.common.util.WidgetFactory; import org.teiid.designer.ui.common.util.WidgetUtil; import org.teiid.designer.ui.common.widget.DefaultTreeViewerController; import org.teiid.designer.ui.common.wizard.IPersistentWizardPage; /** * DdlImportDifferencesPage - shows entities which will be changed in the target model and allows user to de-select if desired * */ public class DdlImportDifferencesPage extends WizardPage implements IPersistentWizardPage { private static final int PANEL_GRID_SPAN = 3; final DdlImporter importer; private Composite stackPanel; private Composite differencesPanel; private Composite parseErrorPanel; private StackLayout stackLayout; private CheckboxTreeController controller; private TreeViewer treeViewer; private Tree tree; boolean treeExpanded = false; private StyledTextEditor ddlContentsArea; private org.eclipse.swt.widgets.List messagesList; Properties options; /** * DdlImportDifferencesPage Constructor * @param importer the DdlImporter * @param options */ public DdlImportDifferencesPage( final DdlImporter importer, Properties options) { super(DdlImportDifferencesPage.class.getSimpleName(), DdlImporterUiI18n.DIFFERENCE_PAGE_TITLE, null); this.importer = importer; this.options = options; } /** * {@inheritDoc} * * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) */ @Override public void createControl( final Composite parent ) { //final IDialogSettings settings = getDialogSettings(); final Composite panel = WidgetFactory.createPanel(parent, SWT.NONE, GridData.FILL_BOTH, 1, PANEL_GRID_SPAN); setControl(panel); // Stack Layout - swap between parse error page and differences page stackPanel = new Composite(panel, SWT.NONE | SWT.FILL); stackLayout = new StackLayout(); stackLayout.marginWidth = 0; stackLayout.marginHeight = 0; stackPanel.setLayout(stackLayout); stackPanel.setLayoutData(new GridData(GridData.FILL_BOTH)); // Differences Panel createDifferencesPanel(stackPanel); // Parse Error Panel createParseErrorPanel(stackPanel); } /** * create the Differences tree Composite * * @param parent the parent composite */ private void createDifferencesPanel( Composite parent ) { differencesPanel = WidgetFactory.createPanel(parent, SWT.NONE, GridData.FILL_BOTH); GridLayout layout = new GridLayout(1, false); differencesPanel.setLayout(layout); SashForm splitter = WidgetFactory.createSplitter(differencesPanel, SWT.VERTICAL); GridData gid = new GridData(); gid.grabExcessHorizontalSpace = gid.grabExcessVerticalSpace = true; gid.horizontalAlignment = gid.verticalAlignment = GridData.FILL; splitter.setLayoutData(gid); // -------------------------- // Group for checkbox tree // -------------------------- Group treeGroup = WidgetFactory.createGroup(splitter, DdlImporterUiI18n.DIFFERENCE_PAGE_IMPORT_TREE_GROUP_TITLE, GridData.FILL_BOTH, 1, 1); // ---------------------------- // TreeViewer // ---------------------------- this.controller = new CheckboxTreeController(); this.treeViewer = WidgetFactory.createTreeViewer(treeGroup, SWT.SINGLE | SWT.CHECK, GridData.FILL_BOTH, controller); this.tree = this.treeViewer.getTree(); tree.setLayoutData(new GridData(GridData.FILL_BOTH)); this.treeViewer.setContentProvider(new CheckboxTreeContentProvider()); this.treeViewer.setLabelProvider(new CheckboxTreeLabelProvider()); this.treeViewer.setInput(null); // -------------------------- // Group for Import Messages // -------------------------- Group messageGroup = WidgetFactory.createGroup(splitter, DdlImporterUiI18n.DIFFERENCE_PAGE_IMPORT_MESSAGES_GROUP_TITLE, GridData.FILL_BOTH, 1, 1); messagesList = new org.eclipse.swt.widgets.List(messageGroup, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); messagesList.setLayoutData(new GridData(GridData.FILL_BOTH)); // position the splitter splitter.setWeights(new int[] {10, 3}); splitter.layout(); } /** * create the Panel to display parse errors * * @param parent the parent composite */ private void createParseErrorPanel( Composite parent ) { parseErrorPanel = WidgetFactory.createPanel(parent, SWT.NONE, GridData.FILL_BOTH); GridLayout layout = new GridLayout(1, false); parseErrorPanel.setLayout(layout); Group ddlGroup = WidgetFactory.createGroup(parseErrorPanel, DdlImporterUiI18n.DIFFERENCE_PAGE_PARSE_ERROR_DDL_GROUP_TITLE, GridData.FILL_BOTH, 1, 1); Composite ddlPanel = WidgetFactory.createPanel(ddlGroup, SWT.NONE, GridData.FILL_BOTH); ddlPanel.setLayout(layout); // -------------------------- // Group for Parse Errors // -------------------------- int messageAreaStyle = SWT.READ_ONLY | SWT.V_SCROLL | SWT.WRAP; GridData gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalAlignment = GridData.FILL; gridData.verticalAlignment = GridData.FILL; gridData.grabExcessHorizontalSpace = true; gridData.grabExcessVerticalSpace = true; ddlContentsArea = StyledTextEditor.createReadOnlyEditor(ddlPanel, messageAreaStyle); ddlContentsArea.setLayoutData(gridData); ddlContentsArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); } /** * {@inheritDoc} * * @see org.teiid.designer.ui.common.wizard.IPersistentWizardPage#saveSettings() */ @Override public void saveSettings() { } /** * {@inheritDoc} * * @see org.eclipse.jface.wizard.WizardPage#setErrorMessage(java.lang.String) */ @Override public void setErrorMessage( final String message ) { if (message == null || getErrorMessage() == null) super.setErrorMessage(message); setPageComplete(message == null); } /** * {@inheritDoc} * * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean) */ @Override public void setVisible( final boolean visible ) { super.setVisible(visible); // If the page is being shown, import the DDL and generate the difference report if(! visible) return; // Perform the DDL Import final Exception importException[] = new Exception[1]; try { new ProgressMonitorDialog(getShell()).run(true, true, new IRunnableWithProgress() { @Override public void run(final IProgressMonitor monitor) { monitor.beginTask(DdlImporterUiI18n.IMPORTING_DDL_MSG, 100); try { importer.importDdl(monitor, 100, options); } catch (Exception ex) { importException[0] = ex; } monitor.done(); } }); if (importException[0] != null) throw importException[0]; } catch (Exception ex) { String msg = I18n.format(DdlImporterUiI18n.DIFFERENCE_PAGE_DDLIMPORT_ERROR_MSG, ex.getLocalizedMessage()); DdlImporterUiPlugin.UTIL.log(IStatus.ERROR, ex, msg); importer.addProgressMessage(msg); } // Hard Failure (eg parse error) will show the error page if (importException[0] != null || importer.hasParseError()) { String title = null; String msg = null; this.stackLayout.topControl = parseErrorPanel; this.ddlContentsArea.setText(importer.getDdlString()); if (importException[0] != null) { title = DdlImporterUiI18n.DIFFERENCE_PAGE_GENERAL_ERROR_TITLE; msg = I18n.format(DdlImporterUiI18n.DIFFERENCE_PAGE_DDLIMPORT_ERROR_MSG, importException[0].getLocalizedMessage()); } else if (importer.hasParseError()) { title = DdlImporterUiI18n.DIFFERENCE_PAGE_PARSE_ERROR_TITLE; String parseErrorMessage = importer.getParseErrorMessage(); // Get the offSet of the error if set int offset = importer.getParseErrorIndex(); // Highlight the problem line if possible if (offset > -1) { StyledText styledText = ddlContentsArea.getTextWidget(); int line = styledText.getLineAtOffset(offset); if (line > -1 && styledText.getLineCount() > 1) { int startIndx = styledText.getOffsetAtLine(line); int endIndx = styledText.getOffsetAtLine(line + 1); styledText.setSelection(startIndx, endIndx); } } msg = I18n.format(DdlImporterUiI18n.DIFFERENCE_PAGE_PARSE_ERROR_MSG, parseErrorMessage); } setErrorMessage(msg); importer.undoImport(); this.setTitle(title); // Show differences page } else { this.stackLayout.topControl = differencesPanel; this.setTitle(DdlImporterUiI18n.DIFFERENCE_PAGE_TITLE); // Set the checkbox tree roots (create,delete,update) List<OperationList> rootList = new ArrayList<OperationList>(); DifferenceReport diffReport = this.importer.getDifferenceReport(); if (diffReport != null) { OperationList createObjs = this.importer.getDifferenceReport().getObjectsToCreate(); OperationList deleteObjs = this.importer.getDifferenceReport().getObjectsToDelete(); OperationList updateObjs = this.importer.getDifferenceReport().getObjectsToUpdate(); if (!createObjs.getList().isEmpty()) rootList.add(createObjs); if (!deleteObjs.getList().isEmpty()) rootList.add(deleteObjs); if (!updateObjs.getList().isEmpty()) rootList.add(updateObjs); } this.treeViewer.setInput(rootList); this.treeViewer.expandToLevel(2); this.setAllNodesSelected(true); List<String> progressMessages = this.importer.getAllMessages(); messagesList.setItems(progressMessages.toArray(new String[progressMessages.size()])); validate(); } this.stackPanel.layout(); } /** * validate the Differences Page */ void validate( ) { setErrorMessage(null); StringBuffer errMessageBuffer = new StringBuffer(); // Determine if there are any importer messages to show boolean hasImportMessages = importer.getAllMessages().isEmpty() ? false : true; DifferenceReport diffReport = importer.getDifferenceReport(); // No DifferenceReport to show if(diffReport==null) { errMessageBuffer.append(DdlImporterUiI18n.DIFFERENCE_PAGE_NO_DIFFERENCE_REPORT_MSG); } else { if( importer.noDdlImported() ) { // do nothing } else if(!diffReport.hasOperations()) { // DifferenceReport has no operations errMessageBuffer.append(DdlImporterUiI18n.DIFFERENCE_PAGE_NO_DIFFERENCE_OPERATIONS_MSG); // DifferenceReport has nothing selected } else if(!diffReport.hasSelectedOperations()) { errMessageBuffer.append(DdlImporterUiI18n.DIFFERENCE_PAGE_NO_DIFFERENCE_OPERATIONS_SELECTED_MSG); } } // Set error message if an error was found if(errMessageBuffer.length()>0) { if(hasImportMessages) { errMessageBuffer.append('\n'+DdlImporterUiI18n.DIFFERENCE_PAGE_SEE_IMPORT_MESSAGES_MSG); } setErrorMessage(errMessageBuffer.toString()); // Set information message if only import messages are found } else if(hasImportMessages) { setMessage(DdlImporterUiI18n.DIFFERENCE_PAGE_IMPORT_COMPLETED_WITH_MESSAGES_MSG); // Set description to finish } else { if( importer.noDdlImported() ) { setMessage(DdlImporterUiI18n.DIFFERENCE_PAGE_NO_DDL_IMPORTED_MSG); } else { setMessage(null); } setDescription(DdlImporterUiI18n.DIFFERENCE_PAGE_DESCRIPTION); } } /** * @return the options */ public Properties getOptions() { return options; } /** * @param options */ public void setOptions(Properties options) { this.options = options; } class CheckboxTreeLabelProvider extends LabelProvider { private final Image TABLE_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.TABLE_ICON); private final Image COLUMN_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.COLUMN_ICON); private final Image VIEW_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.TABLE_ICON); private final Image PROCEDURE_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.PROCEDURE_ICON); private final Image INDEX_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.INDEX_ICON); private final Image VIEW_TABLE_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.VIRTUAL_TABLE_ICON); private final Image VIRTUAL_PROCEDURE_IMG = UiPlugin.getDefault().getImage(UiConstants.Images.VIRTUAL_PROCEDURE_ICON); @Override public Image getImage( final Object node ) { if(node instanceof RelationalReference) { int type = ((RelationalReference)node).getType(); if(type==RelationalConstants.TYPES.TABLE) { if( importer.modelType() == ModelType.VIRTUAL_LITERAL) { return VIEW_TABLE_IMG; } return TABLE_IMG; } else if(type==RelationalConstants.TYPES.COLUMN) { return COLUMN_IMG; } else if(type==RelationalConstants.TYPES.VIEW) { if( importer.modelType() == ModelType.VIRTUAL_LITERAL) { return VIEW_TABLE_IMG; } return VIEW_IMG; } else if(type==RelationalConstants.TYPES.PROCEDURE) { if( importer.modelType() == ModelType.VIRTUAL_LITERAL) { return VIRTUAL_PROCEDURE_IMG; } return PROCEDURE_IMG; } else if(type==RelationalConstants.TYPES.INDEX) { return INDEX_IMG; } } return null; } @Override public String getText( final Object node ) { if(node instanceof OperationList) { OperationList opList = (OperationList)node; if(opList.getOperationType()==OperationList.OperationType.CREATE) { return DdlImporterUiI18n.DIFFERENCE_PAGE_CREATELIST_LABEL; } else if(opList.getOperationType()==OperationList.OperationType.DELETE) { return DdlImporterUiI18n.DIFFERENCE_PAGE_DELETELIST_LABEL; } else if(opList.getOperationType()==OperationList.OperationType.UPDATE) { return DdlImporterUiI18n.DIFFERENCE_PAGE_UPDATELIST_LABEL; } } else if(node instanceof RelationalReference) { return ((RelationalReference)node).getName(); } return DdlImporterUiI18n.DIFFERENCE_PAGE_UNKNOWN_LABEL; } } class CheckboxTreeContentProvider implements ITreeContentProvider { String[] EMPTY_STRING_ARRAY = new String[0]; @Override public void dispose() { } @Override public Object[] getChildren( final Object node ) { if (node instanceof List) { return ((List<?>)node).toArray(); } else if (node instanceof OperationList) { return ((OperationList)node).getList().toArray(); } return EMPTY_STRING_ARRAY; } @Override public Object[] getElements( final Object inputElement ) { return getChildren(inputElement); } @Override public Object getParent( final Object node ) { return null; } @Override public boolean hasChildren( final Object node ) { if (node instanceof OperationList) { return !((OperationList)node).getList().isEmpty(); } return false; } @Override public void inputChanged( final Viewer viewer, final Object oldInput, final Object newInput ) { } } class CheckboxTreeController extends DefaultTreeViewerController { /** * @see org.teiid.designer.ui.common.widget.DefaultTreeViewerController#checkedStateToggled(org.eclipse.swt.widgets.TreeItem) */ @Override public void checkedStateToggled( TreeItem item ) { Object dataObj = item.getData(); if(dataObj instanceof RelationalReference) { boolean isChecked = item.getChecked(); ((RelationalReference)dataObj).setChecked(isChecked); } validate(); } /** * @see org.teiid.designer.ui.common.widget.ITreeViewerController#isItemCheckable(org.eclipse.swt.widgets.TreeItem) */ @Override public boolean isItemCheckable( final TreeItem item ) { return true; } /** * <p> * </p> * * @see org.teiid.designer.ui.common.widget.ITreeViewerController#update(org.eclipse.swt.widgets.TreeItem, boolean) * @since 4.0 */ @Override public void update( final TreeItem item, final boolean selected ) { Object dataObj = item.getData(); if (dataObj != null) { final boolean checked = !WidgetUtil.isUnchecked(item); if (isItemCheckable(item)) { item.setChecked(checked); } item.setGrayed(WidgetUtil.isPartiallyChecked(item)); if (selected) { updateChildren(item, checked); for (TreeItem parent = item.getParentItem(); parent != null; parent = parent.getParentItem()) { int state = PARTIALLY_CHECKED; final TreeItem[] children = parent.getItems(); for (int ndx = children.length; --ndx >= 0;) { final TreeItem child = children[ndx]; if (WidgetUtil.isPartiallyChecked(child)) { state = PARTIALLY_CHECKED; break; } final int childState = WidgetUtil.getCheckedState(child); if (state == PARTIALLY_CHECKED) { state = childState; } else if (state != childState) { state = PARTIALLY_CHECKED; break; } } if (state != WidgetUtil.getCheckedState(parent)) { WidgetUtil.setCheckedState(parent, state, false, this); } } } if(dataObj instanceof RelationalReference) { boolean isChecked = item.getChecked(); ((RelationalReference)dataObj).setChecked(isChecked); } if (!isItemCheckable(item)) { item.setGrayed(true); item.setChecked(false); } else { item.setGrayed(false); } } } /** * @param item * @param checked * @since 4.0 */ private void updateChildren( final TreeItem item, final boolean checked ) { final TreeItem[] children = item.getItems(); for (int ndx = children.length; --ndx >= 0;) { final TreeItem child = children[ndx]; if (child.getData() != null) { updateChildren(child, checked); WidgetUtil.setChecked(child, checked, false, this); } } } /** * @see org.teiid.designer.ui.common.widget.ITreeViewerController#itemExpanded(org.eclipse.jface.viewers.TreeExpansionEvent) * @since 4.0 */ @Override public void itemExpanded( final TreeExpansionEvent event ) { // Do a length check on selection to avoid Array Index OOB exception if( ((TreeViewer)event.getTreeViewer()).getTree().getSelection().length > 0 ) { final TreeItem item = ((TreeViewer)event.getTreeViewer()).getTree().getSelection()[0]; if (item.getData() != null) { checkRequiredChildren(item); } } } /** * Set the checked states of the tree nodes based on data checked state * @param item the tree item */ public void checkRequiredChildren( TreeItem item ) { final TreeItem[] children = item.getItems(); for (int ndx = children.length; --ndx >= 0;) { final TreeItem child = children[ndx]; Object childData = child.getData(); if (childData != null && childData instanceof RelationalReference) { boolean isChecked = ((RelationalReference)childData).isChecked(); WidgetUtil.setChecked(child, isChecked, false, this); } } } @Override public void itemCollapsed( final TreeExpansionEvent event ) { super.itemCollapsed(event); } } private void setAllNodesSelected( boolean bSelected ) { TreeItem[] items = tree.getItems(); for (int i = 0; i < items.length; i++) { setAllSelected(items[i], bSelected); } } private void setAllSelected( final TreeItem item, final boolean checked ) { WidgetUtil.setChecked(item, checked, false, this.controller); // Apply same checked state to any children final TreeItem[] children = item.getItems(); for (int ndx = 0; ndx < children.length; ndx++) { setAllSelected(children[ndx], checked); } } }