/* * 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.mapping.ui.recursion; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.action.ControlContribution; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.source.VerticalRuler; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; 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.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Spinner; import org.eclipse.ui.texteditor.DefaultRangeIndicator; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.mapping.ui.PluginConstants; import org.teiid.designer.mapping.ui.UiConstants; import org.teiid.designer.mapping.ui.UiPlugin; import org.teiid.designer.mapping.ui.recursion.actions.ClearCriteria; import org.teiid.designer.mapping.ui.recursion.actions.LaunchCriteriaBuilder; import org.teiid.designer.metamodels.transformation.MappingClass; import org.teiid.designer.query.IQueryService; import org.teiid.designer.query.sql.ISQLStringVisitor; import org.teiid.designer.query.sql.lang.ILanguageObject; import org.teiid.designer.query.sql.lang.IQuery; import org.teiid.designer.transformation.ui.builder.CriteriaBuilder; import org.teiid.designer.transformation.validation.SqlTransformationResult; import org.teiid.designer.transformation.validation.TransformationValidator; import org.teiid.designer.ui.common.util.WidgetFactory; import org.teiid.designer.ui.viewsupport.ModelObjectUtilities; import org.teiid.query.ui.builder.util.ElementViewerFactory; /** * RecursionPanel * * @since 8.0 */ public class RecursionPanel extends SashForm implements SelectionListener, UiConstants, PluginConstants { private static final int BUTTON_GRID_STYLE = GridData.HORIZONTAL_ALIGN_BEGINNING; private RecursionObject roRecursionObject; private Composite pnlOuter; CheckBoxContribution recurseContribution; private Composite pnlCountControls; private Spinner spinCountLimit; private int upperRecursionLimit = 10; private CLabel lblErrorIfLimitExceeded; private Combo cbxErrorIfLimitExceeded; private Button btnEditCriteria; boolean listenToTextChange = false; static final String RECURSE_QUERY_TEXT = UiConstants.Util.getString("RecursionPanel.recurseQuery.text"); //$NON-NLS-1$ private static final String COUNT_LIMIT_TEXT = UiConstants.Util.getString("RecursionPanel.countLimit.text"); //$NON-NLS-1$ private static final String ERROR_IF_EXCEEDED_CHECKBOX_TEXT = UiConstants.Util.getString("RecursionPanel.errorIfExceededCheckBox.text"); //$NON-NLS-1$ private static final String LIMIT_CONDITION_TEXT = UiConstants.Util.getString("RecursionPanel.limitCondition.text"); //$NON-NLS-1$ private static final String EDIT_LIMIT_CONDITION_TEXT = UiConstants.Util.getString("RecursionPanel.editButton.text"); //$NON-NLS-1$ private static final String EDIT_LIMIT_CONDITION_TOOLTIP = UiConstants.Util.getString("RecursionPanel.editButton.text"); //$NON-NLS-1$ private Composite pnlConditionControls; private CLabel lblLimitCondition; private SourceViewer svrRecursionConditionCriteria; private Document docRecursionConditionCriteria; private String EMPTY_STRING = ""; //$NON-NLS-1$ private String DEFAULT_SELECT = "Select * from Dummy Where "; //$NON-NLS-1$ protected final static int VERTICAL_RULER_WIDTH = 12; private int CHECKBOX_INDENT = VERTICAL_RULER_WIDTH; String sConditionSql = EMPTY_STRING; private LaunchCriteriaBuilder actLaunchCriteriaBuilder; private ClearCriteria actClearCriteria; private static final int LABEL_GRID_STYLE = GridData.HORIZONTAL_ALIGN_BEGINNING; private static final String EDIT_BUTTON_TOOLTIP = UiConstants.Util.getString("RecursionPanel.editAction.toolTip"); //$NON-NLS-1$ private static final String CLEAR_BUTTON_TOOLTIP = UiConstants.Util.getString("RecursionPanel.clearAction.toolTip"); //$NON-NLS-1$ /** * Constructor. * * @param parent Parent of this control */ public RecursionPanel( Composite parent, RecursionObject roRecursionObject ) { super(parent, SWT.VERTICAL); this.roRecursionObject = roRecursionObject; init(); } public RecursionObject getRecursionObject() { return roRecursionObject; } /** * Initialize the panel. */ private void init() { // ---------------------------------- // Create the Controls (Top) Panel // ---------------------------------- createControl(this); // Initialize the Button states setButtonStates(); // init with content if available if (roRecursionObject != null) { // getRecursionObject(); refreshFromBusinessObject(); } else { // System.out.println("[RecursionPanel.init] icoChoiceObject is NULL"); //$NON-NLS-1$ } } public void setBusinessObject( RecursionObject roRecursionObject ) { this.roRecursionObject = roRecursionObject; // when the business object changes, refresh everything... refreshFromBusinessObject(); } public void refreshFromBusinessObject() { listenToTextChange = false; // System.out.println("[RecursionPanel.refreshFromBusinessObject] TOP"); //$NON-NLS-1$ docRecursionConditionCriteria.set(getRecursionObject().getRecursionCriteria()); // Select the current error mode in the combobox, or default to first one String sText = getRecursionObject().getRecursionLimitErrorMode(); if (sText != null && !sText.equals(EMPTY_STRING)) { int iIndex = cbxErrorIfLimitExceeded.indexOf(sText); if (iIndex > -1) { cbxErrorIfLimitExceeded.select(iIndex); } else { cbxErrorIfLimitExceeded.select(0); } } else { cbxErrorIfLimitExceeded.select(0); } // set the value of the spinner restoreRecursionLimit(); spinCountLimit.setSelection(getRecursionObject().getRecursionLimit()); // set the value of the checkbox getCheckBoxContributionForRecurseQuery().setSelection(getRecursionObject().isRecursive()); // update the enable state of the buttons and actions setButtonStates(); listenToTextChange = true; } private void restoreRecursionLimit() { // set the value of the spinner upperRecursionLimit = ModelerCore.getTransformationPreferences().getUpperRecursionLimit(); upperRecursionLimit = Math.max(upperRecursionLimit, getRecursionObject().getRecursionLimit()); spinCountLimit.setMinimum(1); spinCountLimit.setMaximum(upperRecursionLimit); spinCountLimit.setToolTipText(UiConstants.Util.getString("RecursionPanel.limitSpinner.toolTip", //$NON-NLS-1$ spinCountLimit.getMinimum(), spinCountLimit.getMaximum())); } /** * @See org.teiid.designer.ui.editors.ModelObjectEditor#createControl(org.eclipse.swt.widgets.Composite) */ public void createControl( Composite parent ) { // ------------------------------ // Set layout for the SashForm // ------------------------------ GridLayout gridLayout = new GridLayout(); this.setLayout(gridLayout); gridLayout.numColumns = 1; GridData gridData = new GridData(GridData.FILL_BOTH); gridData.widthHint = 600; gridData.heightHint = 400; this.setLayoutData(gridData); pnlOuter = new Composite(this, SWT.NONE); gridLayout = new GridLayout(); pnlOuter.setLayout(gridLayout); gridLayout.numColumns = 1; // 1. Create the count controls panel createCountControlsPanel(pnlOuter); // 2. Create the condition panel createConditionControlsPanel(pnlOuter); } private void createCountControlsPanel( Composite parent ) { pnlCountControls = new Composite(parent, SWT.NONE); GridLayout gridLayout = new GridLayout(); pnlCountControls.setLayout(gridLayout); gridLayout.numColumns = 4; // 'count limit' label // lblCountLimit = WidgetFactory.createLabel(pnlCountControls, LABEL_GRID_STYLE, COUNT_LIMIT_TEXT); // 'count limit' spinner spinCountLimit = new Spinner(pnlCountControls, SWT.NONE); // Reset from preferences restoreRecursionLimit(); spinCountLimit.addModifyListener(new ModifyListener() { @Override public void modifyText( ModifyEvent theEvent ) { if (listenToTextChange) handleSpinnerChanged(); } }); // 'error if exceeded' label lblErrorIfLimitExceeded = WidgetFactory.createLabel(pnlCountControls, LABEL_GRID_STYLE, ERROR_IF_EXCEEDED_CHECKBOX_TEXT); GridData gridData3 = new GridData(); gridData3.horizontalIndent = 7; lblErrorIfLimitExceeded.setLayoutData(gridData3); // 'error if exceeded' checkbox createErrorIfCountExceededCombobox(pnlCountControls); } private void createErrorIfCountExceededCombobox( Composite parent ) { cbxErrorIfLimitExceeded = new Combo(parent, SWT.READ_ONLY | SWT.BORDER); cbxErrorIfLimitExceeded.setToolTipText(ERROR_IF_EXCEEDED_CHECKBOX_TEXT); cbxErrorIfLimitExceeded.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { handleCountExceededComboboxPressed(); } }); // load ErrorModeValues String[] saErrorModeVals = getRecursionObject().getValidErrorModeValues(); for (int i = 0; i < saErrorModeVals.length; i++) { cbxErrorIfLimitExceeded.add(saErrorModeVals[i]); } } void handleCountExceededComboboxPressed() { int iSelectedIndex = cbxErrorIfLimitExceeded.getSelectionIndex(); if (iSelectedIndex < 0) { return; } String sErrorMode = cbxErrorIfLimitExceeded.getItem(iSelectedIndex); getRecursionObject().setRecursionLimitErrorMode(sErrorMode); } void handleRecursiveQueryCheckBoxChanged() { getRecursionObject().setRecursive(getCheckBoxContributionForRecurseQuery().getSelection()); getCheckBoxContributionForRecurseQuery().getControl().update(); setButtonStates(); } void handleSpinnerChanged() { int newValue = spinCountLimit.getSelection(); if (newValue != this.roRecursionObject.getRecursionLimit()) { getRecursionObject().setRecursionLimit(newValue); } } private void createConditionControlsPanel( Composite parent ) { pnlConditionControls = new Composite(parent, SWT.NONE); GridLayout gridLayout = new GridLayout(); pnlConditionControls.setLayout(gridLayout); gridLayout.numColumns = 3; GridData gridData = new GridData(GridData.FILL_BOTH); gridData.grabExcessHorizontalSpace = true; gridData.grabExcessVerticalSpace = true; pnlConditionControls.setLayoutData(gridData); // 'limit condition' label lblLimitCondition = WidgetFactory.createLabel(pnlConditionControls, LABEL_GRID_STYLE, LIMIT_CONDITION_TEXT); GridData gridData3 = new GridData(); gridData3.horizontalIndent = CHECKBOX_INDENT; lblLimitCondition.setLayoutData(gridData3); // 'edit criteria' button btnEditCriteria = WidgetFactory.createButton(pnlConditionControls, EDIT_LIMIT_CONDITION_TEXT /*, BUTTON_GRID_STYLE */); btnEditCriteria.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { handleEditButtonClicked(); } }); btnEditCriteria.setToolTipText(EDIT_LIMIT_CONDITION_TOOLTIP); GridData gridData4 = new GridData(); gridData4.horizontalIndent = 5; btnEditCriteria.setLayoutData(gridData4); // 'filler' label for edit button row CLabel lblFiller = WidgetFactory.createLabel(pnlConditionControls, LABEL_GRID_STYLE, EMPTY_STRING); GridData gridData5 = new GridData(GridData.FILL_BOTH); gridData5.grabExcessHorizontalSpace = true; lblFiller.setLayoutData(gridData5); // create the SourceViewer for criteria VerticalRuler verticalRuler = new VerticalRuler(VERTICAL_RULER_WIDTH); int styles = SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.FULL_SELECTION; svrRecursionConditionCriteria = new SourceViewer(pnlConditionControls, verticalRuler, styles); // create the document docRecursionConditionCriteria = new Document(); svrRecursionConditionCriteria.setDocument(docRecursionConditionCriteria); svrRecursionConditionCriteria.setEditable(false); svrRecursionConditionCriteria.setRangeIndicator(new DefaultRangeIndicator()); GridData gridData2 = new GridData(GridData.FILL_BOTH); gridData2.horizontalAlignment = GridData.FILL; gridData2.verticalAlignment = GridData.FILL; gridData2.horizontalSpan = 3; gridData2.grabExcessHorizontalSpace = true; gridData2.grabExcessVerticalSpace = true; svrRecursionConditionCriteria.getControl().setLayoutData(gridData2); } void handleEditButtonClicked() { launchCriteriaBuilder(); } public void contributeToolbarActions( ToolBarManager toolBarMgr ) { toolBarMgr.removeAll(); toolBarMgr.add(getCheckBoxContributionForRecurseQuery()); toolBarMgr.add(new Separator()); toolBarMgr.add(getLaunchCriteriaBuilderAction()); toolBarMgr.add(getClearCriteriaBuilderAction()); toolBarMgr.update(true); getCheckBoxContributionForRecurseQuery().setEnabled(true); getCheckBoxContributionForRecurseQuery().setSelection(getRecursionObject().isRecursive()); setButtonStates(); } /** * Set the enabled/disabled states of the Buttons. */ private void setButtonStates() { if (ModelObjectUtilities.isReadOnly(getRecursionObject().getMappingClass())) { spinCountLimit.setEnabled(false); cbxErrorIfLimitExceeded.setEnabled(false); if (getCheckBoxContributionForRecurseQuery().getControl() != null) { getCheckBoxContributionForRecurseQuery().getControl().setEnabled(false); } btnEditCriteria.setEnabled(false); getClearCriteriaBuilderAction().selectionChanged(); getLaunchCriteriaBuilderAction().selectionChanged(); return; } if (getCheckBoxContributionForRecurseQuery().getSelection() == false) { spinCountLimit.setEnabled(false); cbxErrorIfLimitExceeded.setEnabled(false); } else { spinCountLimit.setEnabled(true); cbxErrorIfLimitExceeded.setEnabled(true); } // endif spinCountLimit.update(); cbxErrorIfLimitExceeded.update(); getClearCriteriaBuilderAction().selectionChanged(); getLaunchCriteriaBuilderAction().selectionChanged(); // after updating the Launch Criteria action, set the button enabled state based on it: btnEditCriteria.setEnabled(canLaunchCriteriaBuilder()); } private CheckBoxContribution getCheckBoxContributionForRecurseQuery() { // CheckBoxContribution if (recurseContribution == null) { recurseContribution = new CheckBoxContribution(RECURSE_QUERY_TEXT); } return recurseContribution; } private LaunchCriteriaBuilder getLaunchCriteriaBuilderAction() { if (actLaunchCriteriaBuilder == null) { actLaunchCriteriaBuilder = new LaunchCriteriaBuilder(this); actLaunchCriteriaBuilder.setToolTipText(EDIT_BUTTON_TOOLTIP); } return actLaunchCriteriaBuilder; } private ClearCriteria getClearCriteriaBuilderAction() { if (actClearCriteria == null) { actClearCriteria = new ClearCriteria(this); actClearCriteria.setToolTipText(CLEAR_BUTTON_TOOLTIP); } return actClearCriteria; } public boolean canLaunchCriteriaBuilder() { boolean enable = !ModelObjectUtilities.isReadOnly(getRecursionObject().getMappingClass()); // if not readonly model ok to launch if mapping class has columns if (enable) { enable = !getRecursionObject().getMappingClass().getColumns().isEmpty(); } return enable; } /** * This method launches the Criteria Builder Dialog. If the current SQLTextPanel caret is currently within a criteria, the * Criteria Builder is launched with the supplied criteria. The supplied criteria is replaced with the modified criteria when * the builder is dismissed. If the caret is not within a criteria, the Criteria Builder is launched without a criteria. The * resulting criteria is inserted into the editor panel. * * @param index the index to launch the builder from. */ public void launchCriteriaBuilder() { MappingClass mc = getRecursionObject().getMappingClass(); /* * jh note: BuilderTreeProvider must always be created in this method, * and NEVER maintained in an instance variable between uses. * This is because its constructor modifies the state of the * static class ElementViewerFactory. */ new BuilderTreeProvider(); ElementViewerFactory.setCriteriaStrategy(new RecursionCriteriaStrategy()); List<MappingClass> lstMappingClassWrapper = new ArrayList<MappingClass>(1); lstMappingClassWrapper.add(mc); ElementViewerFactory.setViewerInput(lstMappingClassWrapper); CriteriaBuilder builder = getCriteriaBuilder(); // launch Criteria Builder with the selected language object or with // null to start off with undefined language object // get the sql string, if any for currently selected option String sSql = getRecursionObject().getRecursionCriteria(); // set the language object if (sSql != null && !sSql.trim().equals(EMPTY_STRING)) { builder.setLanguageObject(getCommand(sSql).getCriteria()); } else { builder.setLanguageObject(null); } // ------------------------------------------------------------------------- // Display the Dialog // ------------------------------------------------------------------------- int status = builder.open(); // ------------------------------------------------------------------------- // Insert or Replace when Dialog is OK'd, do nothing if cancelled // ------------------------------------------------------------------------- if (status == Window.OK) { // retrieve the new sql from the criteria builder ILanguageObject newCriteria = builder.getLanguageObject(); IQueryService queryService = ModelerCore.getTeiidQueryService(); ISQLStringVisitor visitor = queryService.getSQLStringVisitor(); String sCriteriaString = visitor.returnSQLString(newCriteria); // this: updateCriteriaForSelectedRow( criteriaString ); docRecursionConditionCriteria.set(sCriteriaString); getRecursionObject().setRecursionCriteria(sCriteriaString); } getLaunchCriteriaBuilderAction().selectionChanged(); setButtonStates(); } private IQuery getCommand( String sSql ) { String sCommand = DEFAULT_SELECT + sSql; SqlTransformationResult result = TransformationValidator.parseSQL(sCommand); return (IQuery)result.getCommand(); } private CriteriaBuilder getCriteriaBuilder() { Shell shell = UiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(); CriteriaBuilder criteriaBuilder = new CriteriaBuilder(shell); criteriaBuilder.create(); return criteriaBuilder; } public boolean canClearCriteria() { if (ModelObjectUtilities.isReadOnly(getRecursionObject().getMappingClass())) { return false; } if (docRecursionConditionCriteria.get().equals(EMPTY_STRING)) { return false; } return true; } public void clearCriteria() { // System.out.println("[RecursionPanel.clearCriteria] " ); //$NON-NLS-1$ docRecursionConditionCriteria.set(EMPTY_STRING); getRecursionObject().setRecursionCriteria(EMPTY_STRING); setButtonStates(); } @Override public void widgetSelected( SelectionEvent e ) { // System.out.println("[RecursionPanel.widgetSelected] e.getSource() is: " + e.getSource() ); //$NON-NLS-1$ // may not need a class-level widgetSelected } @Override public void widgetDefaultSelected( SelectionEvent e ) { widgetSelected(e); } class CheckBoxContribution extends ControlContribution { private Button chkRecurseQuery; Combo cbx = null; public CheckBoxContribution( String id ) { super(id); } /** * @see org.eclipse.jface.action.ControlContribution#createControl(org.eclipse.swt.widgets.Composite) */ @Override protected Control createControl( Composite parent ) { chkRecurseQuery = WidgetFactory.createCheckBox(parent, RECURSE_QUERY_TEXT, BUTTON_GRID_STYLE); chkRecurseQuery.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { handleRecursiveQueryCheckBoxChanged(); } }); // defect 15601 -- keep track of disposal; we will need to recreate the // entire contribution when this happens. // I call this solution evil and messy, but it matches pretty well how // the rest of the class is written. chkRecurseQuery.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed( DisposeEvent e ) { recurseContribution = null; } }); chkRecurseQuery.setEnabled(true); if (getRecursionObject() != null) { chkRecurseQuery.setSelection(getRecursionObject().isRecursive()); } else { chkRecurseQuery.setSelection(true); } return chkRecurseQuery; } public Control getControl() { return chkRecurseQuery; } public void setSelection( boolean b ) { if (chkRecurseQuery != null) { chkRecurseQuery.setSelection(b); } // endif } public boolean getSelection() { if (chkRecurseQuery != null) { return chkRecurseQuery.getSelection(); } // endif // not initialized yet, default to 'true' return true; } public void setEnabled( boolean enabled ) { if (chkRecurseQuery != null) { chkRecurseQuery.setEnabled(enabled); } // endif } } }