/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.ui.views.mapping; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.WorkbenchPart; import org.eclipse.zest.core.viewers.GraphViewer; import org.eclipse.zest.layouts.LayoutAlgorithm; import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import de.fhg.igd.eclipse.util.extension.exclusive.ExclusiveExtension.ExclusiveExtensionListener; import eu.esdihumboldt.hale.common.align.compatibility.CompatibilityMode; import eu.esdihumboldt.hale.common.align.model.Alignment; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.BaseAlignmentCell; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.Entity; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.impl.DefaultCell; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.ui.HaleUI; import eu.esdihumboldt.hale.ui.common.CommonSharedImages; import eu.esdihumboldt.hale.ui.common.function.viewer.FunctionLabelProvider; import eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider; import eu.esdihumboldt.hale.ui.common.service.compatibility.CompatibilityModeFactory; import eu.esdihumboldt.hale.ui.common.service.compatibility.CompatibilityService; import eu.esdihumboldt.hale.ui.function.common.SourceTargetTypeSelector; import eu.esdihumboldt.hale.ui.selection.SchemaSelection; import eu.esdihumboldt.hale.ui.service.align.AlignmentService; import eu.esdihumboldt.hale.ui.service.align.AlignmentServiceAdapter; import eu.esdihumboldt.hale.ui.service.align.AlignmentServiceListener; import eu.esdihumboldt.hale.ui.views.mapping.internal.MappingViewPlugin; import eu.esdihumboldt.hale.ui.views.properties.PropertiesViewPart; /** * View displaying the current alignment * * @author Simon Templer */ public class AlignmentView extends AbstractMappingView { /** * The view ID */ public static final String ID = "eu.esdihumboldt.hale.ui.views.mapping.alignment"; private AlignmentServiceListener alignmentListener; private SourceTargetTypeSelector sourceTargetSelector; // private ComboViewer typeRelations; private final FunctionLabelProvider functionLabels = new FunctionLabelProvider(); private ISelectionListener selectionListener; private ResizingTreeLayoutAlgorithm treeLayout; private AlignmentViewContentProvider contentProvider; private final ViewerFilter baseAlignmentCellFilter = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof Cell) return !((Cell) element).isBaseCell(); else return false; } }; private final ViewerFilter augmentationFilter = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof Cell) { Cell cell = (Cell) element; return !AlignmentUtil.isAugmentation(cell); } else return false; } }; private final ViewerFilter deactivatedCellFilter = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof Cell) return !isDisabledForCurrentType((Cell) element); else return false; } }; private FilterCellAction deactivatedCellFilterAction; private final ViewerFilter inheritedCellFilter = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof Cell) { Cell cell = (Cell) element; if (AlignmentUtil.isTypeCell(cell)) return true; // If reparentCell returns the original cell no change was // necessary so it isn't inherited. // If strict is set to false, the filter would for example also // filter out cells with sources, if the selected cell does not // contain any sources. return AlignmentUtil.reparentCell(cell, sourceTargetSelector.getSelectedCell(), true) == cell; } else return false; } }; private ExclusiveExtensionListener<CompatibilityMode, CompatibilityModeFactory> compListener; /** * @see PropertiesViewPart#getViewContext() */ @Override protected String getViewContext() { return "eu.esdihumboldt.hale.doc.user.alignment"; } /** * @see eu.esdihumboldt.hale.ui.views.mapping.AbstractMappingView#createViewControl(org.eclipse.swt.widgets.Composite) */ @Override public void createViewControl(Composite parent) { Composite page = new Composite(parent, SWT.NONE); page.setLayout(GridLayoutFactory.fillDefaults().create()); // create type relation selection control sourceTargetSelector = new SourceTargetTypeSelector(page); sourceTargetSelector.getControl().setLayoutData(GridDataFactory.swtDefaults() .align(SWT.FILL, SWT.CENTER).grab(true, false).create()); sourceTargetSelector.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { getViewer().setInput(sourceTargetSelector.getSelectedCell()); if (deactivatedCellFilterAction != null) { deactivatedCellFilterAction.setEnabled(sourceTargetSelector.isCellSelected()); if (!sourceTargetSelector.isCellSelected()) deactivatedCellFilterAction.setChecked(true); } refreshGraph(); } }); // typeRelations = new ComboViewer(page, SWT.DROP_DOWN | SWT.READ_ONLY); // typeRelations.setContentProvider(ArrayContentProvider.getInstance()); // typeRelations.setLabelProvider(new LabelProvider() { // // @Override // public Image getImage(Object element) { // if (element instanceof Cell) { // // use function image if possible // Cell cell = (Cell) element; // String functionId = cell.getTransformationIdentifier(); // AbstractFunction<?> function = FunctionUtil.getFunction(functionId); // if (function != null) { // return functionLabels.getImage(function); // } // return null; // } // // return super.getImage(element); // } // // @Override // public String getText(Object element) { // if (element instanceof Cell) { // Cell cell = (Cell) element; // // return CellUtil.getCellDescription(cell); // } // // return super.getText(element); // } // // }); // typeRelations.addSelectionChangedListener(new ISelectionChangedListener() { // // @Override // public void selectionChanged(SelectionChangedEvent event) { // updateGraph(); // } // }); // typeRelations.getControl().setLayoutData( // GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false) // .create()); // create viewer Composite viewerContainer = new Composite(page, SWT.NONE); viewerContainer.setLayout(new FillLayout()); viewerContainer.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); super.createViewControl(viewerContainer); updateLayout(false); AlignmentService as = PlatformUI.getWorkbench().getService(AlignmentService.class); // update(); as.addListener(alignmentListener = new AlignmentServiceAdapter() { @Override public void alignmentCleared() { PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { @Override public void run() { sourceTargetSelector.setSelection(StructuredSelection.EMPTY); } }); } @Override public void cellsRemoved(Iterable<Cell> cells) { if (sourceTargetSelector.isCellSelected() && Iterables.contains(cells, sourceTargetSelector.getSelectedCell())) sourceTargetSelector.setSelection(StructuredSelection.EMPTY); refreshGraph(); } @Override public void cellsReplaced(Map<? extends Cell, ? extends Cell> cells) { if (sourceTargetSelector.isCellSelected() && cells.keySet().contains(sourceTargetSelector.getSelectedCell())) sourceTargetSelector.setSelection(StructuredSelection.EMPTY); refreshGraph(); } @Override public void cellsAdded(Iterable<Cell> cells) { refreshGraph(); } @Override public void alignmentChanged() { PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { @Override public void run() { sourceTargetSelector.setSelection(StructuredSelection.EMPTY); } }); } @Override public void cellsPropertyChanged(Iterable<Cell> cells, String propertyName) { refreshGraph(); } @Override public void customFunctionsChanged() { refreshGraph(); } }); // initialize compatibility checkup and display CompatibilityService cs = PlatformUI.getWorkbench().getService(CompatibilityService.class); cs.addListener( compListener = new ExclusiveExtensionListener<CompatibilityMode, CompatibilityModeFactory>() { @Override public void currentObjectChanged(final CompatibilityMode arg0, final CompatibilityModeFactory arg1) { refreshGraph(); } }); // listen on SchemaSelections getSite().getWorkbenchWindow().getSelectionService() .addPostSelectionListener(selectionListener = new ISelectionListener() { @Override public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (!(selection instanceof SchemaSelection)) { // only react on schema selections return; } if (part != AlignmentView.this) { updateRelation((SchemaSelection) selection); } } }); // select type cell, if it is double clicked getViewer().addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); if (selection.size() == 1) { Object selected = selection.getFirstElement(); if (selected instanceof Cell && AlignmentUtil.isTypeCell((Cell) selected)) sourceTargetSelector.setSelection(selection); } } }); // listen on size changes getViewer().getControl().addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { updateLayout(true); } }); getViewer().setInput(new DefaultCell()); } /** * @see eu.esdihumboldt.hale.ui.views.mapping.AbstractMappingView#menuAboutToShow(org.eclipse.jface.action.IMenuManager) */ @Override protected void menuAboutToShow(IMenuManager manager) { ISelection cellSelection = getViewer().getSelection(); final Cell typeCell = sourceTargetSelector.getSelectedCell(); // is a type relation selected if (!sourceTargetSelector.isCellSelected()) return; // is a cell selected? if (!(cellSelection instanceof IStructuredSelection) || ((IStructuredSelection) cellSelection).size() != 1 || !(((IStructuredSelection) cellSelection).getFirstElement() instanceof Cell)) return; final Cell selectedCell = (Cell) ((IStructuredSelection) cellSelection).getFirstElement(); // ignore type cell if (AlignmentUtil.isTypeCell(selectedCell)) return; // check current disable status if (!selectedCell.getDisabledFor().contains(typeCell.getId())) { manager.add(new Action("Disable") { /** * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { AlignmentService as = PlatformUI.getWorkbench() .getService(AlignmentService.class); as.setCellProperty(selectedCell.getId(), Cell.PROPERTY_DISABLE_FOR, typeCell); } }); } else { manager.add(new Action("Enable") { /** * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { AlignmentService as = PlatformUI.getWorkbench() .getService(AlignmentService.class); as.setCellProperty(selectedCell.getId(), Cell.PROPERTY_ENABLE_FOR, typeCell); } /** * @see org.eclipse.jface.action.Action#isEnabled() */ @Override public boolean isEnabled() { // Not enabled, if the cell was disabled in a base // alignment. // Still show the action for clarity. if (selectedCell instanceof BaseAlignmentCell) return !((BaseAlignmentCell) selectedCell).getBaseDisabledFor() .contains(typeCell.getId()); return true; } }); } } /** * @see AbstractMappingView#createContentProvider() */ @Override protected IContentProvider createContentProvider() { contentProvider = new AlignmentViewContentProvider(); return contentProvider; } /** * @see AbstractMappingView#createLabelProvider(GraphViewer) */ @Override protected IBaseLabelProvider createLabelProvider(GraphViewer viewer) { return new GraphLabelProvider(viewer, HaleUI.getServiceProvider()) { /** * @see eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider#isInherited(eu.esdihumboldt.hale.common.align.model.Cell) */ @Override protected boolean isInherited(Cell cell) { // cannot inherit type cells if (AlignmentUtil.isTypeCell(cell)) return false; return AlignmentUtil.reparentCell(cell, sourceTargetSelector.getSelectedCell(), true) != cell; } private final Color cellDisabledBackgroundColor = new Color(Display.getCurrent(), 240, 240, 240); private final Color cellDisabledForegroundColor = new Color(Display.getCurrent(), 109, 109, 132); private final Color cellDisabledHighlightColor = new Color(Display.getCurrent(), (int) (getCellHighlightColor().getRed() * 0.7), (int) (getCellHighlightColor().getGreen() * 0.7), (int) (getCellHighlightColor().getBlue() * 0.7)); /** * @see eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider#getNodeHighlightColor(java.lang.Object) */ @Override public Color getNodeHighlightColor(Object entity) { if (entity instanceof Cell && isDisabledForCurrentType((Cell) entity)) return cellDisabledHighlightColor; return super.getNodeHighlightColor(entity); } /** * @see eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider#getBackgroundColour(java.lang.Object) */ @Override public Color getBackgroundColour(Object entity) { if (entity instanceof Cell && isDisabledForCurrentType((Cell) entity)) return cellDisabledBackgroundColor; return super.getBackgroundColour(entity); } /** * @see eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider#getForegroundColour(java.lang.Object) */ @Override public Color getForegroundColour(Object entity) { if (entity instanceof Cell && isDisabledForCurrentType((Cell) entity)) return cellDisabledForegroundColor; return super.getForegroundColour(entity); } /** * @see eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider#dispose() */ @Override public void dispose() { cellDisabledBackgroundColor.dispose(); cellDisabledForegroundColor.dispose(); cellDisabledHighlightColor.dispose(); super.dispose(); } }; } /** * @see eu.esdihumboldt.hale.ui.views.mapping.AbstractMappingView#fillToolBar() */ @Override protected void fillToolBar() { super.fillToolBar(); IToolBarManager manager = getViewSite().getActionBars().getToolBarManager(); manager.add(new FilterCellAction("Hide augmentation cells", "Show augmentation cells", MappingViewPlugin.getImageDescriptor("icons/augmentation.gif"), getViewer(), contentProvider, augmentationFilter, true, true)); manager.add(new FilterCellAction("Hide base alignment cells", "Show base alignment cells", MappingViewPlugin.getImageDescriptor("icons/base_alignment.gif"), getViewer(), contentProvider, baseAlignmentCellFilter, true, true)); deactivatedCellFilterAction = new FilterCellAction("Hide deactivated cells", "Show deactivated cells", MappingViewPlugin.getImageDescriptor("icons/progress_rem.gif"), getViewer(), contentProvider, deactivatedCellFilter, true, true); if (sourceTargetSelector != null) { deactivatedCellFilterAction.setEnabled(sourceTargetSelector.isCellSelected()); if (!sourceTargetSelector.isCellSelected()) deactivatedCellFilterAction.setChecked(true); } manager.add(deactivatedCellFilterAction); manager.add(new FilterCellAction("Hide inherited cells", "Show inherited cells", CommonSharedImages.getImageRegistry() .getDescriptor(CommonSharedImages.IMG_INHERITED_ARROW), getViewer(), contentProvider, inheritedCellFilter, true, true)); } @Override protected LayoutAlgorithm createLayout() { treeLayout = new ResizingTreeLayoutAlgorithm(TreeLayoutAlgorithm.RIGHT_LEFT, new AlignmentViewResizingStrategy()); return treeLayout; } /** * Update the layout to the view size. * * @param triggerLayout if the layout should be applied directly */ private void updateLayout(boolean triggerLayout) { int width = getViewer().getControl().getSize().x; treeLayout.setNodeSpace(new Dimension((width - 10) / 3, 30)); if (triggerLayout) { getViewer().applyLayout(); } } /** * Update the selected type relation to a cell that is related to the given * schema selection. * * @param selection the schema selection */ private void updateRelation(SchemaSelection selection) { Cell typeCell = sourceTargetSelector.getSelectedCell(); if (typeCell != null && (associatedWithType(typeCell.getSource(), selection.getSourceItems()) && associatedWithType(typeCell.getTarget(), selection.getTargetItems()))) { // type cell is associated with source and target, don't change return; } AlignmentService as = PlatformUI.getWorkbench().getService(AlignmentService.class); Alignment alignment = as.getAlignment(); // find type cell associated with both source and target for (Cell cell : alignment.getTypeCells()) { if ((associatedWithType(cell.getSource(), selection.getSourceItems())) && associatedWithType(cell.getTarget(), selection.getTargetItems())) { // typeRelations.setSelection(new StructuredSelection(cell)); sourceTargetSelector.setSelection(new StructuredSelection(cell)); return; } } if (typeCell != null && (associatedWithType(typeCell.getSource(), selection.getSourceItems()) || associatedWithType(typeCell.getTarget(), selection.getTargetItems()))) { // type cell is associated with source or target, don't change return; } // find type cell associated with source or target for (Cell cell : alignment.getTypeCells()) { if ((associatedWithType(cell.getSource(), selection.getSourceItems())) || associatedWithType(cell.getTarget(), selection.getTargetItems())) { sourceTargetSelector.setSelection(new StructuredSelection(cell)); // typeRelations.setSelection(new StructuredSelection(cell)); return; } } } private boolean associatedWithType(ListMultimap<String, ? extends Entity> entities, Set<EntityDefinition> entityDefs) { if (entities == null) return false; Set<TypeDefinition> types = new HashSet<TypeDefinition>(); // XXX must // be // TypeEntityDefintions // when // there are // contexts // for types for (EntityDefinition entityDef : entityDefs) { types.add(entityDef.getType()); } for (Entity entity : entities.values()) { if (types.contains(entity.getDefinition().getType())) { return true; } } return false; } private boolean isDisabledForCurrentType(Cell cell) { if (sourceTargetSelector.isCellSelected()) return cell.getDisabledFor().contains(sourceTargetSelector.getSelectedCell().getId()); else return false; } // /** // * Set the current alignment // */ // private void update() { // final Display display = PlatformUI.getWorkbench().getDisplay(); // display.syncExec(new Runnable() { // // @Override // public void run() { // ISelection selection = typeRelations.getSelection(); // Cell lastSelected = null; // if (!selection.isEmpty() && selection instanceof IStructuredSelection) { // lastSelected = (Cell) ((IStructuredSelection) selection).getFirstElement(); // } // // // update type relations // AlignmentService as = (AlignmentService) PlatformUI.getWorkbench().getService( // AlignmentService.class); // Collection<? extends Cell> typeCells = as.getAlignment().getTypeCells(); // typeRelations.setInput(typeCells); // // ISelection newSelection; // if (lastSelected != null && typeCells.contains(lastSelected)) { // newSelection = new StructuredSelection(lastSelected); // } // else if (typeCells.isEmpty()) { // newSelection = new StructuredSelection(); // } // else { // newSelection = new StructuredSelection(typeCells.iterator().next()); // } // typeRelations.setSelection(newSelection); // // // call to updateGraph is done implicitly through selection // // change // } // }); // } private void refreshGraph() { final Display display = PlatformUI.getWorkbench().getDisplay(); display.syncExec(new Runnable() { @Override public void run() { getViewer().refresh(); updateLayout(true); } }); // ISelection selection = typeRelations.getSelection(); // // Cell typeCell = null; // if (!selection.isEmpty() && selection instanceof IStructuredSelection) { // typeCell = (Cell) ((IStructuredSelection) selection).getFirstElement(); // } // // if (typeCell != null) { // getViewer().setInput(typeCell); // } // else { // getViewer().setInput(Collections.EMPTY_LIST); // } } /** * @see WorkbenchPart#dispose() */ @Override public void dispose() { if (alignmentListener != null) { AlignmentService as = PlatformUI.getWorkbench().getService(AlignmentService.class); as.removeListener(alignmentListener); } if (compListener != null) { CompatibilityService cs = PlatformUI.getWorkbench() .getService(CompatibilityService.class); cs.removeListener(compListener); } if (selectionListener != null) { getSite().getWorkbenchWindow().getSelectionService() .removePostSelectionListener(selectionListener); } functionLabels.dispose(); super.dispose(); } }