CHESSIoT_Dev_old-master/org.polarsys.chess.service/src/org/polarsys/chess/service/gui/utils/CHESSEditorUtils.java
/*
-----------------------------------------------------------------------
--          			CHESS Service plug-in						 --
--                                                                   --
--                    Copyright (C) 2011-2015                        --
--                 University of Padova, ITALY                       --
--                                                                   --
-- Author: Alessandro Zovi         azovi@math.unipd.it 		         --
--                                                                   --
-- 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-v20.html                         --
-----------------------------------------------------------------------
 */

package org.polarsys.chess.service.gui.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.cdo.util.CDOURIUtil;
import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramCommandStack;
import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramGraphicalViewer;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.papyrus.cdo.internal.core.CDOUtils;
import org.eclipse.papyrus.editor.PapyrusMultiDiagramEditor;
import org.eclipse.papyrus.infra.core.sashwindows.di.PageRef;
import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
import org.eclipse.papyrus.infra.gmfdiag.css.CSSConnectorImpl;
import org.eclipse.papyrus.infra.gmfdiag.css.CSSShapeImpl;
import org.eclipse.papyrus.infra.gmfdiag.css.notation.CSSDiagramImpl;
import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
import org.eclipse.papyrus.infra.ui.util.EditorUtils;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.internal.impl.ProfileImpl;
import org.polarsys.chess.core.natures.CHESSNature;
import org.polarsys.chess.core.views.DiagramStatus;
import org.polarsys.chess.service.internal.Activator;
import org.polarsys.chess.service.internal.service.CHESSService;
import org.polarsys.chess.service.internal.utils.CHESSInternalEditorUtils;

public class CHESSEditorUtils {

	/**
	 * If the active editor's input belongs to a project with the CHESS nature then return the 
	 * editor.
	 * @return the editor if it is a CHESS editor, null otherwise.
	 */
	public static /*@Nullable*/ PapyrusMultiDiagramEditor getCHESSEditor() {
		IEditorPart editor = CHESSInternalEditorUtils.getEditor();
		PapyrusMultiDiagramEditor r = null;
		if (editor != null && editor instanceof PapyrusMultiDiagramEditor
				&& isCHESSProject(editor))
			r = (PapyrusMultiDiagramEditor) editor;
		return r;
	}

	/**
	 * Get the current active diagram from the active editor.
	 * It uses the org.eclipse.papyrus.infra.core.utils.EditorUtils.getMultiDiagramEditor()
	 * utility method.
	 * @return the active Diagram object. It can be null.
	 */
	static public /*@Nullable*/ Diagram getDiagram() {
		IMultiDiagramEditor editor = EditorUtils.getMultiDiagramEditor();
		return (Diagram) editor.getAdapter(Diagram.class);
	}

	/**
	 * Get the current active diagram given the editor.
	 * @param the editor. It should be not null.
	 * @return the active Diagram object. It can be null.
	 */
	public static /*@Nullable*/ Diagram getDiagram(/*@NunNull*/PapyrusMultiDiagramEditor editor) {
		Object model = editor.getISashWindowsContainer()
				.getActiveSashWindowsPage().getRawModel();
		Diagram diagram = null;
		if (model instanceof PageRef) {
			EObject diagramEObj = ((PageRef) model).getEmfPageIdentifier();
			if (diagramEObj instanceof Diagram) {
				diagram = (Diagram) diagramEObj;
			}

		}
		return diagram;
	}

	

	/**
	 * Return the DiagramEditPart from the active workbench page.
	 * @return the DiagramCommandStack. It can be null.
	 */
	static public /*@Nullable*/ DiagramCommandStack getDiagramCommandStack() {
		try {
			Object stack = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
					.getActivePage().getActivePart()
					.getAdapter(CommandStack.class);
			return (stack instanceof DiagramCommandStack) ? (DiagramCommandStack) stack
					: null;
		} catch (Exception e) {
			Activator.error("Unexpected error", e);
		}
		return null;
	}

	/**
	 * Return the DiagramEditPart from the active editor. It uses the 
	 * org.eclipse.papyrus.infra.core.utils.EditorUtils.getMultiDiagramEditor()
	 * utility method.
	 * @return the DiagramEditPart. It can be null.
	 */
	static public /*@Nullable*/ DiagramEditPart getDiagramEditPart() {
		IMultiDiagramEditor editor = EditorUtils.getMultiDiagramEditor();
		if (editor != null)
			return (DiagramEditPart) editor.getAdapter(DiagramEditPart.class);
		else
			return null;
	}

