/******************************************************************************* * Copyright (c) 2005, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.bpel.ui.properties; import java.util.ArrayList; import org.eclipse.bpel.common.ui.details.IDetailsAreaConstants; import org.eclipse.bpel.common.ui.details.widgets.DecoratedLabel; import org.eclipse.bpel.common.ui.details.widgets.StatusLabel2; import org.eclipse.bpel.common.ui.flatui.FlatFormAttachment; import org.eclipse.bpel.common.ui.flatui.FlatFormData; import org.eclipse.bpel.model.BPELFactory; import org.eclipse.bpel.model.Expression; import org.eclipse.bpel.model.util.BPELConstants; import org.eclipse.bpel.ui.BPELUIPlugin; import org.eclipse.bpel.ui.Messages; import org.eclipse.bpel.ui.commands.CompoundCommand; import org.eclipse.bpel.ui.commands.SetCommand; import org.eclipse.bpel.ui.details.providers.ExpressionEditorDescriptorContentProvider; import org.eclipse.bpel.ui.expressions.IEditorConstants; import org.eclipse.bpel.ui.expressions.IExpressionEditor; import org.eclipse.bpel.ui.extensions.BPELUIRegistry; import org.eclipse.bpel.ui.extensions.ExpressionEditorDescriptor; import org.eclipse.bpel.ui.util.BPELUtil; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.gef.commands.Command; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.layout.FillLayout; 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.Label; /** * Base class with some shared behavior for details panes that edit an expression. * * NB: Nov 2nd, 2006 * * The code used CComboViewer and CCombo to show the expression language drop down. * There used to be a lot of code that dealt with refresh and selection disappearing in the * CCombo to the point that the XPath editor was created and destroyed 14 times every time a * object was selected in the process map that required this particular expression section. * * I debugged the code, I swear it was this bad. * * I had done some cleanup here to make it easier to understand because there is truly a little * too much magic for this simple thing. So ... * * <ol> * <li> The expression language is a Combo, wrapped in a ComboViewer. * <li> The ComboViewer has a content provider which it will refresh every time a setInput is * used on the ComboViewer. Any other refresh logic is not necessary. * <li> The section is configured when the basicSetInput is called. Then the editor is picked,created, etc. * <li> There is a general problem with refresh() method calls, .aboutToBeShown(), etc. many of them simply * used to call refresh() which just destroyed and re-created the views. * * </ol> * * @author Michal Chmielewski (michal.chmielewski@oracle.com) * */ public abstract class ExpressionSection extends TextSection { protected String editorLanguage; protected ComboViewer expressionLanguageViewer; protected ExpressionComboContentProvider expressionComboContentProvider; /** The editor area composite, it is used to display editors or the no-editor widgets in it */ protected Composite fEditorArea ; // Pseudo-model object to represent no expression at all (in which case no editor // is used). protected static final Object NO_EXPRESSION = new Object(); // Pseudo-model object to represent the absence of an expression language within // the expression (i.e. the expression language is inherited from the Process). protected static final Object SAME_AS_PARENT = new Object(); /** The composite which holds the no-editor widgets */ protected Composite fNoEditorWidgets; /** The parent composite, it owns the expression language combo and the editor area */ protected Composite fParentComposite; protected Font boldFont; protected String title; protected Label titleLabel; StackLayout fEditorAreaStackLayout; Composite fEditorComposite; protected StatusLabel2 expressionLanguageLabel; /** The EMF structural feature that is this expression */ protected EStructuralFeature fStructuralFeature; protected Button fCreateExpressionButton; Combo expressionLanguageCombo; protected static boolean objectsEqual(Object lhs, Object rhs) { if (lhs == null) return (rhs == null); return lhs.equals(rhs); } /** * A content provider which (1) adds the NO_EXPRESSION and SAME_AS_PARENT elements, * (2) ensures that the selected object is in the list, and (3) removes any * elements from the list which are not supported (unless they happen to be * the selected object at the moment--an error case). */ class ExpressionComboContentProvider extends ExpressionEditorDescriptorContentProvider { Object selectedObject = SAME_AS_PARENT; /** * @see org.eclipse.bpel.ui.details.providers.AbstractContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements(Object inputElement) { Object[] descriptors = super.getElements(inputElement); ArrayList<Object> result = new ArrayList<Object>(descriptors.length + 2); if (isExpressionOptional() || selectedObject==NO_EXPRESSION) { result.add(NO_EXPRESSION); } if (selectedObject==SAME_AS_PARENT || allowItem(SAME_AS_PARENT)) { result.add(SAME_AS_PARENT); } for (Object descriptor : descriptors) { if (objectsEqual(selectedObject, descriptor) || allowItem(descriptor)) { result.add(descriptor); } } if (!result.contains(selectedObject)) { result.add(selectedObject); } return result.toArray(); } /** * * @param element * @return true if item is to be allowed, false otherwise. */ public boolean allowItem(Object element) { String language = getEffectiveLanguage(getExpressionLanguage(element)); try { IExpressionEditor exEditor = BPELUIRegistry.getInstance().getExpressionEditor(language); return (exEditor == null)? false : isEditorSupported(exEditor); } catch (CoreException e) { BPELUIPlugin.log(e); return false; } } } @Override protected void addAllAdapters() { super.addAllAdapters(); Expression e = getExprFromModel(); if (e != null) { fAdapters[0].addToObject(e); } } /** * Get rid of the current editor. * We do this when the Expression Language changes and we have to replace the editor with the right * editor for the expression language. */ @Override protected void disposeEditor() { super.disposeEditor(); editorLanguage = null; if (fEditorComposite != null) { fEditorComposite.dispose(); fEditorComposite = null; } } protected Object getDefaultBody (String newLanguage, String exprType ) { IExpressionEditor ed = null; try { newLanguage = getEffectiveLanguage(newLanguage); ed = BPELUIRegistry.getInstance().getExpressionEditor(newLanguage); } catch (CoreException e) { BPELUIPlugin.log(e); return EMPTY_STRING; } // TODO: call supportsExpressionType in the right place ed.setExpressionType(exprType); ed.setModelObject( getInput() ); return ed.getDefaultContent() ; } protected void createTitleWidgets(Composite composite) { FlatFormData data; titleLabel = fWidgetFactory.createLabel(composite, title); titleLabel.setFont(boldFont); data = new FlatFormData(); data.left = new FlatFormAttachment(0, 0); data.top = new FlatFormAttachment(0, 0); data.right = new FlatFormAttachment(100, 0); titleLabel.setLayoutData(data); } protected void createExpressionLanguageWidgets(final Composite composite) { FlatFormData data; DecoratedLabel nameLabel = new DecoratedLabel(composite,SWT.LEFT); fWidgetFactory.adapt(nameLabel); nameLabel.setText( Messages.ExpressionSection_Expression_language_1); expressionLanguageLabel = new StatusLabel2( nameLabel ); expressionLanguageCombo = new Combo (composite, SWT.FLAT | SWT.READ_ONLY ); fWidgetFactory.adapt(expressionLanguageCombo); expressionLanguageViewer = new ComboViewer(expressionLanguageCombo); data = new FlatFormData(); data.left = new FlatFormAttachment(0, BPELUtil.calculateLabelWidth(nameLabel, STANDARD_LABEL_WIDTH_LRG)); data.right = new FlatFormAttachment(100, 0); if (this.title != null) { data.top = new FlatFormAttachment(this.titleLabel, IDetailsAreaConstants.VSPACE); } else { data.top = new FlatFormAttachment(0, 0); } expressionLanguageViewer.getControl().setLayoutData(data); expressionLanguageViewer.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { if (element == NO_EXPRESSION) { return Messages.ExpressionSection_No_Expression_2; } if (element == SAME_AS_PARENT) { String text = getBPELEditor().getProcess().getExpressionLanguage(); if (text == null) { text = BPELConstants.XMLNS_XPATH_EXPRESSION_LANGUAGE; } ExpressionEditorDescriptor descriptor = BPELUIRegistry.getInstance().getExpressionEditorDescriptor(text); if (descriptor != null) { text = descriptor.getLabel(); } return NLS.bind(Messages.ExpressionSection_Same_as_Process_1, (new Object[] { text })); } if (element instanceof String) { return (String)element; } ExpressionEditorDescriptor descriptor = (ExpressionEditorDescriptor) element; String text = descriptor.getLabel(); return (text != null) ? text : descriptor.getExpressionLanguage(); } }); expressionComboContentProvider = new ExpressionComboContentProvider(); expressionLanguageViewer.setContentProvider(expressionComboContentProvider); expressionLanguageViewer.setInput( SAME_AS_PARENT ); data = new FlatFormData(); data.left = new FlatFormAttachment(0, 0); data.right = new FlatFormAttachment(expressionLanguageViewer.getControl(), -IDetailsAreaConstants.HSPACE); data.top = new FlatFormAttachment(expressionLanguageViewer.getControl(), 0, SWT.CENTER); expressionLanguageLabel.setLayoutData(data); // Selection on the combo. expressionLanguageCombo.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } public void widgetSelected(SelectionEvent e) { updateFromSelection ( selectedExpressionLanguage() ); } }); } protected void updateFromSelection ( Object elm ) { Command cmd; if (elm == NO_EXPRESSION || elm == null ) { cmd = new SetCommand(getExpressionTarget(), null, getStructuralFeature() ); } else { String language = getExpressionLanguage(elm); Expression exp = BPELFactory.eINSTANCE.createCondition(); exp.setExpressionLanguage(language); Object newDefaultBody = getDefaultBody(language, getExpressionType() ); // Figure out what the new default value should be for this expression, // and install it in the model. It is necessary to do this before we // properly create the editor (which happens when we update widgets in // response to the model change). exp.setBody(newDefaultBody); cmd = new SetCommand(getExpressionTarget(), getExpression4Target (exp), getStructuralFeature()) ; } getCommandFramework().execute( wrapInShowContextCommand(cmd) ); } /** * Return the currently selected expression language. * * @return the currently selected expression language */ protected Object selectedExpressionLanguage () { IStructuredSelection selection = (IStructuredSelection) expressionLanguageViewer.getSelection(); return selection.getFirstElement(); } /** * This method is used by subclasses who need to select a language programmatically. * */ protected void doChooseExpressionLanguage (Object model) { Object selection = selectedExpressionLanguage(); if (selection == model) { return ; } // By setting the selection on the combo box, we are kicking off a selection changed event. // This is intentional, because we are emulating the a user interface command. expressionLanguageViewer.setSelection(new StructuredSelection( model ), true); updateFromSelection( selectedExpressionLanguage() ); } protected void updateExpressionLanguageWidgets() { Object model = NO_EXPRESSION; if (getExprFromModel() != null) { String language = getExpressionLanguageFromModel(); if (language == null) { model = SAME_AS_PARENT; } else { model = BPELUIRegistry.getInstance().getExpressionEditorDescriptor(language); if (model == null) { model = language; } } } expressionLanguageViewer.setSelection( new StructuredSelection( model ), true ); // Reflect the model in the editor updateEditor(); // Markers updateMarkers(); } /** * When this method is called, the section has already been created. * * The widgets are available to be configured, however, the section may not be shown yet. * * The concept here is that we are reflecting the input selected in the UI. So the path that is taken * by this code must not create any re-doable commands, just set the widgets to what they are supposed to * be based on the model. */ @Override protected void basicSetInput (EObject newInput) { super.basicSetInput(newInput); /** Figure out based in the input, what EMF structural feature we are setting */ setStructuralFeature ( getStructuralFeature (newInput) ); // A different input may have different expression language settings. expressionLanguageViewer.refresh(true); // Reveal the right selection in the widget. updateExpressionLanguageWidgets(); } protected void setStructuralFeature ( EStructuralFeature feature ) { fStructuralFeature = feature; } /** * Get the structural feature of this input object. * This will be something like BPELPackage.eINSTANCE.getWait_Until() * * @param eObject * @return the structural feature that we will be setting. */ protected EStructuralFeature getStructuralFeature ( EObject eObject ) { return null; } /** * Return the previously computed structural feature of the input object. * @return */ protected EStructuralFeature getStructuralFeature () { return fStructuralFeature; } protected boolean isExpressionOptional() { return true; } protected String getExpressionType() { return IEditorConstants.ET_ANY ; } /** * The expression target is the target object on which we can execute * the SetCommand(target,object,structural-feature). * * In most cases, it is just the input of the section. But in some cases * the input if the section does not match the target, so sub-classes * may override this method. * * @return */ protected EObject getExpressionTarget () { return getInput(); } /** * Returns the expressionLanguage string underlying the given combo element. For * cases other than NO_EXPRESSION, this is the proper value to store into the model. */ protected String getExpressionLanguage (Object comboElement) { if (comboElement == NO_EXPRESSION || comboElement == SAME_AS_PARENT) { return null; } String language = null; if (comboElement instanceof ExpressionEditorDescriptor) { language = ((ExpressionEditorDescriptor)comboElement).getExpressionLanguage(); } else if (comboElement instanceof String) { language = (String)comboElement; } if (EMPTY_STRING.equals(language)) { language = null; } return language; } /** * Returns the expression language which will be in effect if this language is * read from/stored into the model. (i.e. if language is null, the value is * inherited from the process or the default, XPath). */ protected String getEffectiveLanguage(String language) { if (language == null) { language = getBPELEditor().getProcess().getExpressionLanguage(); if (language == null) { language = BPELConstants.XMLNS_XPATH_EXPRESSION_LANGUAGE; } } return language; } /** * Return the actual namespace from the expression, or null if not set. */ protected String getExpressionLanguageFromModel() { Expression expression = getExprFromModel(); if (expression == null) { return null; } return expression.getExpressionLanguage(); } protected Expression getExprFromModel() { EObject target = getExpressionTarget(); if (target != null) { Object result = target.eGet( getStructuralFeature() ); if (result instanceof Expression) { return (Expression) result; } } return null; } protected Expression getExpression4Target ( Expression expression ) { return expression; } @Override protected boolean isBodyAffected(Notification n) { if (n.getOldValue() instanceof Expression || n.getNewValue() instanceof Expression || n.getNotifier() instanceof Expression) { return true; } return n.getFeature() == getStructuralFeature(); } @Override protected Command newStoreToModelCommand (Object body) { CompoundCommand result = new CompoundCommand(); Expression oldExp = getExprFromModel(); Expression exp = BPELFactory.eINSTANCE.createCondition(); // Don't set the language, because if the user has changed the // language, a condition would already exist at this point. if (oldExp != null) { exp.setExpressionLanguage(oldExp.getExpressionLanguage()); } exp.setBody(body); result.add (new SetCommand(getExpressionTarget(), getExpression4Target(exp) , getStructuralFeature() )); fEditor.addExtraStoreCommands (result); return result; } /** * Create the client area. This is just done once. */ @Override protected void createClient (Composite parent) { fParentComposite = createFlatFormComposite(parent); if (this.title != null) { createBoldFont(fParentComposite); createTitleWidgets(fParentComposite); } createExpressionLanguageWidgets(fParentComposite); FlatFormData data = new FlatFormData(); data.top = new FlatFormAttachment(expressionLanguageViewer.getControl(),IDetailsAreaConstants.VSPACE); data.left = new FlatFormAttachment(0,0); data.right = new FlatFormAttachment(100,0); data.bottom = new FlatFormAttachment(100,0); fEditorArea = fWidgetFactory.createComposite(fParentComposite); fEditorAreaStackLayout = new StackLayout(); fEditorArea.setLayout( fEditorAreaStackLayout ); fEditorArea.setLayoutData(data); fNoEditorWidgets = createNoEditorWidgets(fEditorArea); fEditorAreaStackLayout.topControl = fNoEditorWidgets; createChangeHelper(); } protected Composite createNoEditorWidgets (Composite composite) { return fWidgetFactory.createComposite(composite); } protected Composite createNoEditorWidgetsCreateComposite (Composite composite, String message, String buttonMessage ) { FlatFormData ffdata; Composite section = createFlatFormComposite(composite); Label label = fWidgetFactory.createLabel(section,EMPTY_STRING, SWT.WRAP | SWT.READ_ONLY ); ffdata = new FlatFormData(); ffdata.left = new FlatFormAttachment(0, IDetailsAreaConstants.VSPACE ); ffdata.top = new FlatFormAttachment(0, IDetailsAreaConstants.VSPACE * 2); ffdata.right = new FlatFormAttachment(100, 0); label.setLayoutData(ffdata); fCreateExpressionButton = fWidgetFactory.createButton(section, buttonMessage , SWT.PUSH); ffdata = new FlatFormData(); ffdata.left = new FlatFormAttachment(0, IDetailsAreaConstants.VSPACE); ffdata.top = new FlatFormAttachment(label, IDetailsAreaConstants.VSPACE * 2); fCreateExpressionButton.setLayoutData(ffdata); fCreateExpressionButton.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { doChooseExpressionLanguage(SAME_AS_PARENT); } public void widgetDefaultSelected(SelectionEvent e) { } }); label.setText(message); return section; } /** * Create the editor. * */ @Override protected void createEditor (Composite parent) { String language = getEffectiveLanguage(getExpressionLanguageFromModel()); try { fEditor = BPELUIRegistry.getInstance().getExpressionEditor(language); } catch (CoreException e) { BPELUIPlugin.log(e); return ; } editorLanguage = language; fEditor.createControls(parent, getWidgetFactory()); fEditor.addListener(new IExpressionEditor.Listener() { public void notifyChanged() { if (!updating) { // && !isExecutingStoreCommand()) { notifyEditorChanged(); } } public void focusOut() { // If expression editor has lost focus then apply current changes getCommandFramework().applyCurrentChange(); } public void focusIn() { // TODO Auto-generated method stub } }); } /** * We update the editor from the model. This is a model-to-ui operation. * */ @Override protected void updateEditor() { Expression expr = getExprFromModel(); Control previousTop = fEditorAreaStackLayout.topControl; if (expr == null) { fEditorAreaStackLayout.topControl = fNoEditorWidgets; } else { String newLanguage = getEffectiveLanguage(getExpressionLanguageFromModel()); if ( newLanguage.equals(editorLanguage) == false || hasEditor() == false) { // get rid of the old editor // The "old" editorComposite will be buried in the stack layout disposeEditor(); fEditorComposite = fWidgetFactory.createComposite(fEditorArea); fEditorComposite.setLayout( new FillLayout() ); createEditor (fEditorComposite); } fEditorAreaStackLayout.topControl = fEditorComposite; // we update our editor with the model, since something may have changed // in it that needs to be reflected in the editor. fEditor.setExpressionType( getExpressionType() ); // editor.setExpressionType(getExpressionType(), getExpressionContext()); fEditor.setModelObject(getInput()); // TODO: WTF ? fEditor.setEditorContent( (String) expr.getBody()); fEditor.aboutToBeShown(); } if ( previousTop != fEditorAreaStackLayout.topControl ) { // Layout is necessary after swapping the top element in the stack layout fEditorArea.layout(new Control[] { fEditorAreaStackLayout.topControl } ); } } /** * This is used by the Expression Language combo to filter out editors that * can't be used with the current type/context. */ protected boolean isEditorSupported(IExpressionEditor exEditor) { return exEditor.supportsExpressionType( getExpressionType() ); } /** * Whether the marker use type is useful for this section. */ protected boolean isValidClientUseType (String useType) { return false; } /** * Answer if we have an editor visible. * * @return true if visible, false otherwise. */ public boolean hasEditor () { return (fEditor != null && fEditorAreaStackLayout.topControl != fNoEditorWidgets) ; } private void createBoldFont (Composite parent) { FontData[] data = parent.getDisplay().getSystemFont().getFontData(); FontData data0 = data[0]; data0.setStyle(SWT.BOLD); boldFont = new Font(parent.getDisplay(), data0); } /** (non-Javadoc) * @see org.eclipse.bpel.ui.properties.TextSection#dispose() */ @Override public void dispose() { if (boldFont != null) { boldFont.dispose(); } super.dispose(); } @Override protected void updateMarkers () { expressionLanguageLabel.clear(); for(IMarker m : getMarkers(getInput())) { if (expressionLanguageLabel.getControl().isDisposed() == false) { expressionLanguageLabel.addStatus( BPELUtil.adapt(m, IStatus.class) ); } else { new Throwable("FixMe: Why is update markers being called ?").printStackTrace(); } } } // public void aboutToBeHidden() { // super.aboutToBeHidden(); // if (expressionChangeHelper != null) { // getCommandFramework().notifyChangeDone(expressionChangeHelper); // } // } }