/*******************************************************************************
* 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.editors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jubula.client.core.businessprocess.CleanupObjectMapping;
import org.eclipse.jubula.client.core.businessprocess.CompNameResult;
import org.eclipse.jubula.client.core.businessprocess.CompNamesBP;
import org.eclipse.jubula.client.core.businessprocess.IComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.IObjectMappingObserver;
import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.ObjectMappingEventDispatcher;
import org.eclipse.jubula.client.core.businessprocess.TestExecution;
import org.eclipse.jubula.client.core.businessprocess.db.TestSuiteBP;
import org.eclipse.jubula.client.core.businessprocess.db.TimestampBP;
import org.eclipse.jubula.client.core.commands.AUTModeChangedCommand;
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.OMState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.UpdateState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.IDataChangedListener;
import org.eclipse.jubula.client.core.model.IAUTMainPO;
import org.eclipse.jubula.client.core.model.ICapPO;
import org.eclipse.jubula.client.core.model.IComponentNamePO;
import org.eclipse.jubula.client.core.model.INodePO;
import org.eclipse.jubula.client.core.model.IObjectMappingAssoziationPO;
import org.eclipse.jubula.client.core.model.IObjectMappingCategoryPO;
import org.eclipse.jubula.client.core.model.IObjectMappingPO;
import org.eclipse.jubula.client.core.model.IObjectMappingProfilePO;
import org.eclipse.jubula.client.core.model.IPersistentObject;
import org.eclipse.jubula.client.core.model.ITestSuitePO;
import org.eclipse.jubula.client.core.model.ITimestampPO;
import org.eclipse.jubula.client.core.model.PoMaker;
import org.eclipse.jubula.client.core.persistence.CompNamePM;
import org.eclipse.jubula.client.core.persistence.EditSupport;
import org.eclipse.jubula.client.core.persistence.GeneralStorage;
import org.eclipse.jubula.client.core.persistence.PMAlreadyLockedException;
import org.eclipse.jubula.client.core.persistence.PMException;
import org.eclipse.jubula.client.core.persistence.PMReadException;
import org.eclipse.jubula.client.core.persistence.PMSaveException;
import org.eclipse.jubula.client.core.utils.AbstractNonPostOperatingTreeNodeOperation;
import org.eclipse.jubula.client.core.utils.ITreeTraverserContext;
import org.eclipse.jubula.client.core.utils.TreeTraverser;
import org.eclipse.jubula.client.ui.constants.ContextHelpIds;
import org.eclipse.jubula.client.ui.constants.IconConstants;
import org.eclipse.jubula.client.ui.rcp.Plugin;
import org.eclipse.jubula.client.ui.rcp.businessprocess.CompletenessBP;
import org.eclipse.jubula.client.ui.rcp.businessprocess.OMEditorBP;
import org.eclipse.jubula.client.ui.rcp.constants.RCPCommandIDs;
import org.eclipse.jubula.client.ui.rcp.controllers.OpenOMETracker;
import org.eclipse.jubula.client.ui.rcp.controllers.PMExceptionHandler;
import org.eclipse.jubula.client.ui.rcp.controllers.TestExecutionContributor;
import org.eclipse.jubula.client.ui.rcp.controllers.dnd.objectmapping.LimitingDragSourceListener;
import org.eclipse.jubula.client.ui.rcp.controllers.dnd.objectmapping.OMDropTargetListener;
import org.eclipse.jubula.client.ui.rcp.dialogs.NagDialog;
import org.eclipse.jubula.client.ui.rcp.editors.JBEditorHelper.EditableState;
import org.eclipse.jubula.client.ui.rcp.events.GuiEventDispatcher;
import org.eclipse.jubula.client.ui.rcp.events.GuiEventDispatcher.IEditorDirtyStateListener;
import org.eclipse.jubula.client.ui.rcp.filter.JBFilteredTree;
import org.eclipse.jubula.client.ui.rcp.filter.ObjectMappingEditorPatternFilter;
import org.eclipse.jubula.client.ui.rcp.i18n.Messages;
import org.eclipse.jubula.client.ui.rcp.propertytester.EditorPartPropertyTester;
import org.eclipse.jubula.client.ui.rcp.provider.contentprovider.objectmapping.OMEditorTreeContentProvider;
import org.eclipse.jubula.client.ui.rcp.provider.labelprovider.OMEditorTreeLabelProvider;
import org.eclipse.jubula.client.ui.rcp.provider.selectionprovider.SelectionProviderIntermediate;
import org.eclipse.jubula.client.ui.utils.CommandHelper;
import org.eclipse.jubula.client.ui.utils.DialogUtils;
import org.eclipse.jubula.client.ui.utils.LayoutUtil;
import org.eclipse.jubula.client.ui.views.IJBPart;
import org.eclipse.jubula.client.ui.views.IMultiTreeViewerContainer;
import org.eclipse.jubula.communication.internal.message.ChangeAUTModeMessage;
import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException;
import org.eclipse.jubula.tools.internal.i18n.I18n;
import org.eclipse.jubula.tools.internal.objects.IComponentIdentifier;
import org.eclipse.jubula.tools.internal.xml.businessmodell.ConcreteComponent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.services.IEvaluationService;
import org.eclipse.ui.swt.IFocusService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Editor for managing Object Mapping in Jubula.
*
* @author BREDEX GmbH
* @created Oct 21, 2008
*/
public class ObjectMappingMultiPageEditor extends MultiPageEditorPart
implements IJBPart, IJBEditor, IObjectMappingObserver,
IEditorDirtyStateListener, IMultiTreeViewerContainer,
IPropertyListener, IDataChangedListener {
/** Show-menu */
public static final String CLEANUP_ID = PlatformUI.PLUGIN_ID + ".CleanupSubMenu"; //$NON-NLS-1$
/** default sash weights */
private static final int[] DEFAULT_SASH_WEIGHTS = new int[] { 25, 100 };
/** the logger */
private static final Logger LOG =
LoggerFactory.getLogger(ObjectMappingMultiPageEditor.class);
/** page index of the split view */
private static final int SPLIT_PAGE_IDX = 0;
/** the object responsible for handling JBEditor-related tasks */
private JBEditorHelper m_editorHelper;
/** handles the business process operations for this editor */
private OMEditorBP m_omEditorBP;
/** the tree viewer for unmapped Component Names in the Split Pane view */
private TreeViewer m_compNameTreeViewer;
/** the tree viewer for unmapped UI Elements in the Split Pane view */
private TreeViewer m_uiElementTreeViewer;
/** the tree viewer for mapped Component Names in the Split Pane view */
private TreeViewer m_mappedComponentTreeViewer;
/**
* the component responsible for handling the profile
* configuration page
*/
private ObjectMappingConfigComponent m_mappingConfigComponent;
/** mapping: page number => selection provider for that page */
private Map<Integer, ISelectionProvider> m_pageToSelectionProvider =
new HashMap<Integer, ISelectionProvider>();
/**
* Always provides an empty selection. Does not track selection listeners.
*
* @author BREDEX GmbH
* @created Jan 20, 2009
*/
private static class NullSelectionProvider implements ISelectionProvider {
/**
* {@inheritDoc}
*/
public void addSelectionChangedListener(
ISelectionChangedListener listener) {
// Do nothing
}
/**
* {@inheritDoc}
*/
public ISelection getSelection() {
return StructuredSelection.EMPTY;
}
/**
* {@inheritDoc}
*/
public void removeSelectionChangedListener(
ISelectionChangedListener listener) {
// Do nothing
}
/**
* {@inheritDoc}
*/
public void setSelection(ISelection selection) {
// Do nothing.
}
}
/**
* Sorter for the Object Mapping Editor's tree view.
*
* @author BREDEX GmbH
* @created Mar 10, 2009
*/
private static class ObjectMappingTreeSorter extends ViewerComparator {
/**
* {@inheritDoc}
*/
public int category(Object element) {
if (element instanceof IObjectMappingCategoryPO) {
return 0;
} else if (element instanceof IObjectMappingAssoziationPO) {
return 1;
} else if (element instanceof IComponentNamePO) {
return 2;
}
return super.category(element);
}
}
/**
* This class operates on any node of the test suite tree to extract the
* component names. They are added to the object mapping tree and stored in
* the member <code>m_componentNames</code>.
*/
private class CollectLogicalNamesOp
extends AbstractNonPostOperatingTreeNodeOperation<INodePO> {
/** Number of added component names (nodes). */
private int m_addedNodeCount = 0;
/** list of added GuiNodes */
private List<IObjectMappingAssoziationPO> m_addedNodes =
new ArrayList<IObjectMappingAssoziationPO>();
/** The business process that performs component name operations */
private CompNamesBP m_compNamesBP = new CompNamesBP();
/**
* {@inheritDoc}
*/
@SuppressWarnings("synthetic-access")
public boolean operate(ITreeTraverserContext<INodePO> ctx,
INodePO parent, INodePO node, boolean alreadyVisited) {
if (!(node instanceof ICapPO)) {
return true;
}
final ICapPO cap = (ICapPO)node;
CompNameResult result =
m_compNamesBP.findCompName(ctx.getCurrentTreePath(),
cap, cap.getComponentName(), getCompNameCache());
final IComponentNamePO compNamePo =
getCompNameCache().getResCompNamePOByGuid(result.getCompName());
if (compNamePo == null) {
return true;
}
if (!(cap.getMetaComponentType() instanceof ConcreteComponent
&& ((ConcreteComponent)cap.getMetaComponentType())
.hasDefaultMapping())
&& m_omEditorBP.getAssociation(
compNamePo.getGuid()) == null) {
if (getEditorHelper().requestEditableState()
!= EditableState.OK) {
return true;
}
if (checkForExistingLogicalName(compNamePo)) {
return true;
}
IObjectMappingAssoziationPO assoc =
PoMaker.createObjectMappingAssoziationPO(
null, new HashSet<String>());
assoc.setParentProjectId(GeneralStorage.getInstance().
getProject().getId());
getCompNameCache().changeReuse(
assoc, null, compNamePo.getGuid());
getAut().getObjMap().getUnmappedLogicalCategory()
.addAssociation(assoc);
m_addedNodes.add(assoc);
m_addedNodeCount++;
}
return true;
}
/**
* Checks whether a Component Name with the given logical name is already created in this editor
* In this case the cache version has a different guid than the main session version
* If yes, replaces the cache version by the main session version, so we don't have to add this CN
* to the to-be-mapped list...
* @param cN the Component Name
* @return whether a locally created CN existed with the same logical name but different guid
*/
private boolean checkForExistingLogicalName(IComponentNamePO cN) {
Map<String, IComponentNamePO> localChanges = getCompNameCache().
getLocalChanges();
if (localChanges.containsKey(cN.getGuid())) {
return false;
}
for (String guid : localChanges.keySet()) {
IComponentNamePO localCN = localChanges.get(guid);
if (localCN.getName().equals(cN.getName())) {
if (localCN.getId() != null) {
// The CN is already persisted - it may be from a different project
continue;
}
IObjectMappingAssoziationPO assoc = getAut().getObjMap()
.getLogicalNameAssoc(localCN.getGuid());
// this should not be null, because the CN should have
// been removed from the local cache if no association holds the CN
Assert.isNotNull(assoc);
getCompNameCache().changeReuse(assoc, localCN.getGuid(),
cN.getGuid());
getCompNameCache().removeCompName(localCN.getGuid());
return true;
}
}
return false;
}
/**
* @return Returns the addedNodeCount.
*/
public int getAddedNodeCount() {
return m_addedNodeCount;
}
/**
* @return Returns the addedNodeCount.
*/
public List<IObjectMappingAssoziationPO> getAddedNodes() {
return m_addedNodes;
}
}
/** the selection provider for this editor */
private SelectionProviderIntermediate m_selectionProvider;
/** the active tree viewer */
private TreeViewer m_activeTreeViewer = null;
/**
* <code>m_treeFilterText</code>tree Viewer
*/
private Text m_treeFilterText;
/** selection provider for the split pane view */
private SelectionProviderIntermediate m_splitPaneSelectionProvider;
/**
* {@inheritDoc}
*/
protected void createPages() {
if (m_editorHelper == null) {
m_editorHelper = new JBEditorHelper(this);
}
m_omEditorBP = new OMEditorBP(this);
IObjectMappingPO objMap = getAut().getObjMap();
if (objMap == null) {
objMap = PoMaker.createObjectMappingPO();
getAut().setObjMap(objMap);
}
checkMasterSessionUpToDate();
// Create menu manager.
MenuManager menuMgr = createContextMenu();
GuiEventDispatcher.getInstance().addEditorDirtyStateListener(
this, true);
getEditorHelper().addListeners();
getOmEditorBP().collectNewLogicalComponentNames();
int splitPaneViewIndex = addPage(
createSplitPanePageControl(getContainer(), menuMgr));
int configViewIndex = addPage(createConfigPageControl(getContainer()));
setPageText(
splitPaneViewIndex,
Messages.ObjectMappingEditorSplitPaneView);
setPageText(
configViewIndex,
Messages.ObjectMappingEditorConfigView);
m_pageToSelectionProvider.put(splitPaneViewIndex,
m_splitPaneSelectionProvider);
m_pageToSelectionProvider.put(
configViewIndex, new NullSelectionProvider());
m_selectionProvider = new SelectionProviderIntermediate();
m_selectionProvider.setSelectionProviderDelegate(
m_pageToSelectionProvider.get(getActivePage()));
getSite().setSelectionProvider(m_selectionProvider);
getEditorSite().registerContextMenu(menuMgr, m_selectionProvider);
ObjectMappingEventDispatcher.addObserver(this);
checkAndFixInconsistentData();
OpenOMETracker.INSTANCE.addOME(this);
DataEventDispatcher.getInstance().addDataChangedListener(this, false);
}
/**
* @return the context menu manager
*/
private MenuManager createContextMenu() {
MenuManager menuMgr = new MenuManager();
menuMgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
return menuMgr;
}
/**
* Checks whether data from the editor input is inconsistent and fixes any
* inconsistencies, saving immediately afterward if necessary.
*/
private void checkAndFixInconsistentData() {
boolean isChanged = false;
IObjectMappingPO objMap = getAut().getObjMap();
isChanged |= fixCompNameReferences(objMap, getCompNameCache());
isChanged |= removeDeletedCompNames(objMap, getCompNameCache());
isChanged |= CleanupObjectMapping.cleanupObjectMapping(
getAut().getObjMap());
if (isChanged) {
try {
final EditSupport editSupport = m_editorHelper.getEditSupport();
editSupport.lockWorkVersion();
m_editorHelper.setDirty(true);
doSave(new NullProgressMonitor());
} catch (PMAlreadyLockedException e) {
// ignore, we are only doing housekeeping
} catch (PMException e) {
PMExceptionHandler.handlePMExceptionForMasterSession(e);
}
}
}
/**
* Removes deleted Component Names and empty associations from the given
* Object Map.
*
* @param objectMap The Object Map to fix.
* @param cache The cache to use for retrieving Component Names.
*
* @return <code>true</code> if this method call caused any change
* (i.e. if any Component Names were removed).
* Otherwise, <code>false</code>.
*/
private boolean removeDeletedCompNames(
IObjectMappingPO objectMap,
IComponentNameCache cache) {
boolean isChanged = false;
Set<IObjectMappingAssoziationPO> assocsToDelete =
new HashSet<IObjectMappingAssoziationPO>();
for (IObjectMappingAssoziationPO assoc : objectMap.getMappings()) {
if (assoc.getTechnicalName() == null) {
Set<String> compNamesToRemove = new HashSet<String>();
for (String compNameGuid : assoc.getLogicalNames()) {
if (cache.getResCompNamePOByGuid(compNameGuid) == null) {
compNamesToRemove.add(compNameGuid);
}
}
for (String toRemove : compNamesToRemove) {
assoc.removeLogicalName(toRemove);
isChanged = true;
}
if (assoc.getLogicalNames().isEmpty()) {
isChanged = true;
assocsToDelete.add(assoc);
}
}
}
for (IObjectMappingAssoziationPO assoc : assocsToDelete) {
assoc.getCategory().removeAssociation(assoc);
getEditorHelper().getEditSupport().getSession()
.remove(assoc);
}
return isChanged;
}
/**
* Creates the profile configuration page of the editor.
*
* @param parent The parent composite.
* @return the base control of the profile configuration page.
*/
private Control createConfigPageControl(Composite parent) {
GridLayout layout = new GridLayout();
layout.numColumns = 1;
layout.verticalSpacing = 3;
layout.marginWidth = LayoutUtil.MARGIN_WIDTH;
layout.marginHeight = LayoutUtil.MARGIN_HEIGHT;
parent.setLayout(layout);
Composite configComposite = new Composite(parent, SWT.NONE);
GridData gridData =
new GridData (GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_BOTH);
configComposite.setLayoutData(gridData);
layout = new GridLayout();
layout.numColumns = 1;
layout.verticalSpacing = 3;
layout.marginWidth = LayoutUtil.MARGIN_WIDTH;
layout.marginHeight = LayoutUtil.MARGIN_HEIGHT;
configComposite.setLayout(layout);
m_mappingConfigComponent = new ObjectMappingConfigComponent(
configComposite, getAut().getObjMap(), this);
createConfigContextMenu(configComposite);
return configComposite;
}
/**
* Creates the split pane page of the editor.
*
* @param parent The parent composite.
* @return the base control of the split pane view.
* @param contextMenuMgr The manager for the context menu for the created
* trees.
*/
private Control createSplitPanePageControl(Composite parent,
MenuManager contextMenuMgr) {
m_splitPaneSelectionProvider = new SelectionProviderIntermediate();
GridLayout layout = new GridLayout();
layout.numColumns = 1;
layout.verticalSpacing = 1;
layout.marginWidth = 1;
layout.marginHeight = 1;
parent.setLayout(layout);
SashForm mainSash = new SashForm(parent, SWT.VERTICAL);
SashForm topSash = new SashForm(mainSash, SWT.HORIZONTAL);
m_compNameTreeViewer = createSplitPaneViewer(topSash,
"ObjectMappingEditor.UnAssignedLogic", //$NON-NLS-1$
getAut().getObjMap().getUnmappedLogicalCategory(),
contextMenuMgr);
m_splitPaneSelectionProvider.setSelectionProviderDelegate(
m_compNameTreeViewer);
m_uiElementTreeViewer = createSplitPaneViewer(topSash,
"ObjectMappingEditor.UnAssignedTech", //$NON-NLS-1$
getAut().getObjMap().getUnmappedTechnicalCategory(),
contextMenuMgr);
m_mappedComponentTreeViewer = createMappedSplitPaneViewer(mainSash,
"ObjectMappingEditor.Assigned", //$NON-NLS-1$
getAut().getObjMap().getMappedCategory(),
contextMenuMgr);
linkSelection(new TreeViewer[] {
m_compNameTreeViewer, m_uiElementTreeViewer,
m_mappedComponentTreeViewer});
Plugin.getHelpSystem().setHelp(parent,
ContextHelpIds.OBJECT_MAP_EDITOR);
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
return mainSash;
}
/**
* "Links" the selections of the given viewers. This means that the viewer
* selections are mutually exclusive (i.e. if something is already selected
* in viewer 1, and something becomes selected in viewer 2, then the
* selection in viewer 1 is cleared). This method also adds the given
* viewers to the Split Pane view's selection provider group.
*
* @param treeViewersToLink The viewers to link.
*/
private void linkSelection(final TreeViewer[] treeViewersToLink) {
for (final TreeViewer viewer : treeViewersToLink) {
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
if (event.getSelection() != null
&& !event.getSelection().isEmpty()) {
m_splitPaneSelectionProvider
.setSelectionProviderDelegate(viewer);
for (TreeViewer viewerToDeselect : treeViewersToLink) {
if (viewer != viewerToDeselect) {
viewerToDeselect.setSelection(
StructuredSelection.EMPTY);
}
}
}
}
});
}
}
/**
* Creates and returns a tree viewer suitable for use in the split pane
* view.
*
* @param parent The parent composite for the viewer.
* @param title the title to display for the viewer.
* @param topLevelCategory The input for the viewer.
* @param contextMenuMgr The manager for the context menu for the created
* tree.
* @return the created viewer.
*/
private TreeViewer createSplitPaneViewer(
Composite parent,
String title,
IObjectMappingCategoryPO topLevelCategory,
MenuManager contextMenuMgr) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout());
Label titleLabel = new Label(composite, SWT.NONE);
titleLabel.setText(I18n.getString(title));
titleLabel.setLayoutData(
GridDataFactory.defaultsFor(titleLabel).create());
final TreeViewer viewer = new TreeViewer(composite);
viewer.getTree().setLayoutData(
GridDataFactory.fillDefaults().grab(true, true).create());
setProviders(viewer, getCompNameCache());
viewer.setUseHashlookup(true);
viewer.setComparator(new ObjectMappingTreeSorter());
viewer.setComparer(new PersistentObjectComparer());
viewer.setInput(topLevelCategory);
Transfer[] transfers =
new Transfer[] {
org.eclipse.jface.util.LocalSelectionTransfer.getTransfer()};
viewer.addDragSupport(DND.DROP_MOVE, transfers,
new LimitingDragSourceListener(viewer, getAut()));
viewer.addDropSupport(DND.DROP_MOVE, transfers,
new OMDropTargetListener(this, viewer));
createTreeContextMenu(viewer, contextMenuMgr);
DialogUtils.setWidgetName(viewer.getTree(), title);
IFocusService focusService = getSite().getService(IFocusService.class);
focusService.addFocusTracker(viewer.getTree(), title);
viewer.getTree().addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
m_activeTreeViewer = viewer;
}
});
return viewer;
}
/**
* Creates and returns a tree viewer suitable for use in the split pane
* view.
*
* @param parent The parent composite for the viewer.
* @param i18nTitleKey the title to display for the viewer.
* @param topLevelCategory The input for the viewer.
* @param contextMenuMgr The manager for the context menu for the created
* tree.
* @return the created viewer.
*/
private TreeViewer createMappedSplitPaneViewer(
Composite parent,
String i18nTitleKey,
IObjectMappingCategoryPO topLevelCategory,
MenuManager contextMenuMgr) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout());
final FilteredTree ft = new JBFilteredTree(composite, SWT.MULTI
| SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER,
new ObjectMappingEditorPatternFilter(), true);
setTreeFilterText(ft.getFilterControl());
final TreeViewer viewer = ft.getViewer();
viewer.getTree().setLayoutData(
GridDataFactory.fillDefaults().grab(true, true).create());
setProviders(viewer, getCompNameCache());
viewer.setUseHashlookup(true);
viewer.setComparator(new ObjectMappingTreeSorter());
viewer.setComparer(new PersistentObjectComparer());
viewer.setInput(topLevelCategory);
Transfer[] transfers =
new Transfer[] {
org.eclipse.jface.util.LocalSelectionTransfer.getTransfer()};
viewer.addDragSupport(DND.DROP_MOVE, transfers,
new LimitingDragSourceListener(viewer, getAut()));
viewer.addDropSupport(DND.DROP_MOVE, transfers,
new OMDropTargetListener(this, viewer));
createTreeContextMenu(viewer, contextMenuMgr);
DialogUtils.setWidgetName(viewer.getTree(), i18nTitleKey);
IFocusService focusService = getSite().getService(IFocusService.class);
focusService.addFocusTracker(viewer.getTree(), i18nTitleKey);
viewer.getTree().addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
m_activeTreeViewer = viewer;
}
});
return viewer;
}
/**
* @param treeFilterText the treeFilterText to set
*/
public void setTreeFilterText(Text treeFilterText) {
m_treeFilterText = treeFilterText;
}
/**
* @return the treeFilterText
*/
public Text getTreeFilterText() {
return m_treeFilterText;
}
/**
*
* @param viewer The viewer on which to create the context menu.
* @param menuMgr The manager for the context menu.
*/
private void createTreeContextMenu(TreeViewer viewer, MenuManager menuMgr) {
// Create menu.
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
}
/**
* Create context menu for the configuration editor view.
*
* @param configComposite The composite that holds the configuration page.
*/
private void createConfigContextMenu(Composite configComposite) {
// Create menu manager.
MenuManager menuMgr = new MenuManager();
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager mgr) {
fillConfigContextMenu(mgr);
}
});
// Create menu.
Menu menu = menuMgr.createContextMenu(configComposite);
setConfigContextMenu(configComposite, menu);
}
/**
* Recursively sets the context menu for <code>control</code> and all of its
* children to <code>menu</code>.
*
* @param control The start point for setting the menu.
* @param menu The menu to use.
*/
private void setConfigContextMenu(Control control, Menu menu) {
control.setMenu(menu);
if (control instanceof Composite) {
for (Control child : ((Composite)control).getChildren()) {
setConfigContextMenu(child, menu);
}
}
}
/**
* fill the tree context menu
*
* @param mgr
* IMenuManager
*/
protected void fillConfigContextMenu(IMenuManager mgr) {
CommandHelper.createContributionPushItem(mgr,
RCPCommandIDs.REVERT_CHANGES);
}
/**
* {@inheritDoc}
*/
public void doSave(IProgressMonitor monitor) {
monitor.beginTask(Messages.EditorsSaveEditors,
IProgressMonitor.UNKNOWN);
boolean errorOccured = false;
IObjectMappingPO objMap = getAut().getObjMap();
TimestampBP.refreshTimestamp(objMap);
try {
if (getEditorHelper().isDirty()) {
performSave();
}
} catch (PMException e) {
PMExceptionHandler.handlePMExceptionForEditor(e, this);
errorOccured = true;
} catch (ProjectDeletedException e) {
PMExceptionHandler.handleProjectDeletedException();
errorOccured = true;
} finally {
monitor.done();
if (!errorOccured) {
try {
reOpenEditor(((PersistableEditorInput)getEditorInput())
.getNode());
} catch (PMException e) {
PMExceptionHandler.handlePMExceptionForEditor(e, this);
}
}
}
}
/** Performs the save operation */
private void performSave() throws PMReadException, PMSaveException,
PMException, ProjectDeletedException {
EditSupport editSupport = getEditorHelper().getEditSupport();
IWritableComponentNameCache compNameCache = editSupport.getCache();
IObjectMappingProfilePO origProfile =
((IAUTMainPO)editSupport.getOriginal()).getObjMap()
.getProfile();
IObjectMappingProfilePO workProfile =
((IAUTMainPO)editSupport.getWorkVersion()).getObjMap()
.getProfile();
fixCompNameReferences(getAut().getObjMap(),
compNameCache);
editSupport.saveWorkVersion();
compNameCache.clear();
DataEventDispatcher.getInstance().fireDataChangedListener(
this.getAut().getObjMap(),
DataState.StructureModified,
UpdateState.all);
DataEventDispatcher.getInstance().fireDataChangedListener(
getAut().getObjMap(), DataState.Saved, UpdateState.all);
if (getAut().equals(
TestExecution.getInstance().getConnectedAut())
&& !workProfile.equals(origProfile)) {
NagDialog.runNagDialog(
Plugin.getActiveWorkbenchWindowShell(),
"InfoNagger.ObjectMappingProfileChanged", //$NON-NLS-1$
ContextHelpIds.OBJECT_MAP_EDITOR);
}
}
/**
* Replaces Component Name references with the referenced Component Names
* and deletes any Component Name references that are no longer used.
*
* @param objectMap The Object Map to fix.
* @param compNameCache The cache to use for retrieving Component Names.
*
* @return <code>true</code> if this method call caused any change
* (i.e. if any references were fixed).
* Otherwise, <code>false</code>.
*/
private boolean fixCompNameReferences(
IObjectMappingPO objectMap,
IComponentNameCache compNameCache) {
boolean isChanged = false;
// Replace all reference guids with referenced guids
for (IObjectMappingAssoziationPO assoc
: objectMap.getMappings()) {
Set<String> guidsToRemove = new HashSet<String>();
for (String compNameGuid : assoc.getLogicalNames()) {
IComponentNamePO compNamePo =
compNameCache.getResCompNamePOByGuid(compNameGuid);
if (compNamePo != null
&& !compNamePo.getGuid().equals(compNameGuid)) {
guidsToRemove.add(compNameGuid);
}
}
for (String toRemove : guidsToRemove) {
isChanged = true;
assoc.removeLogicalName(toRemove);
}
}
if (isChanged) {
CompNamePM.removeUnusedCompNames(
GeneralStorage.getInstance().getProject().getId(),
getEditorHelper().getEditSupport().getSession());
}
return isChanged;
}
/**
* {@inheritDoc}
*/
public void doSaveAs() {
// do nothing
}
/**
* {@inheritDoc}
*/
public boolean isSaveAsAllowed() {
return false;
}
/**
*
* @return the aut the editor is editing
*/
public IAUTMainPO getAut() {
return (IAUTMainPO)getEditorHelper().getEditSupport().getWorkVersion();
}
/**
* Checks if the MasterSession is up to date.
*/
private void checkMasterSessionUpToDate() {
ITimestampPO objMap = getAut().getObjMap();
final boolean isUpToDate = TimestampBP.refreshEditorNodeInMasterSession(
objMap);
if (!isUpToDate) {
CompletenessBP.getInstance().completeProjectCheck();
}
}
/**
* {@inheritDoc}
*/
public Image getDisabledTitleImage() {
return IconConstants.DISABLED_OM_EDITOR_IMAGE;
}
/**
* {@inheritDoc}
*/
public Composite getParentComposite() {
return getContainer().getParent();
}
/**
* {@inheritDoc}
*/
public void reOpenEditor(IPersistentObject obj) throws PMException {
getEditorHelper().setDirty(false);
getEditorHelper().getEditSupport().close();
PersistableEditorInput input = new PersistableEditorInput(obj);
try {
init(getEditorSite(), input);
// MultiPageEditorPart sets the selection provider to a
// MultiPageSelectionProvider during init. We want to continue
// using our own selection provider, so we re-set it here.
m_selectionProvider.setSelectionProviderDelegate(
m_pageToSelectionProvider.get(getActivePage()));
getSite().setSelectionProvider(m_selectionProvider);
final IObjectMappingPO om = getAut().getObjMap();
m_mappingConfigComponent.setInput(om);
Map<TreeViewer, IObjectMappingCategoryPO> viewerToInput =
new HashMap<TreeViewer, IObjectMappingCategoryPO>();
viewerToInput.put(m_compNameTreeViewer,
om.getUnmappedLogicalCategory());
viewerToInput.put(m_uiElementTreeViewer,
om.getUnmappedTechnicalCategory());
viewerToInput.put(m_mappedComponentTreeViewer,
om.getMappedCategory());
for (TreeViewer splitViewer : viewerToInput.keySet()) {
Object [] expandedSplitViewerElements =
splitViewer.getExpandedElements();
setProviders(splitViewer, getCompNameCache());
splitViewer.setInput(viewerToInput.get(splitViewer));
splitViewer.setExpandedElements(expandedSplitViewerElements);
// Clearing the selection seems to help prevent the behavior
// noted in bug http://eclip.se/334269
splitViewer.setSelection(StructuredSelection.EMPTY);
}
} catch (PartInitException e) {
getSite().getPage().closeEditor(this, false);
}
}
/**
* Assigns new (Object Mapping related) content and label providers to
* the given viewer.
*
* @param viewer The viewer to receive new providers.
* @param compNameCache The cache to use to initialize the providers.
*/
private static void setProviders(AbstractTreeViewer viewer,
IWritableComponentNameCache compNameCache) {
viewer.setLabelProvider(
new OMEditorTreeLabelProvider(compNameCache));
viewer.setContentProvider(
new OMEditorTreeContentProvider(compNameCache));
}
/**
* {@inheritDoc}
*/
public void setFocus() {
if (getActivePage() == SPLIT_PAGE_IDX) {
if (!m_compNameTreeViewer.getSelection().isEmpty()) {
m_compNameTreeViewer.getControl().setFocus();
} else if (!m_uiElementTreeViewer.getSelection().isEmpty()) {
m_uiElementTreeViewer.getControl().setFocus();
} else {
m_mappedComponentTreeViewer.getControl().setFocus();
}
} else {
super.setFocus();
}
Plugin.showStatusLine(this);
}
/**
* {@inheritDoc}
*/
public void fireDirtyProperty(boolean isDirty) {
// fire property for change of dirty state
firePropertyChange(IEditorPart.PROP_DIRTY);
if (!isDirty) {
firePropertyChange(IEditorPart.PROP_INPUT);
}
}
/**
* {@inheritDoc}
*/
public JBEditorHelper getEditorHelper() {
return m_editorHelper;
}
/**
* {@inheritDoc}
*/
public String getEditorPrefix() {
return Messages.ObjectMappingEditorEditor;
}
/**
* {@inheritDoc}
*/
public void initTextAndInput(IEditorSite site, IEditorInput input) {
setSite(site);
setInput(input);
setPartName(getEditorPrefix() + input.getName());
getEditorSite().getActionBars().getMenuManager();
}
/**
* {@inheritDoc}
*/
public void update(final int event, final Object obj) {
Plugin.getDisplay().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
public void run() {
switchEvent(event, obj);
}
});
}
/**
* inserts a new Technical Name into GUIModel
*
* @param components
* IComponentIdentifier
*/
private void createNewTechnicalNames(
final IComponentIdentifier[] components) {
if (getEditorHelper().requestEditableState() != EditableState.OK) {
return;
}
List<IObjectMappingAssoziationPO> alteredOMAs =
new ArrayList<IObjectMappingAssoziationPO>();
final IObjectMappingPO objMap = getAut().getObjMap();
for (IComponentIdentifier component : components) {
IObjectMappingAssoziationPO techNameAssoc = objMap
.addTechnicalName(component, getAut());
if (techNameAssoc != null) {
final IObjectMappingCategoryPO categoryToCreateIn = m_omEditorBP
.getCategoryToCreateIn();
if (categoryToCreateIn != null) {
categoryToCreateIn.addAssociation(techNameAssoc);
} else {
objMap.getUnmappedTechnicalCategory().addAssociation(
techNameAssoc);
}
alteredOMAs.add(techNameAssoc);
} else {
// Technical Name already exists
for (IObjectMappingAssoziationPO assoc : objMap.getMappings()) {
IComponentIdentifier techName = assoc.getTechnicalName();
if (techName != null && techName.equals(component)) {
techNameAssoc = assoc;
techNameAssoc.setCompIdentifier(component);
alteredOMAs.add(techNameAssoc);
break;
}
}
}
}
DataEventDispatcher ded = DataEventDispatcher.getInstance();
ded.fireDataChangedListener(objMap,
DataState.StructureModified, UpdateState.onlyInEditor);
for (IObjectMappingAssoziationPO alteredOMA : alteredOMAs) {
ded.fireDataChangedListener(alteredOMA,
DataState.StructureModified, UpdateState.onlyInEditor);
IStructuredSelection techNameSelection =
new StructuredSelection(alteredOMA);
m_uiElementTreeViewer.setSelection(techNameSelection);
m_mappedComponentTreeViewer.setSelection(techNameSelection);
}
getSite().getPage().activate(this);
if (!alteredOMAs.isEmpty()) {
getEditorHelper().setDirty(true);
refreshAllViewer();
}
}
/**
* call refresh() for all the different viewers in this editor
*/
private void refreshAllViewer() {
m_uiElementTreeViewer.refresh();
m_compNameTreeViewer.refresh();
m_mappedComponentTreeViewer.refresh();
}
/**
* executes the right update
* @param event
* int
* @param obj
* Object
*/
private void switchEvent(int event, Object obj) {
switch (event) {
case IObjectMappingObserver.EVENT_STEP_RECORDED :
IAUTMainPO aut = (IAUTMainPO)obj;
if (getAut().equals(aut)) {
cleanupNames();
synchronizeViewers();
}
break;
case IObjectMappingObserver.EVENT_COMPONENT_MAPPED :
IAUTMainPO connectedAut =
TestExecution.getInstance().getConnectedAut();
if (getAut().equals(connectedAut)) {
IComponentIdentifier[] comp = (IComponentIdentifier[])obj;
createNewTechnicalNames(comp);
}
break;
default:
}
}
/**
* Synchronizes the Viewers with the Edit Support after the latter changes
*/
public void synchronizeViewers() {
final IObjectMappingPO om = getAut().getObjMap();
m_compNameTreeViewer.setInput(om.getUnmappedLogicalCategory());
m_uiElementTreeViewer.setInput(om.getUnmappedTechnicalCategory());
m_mappedComponentTreeViewer.setInput(om.getMappedCategory());
}
/**
* {@inheritDoc}
*/
public void handleEditorDirtyStateChanged(
IJBEditor gdEditor, boolean isDirty) {
if (gdEditor == this) {
IEvaluationService service = getSite().getService(
IEvaluationService.class);
service.requestEvaluation(EditorPartPropertyTester.FQN_IS_DIRTY);
}
}
/**
* @return Returns the omEditorBP.
*/
public OMEditorBP getOmEditorBP() {
return m_omEditorBP;
}
/**
* {@inheritDoc}
*/
public TreeViewer getTreeViewer() {
return getActiveTreeViewer();
}
/**
* removed all not used logical names
* @return int the number of added items
*/
public int cleanupNames() {
int addedItems = 0;
for (ITestSuitePO ts : TestSuiteBP.getListOfTestSuites()) {
if (ts.getAut() == null) {
continue;
}
if (ts.getAut().equals(getAut())) {
CollectLogicalNamesOp op = new CollectLogicalNamesOp();
TreeTraverser traverser = new TreeTraverser(ts, op);
traverser.traverse(true);
addedItems += op.getAddedNodeCount();
if (m_compNameTreeViewer != null) {
m_compNameTreeViewer.refresh();
}
}
}
if (addedItems > 0) {
getEditorHelper().setDirty(true);
}
if (!isDirty()) {
try {
getEditorHelper().getEditSupport().reinitializeEditSupport();
getEditorHelper().resetEditableState();
} catch (PMException e) {
PMExceptionHandler.handlePMExceptionForEditor(e, this);
}
}
return addedItems;
}
/**
* adding new component names to the OMEditor, if present
* @return number of new comp names
*/
public int addNewCompNames() {
return 0;
}
/**
* {@inheritDoc}
*/
public void propertyChanged(Object source, int propId) {
if (propId == IWorkbenchPartConstants.PROP_DIRTY) {
((IEditorPart)source).isDirty();
}
}
/**
* {@inheritDoc}
*/
public boolean isDirty() {
return super.isDirty() || getEditorHelper().isDirty();
}
/**
* {@inheritDoc}
*/
public void dispose() {
DataEventDispatcher.getInstance().removeDataChangedListener(this);
ObjectMappingEventDispatcher.removeObserver(this);
OpenOMETracker.INSTANCE.removeOME(this);
getEditorSite().getActionBars().setGlobalActionHandler(
ActionFactory.REFRESH.getId(), null);
IAUTMainPO connectedAut = TestExecution.getInstance().getConnectedAut();
if (AUTModeChangedCommand.getAutMode()
== ChangeAUTModeMessage.OBJECT_MAPPING
&& connectedAut != null
&& connectedAut.equals(getAut())) {
TestExecutionContributor.getInstance()
.getClientTest().resetToTesting();
DataEventDispatcher.getInstance()
.fireOMStateChanged(OMState.notRunning);
}
getSite().setSelectionProvider(null);
GuiEventDispatcher.getInstance().removeEditorDirtyStateListener(this);
if (m_editorHelper != null) {
m_editorHelper.dispose();
}
super.dispose();
}
/**
* {@inheritDoc}
*/
public Object getAdapter(Class adapter) {
Object superAdapter = super.getAdapter(adapter);
if (superAdapter != null) {
return superAdapter;
}
if (m_editorHelper != null) {
return m_editorHelper.getAdapter(adapter);
}
return null;
}
/**
* Handles those events which may change the component names from outside the editor's scope
* These are so far: removing a component name through the Browser or renaming one anywhere
* @param events the events
*/
public void handleDataChanged(DataChangedEvent... events) {
boolean refreshView = false;
for (DataChangedEvent e : events) {
if (e.getUpdateState() != UpdateState.onlyInEditor
&& e.getPo() instanceof IComponentNamePO) {
handleOneChange((IComponentNamePO) e.getPo(), e.getDataState());
break;
}
}
}
/**
* Deals with a single data change event
* @param compName the Component Name
* @param state the data state
*/
private void handleOneChange(IComponentNamePO compName, DataState state) {
switch (state) {
case Renamed:
getCompNameCache().renamedCompName(compName.getGuid(),
compName.getName());
break;
case Deleted:
getOmEditorBP().deleteCompName(compName, false);
break;
default:
}
m_compNameTreeViewer.refresh();
m_uiElementTreeViewer.refresh();
m_mappedComponentTreeViewer.refresh();
}
/**
* {@inheritDoc}
*/
public void init(IEditorSite site,
IEditorInput input) throws PartInitException {
super.init(site, input);
if (m_editorHelper == null) {
m_editorHelper = new JBEditorHelper(this);
}
m_editorHelper.init(site, input);
}
/**
* {@inheritDoc}
*/
protected void pageChange(int newPageIndex) {
super.pageChange(newPageIndex);
m_selectionProvider.setSelectionProviderDelegate(
m_pageToSelectionProvider.get(newPageIndex));
}
/**
* {@inheritDoc}
*/
public TreeViewer getActiveTreeViewer() {
return m_activeTreeViewer;
}
/**
* {@inheritDoc}
*/
public TreeViewer[] getTreeViewers() {
return new TreeViewer[] {
m_compNameTreeViewer,
m_uiElementTreeViewer,
m_mappedComponentTreeViewer };
}
/**
*
* @return the component name TreeViewer
*/
public TreeViewer getCompNameTreeViewer() {
return m_compNameTreeViewer;
}
/**
*
* @return the UI-Element TreeViewer
*/
public TreeViewer getUIElementTreeViewer() {
return m_uiElementTreeViewer;
}
/**
*
* @return the mapped components TreeViewer
*/
public TreeViewer getMappedTreeViewer() {
return m_mappedComponentTreeViewer;
}
/**
*
* {@inheritDoc}
*/
public EntityManager getEntityManager() {
return getEditorHelper().getEditSupport().getSession();
}
/** {@inheritDoc} */
public IWritableComponentNameCache getCompNameCache() {
return getEditorHelper().getEditSupport().getCache();
}
}