Java tutorial
/******************************************************************************* * Copyright (c) 2006, 2011 Obeo. * 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: * Obeo - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.ui.viewer.structure; import java.util.ArrayList; import java.util.List; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareUI; import org.eclipse.compare.CompareViewerPane; import org.eclipse.core.resources.IFile; import org.eclipse.emf.compare.diff.metamodel.AbstractDiffExtension; import org.eclipse.emf.compare.diff.metamodel.ComparisonResourceSetSnapshot; import org.eclipse.emf.compare.diff.metamodel.ComparisonResourceSnapshot; import org.eclipse.emf.compare.diff.metamodel.ComparisonSnapshot; import org.eclipse.emf.compare.diff.metamodel.DiffElement; import org.eclipse.emf.compare.diff.metamodel.UpdateAttribute; import org.eclipse.emf.compare.ui.CompareTextDialog; import org.eclipse.emf.compare.ui.EMFCompareUIMessages; import org.eclipse.emf.compare.ui.ModelCompareInput; import org.eclipse.emf.compare.ui.export.ExportMenu; import org.eclipse.emf.compare.ui.internal.ModelComparator; import org.eclipse.emf.compare.ui.util.EMFCompareConstants; import org.eclipse.emf.compare.util.AdapterUtils; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; /** * Compare and merge viewer with an area showing diffs as a structured tree. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> * @noextend This class is not intended to be subclassed by clients. */ public class ModelStructureMergeViewer extends TreeViewer { /** Configuration element of the underlying comparison. */ protected CompareConfiguration configuration; /** This is the action displaying the "export diff as..." menu. */ protected ExportMenu exportMenu; /** * The Dialog for the text comparison. * * @since 1.2 */ protected CompareTextDialog textDialog; /** * Allows us to ignore a selection event in the content viewer if it is one caused by a selection event in * the structure viewer. */ /* package */boolean ignoreContentSelection; /** * Creates a new model structure merge viewer and intializes it. * * @param parent * Parent composite for this viewer. * @param compareConfiguration * The configuration object. */ public ModelStructureMergeViewer(Composite parent, CompareConfiguration compareConfiguration) { super(parent); initialize(compareConfiguration); createToolItems(); } /** * Returns the compare configuration of this viewer, or <code>null</code> if this viewer does not yet have * a configuration. * * @return the compare configuration, or <code>null</code> if none */ public CompareConfiguration getCompareConfiguration() { return configuration; } /** * Returns the viewer's title. * * @return The viewer's title. * @see CompareUI#COMPARE_VIEWER_TITLE */ public String getTitle() { return EMFCompareUIMessages.getString("ModelStructureMergeViewer.viewerTitle"); //$NON-NLS-1$ } /** * Returns the widget representing the given element. * * @param element * Element we seek the {@link TreeItem} for. * @return The widget representing the given element. */ /* package */Widget find(Object element) { final Widget widget = super.findItem(element); return widget; } /** * Creates this viewer's content provider. * * @param compareConfiguration * Compare configuration that's been fed this viewer. * @return This viewer's content provider. * @since 1.1 */ protected ModelStructureContentProvider createContentProvider(CompareConfiguration compareConfiguration) { return new ModelStructureContentProvider(compareConfiguration); } /** * Creates this viewer's label provider. * * @param compareConfiguration * Compare configuration that's been fed this viewer. * @return This viewer's label provider. * @since 1.1 */ protected ModelStructureLabelProvider createLabelProvider( @SuppressWarnings("unused") CompareConfiguration compareConfiguration) { return new ModelStructureLabelProvider(); } /** * This will initialize the "save as emfdiff" action and put its icon in the {@link CompareViewerPane} * toolbar. It will also initializes the contextual menu for the text comparison action. */ protected void createToolItems() { final ToolBarManager tbm = CompareViewerPane.getToolBarManager(getControl().getParent()); if (exportMenu == null) { exportMenu = new ExportMenu(tbm.getControl(), this); } tbm.add(new Separator("IO")); //$NON-NLS-1$ tbm.appendToGroup("IO", exportMenu); //$NON-NLS-1$ tbm.update(true); final MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { /** * {@inheritDoc} * * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) */ public void menuAboutToShow(IMenuManager manager) { final Action action = new CompareTextAction(); action.setText(EMFCompareUIMessages.getString("CompareTextDialog_labelAction")); //$NON-NLS-1$ if (action.isEnabled()) manager.add(action); } }); final Menu menu = menuMgr.createContextMenu(getTree()); getTree().setMenu(menu); getCompareConfiguration().getContainer().getWorkbenchPart().getSite().registerContextMenu(menuMgr, this); } /** * Returns the {@link UpdateAttribute} that is selected in the Tree if any. * * @return The {@link UpdateAttribute} that is selected in the Tree if any, <code>null</code> otherwise. * @since 1.2 */ protected UpdateAttribute getUpdateAttribute() { final TreeItem[] items = getTree().getSelection(); if (items.length > 0) { final TreeItem item = items[0]; if (item.getData() instanceof UpdateAttribute) { return (UpdateAttribute) item.getData(); } } return null; } /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.StructuredViewer#fireOpen(org.eclipse.jface.viewers.OpenEvent) */ @Override protected void fireOpen(OpenEvent event) { // DIRTY // cancels open events that could be fired to avoid "NullViewer" // opening. } /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.Viewer#fireSelectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ @Override protected void fireSelectionChanged(SelectionChangedEvent event) { // DIRTY // cancels selection changed events that could be fired to avoid // "NullViewer" opening. } /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.ContentViewer#handleDispose(org.eclipse.swt.events.DisposeEvent) */ @Override protected void handleDispose(DisposeEvent event) { super.handleDispose(event); exportMenu.dispose(); } /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.AbstractTreeViewer#inputChanged(java.lang.Object, java.lang.Object) */ @Override protected void inputChanged(Object input, Object oldInput) { final TreePath[] expandedPaths = getExpandedTreePaths(); super.inputChanged(input, oldInput); if (input != null) { final ComparisonSnapshot snapshot; if (input instanceof ModelCompareInput && ((ModelCompareInput) input).getComparisonSnapshot() != null) { snapshot = ((ModelCompareInput) input).getComparisonSnapshot(); } else if (!(input instanceof ComparisonSnapshot) && input != oldInput) { snapshot = ModelComparator.getComparator(configuration).getComparisonResult(); } else { snapshot = null; } if (snapshot != null) { Object match = null; // check whether a resource or resource set comparison was performed if (snapshot instanceof ComparisonResourceSnapshot) { match = ((ComparisonResourceSnapshot) snapshot).getMatch(); } else { match = ((ComparisonResourceSetSnapshot) snapshot).getMatchResourceSet(); } if (match != null) { setInput(snapshot); } else { setInput(null); } } updateToolItems(); setExpandedTreePaths(expandedPaths); } else { hideStructurePane(); } } /** * Updates the Structure viewer's tool items. This will modify the actions of the "export diff as..." * menu. */ protected void updateToolItems() { final ModelComparator comparator = ModelComparator.getComparator(configuration); if (comparator != null) { exportMenu.enableSave(!comparator.isLeftRemote() && !comparator.isRightRemote()); } else { exportMenu.enableSave(false); } CompareViewerPane.getToolBarManager(getControl().getParent()).update(true); } /** * This will be called when the input of this viewer is set to <code>null</code> and hide the whole * viewer. */ private void hideStructurePane() { getControl().getParent().getParent().setVisible(false); } /** * initializer of this structure merge viewer. It sets the {@link LabelProvider label} and content * provider for the tree and creates the different needed listeners. * * @param compareConfiguration * Configuration of the underlying comparison. */ private void initialize(CompareConfiguration compareConfiguration) { configuration = compareConfiguration; setLabelProvider(createLabelProvider(compareConfiguration)); setUseHashlookup(true); setContentProvider(createContentProvider(compareConfiguration)); final Tree tree = getTree(); tree.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle()); tree.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { // Nothing to do here } public void widgetSelected(SelectionEvent e) { final List<DiffElement> selectedElements = new ArrayList<DiffElement>( getTree().getSelection().length); for (final TreeItem item : getTree().getSelection()) if (item.getData() instanceof DiffElement) { selectedElements.add((DiffElement) item.getData()); } ignoreContentSelection = true; configuration.setProperty(EMFCompareConstants.PROPERTY_STRUCTURE_SELECTION, selectedElements); } }); configuration.addPropertyChangeListener(new ConfigurationPropertyListener()); final IWorkbenchPart part = configuration.getContainer().getWorkbenchPart(); if (part != null) { part.getSite().setSelectionProvider(this); } } /** * Listens to property change events on the compare configuration. Typically responds to selection change * events in the content viewer and preferences change. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private class ConfigurationPropertyListener implements IPropertyChangeListener { /** * Default constructor. Implemented to increase its visibility. */ public ConfigurationPropertyListener() { // no action is to be taken here } /** * {@inheritDoc} * * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(EMFCompareConstants.PROPERTY_CONTENT_SELECTION)) { if (ignoreContentSelection) { ignoreContentSelection = false; } else { TreeItem item = (TreeItem) find(event.getNewValue()); /* * if we could not find the item, we need to expand the whole tree to try and get it. This * is due to the lazy loading of the tree content. */ if (item == null) { final Object[] expandedElements = getExpandedElements(); expandAll(); item = (TreeItem) find(event.getNewValue()); setExpandedElements(expandedElements); } if (item != null) { setSelection(new StructuredSelection(item.getData()), true); expandToLevel(item.getData(), 0); } } } else if (event.getProperty().equals(EMFCompareConstants.PROPERTY_CONTENT_INPUT_CHANGED)) { setInput(event.getNewValue()); } } } /** * {@link LabelProvider} of this viewer. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private class ModelStructureLabelProvider extends LabelProvider { /** * We use this generic label provider, but we want to customize some aspects that's why we choose to * aggregate it. */ /* package */AdapterFactoryLabelProvider adapterProvider; /** * Default constructor. */ public ModelStructureLabelProvider() { adapterProvider = new AdapterFactoryLabelProvider(AdapterUtils.getAdapterFactory()); } /** * Returns the platform icon for a given {@link IFile}. If not an {@link IFile}, delegates to the * {@link AdapterFactoryLabelProvider} to get the {@link Image}. * * @param object * Object to get the {@link Image} for. * @return The platform icon for the given object. * @see AdapterFactoryLabelProvider#getImage(Object) */ @Override public Image getImage(Object object) { Image image = null; if (object instanceof AbstractDiffExtension) { image = (Image) ((AbstractDiffExtension) object).getImage(); } else if (object instanceof IFile) { image = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE); } // fallback to ItemProvider if (image == null) { image = adapterProvider.getImage(object); } return image; } /** * Returns the name of the given {@link IFile}, delegates to * {@link AdapterFactoryLabelProvider#getText(Object)} if not an {@link IFile}. * * @param object * Object we seek the name for. * @return The name of the given object. * @see AdapterFactoryLabelProvider#getText(Object) */ @Override public String getText(Object object) { String text = null; if (object instanceof AbstractDiffExtension) { text = ((AbstractDiffExtension) object).getText(); } else if (object instanceof IFile) { text = ((IFile) object).getName(); } else if (object instanceof Resource) { text = ((Resource) object).getURI().lastSegment(); } // fallback to ItemProvider if (text == null || "".equals(text)) { //$NON-NLS-1$ text = adapterProvider.getText(object); } return text; } } /** * Textual comparison action. * * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a> */ private class CompareTextAction extends Action { /** * Enhances visibility of the default constructor. */ public CompareTextAction() { // Enhances visibility of the default constructor. } /** * {@inheritDoc} * * @see org.eclipse.jface.action.Action#isEnabled() */ @Override public boolean isEnabled() { final UpdateAttribute element = getUpdateAttribute(); if (element != null) { return element.getAttribute().getEType().getInstanceClass().isAssignableFrom(String.class); } return false; } /** * {@inheritDoc} * * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { super.run(); final UpdateAttribute element = getUpdateAttribute(); if (element != null) { if (textDialog != null) { textDialog.setInput(element); textDialog.getShell().setActive(); } else { textDialog = new CompareTextDialog(getTree().getShell(), element, ModelStructureMergeViewer.this, getInput()); textDialog.create(); textDialog.getShell().addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { textDialog = null; } }); textDialog.setBlockOnOpen(false); textDialog.open(); } } } } }