	/**
	 * Return the graphical viewer from the active editor. It uses the 
	 * org.eclipse.papyrus.infra.core.utils.EditorUtils.getMultiDiagramEditor()
	 * utility method.
	 * @return the IDiagramGraphicalViewer. It can be null.
	 */
	static public /*@Nullable*/ IDiagramGraphicalViewer getDiagramGraphicalViewer() {
		DiagramEditPart diagramEP = getDiagramEditPart();
		if (diagramEP != null)
			return (IDiagramGraphicalViewer) diagramEP.getViewer();
		else
			return null;
	}

	/**
	 * Get the DiagramStatus object of the CHESS Service given the Papyrus editor.
	 * @param the editor It should be not null.
	 * @return the DiagramStatus object. It can be null.
	 */
	public static /*@Nullable*/ DiagramStatus getDiagramStatus(
			/* @NonNull */ final PapyrusMultiDiagramEditor editor) {
		DiagramStatus ds = null;
		try {
			ServicesRegistry servicesRegistry = editor.getServicesRegistry();
			CHESSService cs = servicesRegistry.getService(
					CHESSService.class);
			
			ds = cs.getDiagramStatus();
		} catch (ServiceException e) {
			Activator.error("CHESS service does not exist.", e);
		}  catch (NullPointerException e) {
			Activator.error("DiagramStatus does not exist", e);
		}
		return ds;
	}

	/**
	 * Get the DiagramStatus object of the CHESS Service given the Papyrus service registry. 
	 * @param registry. It should be not null.
	 * @return the DiagramStatus object. It can be null.
	 */
	public static /*@Nullable*/DiagramStatus getDiagramStatus(/* @NonNull */final ServicesRegistry registry) {
		DiagramStatus ds = null;
		CHESSService cs;
		try {
			cs = registry.getService(CHESSService.class);
			ds = cs.getDiagramStatus();
		} catch (ServiceException e) {
			Activator.error("CHESS service does not exist.", e);
		} catch (NullPointerException e) {
			Activator.error("DiagramStatus does not exist", e);
		}
		return ds;
	}

	/**
	 * Check if the editor passed as a parameter is a CHESS editor; that is if
	 * the input of the editor belongs to a project with the CHESS nature
	 * 
	 * @param the
	 *            editor. It should be not null.
	 * @return true if it is a CHESS editor, false otherwise.
	 */
	public static boolean isCHESSProject(/* @NonNull */final IEditorPart editor) {
		IEditorInput x = editor.getEditorInput();

		IProjectNature nature = null;
		IFile f = null;
		if (x instanceof URIEditorInput) {
			URIEditorInput input = (URIEditorInput) x;
			
			if (input.getURI().toPlatformString(true) == null){

				if (CDOUtils.isCDOURI(input.getURI())){
				
					String path = CDOURIUtil.extractResourcePath(input.getURI());
//					f = ResourcesPlugin.getWorkspace().getRoot()
//							.getFile(new Path(path));
					return true;
				}
				
				Activator.getDefault().getLog().log(new Status
						(Status.WARNING, Activator.PLUGIN_ID, Status.WARNING, "Unable to get platform string for resource URI, CHESSProject=false ", null));
				return false;
			}
			
			
			f = ResourcesPlugin.getWorkspace().getRoot()
					.getFile(new Path(input.getURI().toPlatformString(true)));
		} else if (x instanceof IFileEditorInput) {
			f = ((IFileEditorInput) x).getFile();
		} else {
			// TODO are there other types of input?
		}

		try {
			if (f != null)
				nature = f.getProject().getNature(CHESSNature.NATURE_ID);
		} catch (CoreException e) {
			System.err.println(e.getStackTrace());
		}

		return nature != null;
	}

