/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * 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: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.ui.rcp.views; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Platform; import org.eclipse.help.IContext; import org.eclipse.help.IContextProvider; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnViewerEditor; import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.IElementComparer; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.TreeViewerEditor; import org.eclipse.jface.viewers.TreeViewerFocusCellManager; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jubula.client.core.businessprocess.IComponentNameCache; import org.eclipse.jubula.client.core.events.DataChangedEvent; import org.eclipse.jubula.client.core.events.DataEventDispatcher; import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState; import org.eclipse.jubula.client.core.events.DataEventDispatcher.IDataChangedListener; import org.eclipse.jubula.client.core.events.DataEventDispatcher.IParamChangedListener; import org.eclipse.jubula.client.core.events.DataEventDispatcher.IPartClosedListener; import org.eclipse.jubula.client.core.model.ICapPO; import org.eclipse.jubula.client.core.model.INodePO; import org.eclipse.jubula.client.core.model.IPersistentObject; import org.eclipse.jubula.client.ui.constants.Constants; import org.eclipse.jubula.client.ui.constants.ContextHelpIds; import org.eclipse.jubula.client.ui.controllers.propertysources.AbstractPropertySource; import org.eclipse.jubula.client.ui.controllers.propertysources.IPropertyController; import org.eclipse.jubula.client.ui.rcp.Plugin; import org.eclipse.jubula.client.ui.rcp.controllers.propertydescriptors.JBPropertyDescriptor; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.AbstractNodePropertySource; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.CapGUIPropertySource.ActionTypeController; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.CapGUIPropertySource.ComponentNameController; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.CapGUIPropertySource.ComponentTypeController; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.CapGUIPropertySource.ParameterNameController; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.CapGUIPropertySource.ParameterTypeController; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.CapGUIPropertySource.ParameterValueController; import org.eclipse.jubula.client.ui.rcp.controllers.propertysources.IParameterPropertyController; import org.eclipse.jubula.client.ui.rcp.editors.AbstractJBEditor; import org.eclipse.jubula.client.ui.rcp.editors.JBEditorHelper.EditableState; import org.eclipse.jubula.client.ui.rcp.i18n.Messages; import org.eclipse.jubula.client.ui.rcp.provider.contextprovider.JBContextProvider; import org.eclipse.jubula.client.ui.utils.LayoutUtil; import org.eclipse.jubula.client.ui.views.IJBPart; import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.utils.generator.ActionInfo; import org.eclipse.jubula.tools.internal.utils.generator.CompSystemProcessor; import org.eclipse.jubula.tools.internal.utils.generator.ComponentInfo; import org.eclipse.jubula.tools.internal.utils.generator.ParamInfo; import org.eclipse.jubula.tools.internal.utils.generator.ToolkitInfo; import org.eclipse.jubula.tools.internal.xml.businessmodell.Action; import org.eclipse.jubula.tools.internal.xml.businessmodell.Component; import org.eclipse.swt.SWT; import org.eclipse.swt.events.HelpEvent; import org.eclipse.swt.events.HelpListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.help.IWorkbenchHelpSystem; import org.eclipse.ui.part.IPage; import org.eclipse.ui.part.Page; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySheetPage; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.properties.PropertySheet; import org.eclipse.ui.views.properties.TextPropertyDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author BREDEX GmbH * @created 19.01.2005 */ @SuppressWarnings("synthetic-access") public class JBPropertiesPage extends Page implements IDataChangedListener, IParamChangedListener, IPartClosedListener, IPropertySheetPage, IAdaptable { /** the logger */ private static final Logger LOG = LoggerFactory.getLogger(JBPropertiesPage.class); /** The property source */ private IPropertySource m_propSource; /** The Tree Viewer of this view */ private TreeViewer m_treeViewer; /** listens for selection changes that influence help context */ private ISelectionChangedListener m_helpContextListener = new HelpContextListener(); /** shows, if the Properties view is editable or not */ private boolean m_isEditable; /** the corresponding part */ private IWorkbenchPart m_correspondingPart; /** * Label provider for property names. * * @author BREDEX GmbH * @created Apr 7, 2010 */ private final class PropertyNameLabelProvider extends ColumnLabelProvider { /** * {@inheritDoc} */ public String getText(Object element) { if (element instanceof IPropertyDescriptor) { return ((IPropertyDescriptor)element).getDisplayName(); } return super.getText(element); } /** * {@inheritDoc} */ public Image getImage(Object element) { if (element instanceof IPropertyDescriptor && m_propSource != null) { IPropertyDescriptor propDesc = (IPropertyDescriptor)element; ILabelProvider labelProvider = propDesc.getLabelProvider(); return labelProvider.getImage(propDesc.getId()); } return super.getImage(element); } /** * {@inheritDoc} */ public Color getForeground(Object element) { return super.getForeground(element); } /** * {@inheritDoc} */ public Color getBackground(Object element) { if (element instanceof IPropertyDescriptor && m_propSource != null) { ILabelProvider labelProvider = ((IPropertyDescriptor)element).getLabelProvider(); if (labelProvider instanceof IColorProvider) { return ((IColorProvider)labelProvider).getBackground( m_propSource); } } return super.getBackground(element); } } /** * Label provider for property values. * * @author BREDEX GmbH * @created Apr 7, 2010 */ private final class PropertyValueLabelProvider extends ColumnLabelProvider { /** * {@inheritDoc} */ public String getText(Object element) { if (element instanceof IPropertyDescriptor && m_propSource != null) { ILabelProvider labelProvider = ((IPropertyDescriptor)element).getLabelProvider(); return labelProvider.getText(m_propSource.getPropertyValue( ((IPropertyDescriptor)element).getId())); } return null; } /** * {@inheritDoc} */ public Color getForeground(Object element) { if (m_propSource instanceof AbstractNodePropertySource) { AbstractNodePropertySource guiNodePropSource = (AbstractNodePropertySource)m_propSource; if (guiNodePropSource.isReadOnly()) { return LayoutUtil.GRAY_COLOR; } } if (!checkEditorPart()) { return LayoutUtil.GRAY_COLOR; } if (element instanceof IPropertyDescriptor && m_propSource != null) { ILabelProvider labelProvider = ((IPropertyDescriptor)element).getLabelProvider(); if (labelProvider instanceof IColorProvider) { return ((IColorProvider)labelProvider).getForeground( ((IPropertyDescriptor)element).getId()); } } return super.getForeground(element); } /** * {@inheritDoc} */ public String getToolTipText(Object element) { String displayedText = getText(element); if (displayedText != null && displayedText.length() > 0) { return displayedText; } return null; } } /** * @author BREDEX GmbH * @created Sep 20, 2010 */ private final class PropertiesElementComparer implements IElementComparer { /** * {@inheritDoc} */ public int hashCode(Object element) { // ignore JBPropertyDescriptor as this leads to incorrect behavior // e.g. when deleting params from a spec tc via edit parameters if (element instanceof IPropertyDescriptor && !(element instanceof JBPropertyDescriptor)) { IPropertyDescriptor pd = (IPropertyDescriptor)element; HashCodeBuilder hb = new HashCodeBuilder(); hb.append(pd.getCategory()); hb.append(pd.getDisplayName()); Object id = pd.getId(); if (id instanceof IPropertyController) { IPropertyController pdpc = (IPropertyController)id; hb.append(pdpc.getProperty()); } return hb.toHashCode(); } return ObjectUtils.hashCode(element); } /** * {@inheritDoc} */ public boolean equals(Object a, Object b) { // ignore JBPropertyDescriptor as this leads to incorrect behavior // e.g. when deleting params from a spec tc via edit parameters if (a instanceof IPropertyDescriptor && b instanceof IPropertyDescriptor && !(a instanceof JBPropertyDescriptor || b instanceof JBPropertyDescriptor)) { IPropertyDescriptor pd1 = (IPropertyDescriptor)a; IPropertyDescriptor pd2 = (IPropertyDescriptor)b; EqualsBuilder eb = new EqualsBuilder(); eb.append(pd1.getCategory(), pd2.getCategory()); eb.append(pd1.getDisplayName(), pd2.getDisplayName()); Object id1 = pd1.getId(); Object id2 = pd2.getId(); if (id1 instanceof IPropertyController && id2 instanceof IPropertyController) { IPropertyController pd1pc = (IPropertyController)id1; IPropertyController pd2pc = (IPropertyController)id2; eb.append(pd1pc.getProperty(), pd2pc.getProperty()); } return eb.isEquals(); } return ObjectUtils.equals(a, b); } } /** the context provider for this view */ private JBContextProvider m_contextProvider = new JBContextProvider(); /** helpListener of this view */ private ContextHelpListener m_helpListener = new ContextHelpListener(); /** the Component Name cache to use when this page is active */ private IComponentNameCache m_compCache; /** current editor */ private AbstractJBEditor m_currentEditor = Plugin.getDefault() .getActiveJBEditor(); /** the focus manager for the tree viewer */ private TreeViewerFocusCellManager m_focusCellManager; /** * Constructor. * * @param isEditable <code>true</code> if the properties shown in the view * should initially be editable. * @param compCache the Component Name cache to use when this page is * active. May be <code>null</code>, if no specific * mapper should be used. */ public JBPropertiesPage( boolean isEditable, IComponentNameCache compCache) { super(); m_compCache = compCache; m_isEditable = isEditable; } /** * {@inheritDoc} */ public void createPartControl(Composite parent) { buildTree(parent); Plugin.getHelpSystem().setHelp(m_treeViewer.getControl(), ContextHelpIds.JB_PROPERTIES_VIEW); final DataEventDispatcher dispatcher = DataEventDispatcher.getInstance(); dispatcher.addDataChangedListener(this, true); dispatcher.addParamChangedListener(this, true); dispatcher.addPartClosedListener(this, true); m_treeViewer.getControl().addHelpListener(m_helpListener); getSite().getWorkbenchWindow().getSelectionService() .addSelectionListener(this); } /** * Creates a new Tree for this View. * @param parent the parent composite */ private void buildTree(Composite parent) { GridData layoutData = new GridData(GridData.FILL_BOTH); layoutData.grabExcessHorizontalSpace = true; Tree tree = new Tree(parent, SWT.BORDER | SWT.HIDE_SELECTION | SWT.FULL_SELECTION); tree.setLayoutData(layoutData); tree.setHeaderVisible(true); tree.setLinesVisible(true); m_treeViewer = new TreeViewer(tree); final int width = m_treeViewer.getTree().getParent() .getClientArea().width; /* We make sure to have an initial size, just in case. */ final int area = (width < 100) ? 100 : width; // add expand/collapse column TreeViewerColumn expandCollapseColumn = new TreeViewerColumn(m_treeViewer, SWT.NONE); expandCollapseColumn.getColumn().setText(StringConstants.EMPTY); expandCollapseColumn.getColumn().setWidth((int) (area * 0.05)); expandCollapseColumn.getColumn().setResizable(false); expandCollapseColumn.setLabelProvider(new CellLabelProvider() { public void update(ViewerCell cell) { // Nothing to display. Nothing to update. } }); // add property name column TreeViewerColumn propertyNameColumn = new TreeViewerColumn(m_treeViewer, SWT.NONE); propertyNameColumn.getColumn().setText( Messages.JubulaPropertiesViewProperty); propertyNameColumn.getColumn().setWidth((int) (area * 0.35)); propertyNameColumn.setLabelProvider(new PropertyNameLabelProvider()); // add property value column TreeViewerColumn propertyValueColumn = new TreeViewerColumn(m_treeViewer, SWT.NONE); propertyValueColumn.getColumn().setText( Messages.JubulaPropertiesViewValue); propertyValueColumn.getColumn().setWidth((int) (area * 0.55)); propertyValueColumn.setLabelProvider(new PropertyValueLabelProvider()); propertyValueColumn.setEditingSupport( new PropertiesEditingSupport(m_treeViewer)); m_treeViewer.addSelectionChangedListener(m_helpContextListener); m_treeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); m_treeViewer.setContentProvider(new PropertiesContentProvider()); ColumnViewerToolTipSupport.enableFor(m_treeViewer); m_treeViewer.setComparer(new PropertiesElementComparer()); m_focusCellManager = new TreeViewerFocusCellManager(m_treeViewer, new FocusCellOwnerDrawHighlighter(m_treeViewer)); ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(m_treeViewer) { protected boolean isEditorActivationEvent( ColumnViewerEditorActivationEvent event) { return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || event.eventType == ColumnViewerEditorActivationEvent. MOUSE_CLICK_SELECTION || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR) || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC; } }; TreeViewerEditor.create(m_treeViewer, m_focusCellManager, actSupport, ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION | ColumnViewerEditor.KEEP_EDITOR_ON_DOUBLE_CLICK); } /** * {@inheritDoc} */ public void setFocus() { getControl().setFocus(); } /** * {@inheritDoc} */ public void dispose() { if (m_treeViewer != null) { m_treeViewer.removeSelectionChangedListener(m_helpContextListener); } final DataEventDispatcher dispatcher = DataEventDispatcher.getInstance(); dispatcher.removeDataChangedListener(this); dispatcher.removeParamChangedListener(this); dispatcher.removePartClosedListener(this); getSite().getWorkbenchWindow() .getSelectionService().removeSelectionListener(this); getSite().setSelectionProvider(null); setCurrentEditor(null); } /** {@inheritDoc} */ public void handleDataChanged(DataChangedEvent... events) { for (DataChangedEvent e : events) { handleDataChanged(e.getPo(), e.getDataState()); } } /** * {@inheritDoc} */ public void handleDataChanged(final IPersistentObject po, final DataState dataState) { Plugin.getDisplay().syncExec(new Runnable() { public void run() { // indirection necessary due to Checkstyle BUG handleDataChangedImpl(po, dataState); } }); } /** * @param po * the po * @param dataState * the data state */ private void handleDataChangedImpl(final IPersistentObject po, final DataState dataState) { if (po == null) { return; } // Check if the patent of the current node has been changed. // This will happen if a child of a SpecTC is displayed // and the editor with that SpecTC is saved. boolean parentMatch = false; if (getCurrentPO() instanceof INodePO) { INodePO parent = ((INodePO)getCurrentPO()).getSpecAncestor(); parentMatch = (parent != null) && po.equals(parent); } if (parentMatch || po.equals(getCurrentPO())) { switch (dataState) { case Added: case StructureModified: if (po != null) { Plugin.getDisplay().syncExec(new Runnable() { public void run() { m_treeViewer.refresh(); } }); expandTrackedChanges(); } break; case Deleted: clearView(); break; case Renamed: Plugin.getDisplay().syncExec(new Runnable() { public void run() { m_treeViewer.refresh(); } }); expandTrackedChanges(); break; default: } } } /** * @return the current PO */ private IPersistentObject getCurrentPO() { return (IPersistentObject) m_treeViewer.getInput(); } /** * Expands the tracked changes in the properties view. * For some reasons the view needs persuasion to expand the tracked changes sometimes. */ private void expandTrackedChanges() { Plugin.getDisplay().syncExec(new Runnable() { public void run() { m_treeViewer.setExpandedState(Messages. SpecTestCaseGUIPropertySourceTrackedChangesCategory, true); } }); } /** * Clears the view. */ private void clearView() { Plugin.getDisplay().syncExec(new Runnable() { public void run() { m_treeViewer.setComparer(null); m_treeViewer.setInput(null); setViewEnabled(false); } }); } /** * {@inheritDoc} */ public void handleParamChanged(Object caller) { if (Plugin.getActivePart() instanceof PropertySheet) { if (((PropertySheet)Plugin .getActivePart()).getCurrentPage() != this) { IPage pag = ((PropertySheet)Plugin .getActivePart()).getCurrentPage(); return; } } Plugin.getDisplay().syncExec(new Runnable() { public void run() { m_treeViewer.refresh(); m_treeViewer.expandToLevel(m_treeViewer.getAutoExpandLevel()); } }); } /** * Reacts on the changes from the SelectionService of Eclipse. * * @param part * the workbench part * @param selection * the selection */ private void reactOnChange(IWorkbenchPart part, IStructuredSelection selection) { m_correspondingPart = part; final Object firstElement = selection.getFirstElement(); Plugin.getDisplay().syncExec(new Runnable() { public void run() { if (firstElement == null) { // e.g. when a project was opened and no view has a selection m_treeViewer.setSelection(null); m_treeViewer.setInput(null); } else { // TestResultNodes must be excluded if (firstElement instanceof IPersistentObject) { m_treeViewer.setInput(firstElement); workaroundSpringySelection(m_focusCellManager); } } // property informations should be collapsed by default m_treeViewer.setExpandedState(Messages. OMTechNameGUIPropertySourcePropertyInformation, false); // parameters should be expanded by default m_treeViewer.setExpandedState(Messages. SpecTestCaseGUIPropertySourceParameter, true); } }); expandTrackedChanges(); setViewEnabled(!(part instanceof TestCaseBrowser || part instanceof TestSuiteBrowser || part instanceof JBPropertiesPage || part instanceof TestResultTreeView || part instanceof CompNamesView)); } /** * Workaround for ticket #3012. Prevents odd cell selection * behavior by pre-emptively setting the focus cell, if it is not already * set. This workaround relies <b>heavily</b> on reflection. so the smallest * change to JFace might break it. A try-catch(throwable) block is used to * minimize the damage that such a breakage would cause. Worst-case * scenario: The bug is not fixed and a small performance penalty is * incurred by looking everything up via reflection. * * @param focusCellManager The focus manager on which to perform the * workaround. */ private void workaroundSpringySelection( TreeViewerFocusCellManager focusCellManager) { try { if (focusCellManager.getFocusCell() == null) { Class focusManagerClass = focusCellManager.getClass(); Class abstractFocusManagerClass = focusManagerClass.getSuperclass(); Method getInitialFocusCellMethod = focusManagerClass.getDeclaredMethod("getInitialFocusCell", //$NON-NLS-1$ new Class[0]); Method setFocusCellMethod = abstractFocusManagerClass.getDeclaredMethod("setFocusCell", //$NON-NLS-1$ ViewerCell.class); getInitialFocusCellMethod.setAccessible(true); setFocusCellMethod.setAccessible(true); Object initialFocusCellObj = getInitialFocusCellMethod.invoke( focusCellManager, new Object[0]); if (initialFocusCellObj instanceof ViewerCell) { ViewerCell initialFocusCell = (ViewerCell)initialFocusCellObj; setFocusCellMethod.invoke(focusCellManager, initialFocusCell); } } } catch (Throwable t) { LOG.info(Messages.ErrorInWorkaroundForSpringySelection, t); } } /** * Sets the widget in the treeView enabled or disabled. * @param enabled The disabled/enabled flag. * <p> It is <code>true</code>, if a node in the editor is selected. </p> * <p> It is <code>false</code>, if a node in one of the treeViews is selected. </p> */ void setViewEnabled(boolean enabled) { m_isEditable = enabled; Color bColor = null; Color fColor = null; if (!enabled) { bColor = LayoutUtil.LIGHT_GRAY_COLOR; fColor = LayoutUtil.GRAY_COLOR; } getControl().setBackground(bColor); getControl().setForeground(fColor); } /** * @return <code>true</code> if the contributing part is the currently * active editor. Otherwise, <code>false</code>. */ private boolean checkEditorPart() { return (Plugin.getActiveEditor() != null && Plugin.getActiveEditor().equals(m_correspondingPart)); } /** * @param text The text field to set a max. char range. * @param propD The propertyDescriptor of the text field. */ private void setMaxChar(Text text, Object propD) { if (!m_isEditable) { return; } if (propD instanceof TextPropertyDescriptor) { LayoutUtil.setMaxChar(text); } } /** * Content provider for properties. * * @author BREDEX GmbH * @created Apr 7, 2010 */ private final class PropertiesContentProvider implements ITreeContentProvider { /** * {@inheritDoc} */ public Object[] getChildren(Object parentElement) { if (parentElement instanceof String) { // category List<IPropertyDescriptor> children = new ArrayList<IPropertyDescriptor>(); for (IPropertyDescriptor propDesc : m_propSource.getPropertyDescriptors()) { if (parentElement.equals(propDesc.getCategory())) { children.add(propDesc); } } return children.toArray(); } return null; } /** * {@inheritDoc} */ public Object getParent(Object element) { if (element instanceof IPropertyDescriptor) { return ((IPropertyDescriptor)element).getCategory(); } return null; } /** * {@inheritDoc} */ public boolean hasChildren(Object element) { return element instanceof String; } /** * {@inheritDoc} */ public Object[] getElements(Object inputElement) { IPropertyDescriptor[] descriptors = getPropertyDescriptors(inputElement); Set<String> categories = new LinkedHashSet<String>(); Set<IPropertyDescriptor> topLevelDescriptors = new LinkedHashSet<IPropertyDescriptor>(); for (IPropertyDescriptor descriptor : descriptors) { String category = descriptor.getCategory(); if (category == null) { topLevelDescriptors.add(descriptor); } else { categories.add(category); } } List<Object> children = new ArrayList<Object>(); children.addAll(topLevelDescriptors); children.addAll(categories); return children.toArray(); } /** * * @param element The adaptable element. * @return the property source for the given <code>element</code>, or * <code>null</code> if no such property source can be found. */ private IPropertySource getPropertySource(Object element) { if (element != null) { Object propertySourceObj = null; if (element instanceof IAdaptable) { IAdaptable adaptable = (IAdaptable)element; propertySourceObj = adaptable.getAdapter(IPropertySource.class); } else { propertySourceObj = Platform.getAdapterManager().getAdapter( element, IPropertySource.class); } if (propertySourceObj instanceof IPropertySource) { return (IPropertySource)propertySourceObj; } } return null; } /** * * @param element The element for which to get the descriptors. * @return the corresponding property descriptors if <code>element</code> * is a valid gui node. Otherwise returns an empty array. */ private IPropertyDescriptor[] getPropertyDescriptors(Object element) { if (element instanceof INodePO) { INodePO guiNodeInput = (INodePO)element; if (isValid(guiNodeInput)) { IPropertySource propertySource = getPropertySource(element); if (propertySource != null) { return propertySource.getPropertyDescriptors(); } } } else { IPropertySource propertySource = getPropertySource(element); if (propertySource != null) { return propertySource.getPropertyDescriptors(); } } return new IPropertyDescriptor[0]; } /** * * @param node The node to check. * @return <code>false</code> if the node is a Test Step with an invalid * component. Otherwise, <code>true</code>. */ private boolean isValid(INodePO node) { return node != null && node.isValid(); } /** * {@inheritDoc} */ public void dispose() { // Nothing to dispose } /** * {@inheritDoc} */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { m_propSource = getPropertySource(newInput); } } /** * Editing support for properties shown in the view. * * @author BREDEX GmbH * @created Apr 8, 2010 */ private final class PropertiesEditingSupport extends EditingSupport { /** * Constructor * * @param viewer The viewer. */ public PropertiesEditingSupport(ColumnViewer viewer) { super(viewer); } /** * {@inheritDoc} */ protected boolean canEdit(Object element) { if (m_correspondingPart instanceof IEditorPart) { if (element instanceof IPropertyDescriptor) { Object propId = ((IPropertyDescriptor)element).getId(); if (m_propSource instanceof AbstractNodePropertySource && propId instanceof IParameterPropertyController) { return ((AbstractNodePropertySource)m_propSource) .isParameterEntryEnabled( (IParameterPropertyController)propId); } } return true; } return false; } /** * {@inheritDoc} */ protected CellEditor getCellEditor(Object element) { if (element instanceof IPropertyDescriptor && m_propSource != null) { CellEditor editor = ((IPropertyDescriptor)element).createPropertyEditor( m_treeViewer.getTree()); if (editor != null) { Control editorControl = editor.getControl(); if (editorControl instanceof Text) { setMaxChar((Text)editorControl, element); } } return editor; } return null; } /** * {@inheritDoc} */ protected Object getValue(Object element) { if (element instanceof IPropertyDescriptor && m_propSource != null) { return m_propSource.getPropertyValue( ((IPropertyDescriptor)element).getId()); } return null; } /** * {@inheritDoc} */ protected void setValue(Object element, Object value) { if (element instanceof IPropertyDescriptor && m_propSource != null) { IPropertyDescriptor propDesc = (IPropertyDescriptor)element; Object oldValue = m_propSource.getPropertyValue(propDesc.getId()); if (oldValue == null || !oldValue.equals(value)) { if (m_currentEditor.getEditorHelper().requestEditableState() == EditableState.OK) { if (LOG.isDebugEnabled()) { logChange(propDesc, oldValue, value); } m_propSource.setPropertyValue(propDesc.getId(), value); if (getCurrentEditor() != null) { getCurrentEditor().getEditorHelper().setDirty(true); } } } } } /** * Logs the change * @param desc the property descriptor * @param old the old value * @param val the new value */ @SuppressWarnings("nls") private void logChange(IPropertyDescriptor desc, Object old, Object val) { if (!(m_propSource instanceof AbstractPropertySource)) { return; } INodePO node = ((AbstractPropertySource) m_propSource). getNodeOrNull(); if (node == null) { return; } StringBuilder str = new StringBuilder(); str.append("\nProperty change of the node "); str.append(node.getName()); str.append("\nThe property descriptor: "); str.append(desc.getDisplayName()); str.append("\nThe old value: "); str.append(old.toString()); str.append("\nThe new value: "); str.append(val.toString()); str.append("\n"); LOG.debug(str.toString()); } } /** * Updates Help Context based on Selection Changed events. * * @author BREDEX GmbH * @created Apr 8, 2010 */ private final class HelpContextListener implements ISelectionChangedListener { /** * <code>m_oldSelection</code> */ private Object m_oldSelection = null; /** * {@inheritDoc} */ public void selectionChanged(SelectionChangedEvent event) { String helpId = ContextHelpIds.PRAEFIX; if (!(event.getSelection() instanceof IStructuredSelection)) { return; } Object selectedObj = ((IStructuredSelection)event.getSelection()).getFirstElement(); if (!(selectedObj instanceof IPropertyDescriptor) || ObjectUtils.equals(m_oldSelection, selectedObj)) { return; } m_oldSelection = selectedObj; Object descriptorId = ((IPropertyDescriptor)selectedObj).getId(); if (descriptorId instanceof ComponentTypeController || descriptorId instanceof ComponentNameController) { helpId += getCompInfo().getHelpid(); } else if (descriptorId instanceof ActionTypeController) { helpId += getActionInfo().getHelpid(); } else if (descriptorId instanceof ParameterNameController) { helpId += getParamInfo(((ParameterNameController)descriptorId) .getName()).getHelpid(); } else if (descriptorId instanceof ParameterValueController) { helpId += getParamInfo(((ParameterValueController)descriptorId) .getParamDesc().getUniqueId()).getHelpid(); } else if (descriptorId instanceof ParameterTypeController) { helpId += getParamInfo(((ParameterTypeController)descriptorId) .getName()).getHelpid(); } if (ContextHelpIds.PRAEFIX.equals(helpId)) { helpId = ContextHelpIds.JB_PROPERTIES_VIEW; } IWorkbenchHelpSystem helpSystem = Plugin.getHelpSystem(); helpSystem.setHelp(m_treeViewer.getControl(), helpId); if (helpSystem.isContextHelpDisplayed() || Plugin.getView(Constants.ECLIPSE_HELP_VIEW_ID) != null) { helpSystem.displayHelp(helpId); } } /** * @param paramName the current parameter name * @return the current parameter helpID */ private ParamInfo getParamInfo(String paramName) { return new ParamInfo(getAction().findParam(paramName), getActionInfo().getHelpid()); } /** * @return the current action helpID */ private ActionInfo getActionInfo() { CompSystemProcessor processor = new CompSystemProcessor( ComponentBuilder.getInstance().getCompSystem()); ComponentInfo definingComp = processor.getDefiningComp( getCompInfo(), getAction()); return new ActionInfo(getAction(), definingComp); } /** * @return the current action */ private Action getAction() { return getComp().findAction(((ICapPO)getCurrentPO()) .getActionName()); } /** * @return the current component helpID */ private ComponentInfo getCompInfo() { Component comp = getComp(); ToolkitInfo tkInfo = CompSystemProcessor.getToolkitInfo( comp.getToolkitDesriptor()); return new ComponentInfo(comp, tkInfo); } /** * @return the current component */ private Component getComp() { return ComponentBuilder.getInstance().getCompSystem().findComponent( ((ICapPO)getCurrentPO()).getComponentType()); } } /** * {@inheritDoc} */ public void handlePartClosed(IWorkbenchPart part) { final IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage(); if (activePage == null) { // if Jubula was closed return; } IWorkbenchPart activePart = activePage.getActivePart(); if (activePart == null) { // if the last Jubula perspective was closed return; } if (part == m_correspondingPart || m_correspondingPart == null || part instanceof JBPropertiesPage) { ISelection sel = activeWorkbenchWindow.getSelectionService().getSelection(); if (sel == null || !(part instanceof IDataChangedListener) || part instanceof JBPropertiesPage || activePart instanceof JBPropertiesPage) { clearView(); return; } if (sel instanceof IStructuredSelection) { reactOnChange(activePart, (IStructuredSelection)sel); } } } /** * {@inheritDoc} */ public Object getAdapter(Class adapter) { if (adapter.equals(IContextProvider.class)) { return m_contextProvider; } else if (adapter.equals(IPropertySheetPage.class)) { return this; } else if (adapter.equals(IComponentNameCache.class) && m_compCache != null) { return m_compCache; } return null; } /** * @author BREDEX GmbH * @created Jan 22, 2007 */ private final class ContextHelpListener implements HelpListener { /** {@inheritDoc} */ public void helpRequested(HelpEvent e) { IContext context = m_contextProvider.getContext( m_treeViewer.getControl().getData(ContextHelpIds.HELP)); Plugin.getHelpSystem().displayHelp(context); } } /** * {@inheritDoc} */ public void createControl(Composite parent) { createPartControl(parent); } /** * {@inheritDoc} */ public Control getControl() { return m_treeViewer.getControl(); } /** * {@inheritDoc} */ public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (part instanceof IJBPart && selection instanceof IStructuredSelection) { reactOnChange(part, (IStructuredSelection)selection); } } /** * @param currentEditor the currentEditor to set */ private void setCurrentEditor(AbstractJBEditor currentEditor) { m_currentEditor = currentEditor; } /** * @return the currentEditor */ public AbstractJBEditor getCurrentEditor() { return m_currentEditor; } /** * @return the current selection */ public ITreeSelection getCurrentTreeSelection() { return m_treeViewer.getStructuredSelection(); } }