/* * 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.schemas; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; 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.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.part.WorkbenchPart; import de.fhg.igd.slf4jplus.ALogger; import de.fhg.igd.slf4jplus.ALoggerFactory; import eu.esdihumboldt.hale.common.align.model.ChildContext; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.impl.PropertyEntityDefinition; import eu.esdihumboldt.hale.common.align.model.impl.TypeEntityDefinition; import eu.esdihumboldt.hale.common.schema.SchemaSpaceID; import eu.esdihumboldt.hale.common.schema.model.ChildDefinition; import eu.esdihumboldt.hale.common.schema.model.Definition; import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.ui.function.contribution.SchemaSelectionFunctionContribution; import eu.esdihumboldt.hale.ui.selection.SchemaSelection; import eu.esdihumboldt.hale.ui.selection.impl.DefaultSchemaSelection; import eu.esdihumboldt.hale.ui.selection.impl.DefaultSchemaSelection.SchemaStructuredMode; import eu.esdihumboldt.hale.ui.util.viewer.ViewerMenu; import eu.esdihumboldt.hale.ui.views.properties.PropertiesViewPart; import eu.esdihumboldt.hale.ui.views.schemas.explorer.EntitySchemaExplorer; import eu.esdihumboldt.hale.ui.views.schemas.explorer.SchemaExplorer; import eu.esdihumboldt.hale.ui.views.schemas.explorer.ServiceSchemaExplorer; import eu.esdihumboldt.hale.ui.views.schemas.internal.Messages; import eu.esdihumboldt.hale.ui.views.schemas.internal.SchemasViewPlugin; /** * This view component handles the display of source and target schemas. * * @author Thorsten Reitz * @author Simon Templer */ public class SchemasView extends PropertiesViewPart { private static final ALogger log = ALoggerFactory.getLogger(SchemasView.class); /** * Function contribution that always uses this view's selection */ private class SchemaFunctionContribution extends SchemaSelectionFunctionContribution { /** * Default constructor */ public SchemaFunctionContribution() { super(); } /** * @see SchemaSelectionFunctionContribution#getSelection() */ @Override public SchemaSelection getSelection() { if (currentSelection instanceof SchemaSelection) { return (SchemaSelection) currentSelection; } return super.getSelection(); } } /** * Selection provider combining selections from source and target schema * explorers */ private class SchemasSelectionProvider implements ISelectionProvider, ISelectionChangedListener { /** * The selection listeners */ private final Set<ISelectionChangedListener> listeners = new HashSet<ISelectionChangedListener>(); private boolean lastSourceFirst = true; /** * Default constructor */ public SchemasSelectionProvider() { super(); sourceExplorer.getTreeViewer().addSelectionChangedListener(this); targetExplorer.getTreeViewer().addSelectionChangedListener(this); } /** * @see ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener) */ @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { listeners.add(listener); } /** * @see ISelectionProvider#getSelection() */ @Override public ISelection getSelection() { return currentSelection; } /** * @see ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener) */ @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { listeners.remove(listener); } /** * @see ISelectionProvider#setSelection(ISelection) */ @Override public void setSelection(ISelection selection) { SchemasView.this.currentSelection = selection; if (selection instanceof SchemaSelection) { SchemaSelection ss = (SchemaSelection) selection; sourceExplorer.getTreeViewer() .setSelection(new StructuredSelection(ss.getSourceItems().toArray())); targetExplorer.getTreeViewer() .setSelection(new StructuredSelection(ss.getTargetItems().toArray())); } } /** * Update the selection and fire a selection change * * @param sourceFirst if the selected objects from the source shall be * added first if the selection is a combination from source * and target */ private void updateSelection(boolean sourceFirst) { lastSourceFirst = sourceFirst; // combine the selections of both viewers // source items ITreeSelection sourceSelection = (ITreeSelection) sourceExplorer.getTreeViewer() .getSelection(); // target items ITreeSelection targetSelection = (ITreeSelection) targetExplorer.getTreeViewer() .getSelection(); /* * XXX because there are problem with the properties view if we * combine the objects here (multiple objects in the selection), we * return only one of the original selections */ SchemaStructuredMode selectionMode = (sourceFirst) ? (SchemaStructuredMode.ONLY_SOURCE) : (SchemaStructuredMode.ONLY_TARGET); Collection<EntityDefinition> sourceItems = collectDefinitions(sourceSelection, SchemaSpaceID.SOURCE); Collection<EntityDefinition> targetItems = collectDefinitions(targetSelection, SchemaSpaceID.TARGET); DefaultSchemaSelection selection = new DefaultSchemaSelection(sourceItems, targetItems, selectionMode); fireSelectionChange(selection); } /** * Collect {@link EntityDefinition} from a {@link TreeSelection} * containing {@link TypeDefinition}s and {@link PropertyDefinition}s * * @param selection the tree selection * @param schemaSpace the schema space identifier * @return the collected entity definitions */ private Collection<EntityDefinition> collectDefinitions(ITreeSelection selection, SchemaSpaceID schemaSpace) { if (selection.isEmpty()) { return Collections.emptyList(); } TreePath[] paths = selection.getPaths(); List<EntityDefinition> result = new ArrayList<EntityDefinition>(paths.length); for (TreePath path : paths) { Object last = path.getLastSegment(); if (last instanceof EntityDefinition) { // use entity definition directly result.add((EntityDefinition) last); } else if (last instanceof TypeDefinition) { // create entity definition for type result.add(new TypeEntityDefinition((TypeDefinition) last, schemaSpace, null)); } else if (last instanceof PropertyDefinition) { // create property entity definition w/ default instance // contexts List<ChildContext> propertyPath = new ArrayList<ChildContext>(); Definition<?> element = (Definition<?>) last; int index = path.getSegmentCount() - 1; while (element != null && !(element instanceof TypeDefinition)) { ChildContext context = new ChildContext((ChildDefinition<?>) element); propertyPath.add(0, context); Object segment = path.getSegment(--index); if (segment instanceof Definition<?>) { element = (Definition<?>) segment; } else { element = null; } } if (element != null) { // remaining element is the type definition result.add(new PropertyEntityDefinition((TypeDefinition) element, propertyPath, schemaSpace, null)); } else { log.error( "No parent type definition for property path found, skipping object for selection."); } } else { // XXX include GroupPropertyDefinitions also in selection? log.debug( "Could determine entity definition for object, skipping object for selection."); } } return result; } /** * Sets the selection to the given selection and fires a selection * change * * @param selection the selection to set */ protected void fireSelectionChange(ISelection selection) { SchemasView.this.currentSelection = selection; SelectionChangedEvent event = new SelectionChangedEvent(this, currentSelection); for (ISelectionChangedListener listener : listeners) { listener.selectionChanged(event); } } /** * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent) */ @Override public void selectionChanged(SelectionChangedEvent event) { updateSelection(event.getSelectionProvider() == sourceExplorer.getTreeViewer()); } /** * @return the lastSourceFirst */ public boolean isLastSourceFirst() { return lastSourceFirst; } } /** * The view id */ public static final String ID = "eu.esdihumboldt.hale.ui.views.schemas"; //$NON-NLS-1$ /** * The current selection */ private ISelection currentSelection; /** * Viewer for the source schema */ private SchemaExplorer sourceExplorer; /** * Viewer for the target schema */ private SchemaExplorer targetExplorer; private ServiceSchemaExplorer sourceExplorerManager; private ServiceSchemaExplorer targetExplorerManager; private Image functionImage; private Image augmentImage; private SchemasSelectionProvider selectionProvider; private CellSyncAction cellSyncAction; /** * @see eu.esdihumboldt.hale.ui.views.properties.PropertiesViewPart#createViewControl(org.eclipse.swt.widgets.Composite) */ @Override public void createViewControl(Composite _parent) { Composite modelComposite = new Composite(_parent, SWT.BEGINNING); GridLayout layout = new GridLayout(3, false); layout.verticalSpacing = 3; layout.horizontalSpacing = 0; modelComposite.setLayout(layout); // source schema toolbar, filter and explorer // sourceExplorer = new SchemaExplorer(modelComposite, "Source"); sourceExplorer = new EntitySchemaExplorer(modelComposite, "Source", SchemaSpaceID.SOURCE); GridDataFactory.fillDefaults().grab(true, true).hint(400, 10) .applyTo(sourceExplorer.getControl()); sourceExplorerManager = new ServiceSchemaExplorer(sourceExplorer, SchemaSpaceID.SOURCE); // function button final Button functionButton = new Button(modelComposite, SWT.PUSH | SWT.FLAT); functionImage = SchemasViewPlugin.getImageDescriptor("icons/mapping.gif").createImage(); //$NON-NLS-1$ augmentImage = SchemasViewPlugin.getImageDescriptor("icons/augment.gif").createImage(); //$NON-NLS-1$ functionButton.setImage(functionImage); functionButton.setToolTipText(Messages.ModelNavigationView_FunctionButtonToolTipText); functionButton.setEnabled(false); functionButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); MenuManager manager = new MenuManager(); manager.setRemoveAllWhenShown(true); final SchemaFunctionContribution functionContribution = new SchemaFunctionContribution(); manager.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { // populate context menu manager.add(functionContribution); } }); final Menu functionMenu = manager.createContextMenu(functionButton); functionButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // show menu on button press functionMenu.setLocation(Display.getCurrent().getCursorLocation()); functionMenu.setVisible(true); } }); // target schema toolbar, filter and explorer targetExplorer = new EntitySchemaExplorer(modelComposite, "Target", SchemaSpaceID.TARGET); GridDataFactory.fillDefaults().grab(true, true).hint(400, 10) .applyTo(targetExplorer.getControl()); targetExplorerManager = new ServiceSchemaExplorer(targetExplorer, SchemaSpaceID.TARGET); // source context menu new ViewerMenu(getSite(), sourceExplorer.getTreeViewer()); // target context menu new ViewerMenu(getSite(), targetExplorer.getTreeViewer()); // register selection provider getSite().setSelectionProvider(selectionProvider = new SchemasSelectionProvider()); // listen for selection changes and update function button selectionProvider.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { functionButton.setEnabled(functionContribution.hasActiveFunctions()); if (event.getSelection() instanceof SchemaSelection) { SchemaSelection selection = (SchemaSelection) event.getSelection(); if (selection.getSourceItems().size() == 0 && selection.getTargetItems().size() > 0) { // augmentation functionButton.setImage(augmentImage); } else { // function functionButton.setImage(functionImage); } } } }); // view toolbar getViewSite().getActionBars().getToolBarManager() .add(cellSyncAction = new CellSyncAction( getSite().getPage().getWorkbenchWindow().getSelectionService(), sourceExplorer, targetExplorer)); } /** * @see PropertiesViewPart#getViewContext() */ @Override protected String getViewContext() { return "eu.esdihumboldt.hale.doc.user.schema_explorer"; } /** * @see WorkbenchPart#setFocus() */ @Override public void setFocus() { if (selectionProvider != null && !selectionProvider.isLastSourceFirst()) { targetExplorer.getTreeViewer().getControl().setFocus(); } else { sourceExplorer.getTreeViewer().getControl().setFocus(); } } /** * @see WorkbenchPart#dispose() */ @Override public void dispose() { if (cellSyncAction != null) { cellSyncAction.dispose(); } if (sourceExplorerManager != null) { sourceExplorerManager.dispose(); } if (targetExplorerManager != null) { targetExplorerManager.dispose(); } if (functionImage != null) { functionImage.dispose(); } if (augmentImage != null) { augmentImage.dispose(); } super.dispose(); } }