	/**
	 * Reopen the Papyrus Editor specified in the parameter. Optionally the
	 * editor input can be saved before reopening.
	 * 
	 * @param editor
	 *            the Editor to reopen. It should be not null.
	 * @param save
	 *            set this parameter to true to save the input
	 */
	public static void reopenEditor(/* @NonNull */final IEditorPart editor,
			final Boolean save) {
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				IWorkbenchPage page = editor.getEditorSite().getPage();
				IEditorInput input = editor.getEditorInput();
				page.closeEditor(editor, save);
				try {
					page.openEditor(input, PapyrusMultiDiagramEditor.EDITOR_ID,
							save);
				} catch (PartInitException e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Utility function to create a confirmation Dialog with "OK", "no" and
	 * "cancel" buttons.
	 * 
	 * @param editor
	 *            the editor the dialog belongs to. It should be not null.
	 * @param dialogTitle
	 *            the title of the dialog
	 * @param dialogMessage
	 *            the message to display
	 * @return the MessageDialog created.
	 */
	public static /* @NonNull */ MessageDialog showConfirmDialog(
	/* @NonNull */final EditorPart editor, String dialogTitle,
			String dialogMessage) {
		MessageDialog md = new MessageDialog(editor.getSite().getShell(),
				dialogTitle, null, dialogMessage, MessageDialog.CONFIRM,
				new String[] { "OK", "No", "Cancel" }, 0);
		md.setBlockOnOpen(true);
		return md;
	}
	
	/**
	 * Utility function to remove orphan views from all the diagrams defined in the notation model
	 * @since 0.11
	 * 
	 */
	public static void cleanAllDiagrams(){
		final PapyrusMultiDiagramEditor editor = CHESSEditorUtils.getCHESSEditor();

		if(editor==null){
			return;
		}
		
		try {
			if(editor != null){
				ServicesRegistry serviceRegistry = (ServicesRegistry)editor.getAdapter(ServicesRegistry.class);
				final IPageManager pageMngr = serviceRegistry.getService(IPageManager.class);
				TransactionalEditingDomain editingDom = (TransactionalEditingDomain) editor.getEditingDomain();
				List<View> viewToDelete = new ArrayList<View>();

				final Map<CSSDiagramImpl, List<View>> mapDiagramViews= new HashMap<CSSDiagramImpl, List<View>>();

				CSSShapeImpl shape = null;
				CSSConnectorImpl edge = null;
				for( Object page : pageMngr.allPages()) {
					if (page instanceof CSSDiagramImpl){
						CSSDiagramImpl diag = (CSSDiagramImpl) page;
						viewToDelete = new ArrayList<View>();

						//pageMngr also has a list of diagram coming from the applied profiles; we have to skip these diagrams
						//TODO use a smart way to identify external diagram in place of the proxy reference
						if ((diag.getElement() != null && diag.getElement().eIsProxy()) || (diag.getElement() != null && diag.getElement() instanceof Profile))
							continue;

						for (Object view : diag.getChildren()){
							if (view instanceof CSSShapeImpl){
								shape = (CSSShapeImpl) view;
								if (shape.getElement() != null && shape.getElement().eIsProxy()){
									viewToDelete.add(shape);
								}


							}
							checkNestedOrphanView ((View)view , viewToDelete);
						}

						for (Object view : diag.getEdges()){
							if (view instanceof CSSConnectorImpl){
								edge = (CSSConnectorImpl) view;
								if (edge.getElement() != null && edge.getElement().eIsProxy()){
									viewToDelete.add(edge);
								}
							}

						}

						if (!viewToDelete.isEmpty())
							mapDiagramViews.put(diag, viewToDelete);

					}

				}

				if (!mapDiagramViews.isEmpty()) {

					editingDom.getCommandStack().execute(new RecordingCommand(editingDom) {
						protected void doExecute() {

							for (CSSDiagramImpl diag : mapDiagramViews.keySet()){
								for (View view : mapDiagramViews.get(diag)){
									if (view instanceof CSSShapeImpl){
										//use econtainer in case of nested views (like parts in composite diagrams)
										((View) view.eContainer()).removeChild(view);
									}
									if (view instanceof CSSConnectorImpl){
										view.setVisible(false);
										diag.removeEdge((CSSConnectorImpl)view);
									}
								}

							}

						}
					});

				}
			}
		}catch (Exception e) {
			Activator.error("Exception while trying to remove orphan views", e);
		}
	}
	
	private static void checkNestedOrphanView(View view, List<View> viewToDelete){
		CSSShapeImpl shape = null;
		for (Object childView : view.getChildren()){
			if (childView instanceof CSSShapeImpl){
				shape = (CSSShapeImpl) childView;
				if (shape.getElement() != null && shape.getElement().eIsProxy()){
					viewToDelete.add(shape);
				} 
				
			}
			checkNestedOrphanView((View) childView, viewToDelete);
		}
	}
	
}
