| /******************************************************************************* |
| * Copyright (c) 2007, 2010 BMW Car IT, Technische Universitaet Muenchen, and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * BMW Car IT - Initial API and implementation |
| * Technische Universitaet Muenchen - Major refactoring and extension |
| *******************************************************************************/ |
| package org.eclipse.emf.edapt.history.instantiation.ui; |
| |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.EventObject; |
| |
| import org.eclipse.emf.common.command.Command; |
| import org.eclipse.emf.common.command.CommandStackListener; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.presentation.EcoreEditor; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.edapt.common.ui.ModelSash; |
| import org.eclipse.emf.edapt.common.ui.PartAdapter; |
| import org.eclipse.emf.edapt.common.ui.StructureTreeViewer; |
| import org.eclipse.emf.edapt.history.presentation.HistoryEditorPlugin; |
| import org.eclipse.emf.edapt.history.reconstruction.Mapping; |
| import org.eclipse.emf.edapt.history.reconstruction.ui.DiffSelectionAdapter; |
| import org.eclipse.emf.edapt.history.recorder.ui.EcoreEditorDetector; |
| import org.eclipse.emf.edapt.internal.common.LoggingUtils; |
| import org.eclipse.emf.edapt.internal.common.ResourceUtils; |
| import org.eclipse.emf.edapt.spi.history.HistoryFactory; |
| import org.eclipse.emf.edapt.spi.history.NoChange; |
| import org.eclipse.emf.edapt.spi.history.Release; |
| import org.eclipse.emf.edit.command.ChangeCommand; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.viewers.DecoratingLabelProvider; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IColorDecorator; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.ILabelDecorator; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ILabelProviderListener; |
| 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.swt.SWT; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.part.ViewPart; |
| // CB Migrate |
| // import org.eclipse.emf.compare.diff.merge.EMFCompareEObjectCopier; |
| // import org.eclipse.emf.compare.diff.merge.service.MergeService; |
| // import org.eclipse.emf.compare.diff.metamodel.ComparisonResourceSnapshot; |
| // import org.eclipse.emf.compare.diff.metamodel.DiffElement; |
| // import org.eclipse.emf.compare.diff.metamodel.DiffFactory; |
| // import org.eclipse.emf.compare.diff.metamodel.DiffModel; |
| // import org.eclipse.emf.compare.diff.service.DiffService; |
| // import org.eclipse.emf.compare.match.metamodel.Match2Elements; |
| // import org.eclipse.emf.compare.match.metamodel.MatchModel; |
| // import org.eclipse.emf.compare.match.service.MatchService; |
| |
| /** |
| * View to support the convergence of a source metamodel to a target metamodel |
| * |
| * @author herrmama |
| * @author $Author$ |
| * @version $Rev$ |
| * @levd.rating RED Rev: |
| */ |
| public class ConvergenceView extends ViewPart implements CommandStackListener, |
| ISelectionChangedListener { |
| |
| /** |
| * Identifier of the view for convenience |
| */ |
| public static final String ID = ConvergenceView.class.getName(); |
| |
| /** |
| * Viewer for the difference model |
| */ |
| private ModelSash differenceSash; |
| |
| /** |
| * Viewer for the target metamodel |
| */ |
| private ModelSash targetSash; |
| |
| /** |
| * Editor with the source metamodel |
| */ |
| private EcoreEditor editor; |
| |
| /** |
| * Resource containing the source metamodel |
| */ |
| @SuppressWarnings("unused") |
| private Resource sourceResource; |
| |
| /** |
| * Resource containing the target metamodel |
| */ |
| private Resource targetResource; |
| |
| /** |
| * Adapter to listen to the difference viewer that automatically updates |
| * source and target metamodel viewer |
| */ |
| private DiffSelectionAdapter selectionAdapter; |
| |
| /** |
| * Mapping between source and target metamodel |
| */ |
| private Mapping mapping; |
| |
| /** |
| * Flag whether selection is currently going on (in order to avoid {@link StackOverflowError}) |
| */ |
| private boolean selecting = false; |
| |
| /** |
| * Set of breaking changes |
| */ |
| |
| // CB Migrate. |
| // private Set<DiffElement> breakingChanges; |
| |
| /** |
| * Whether synchronization is turned on or off |
| */ |
| private boolean synchronization = true; |
| |
| /** |
| * Whether the view is currently refreshing |
| */ |
| private boolean refresh; |
| |
| /** Check whether the editor is closed. */ |
| private final IPartListener partListener = new PartAdapter() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void partClosed(IWorkbenchPart part) { |
| if (editor == part) { |
| differenceSash.getStructureViewer().setInput(null); |
| targetSash.getStructureViewer().setInput(null); |
| |
| detachOldEditor(); |
| } |
| } |
| |
| }; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void createPartControl(Composite parent) { |
| final SashForm sash = new SashForm(parent, SWT.HORIZONTAL); |
| |
| differenceSash = new ModelSash(sash, SWT.None); |
| final StructureTreeViewer differenceViewer = differenceSash.getStructureViewer(); |
| differenceViewer.setLabelProvider(new DecoratingLabelProvider((ILabelProvider) differenceViewer |
| .getLabelProvider(), new Decorator())); |
| |
| targetSash = new ModelSash(sash, SWT.None); |
| targetSash.getStructureViewer().addSelectionChangedListener(new ISelectionChangedListener() { |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| if (selecting) { |
| return; |
| } |
| if (event.getSelection() instanceof IStructuredSelection) { |
| final IStructuredSelection selection = (IStructuredSelection) event.getSelection(); |
| if (selection.getFirstElement() instanceof EObject) { |
| final EObject element = (EObject) selection.getFirstElement(); |
| final EObject source = mapping.getSource(element); |
| if (source != null) { |
| selecting = true; |
| editor.setSelectionToViewer(Collections.singleton(source)); |
| selecting = false; |
| } |
| } |
| } |
| } |
| |
| }); |
| |
| differenceViewer.addDoubleClickListener(new IDoubleClickListener() { |
| @Override |
| public void doubleClick(DoubleClickEvent event) { |
| if (event.getSelection() instanceof IStructuredSelection) { |
| final IStructuredSelection selection = (IStructuredSelection) event.getSelection(); |
| |
| System.out.println("DifferenViewer doubleclick, selection" + selection); //$NON-NLS-1$ |
| // CB Migrate |
| // Execute a change command on the editor editing domain. |
| |
| // if(selection.getFirstElement() instanceof DiffElement) { |
| // final DiffElement element = (DiffElement) selection.getFirstElement(); |
| // |
| // editor.getEditingDomain().getCommandStack().execute(new |
| // ChangeCommand(editor.getEditingDomain().getResourceSet()) { |
| // |
| // @Override |
| // protected void doExecute() { |
| // fixCopier(); |
| // MergeService.merge(element, true); |
| // } |
| // |
| // }); |
| // } |
| } |
| } |
| }); |
| |
| getSite().getPage().addPartListener(partListener); |
| |
| getViewSite().getActionBars().getToolBarManager().add( |
| new Action("Sync", IAction.AS_CHECK_BOX) { //$NON-NLS-1$ |
| |
| { |
| setChecked(true); |
| setImageDescriptor(PlatformUI.getWorkbench() |
| .getSharedImages().getImageDescriptor( |
| ISharedImages.IMG_ELCL_SYNCED)); |
| setDisabledImageDescriptor(PlatformUI.getWorkbench() |
| .getSharedImages().getImageDescriptor( |
| ISharedImages.IMG_ELCL_SYNCED_DISABLED)); |
| } |
| |
| @Override |
| public void run() { |
| setSynchronization(isChecked()); |
| } |
| }); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setFocus() { |
| if (!differenceSash.isDisposed()) { |
| differenceSash.setFocus(); |
| } |
| } |
| |
| /** |
| * Initialize the contents of the view |
| */ |
| public void init(URI targetURI, EcoreEditor editor) { |
| detachOldEditor(); |
| attachNewEditor(targetURI, editor); |
| } |
| |
| /** |
| * Detach the view from the old editor |
| */ |
| private void detachOldEditor() { |
| if (editor != null) { |
| editor.getEditingDomain().getCommandStack().removeCommandStackListener(this); |
| editor.removeSelectionChangedListener(this); |
| if (!differenceSash.getStructureViewer().getTree().isDisposed()) { |
| differenceSash.getStructureViewer().getTree().removeSelectionListener(selectionAdapter); |
| } |
| editor = null; |
| |
| // CB Migrate |
| // breakingChanges = null; |
| } |
| } |
| |
| /** |
| * Attach the view to the new editor |
| */ |
| private void attachNewEditor(URI targetURI, EcoreEditor editor) { |
| try { |
| sourceResource = editor.getEditingDomain().getResourceSet().getResources().get(0); |
| |
| final ResourceSet resourceSet = ResourceUtils.loadResourceSet(targetURI); |
| targetResource = resourceSet.getResources().get(0); |
| |
| this.editor = editor; |
| this.editor.getEditingDomain().getCommandStack().addCommandStackListener(this); |
| selectionAdapter = new DiffSelectionAdapter(editor.getViewer(), null, targetSash.getStructureViewer(), |
| targetSash.getPropertiesViewer()) { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| selecting = true; |
| super.widgetSelected(e); |
| selecting = false; |
| } |
| }; |
| differenceSash.getStructureViewer().getTree().addSelectionListener(selectionAdapter); |
| this.editor.addSelectionChangedListener(this); |
| |
| refresh(); |
| targetSash.getStructureViewer().setInput(targetResource); |
| targetSash.getStructureViewer().expandToLevel(2); |
| |
| } catch (final IOException e) { |
| LoggingUtils |
| .logError(HistoryEditorPlugin.getPlugin(), e); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void commandStackChanged(EventObject event) { |
| if (!refresh && synchronization) { |
| refresh(); |
| } |
| } |
| |
| /** |
| * Refresh viewer contents |
| */ |
| private void refresh() { |
| // try { |
| refresh = true; |
| |
| // CB Migrate, we need a comparison snapshot here. |
| |
| // ComparisonResourceSnapshot snapshot = compare(); |
| |
| // if (ModelAssert.numberOfChanges(snapshot.getDiff()) == 0) { |
| // if(targetResource != null) { |
| // URI targetURI = targetResource.getURI(); |
| // Integer number = extractNumber(targetURI); |
| // if(number != null) { |
| // addMarker(number+1); |
| // String name = targetURI.lastSegment().replace(number.toString(), "" + (number+1)); |
| // try { |
| // targetURI = targetURI.trimSegments(1).appendSegment(name); |
| // |
| // ResourceSet resourceSet; |
| // resourceSet = ResourceUtils.loadResourceSet(targetURI); |
| // targetResource = resourceSet.getResources().get(0); |
| // |
| // targetSash.getStructureViewer().setInput(targetResource); |
| // targetSash.getStructureViewer().expandToLevel(2); |
| // |
| // snapshot = compare(); |
| // } catch (IOException e) { |
| // // ignore |
| // } |
| // } |
| // } |
| // } |
| |
| // initMapping(snapshot.getMatch()); |
| // calculateBreaking(snapshot.getDiff()); |
| |
| // differenceSash.getStructureViewer().setInput(snapshot.getDiff()); |
| |
| differenceSash.getStructureViewer().expandToLevel(3); |
| |
| // } catch (InterruptedException e) { |
| // LoggingUtils |
| // .logError(HistoryEditorPlugin.getPlugin(), e); |
| // } finally { |
| // refresh = false; |
| // } |
| } |
| |
| /** Compare source and target resource. */ |
| // CB Migrate |
| // Returns a comparison snapshot. |
| |
| // private ComparisonResourceSnapshot compare() throws InterruptedException { |
| // ComparisonResourceSnapshot snapshot = DiffFactory.eINSTANCE |
| // .createComparisonResourceSnapshot(); |
| // |
| // MatchModel match = MatchService.doResourceMatch(targetResource, |
| // sourceResource, null); |
| // DiffModel diff = DiffService.doDiff(match); |
| // IDiffModelFilter filter = DiffModelFilterUtils |
| // .and(DiffModelOrderFilter.INSTANCE, |
| // DiffModelResourceFilter.INSTANCE); |
| // DiffModelFilterUtils.filter(diff, filter); |
| // |
| // snapshot.setMatch(match); |
| // snapshot.setDiff(diff); |
| // |
| // return snapshot; |
| // |
| // } |
| |
| /** |
| * Add a marker with the current revision number to the history |
| */ |
| @SuppressWarnings("unused") |
| private void addMarker(final Integer number) { |
| final Release release = EcoreEditorDetector.getInstance().getListener( |
| editor).getHistory().getLastRelease(); |
| final Command command = new ChangeCommand(release) { |
| @Override |
| protected void doExecute() { |
| final NoChange marker = HistoryFactory.eINSTANCE.createNoChange(); |
| marker.setDescription(number.toString()); |
| release.getChanges().add(marker); |
| } |
| }; |
| editor.getEditingDomain().getCommandStack().execute(command); |
| } |
| |
| /** |
| * Initialize the mapping |
| */ |
| |
| // CB Migrate |
| // private void initMapping(MatchModel match) { |
| // mapping = new Mapping(); |
| // for(Iterator<EObject> i = match.eAllContents(); i.hasNext(); ) { |
| // EObject element = i.next(); |
| // if(element instanceof Match2Elements) { |
| // Match2Elements match2Elements = (Match2Elements) element; |
| // mapping.map(match2Elements.getRightElement(), match2Elements |
| // .getLeftElement()); |
| // } |
| // } |
| // selectionAdapter.setMapping(mapping); |
| // } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void dispose() { |
| getSite().getPage().removePartListener(partListener); |
| detachOldEditor(); |
| super.dispose(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| if (selecting) { |
| return; |
| } |
| if (event.getSelection() instanceof IStructuredSelection) { |
| final IStructuredSelection selection = (IStructuredSelection) event.getSelection(); |
| if (selection.getFirstElement() instanceof EObject) { |
| final EObject element = (EObject) selection.getFirstElement(); |
| final EObject target = mapping.getTarget(element); |
| if (target != null) { |
| selecting = true; |
| targetSash.getStructureViewer().setSelection(new StructuredSelection(target), true); |
| selecting = false; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Calculate the breaking changes |
| */ |
| |
| // CB Migrate |
| // public void calculateBreaking(DiffModel model) { |
| // |
| // breakingChanges = new HashSet<DiffElement>(); |
| // BreakingSwitch s = new BreakingSwitch(); |
| // |
| // for(Iterator<EObject> i = model.eAllContents(); i.hasNext(); ) { |
| // EObject eObject = i.next(); |
| // if(eObject instanceof DiffElement) { |
| // DiffElement element = (DiffElement) eObject; |
| // boolean breaking = s.doSwitch(element); |
| // if(breaking) { |
| // breakingChanges.add(element); |
| // } |
| // } |
| // } |
| // |
| // for(DiffElement change : new HashSet<DiffElement>(breakingChanges)) { |
| // while(change.eContainer() != null && change.eContainer() instanceof DiffElement) { |
| // change = (DiffElement) change.eContainer(); |
| // breakingChanges.add(change); |
| // } |
| // } |
| // } |
| |
| /** |
| * Extract the revision number from the URI |
| * |
| * @param targetURI |
| * @return Revision number |
| */ |
| @SuppressWarnings("unused") |
| private Integer extractNumber(URI targetURI) { |
| String name = targetURI.trimFileExtension().lastSegment(); |
| int index = name.indexOf('_'); |
| if (index >= 0) { |
| name = name.substring(index + 1); |
| index = name.indexOf('.'); |
| if (index >= 0) { |
| name = name.substring(index + 1); |
| try { |
| return Integer.parseInt(name); |
| } catch (final NumberFormatException e) { |
| return null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Enable or disable synchronization |
| * |
| * @param synchronization |
| */ |
| private void setSynchronization(boolean synchronization) { |
| this.synchronization = synchronization; |
| if (synchronization) { |
| refresh(); |
| } |
| } |
| |
| /** |
| * @return Widget for difference representation |
| */ |
| public ModelSash getDifferenceSash() { |
| return differenceSash; |
| } |
| |
| /** |
| * Fix the copier of EMF Compare |
| */ |
| |
| // CB Migrate |
| // private void fixCopier() { |
| // try { |
| // DiffModel diff = (DiffModel) differenceSash.getStructureViewer() |
| // .getInput(); |
| // Field field = MergeService.class |
| // .getDeclaredField("copier"); |
| // field.setAccessible(true); |
| // field.set(null, new EMFCompareEObjectCopier(diff) { |
| // @Override |
| // public EObject get(Object key) { |
| // EObject value = super.get(key); |
| // if (value == null) { |
| // if (key instanceof EDataType) { |
| // EDataType type = (EDataType) key; |
| // EPackage ePackage = type.getEPackage(); |
| // if (ePackage == EcorePackage.eINSTANCE) { |
| // value = type; |
| // } |
| // } else if (key instanceof EObject) { |
| // value = mapping.getSource((EObject) key); |
| // } |
| // } |
| // return value; |
| // } |
| // }); |
| // } catch (SecurityException e) { |
| // // ignore |
| // } catch (NoSuchFieldException e) { |
| // // ignore |
| // } catch (IllegalArgumentException e) { |
| // // ignore |
| // } catch (IllegalAccessException e) { |
| // // ignore |
| // } |
| // } |
| |
| /** |
| * Decorator for breaking changes |
| * |
| * @author herrmama |
| * @author $Author$ |
| * @version $Rev$ |
| * @levd.rating RED Rev: |
| */ |
| public class Decorator implements ILabelDecorator, IColorDecorator { |
| |
| /** |
| * Color for breaking changes |
| */ |
| private final Color red = new Color(Display.getDefault(), 255, 0, 0); |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Color decorateBackground(Object element) { |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Color decorateForeground(Object element) { |
| |
| // CB Migrate |
| // if(breakingChanges.contains(element)) { |
| // return red; |
| // } |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Image decorateImage(Image image, Object element) { |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String decorateText(String text, Object element) { |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addListener(ILabelProviderListener listener) { |
| // not required |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void dispose() { |
| red.dispose(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isLabelProperty(Object element, String property) { |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void removeListener(ILabelProviderListener listener) { |
| // not required |
| } |
| } |
| } |