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.content.part.property; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.compare.diff.metamodel.AttributeChange; import org.eclipse.emf.compare.diff.metamodel.DiffElement; import org.eclipse.emf.compare.diff.metamodel.ReferenceChange; import org.eclipse.emf.compare.match.metamodel.Match2Elements; import org.eclipse.emf.compare.match.metamodel.UnmatchElement; import org.eclipse.emf.compare.ui.EMFCompareUIMessages; import org.eclipse.emf.compare.ui.util.EMFCompareConstants; import org.eclipse.emf.compare.ui.util.EMFCompareEObjectUtils; import org.eclipse.emf.compare.ui.viewer.content.ModelContentMergeViewer; import org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab; import org.eclipse.emf.compare.ui.viewer.content.part.ModelContentMergeTabFolder; import org.eclipse.emf.compare.ui.viewer.content.part.ModelContentMergeTabItem; import org.eclipse.emf.compare.util.AdapterUtils; import org.eclipse.emf.compare.util.EMFCompareMap; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Scrollable; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Widget; /** * Represents the property view under a {@link ModelContentMergeTabFolder}'s property tab. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> * @noextend This class is not intended to be subclassed by clients. */ public final class ModelContentMergePropertyTab extends TableViewer implements IModelContentMergeViewerTab { /** <code>int</code> representing this viewer part side. */ protected final int partSide; /** Maps a TreeItem to its data. The key used is the URI of the data. */ private final Map<String, ModelContentMergeTabItem> dataToItem = new EMFCompareMap<String, ModelContentMergeTabItem>(); /** * Keeps a reference to all the differences detected by the comparison. This list will be cleared as soon * as {@link #dataToItem} will be populated. */ private final List<DiffElement> differences = new ArrayList<DiffElement>(); /** Keeps a reference to the containing tab folder. */ private final ModelContentMergeTabFolder parent; /** * Creates a table viewer on a newly-created table control under the given parent. The table control is * created using the given style bits. * * @param parentComposite * the parent control. * @param side * Side of this viewer part. * @param parentFolder * Parent folder of this tab. */ public ModelContentMergePropertyTab(Composite parentComposite, int side, ModelContentMergeTabFolder parentFolder) { super(parentComposite, SWT.NONE); partSide = side; parent = parentFolder; setContentProvider(new PropertyContentProvider()); setLabelProvider(new PropertyLabelProvider(AdapterUtils.getAdapterFactory())); getTable().addPaintListener(new PropertyPaintListener()); setUseHashlookup(true); getTable().setLinesVisible(true); getTable().setHeaderVisible(true); final GC gc = new GC(getTable()); gc.setFont(getTable().getFont()); final FontMetrics metrics = gc.getFontMetrics(); gc.dispose(); final TableColumn nameColumn = new TableColumn(getTable(), SWT.LEFT); nameColumn.setText(EMFCompareUIMessages.getString("ModelContentMergePropertyPart.column1.name")); //$NON-NLS-1$ nameColumn.setWidth(Dialog.convertWidthInCharsToPixels(metrics, nameColumn.getText().length() * 3)); final TableColumn weightsColumn = new TableColumn(getTable(), SWT.RIGHT); weightsColumn.setText(EMFCompareUIMessages.getString("ModelContentMergePropertyPart.column2.name")); //$NON-NLS-1$ weightsColumn.setWidth(Dialog.convertWidthInCharsToPixels(metrics, weightsColumn.getText().length() * 3)); mapDifferences(); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab#dispose() */ public void dispose() { dataToItem.clear(); differences.clear(); getTable().dispose(); } /** * Returns the widget representing the given {@link DiffElement} in the table. * * @param diff * {@link DiffElement} to seek in the table. * @return The widget representing the given {@link DiffElement}. * @see org.eclipse.jface.viewers.StructuredViewer#findItem(Object) */ public TableItem find(DiffElement diff) { final EObject inputEObject = ((PropertyContentProvider) getContentProvider()).getInputEObject(); TableItem item = null; if (diff instanceof AttributeChange) { final AttributeChange theDiff = (AttributeChange) diff; if (theDiff.getLeftElement() == inputEObject || theDiff.getRightElement() == inputEObject) item = (TableItem) findItem(theDiff.getAttribute()); } else if (diff instanceof ReferenceChange) { final ReferenceChange theDiff = (ReferenceChange) diff; if (theDiff.getLeftElement() == inputEObject || theDiff.getRightElement() == inputEObject) item = (TableItem) findItem(theDiff.getReference()); } return item; } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab#getSelectedElements() */ public List<? extends Item> getSelectedElements() { final List<TableItem> result = new ArrayList<TableItem>(); final Control control = getControl(); if (control != null && !control.isDisposed()) { final List<?> list = getSelectionFromWidget(); for (Object o : list) { final Widget w = findItem(o); if (w instanceof TableItem) result.add((TableItem) w); } } return result; } /** * Returns the side of this viewer part. * * @return The side of this viewer part. */ public int getSide() { return partSide; } /** * Returns the width of the columns displayed. * * @return The width of the columns displayed. */ public int getTotalColumnsWidth() { int width = 0; for (final TableColumn col : getTable().getColumns()) width += col.getWidth(); return width; } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab#getUIItem(org.eclipse.emf.ecore.EObject) */ public ModelContentMergeTabItem getUIItem(EObject data) { final EObject element; if (getInput() instanceof UnmatchElement) element = ((UnmatchElement) getInput()).getElement(); else if (partSide == EMFCompareConstants.LEFT) element = ((Match2Elements) getInput()).getLeftElement(); else element = ((Match2Elements) getInput()).getRightElement(); String key = EcoreUtil.getURI(element).fragment(); final String[] fragments = EcoreUtil.getURI(data).fragment().split("/"); //$NON-NLS-1$ if (fragments.length > 0) key += '/' + fragments[fragments.length - 1]; final ModelContentMergeTabItem item = dataToItem.get(key); if (item != null) { computeUIInfoFor(item); } return item; } /** * Returns a list of this tab's visible elements. * <p> * For the property tab, we won't try and find the "visible" elements and this will return the whole * table's content. * </p> * * @return List of this tab's elements. */ public List<ModelContentMergeTabItem> getVisibleElements() { if (dataToItem.size() > 0 && dataToItem.values().iterator().next().getActualItem().isDisposed()) mapTableItems(); final List<ModelContentMergeTabItem> result = new ArrayList<ModelContentMergeTabItem>(); for (String data : dataToItem.keySet()) { final EObject element; if (getInput() instanceof UnmatchElement) element = ((UnmatchElement) getInput()).getElement(); else if (partSide == EMFCompareConstants.LEFT) element = ((Match2Elements) getInput()).getLeftElement(); else element = ((Match2Elements) getInput()).getRightElement(); if (data.startsWith(EcoreUtil.getURI(element).fragment())) { final ModelContentMergeTabItem next = dataToItem.get(data); final TableItem nextTableItem = (TableItem) next.getActualItem(); if (nextTableItem.getBounds().y >= getTable().getClientArea().y && nextTableItem.getBounds().y <= getTable().getClientArea().y + getTable().getClientArea().height) { computeUIInfoFor(next); result.add(next); } } } return result; } /** * This will compute the necessary GUI information for the given {@link ModelContentMergeTabItem}. * * @param item * The item which UI information is to be set. */ private void computeUIInfoFor(ModelContentMergeTabItem item) { final TableItem actualItem = (TableItem) item.getActualItem(); // compute vertical offset final Scrollable scrollable = (Scrollable) getControl(); int offset = scrollable.getBounds().y + scrollable.getClientArea().height - (scrollable.getClientArea().y + scrollable.getBounds().height); // if horizontal scrollbar is visible, compensate this as well if (scrollable.getClientArea().width < actualItem.getBounds().width) { offset += scrollable.getHorizontalBar().getSize().y; } item.setVerticalOffset(offset); // compute curve item.setCurveY(actualItem.getBounds().y + actualItem.getBounds().height / 2); if (getSelectedElements().contains(item.getActualItem())) { item.setCurveSize(2); } else { item.setCurveSize(1); } } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab#redraw() */ public void redraw() { getTable().redraw(); } /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.ColumnViewer#refresh(java.lang.Object, boolean) */ @Override public void refresh(Object element, boolean updateLabels) { super.refresh(element, updateLabels); mapDifferences(); mapTableItems(); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab#setReflectiveInput(org.eclipse.emf.ecore.EObject) */ public void setReflectiveInput(Object input) { if (input instanceof Match2Elements) { setInput(input); } else { // forces refresh setInput(getInput()); } mapDifferences(); mapTableItems(); } /** * Ensures that the given diff is visible in the table. * * @param diff * {@link DiffElement} to make visible. */ public void showItem(DiffElement diff) { final TableItem elementItem = find(diff); if (elementItem != null) { getTable().setSelection(elementItem); } } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ui.viewer.content.part.IModelContentMergeViewerTab#showItems(java.util.List) */ public void showItems(List<DiffElement> items) { if (items.size() > 0) showItem(items.get(0)); } /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.StructuredViewer#associate(Object, Item) */ @Override protected void associate(Object element, Item item) { if (element instanceof Object[]) { final Object data = item.getData(); if (data != ((Object[]) element)[0]) { if (data != null) { disassociate(item); } item.setData(((Object[]) element)[0]); } // Always map the element, even if data == ((Object[])element)[0], // since unmapAllElements() can leave the map inconsistent // See bug 2741 for details. mapElement(((Object[]) element)[0], item); } else { if (element instanceof List) { final Object data = item.getData(); if (data != ((List<?>) element).get(0)) { if (data != null) { disassociate(item); } item.setData(((List<?>) element).get(0)); } // Always map the element, even if data == ((List)element).get(0), // since unmapAllElements() can leave the map inconsistent // See bug 2741 for details. mapElement(((List<?>) element).get(0), item); } else { super.associate(element, item); } } } /** * Maps the input's differences if any. */ private void mapDifferences() { differences.clear(); final Iterator<DiffElement> diffIterator = parent.getDiffAsList().iterator(); while (diffIterator.hasNext()) { final DiffElement diff = diffIterator.next(); if (diff instanceof ReferenceChange || diff instanceof AttributeChange) { differences.add(diff); } } } /** * This will map all the TableItems in this TreeViewer that need be taken into account when drawing diff * markers to a corresponding ModelContentMergeTabItem. This will allow us to browse everything once and * for all. */ private void mapTableItems() { dataToItem.clear(); for (int i = 0; i < differences.size(); i++) { final DiffElement diff = differences.get(i); // Defines the TableItem corresponding to this difference final EObject data; if (diff instanceof ReferenceChange) data = ((ReferenceChange) diff).getReference(); else data = ((AttributeChange) diff).getAttribute(); final TableItem item = (TableItem) findItem(data); if (item != null) { // and now the color which should be used for this kind of differences final String color = EMFCompareConstants.PREFERENCES_KEY_CHANGED_COLOR; final ModelContentMergeTabItem wrappedItem = new ModelContentMergeTabItem(diff, item, color); String key = ""; //$NON-NLS-1$ if (partSide == EMFCompareConstants.LEFT) key = EcoreUtil.getURI(EMFCompareEObjectUtils.getLeftElement(diff)).fragment(); else if (partSide == EMFCompareConstants.RIGHT) key = EcoreUtil.getURI(EMFCompareEObjectUtils.getRightElement(diff)).fragment(); final String[] fragments = EcoreUtil.getURI(data).fragment().split("/"); //$NON-NLS-1$ key += '/' + fragments[fragments.length - 1]; dataToItem.put(key, wrappedItem); } } } /** * This implementation of {@link PaintListener} handles the drawing of blocks around modified members in * the properties tab. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ class PropertyPaintListener implements PaintListener { /** * {@inheritDoc} * * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent) */ public void paintControl(PaintEvent event) { if (ModelContentMergeViewer.shouldDrawDiffMarkers()) { for (ModelContentMergeTabItem item : getVisibleElements()) { drawLine(event, item); } } } /** * Handles the drawing itself. * * @param event * {@link PaintEvent} that triggered this operation. * @param item * Item we want connected to the center part. */ private void drawLine(PaintEvent event, ModelContentMergeTabItem item) { final Rectangle tableBounds = getTable().getBounds(); // properties that present differences should be highlighted ((TableItem) item.getActualItem()).setBackground(new Color(getControl().getDisplay(), ModelContentMergeViewer.getColor(EMFCompareConstants.PREFERENCES_KEY_HIGHLIGHT_COLOR))); event.gc.setLineWidth(2); event.gc.setForeground(new Color(item.getActualItem().getDisplay(), ModelContentMergeViewer.getColor(item.getCurveColor()))); if (partSide == EMFCompareConstants.LEFT) event.gc.drawLine(getTotalColumnsWidth(), item.getCurveY(), tableBounds.width, item.getCurveY()); } } /** * Label provider used by the table control of this part. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ private class PropertyLabelProvider extends AdapterFactoryLabelProvider { /** * Instantiates this label provider given its {@link AdapterFactory}. * * @param theAdapterFactory * Adapter factory providing this {@link org.eclipse.jface.viewers.LabelProvider}'s text * and images. */ public PropertyLabelProvider(AdapterFactory theAdapterFactory) { super(theAdapterFactory); } /** * {@inheritDoc} * * @see AdapterFactoryLabelProvider#getColumnImage(Object, int) */ @Override public Image getColumnImage(Object object, int columnIndex) { Image image = super.getColumnImage(object, columnIndex); if (object instanceof List) { image = super.getColumnImage(((List<?>) object).get(columnIndex), columnIndex); } return image; } /** * {@inheritDoc} * * @see AdapterFactoryLabelProvider#getColumnText(Object, int) */ @Override public String getColumnText(Object object, int columnIndex) { String text = super.getColumnText(object, columnIndex); if (object instanceof List) { text = super.getColumnText(((List<?>) object).get(columnIndex), columnIndex); } return text; } } }