/******************************************************************************* * Copyright (c) 2010 Michal Antkiewicz. * 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: * Michal Antkiewicz - initial API and implementation ******************************************************************************/ package ca.uwaterloo.gsd.fsml.ui; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.plugin.AbstractUIPlugin; import ca.uwaterloo.gsd.fsml.sync.ClassSyncItem; import ca.uwaterloo.gsd.fsml.sync.ReconciliationDecision; import ca.uwaterloo.gsd.fsml.sync.StructuralFeatureSyncItem; import ca.uwaterloo.gsd.fsml.sync.SyncItem; import ca.uwaterloo.gsd.fsml.sync.SynchronizationResult; import ca.uwaterloo.gsd.fsml.sync.provider.SyncEditPlugin; import ca.uwaterloo.gsd.fsml.sync.provider.SyncItemProviderAdapterFactory; /** * @author Michal Antkiewicz <mantkiew@gsd.uwaterloo.ca> * */ public class ModelCodeSynchronizationView extends ViewPart implements IPartListener, ISelectionProvider { protected TreeViewer viewer; protected ReconciliationDecisionAction enforceAction; protected ReconciliationDecisionAction updateAction; protected ReconciliationDecisionAction enforceAndUpdateAction; protected ReconciliationDecisionAction replaceAndEnforceAction; protected ReconciliationDecisionAction replaceAndUpdateAction; protected ReconciliationDecisionAction ignore; private IFsmlEditor editor; private SynchronizationResult synchronizationResult; private AdapterFactory adapterFactory; public ModelCodeSynchronizationView() { adapterFactory = new SyncItemProviderAdapterFactory(); } class NameSorter extends ViewerSorter { } public void createPartControl(Composite parent) { viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setSorter(new NameSorter()); viewer.addFilter(new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof SyncItem) { SyncItem syncItem = (SyncItem) element; if (!syncItem.getSynchronizationState().isConsistent()) return true; } return false; } }); viewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory)); viewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory)); makeActions(); hookContextMenu(); contributeToActionBars(); getSite().getPage().addPartListener(this); getSite().setSelectionProvider(this); } public void refresh() { viewer.refresh(); // refresh actions } private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { manager.updateAll(true); ModelCodeSynchronizationView.this.fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, viewer); } private void contributeToActionBars() { IActionBars bars = getViewSite().getActionBars(); fillLocalToolBar(bars.getToolBarManager()); } private void fillContextMenu(IMenuManager manager) { manager.update(true); // Other plug-ins can contribute their actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); manager.add(enforceAction); manager.add(updateAction); manager.add(enforceAndUpdateAction); manager.add(replaceAndEnforceAction); manager.add(replaceAndUpdateAction); manager.add(ignore); } private void fillLocalToolBar(IToolBarManager manager) { manager.update(true); manager.add(enforceAction); manager.add(updateAction); manager.add(enforceAndUpdateAction); manager.add(replaceAndEnforceAction); manager.add(replaceAndUpdateAction); manager.add(ignore); } private void makeActions() { enforceAction = new ReconciliationDecisionAction() { public void run() { setDecision(item, ReconciliationDecision.ENFORCE_LITERAL, true); } @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); if (item != null) setEnabled(item.isDecisionValid(ReconciliationDecision.ENFORCE_LITERAL)); else setEnabled(false); }; }; enforceAction.setText("Enforce in the code"); enforceAction.setToolTipText("Propagate the change from the model in the code"); enforceAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( "ca.uwaterloo.gsd.fsml", "icons/reconciliationDecisions/enforce.gif")); viewer.addSelectionChangedListener(enforceAction); updateAction = new ReconciliationDecisionAction() { public void run() { setDecision(item, ReconciliationDecision.UPDATE_LITERAL, true); } public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); if (item != null) setEnabled(item.isDecisionValid(ReconciliationDecision.UPDATE_LITERAL)); else setEnabled(false); }; }; updateAction.setText("Update in the model"); updateAction.setToolTipText("Propagate the change from the code to the model"); updateAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( "ca.uwaterloo.gsd.fsml", "icons/reconciliationDecisions/update.gif")); viewer.addSelectionChangedListener(updateAction); enforceAndUpdateAction = new ReconciliationDecisionAction() { public void run() { setDecision(item, ReconciliationDecision.ENFORCE_AND_UPDATE_LITERAL, false); } @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); setEnabled(item instanceof ClassSyncItem); }; }; // should be enabled only for ClassSyncItems enforceAndUpdateAction.setText("Enforce And Update"); enforceAndUpdateAction.setToolTipText("Enforce and update the children depending on their individual decisions"); enforceAndUpdateAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( "ca.uwaterloo.gsd.fsml", "icons/reconciliationDecisions/enforceAndUpdate.gif")); viewer.addSelectionChangedListener(enforceAndUpdateAction); replaceAndEnforceAction = new ReconciliationDecisionAction() { public void run() { setDecision(item, ReconciliationDecision.OVERRIDE_AND_ENFORCE_LITERAL, true); } @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); if (item != null && item instanceof SyncItem) { boolean valid = item.isDecisionValid(ReconciliationDecision.OVERRIDE_AND_ENFORCE_LITERAL); if (valid) { setEnabled(true); if (item instanceof StructuralFeatureSyncItem){ StructuralFeatureSyncItem asi = (StructuralFeatureSyncItem) item; if (asi.getStructuralFeature().getEAnnotation("readOnly") != null) { setEnabled(false); } } } } else setEnabled(false); }; }; replaceAndEnforceAction.setText("Override And Enforce"); replaceAndEnforceAction.setToolTipText("Override the code and enforce the change from the model"); replaceAndEnforceAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( "ca.uwaterloo.gsd.fsml", "icons/reconciliationDecisions/enforce.gif")); viewer.addSelectionChangedListener(replaceAndEnforceAction); replaceAndUpdateAction = new ReconciliationDecisionAction() { public void run() { setDecision(item, ReconciliationDecision.OVERRIDE_AND_UPDATE_LITERAL, true); } @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); if (item != null) setEnabled(item.isDecisionValid(ReconciliationDecision.OVERRIDE_AND_UPDATE_LITERAL)); else setEnabled(false); }; }; replaceAndUpdateAction.setText("Override And Update"); replaceAndUpdateAction.setToolTipText("Override the model and update the model from the code"); replaceAndUpdateAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( "ca.uwaterloo.gsd.fsml", "icons/reconciliationDecisions/update.gif")); viewer.addSelectionChangedListener(replaceAndUpdateAction); ignore = new ReconciliationDecisionAction() { public void run() { setDecision(item, ReconciliationDecision.IGNORE_LITERAL, true); } @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); setEnabled(true); }; }; ignore.setText("Ignore"); ignore.setToolTipText("Do not propagate"); ignore.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( "ca.uwaterloo.gsd.fsml", "icons/reconciliationDecisions/ignore.gif")); viewer.addSelectionChangedListener(ignore); } /** * @param propagate TODO * @param classSyncItem * Propagates decision made for the concept to all its undecided subfeatures. * Decision is set only for valid synchronization states (i.e., Update only for Reverse*) * This assumes that only valid @param reconciliationDecision was made. */ private static void setDecision(SyncItem syncItem, ReconciliationDecision decision, boolean propagate) { // decide for the concept syncItem.setReconciliationDecision(decision); // decide for sub features if (propagate && syncItem instanceof ClassSyncItem) { ClassSyncItem classSyncItem = (ClassSyncItem) syncItem; for (SyncItem aux : classSyncItem.getSyncItems()) setDecision(aux, decision, propagate); } } /** * Passing the focus request to the viewer's control. */ public void setFocus() { viewer.getControl().setFocus(); } /** * this is called by the ModelCodeSynchronizeAction.run() * @param synchronizationResult */ public void setSynchronizationResult(SynchronizationResult synchronizationResult) { setInput(synchronizationResult); } protected void setInput(SynchronizationResult result) { this.synchronizationResult = result; if (result != null) { viewer.setInput(this.synchronizationResult); viewer.refresh(); viewer.expandAll(); } else viewer.setInput(null); } public void dispose() { getSite().getPage().removePartListener(this); viewer.getTree().dispose(); super.dispose(); } /** * always show synchronization results of the currently active WPI editor */ public void partActivated(IWorkbenchPart part) { if (part instanceof IFsmlEditor) { if (editor != part) { editor = (IFsmlEditor) part; EObject model = editor.getModel(); if (model != null) setInput(SyncEditPlugin.INSTANCE.getSynchronizationResult(model)); } } } /** * clear the view if current editor was closed */ public void partClosed(IWorkbenchPart part) { if (editor == part) { editor = null; setInput(null); } } public void partBroughtToTop(IWorkbenchPart part) { } public void partDeactivated(IWorkbenchPart part) { } public void partOpened(IWorkbenchPart part) { } public IFsmlEditor getEditor() { return editor; } public void addSelectionChangedListener(ISelectionChangedListener listener) { viewer.addSelectionChangedListener(listener); } public ISelection getSelection() { return viewer.getSelection(); } public void removeSelectionChangedListener(ISelectionChangedListener listener) { viewer.removeSelectionChangedListener(listener); } public void setSelection(ISelection selection) { viewer.setSelection(selection); } }