blob: 6e10290633b7859c020314bf779ff349ba5d5697 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2016 CEA LIST.
*
* 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:
* CEA LIST Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.moka.animation.engine.rendering;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.View;
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.core.utils.ServiceUtils;
import org.eclipse.papyrus.infra.gmfdiag.common.utils.DiagramUtils;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.papyrus.moka.animation.presentation.control.AnimationControlView;
import org.eclipse.papyrus.moka.animation.presentation.data.AnimatedDiagramTree;
import org.eclipse.papyrus.moka.animation.presentation.data.AnimatingInstanceNode;
import org.eclipse.papyrus.moka.animation.presentation.data.AnimationTreeNodeFactory;
import org.eclipse.papyrus.moka.animation.presentation.data.DiagramAnimationNode;
import org.eclipse.papyrus.moka.animation.presentation.data.IAnimationTreeNode;
import org.eclipse.papyrus.moka.fuml.Semantics.Classes.Kernel.IObject_;
import org.eclipse.papyrus.moka.utils.helper.EditorUtils;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.PlatformUI;
public class DiagramHandler {
// The set of diagrams identified for a given model
protected HashSet<Diagram> modelDiagrams;
// The cache linking a model element to the of diagram in which it appears
protected HashMap<EObject, HashSet<Diagram>> modelDiagramMapping;
protected AnimatedDiagramTree animatedDiagrams;
public DiagramHandler() {
this.modelDiagrams = new HashSet<Diagram>();
this.modelDiagramMapping = new HashMap<EObject, HashSet<Diagram>>();
}
public DiagramHandler(Model model) {
this();
this.init(model);
}
public void init(EObject modelElement) {
// Initialize the animated diagrams manager. The initialize process consist in:
// 1 - Identifying diagrams located in a model
// 2 - Build a cache linking a model element to a list of diagrams in which it appears
if (modelElement instanceof Element) {
// Find all diagrams available in this model
Element owner = ((Element)modelElement).getOwner();
while(owner.getOwner() != null) {
owner = owner.getOwner();
}
Job diagramsLoading = new InitiliazeDiagramManagerJob(owner.getModel());
diagramsLoading.schedule();
try {
diagramsLoading.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.hookView();
}
}
private void hookView() {
// Populate initial data model to be displayed.
this.animatedDiagrams = new AnimatedDiagramTree();
IAnimationTreeNode root = this.animatedDiagrams.getRoot();
for (Diagram diagram : this.modelDiagrams) {
root.addChild(AnimationTreeNodeFactory.getInstance().createDiagramAnimationNode(diagram));
}
// Locate view to populate
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
IViewPart animationControlView = DiagramHandler.this.getView(AnimationControlView.ID, false);
if (animationControlView != null && animationControlView instanceof AnimationControlView) {
((AnimationControlView) animationControlView).setInitialInput(DiagramHandler.this.animatedDiagrams);
}
}
});
}
private IViewPart getView(final String ID, boolean restore) {
// Find out a view using the specified ID
IViewPart view = null;
IViewReference viewReferences[] = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getActivePage().getViewReferences();
int i = 0;
while (view == null && i < viewReferences.length) {
if (viewReferences[i].getId().equals(ID)) {
view = viewReferences[i].getView(restore);
}
i++;
}
return view;
}
private List<Diagram> getAssociatedDiagrams(EObject modelElement, Resource notationResource) {
// Find the set of diagrams in which the given model element appears
List<Diagram> associatedDiagrams = new ArrayList<Diagram>();
if (notationResource != null && modelElement != null) {
return DiagramUtils.getAssociatedDiagramsFromNotationResource(modelElement, notationResource);
}
return associatedDiagrams;
}
private void searchDiagrams(Model model, IProgressMonitor monitor) {
// Identify all diagrams that are available in a model. The unique place from which
// this operation is called is the job in charge of executing the search.
Resource resource = model.eResource();
ResourceSet resourceSet = resource.getResourceSet();
// Load the resource corresponding to the notation
final String resourceNotationURI = model.eResource().getURI().toString().replaceAll("\\.uml$", ".notation");
Resource notationResource = resourceSet.getResource(URI.createURI(resourceNotationURI), true);
// Discover all diagrams
monitor.subTask("Find all diagrams");
Iterator<EObject> modelContentIterator = model.eAllContents();
while (modelContentIterator.hasNext()) {
EObject currentModelElement = modelContentIterator.next();
// Find the associated diagrams
List<Diagram> diagrams = this.getAssociatedDiagrams(currentModelElement, notationResource);
// Build the cache to relate the model element the set of diagrams where it is shown
if (!diagrams.isEmpty()) {
// Add newly found diagrams to the set
this.modelDiagrams.addAll(diagrams);
}
}
monitor.worked(1);
// Build the map to enable the possibility
monitor.subTask("Build mapping with model elements");
Iterator<Diagram> diagramIterator = this.modelDiagrams.iterator();
while (diagramIterator.hasNext()) {
Diagram currentDiagram = diagramIterator.next();
Iterator<EObject> diagramViews = currentDiagram.eAllContents();
while (diagramViews.hasNext()) {
EObject potentialView = diagramViews.next();
if (potentialView instanceof View) {
EObject modelElement = ((View) (potentialView)).getElement();
if (this.modelDiagramMapping.containsKey(modelElement)) {
HashSet<Diagram> diagrams = this.modelDiagramMapping.get(modelElement);
if (!diagrams.contains(currentDiagram)) {
diagrams.add(currentDiagram);
}
} else {
HashSet<Diagram> diagramSet = new HashSet<Diagram>();
diagramSet.add(currentDiagram);
this.modelDiagramMapping.put(modelElement, diagramSet);
}
}
}
}
monitor.worked(1);
}
/**
* The diagrams search is executed in a separate job.
*/
class InitiliazeDiagramManagerJob extends Job {
private Model model;
public InitiliazeDiagramManagerJob(Model model) {
super("Diagrams lookup");
this.model = model;
this.setUser(true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask("Prepare execution", 2);
DiagramHandler.this.searchDiagrams(this.model, monitor);
monitor.done();
return Status.OK_STATUS;
}
}
public boolean hasOpenedDiagram(EObject modelElement) {
// Determine if a model element has at least one of its diagrams opened (i.e., visible to the user)
boolean opened = false;
Set<Diagram> diagramSet = this.modelDiagramMapping.get(modelElement);
if (diagramSet != null) {
Iterator<Diagram> diagramIterator = diagramSet.iterator();
while (!opened && diagramIterator.hasNext()) {
Diagram diagram = diagramIterator.next();
IEditorPart editorPart = EditorUtils.getEditorPart(diagram);
ServicesRegistry servicesRegistry = (ServicesRegistry) editorPart.getAdapter(ServicesRegistry.class);
IPageManager pageManager = null;
try {
pageManager = ServiceUtils.getInstance().getIPageManager(servicesRegistry);
} catch (ServiceException e) {
e.printStackTrace();
}
if (pageManager != null) {
opened = pageManager.isOpen(diagram);
}
}
}
return opened;
}
public void openDiagrams(EObject modelElement) {
// Open every diagrams on which the specify on which this model element appear
HashSet<Diagram> diagrams = this.modelDiagramMapping.get(modelElement);
if (!diagrams.isEmpty()) {
for (Diagram diagram : diagrams) {
IEditorPart editorPart = EditorUtils.getEditorPart(diagram);
ServicesRegistry servicesRegistry = (ServicesRegistry) editorPart.getAdapter(ServicesRegistry.class);
IPageManager pageManager = null;
try {
pageManager = ServiceUtils.getInstance().getIPageManager(servicesRegistry);
} catch (ServiceException e) {
e.printStackTrace();
}
if (pageManager != null) {
pageManager.openPage(diagram);
pageManager.selectPage(diagram);
}
}
}
}
public boolean isRenderable(EObject modelElement) {
// A model element can be rendered as soon as it exists a diagram in which it
// appear in the model
Set<Diagram> diagramSet = this.modelDiagramMapping.get(modelElement);
return diagramSet != null && !diagramSet.isEmpty();
}
public boolean isRegistered(IObject_ instance) {
// Verifies if a particular instance is allowed to trigger animation
// An instance is allowed to trigger animation if it is part of the "AnimatedDiagramTree"
// Note that users may have decided to manually disallow a particular instance
// to trigger animation. In this case even if previously presented preconditions
// are full filled this operation returns false
boolean isRenderable = false;
if (instance != null && this.animatedDiagrams != null) {
Iterator<IAnimationTreeNode> nodesIterator = this.animatedDiagrams.getRoot().getChildren().iterator();
while (!isRenderable && nodesIterator.hasNext()) {
DiagramAnimationNode node = (DiagramAnimationNode) nodesIterator.next();
if (node.hasAnimator(instance)) {
isRenderable = true;
}
}
}
return isRenderable;
}
public Set<Diagram> getAnimatedDiagrams(IObject_ instance) {
// Provides the list of diagrams on which the given instance is allowed
// to trigger animation actions.
Set<Diagram> diagrams = new HashSet<Diagram>();
if (instance != null && this.animatedDiagrams != null) {
Iterator<IAnimationTreeNode> nodesIterator = this.animatedDiagrams.getRoot().getChildren().iterator();
while (nodesIterator.hasNext()) {
DiagramAnimationNode node = (DiagramAnimationNode) nodesIterator.next();
if (node.isAnimatorAllowed(instance)) {
diagrams.add(node.getAnimatedDiagram());
}
}
}
return diagrams;
}
public Set<Diagram> findDiagramsInvolved(IObject_ instance) {
// Find any diagram on which this particular instance may trigger
// animations. The research is based on the analysis of the classifier
// behavior (if any) and operation implementations
HashSet<Diagram> relatedDiagrams = new HashSet<Diagram>();
if (instance != null) {
Iterator<Classifier> types = instance.getTypes().iterator();
while (types.hasNext()) {
Class type = (Class) types.next();
if (type.isActive() || type instanceof Behavior) {
Behavior behavior = null;
if (type instanceof Behavior) {
behavior = (Behavior) type;
} else {
behavior = type.getClassifierBehavior();
}
if (behavior != null) {
Iterator<Element> elementIterator = behavior.getOwnedElements().iterator();
while (elementIterator.hasNext()) {
Element behaviorElement = elementIterator.next();
if (this.isRenderable(behaviorElement)) {
relatedDiagrams.addAll(this.modelDiagramMapping.get(behaviorElement));
}
}
}
}
Iterator<Operation> operationsIterator = type.getOperations().iterator();
while (operationsIterator.hasNext()) {
Operation currentOperation = operationsIterator.next();
Iterator<Behavior> implementationIterator = currentOperation.getMethods().iterator();
while (implementationIterator.hasNext()) {
Behavior currentImplementation = implementationIterator.next();
Iterator<Element> elementIterator = currentImplementation.getOwnedElements().iterator();
while (elementIterator.hasNext()) {
Element behaviorElement = elementIterator.next();
if (this.isRenderable(behaviorElement)) {
relatedDiagrams.addAll(this.modelDiagramMapping.get(behaviorElement));
}
}
}
}
}
}
return relatedDiagrams;
}
public void addRenderable(IObject_ instance, Diagram diagram) {
// Add the relation between the diagram and the instance. This relation
// is formalized through the data model (see AnimatedDiagramExecutionTree).
if (instance != null && diagram != null) {
Iterator<IAnimationTreeNode> nodesIterator = this.animatedDiagrams.getRoot().getChildren().iterator();
while (nodesIterator.hasNext()) {
IAnimationTreeNode node = nodesIterator.next();
if (((DiagramAnimationNode) node).getAnimatedDiagram() == diagram) {
node.addChild(AnimationTreeNodeFactory.getInstance().createAnimatingInstanceNode(instance));
}
}
}
}
public void deleteRenderable(IObject_ instance) {
// Removes the relation existing between this instance and any diagram. This relationship
// is formalized through the data model.
if (instance != null && this.animatedDiagrams != null) {
Iterator<IAnimationTreeNode> nodesIterator = this.animatedDiagrams.getRoot().getChildren().iterator();
while (nodesIterator.hasNext()) {
IAnimationTreeNode node = nodesIterator.next();
Iterator<IAnimationTreeNode> childrenIterator = node.getChildren().iterator();
List<IAnimationTreeNode> unregisteredAnimators = new ArrayList<IAnimationTreeNode>();
while (childrenIterator.hasNext()) {
IAnimationTreeNode animator = childrenIterator.next();
if (((AnimatingInstanceNode) animator).instance == instance) {
unregisteredAnimators.add(animator);
}
}
for (IAnimationTreeNode animator : unregisteredAnimators) {
node.removeChild(animator);
}
}
}
}
public synchronized void clean() {
// Release data and dispose view that is linked to these data
this.modelDiagrams.clear();
this.modelDiagramMapping.clear();
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
IViewPart animationControlView = DiagramHandler.this.getView(AnimationControlView.ID, false);
if (animationControlView != null && animationControlView instanceof AnimationControlView) {
((AnimationControlView) animationControlView).dispose();
}
}
});
this.animatedDiagrams = null;
}
}