/******************************************************************************
* Copyright (c) 2016 Oracle
* 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:
* Shenxue Zhou - initial implementation and ongoing maintenance
* Konstantin Komissarchik - [378756] Convert ModelElementListener and ModelPropertyListener to common listener infrastructure
* Ling Hao - [383924] Flexible diagram node shapes
******************************************************************************/
package org.eclipse.sapphire.ui.diagram.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.ElementList;
import org.eclipse.sapphire.ElementProperty;
import org.eclipse.sapphire.ElementType;
import org.eclipse.sapphire.FilteredListener;
import org.eclipse.sapphire.ListProperty;
import org.eclipse.sapphire.PropertyContentEvent;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.modeling.ModelPath;
import org.eclipse.sapphire.modeling.annotations.Reference;
import org.eclipse.sapphire.ui.diagram.ConnectionDeleteEvent;
import org.eclipse.sapphire.ui.diagram.def.IDiagramConnectionDef;
import org.eclipse.sapphire.ui.diagram.def.IDiagramConnectionEndpointBindingDef;
import org.eclipse.sapphire.ui.diagram.def.IDiagramExplicitConnectionBindingDef;
import org.eclipse.sapphire.ui.diagram.editor.DiagramNodePart;
import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeTemplate;
/**
* @author <a href="mailto:shenxue.zhou@oracle.com">Shenxue Zhou</a>
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
* @author <a href="mailto:ling.hao@oracle.com">Ling Hao</a>
*/
public class DiagramEmbeddedConnectionTemplate extends DiagramConnectionTemplate
{
private DiagramNodeTemplate nodeTemplate;
private Map<Element, List<StandardDiagramConnectionPart>> diagramConnectionMap;
private ModelPath endpointPath;
public DiagramEmbeddedConnectionTemplate(IDiagramExplicitConnectionBindingDef connBindingDef)
{
super(connBindingDef);
}
@Override
public void init()
{
this.nodeTemplate = (DiagramNodeTemplate)parent();
this.diagramEditor = this.nodeTemplate.getDiagramEditorPart();
this.connectionDef = (IDiagramConnectionDef)super.definition();
this.diagramConnectionMap = new HashMap<Element, List<StandardDiagramConnectionPart>>();
ListProperty nodeProperty = (ListProperty)this.nodeTemplate.getModelProperty();
this.propertyName = this.bindingDef.getProperty().content();
this.connListProperty = (ListProperty)this.nodeTemplate.getNodeType().property(this.propertyName);
initConnPartListener();
this.templateListeners = new CopyOnWriteArraySet<DiagramConnectionTemplateListener>();
this.modelPropertyListener = new FilteredListener<PropertyContentEvent>()
{
@Override
protected void handleTypedEvent( final PropertyContentEvent event )
{
handleModelPropertyChange( event );
}
};
String endpointPropStr = this.bindingDef.getEndpoint2().content().getProperty().content();
this.endpointPath = new ModelPath(endpointPropStr);
ElementList<Element> srcNodeList = getModelElement().property(nodeProperty);
for (Element srcNodeModel : srcNodeList)
{
if (srcNodeModel.type() == this.nodeTemplate.getNodeType())
{
PropertyDef connProp = srcNodeModel.property(this.propertyName).definition();
if (connProp instanceof ListProperty)
{
ListProperty connListProperty = (ListProperty)connProp;
ElementList<?> connList = srcNodeModel.property(connListProperty);
for (Element endpointModel : connList)
{
createNewConnectionPart(endpointModel, srcNodeModel);
}
addModelListener(srcNodeModel);
}
}
}
}
public void refreshConnections(Element srcNodeModel)
{
PropertyDef connProp = srcNodeModel.property(this.propertyName).definition();
handleConnectionListChange(srcNodeModel, (ListProperty)connProp);
}
@Override
public boolean canStartNewConnection(DiagramNodePart srcNode)
{
Element srcNodeModel = srcNode.getLocalModelElement();
// check the source node type
ElementType srcNodeType = srcNodeModel.type();
ElementType desiredsrcNodeType = this.nodeTemplate.getNodeType();
if (!srcNodeType.equals(desiredsrcNodeType))
{
return false;
}
return true;
}
@Override
public boolean canCreateNewConnection(DiagramNodePart srcNode, DiagramNodePart targetNode)
{
if (!canStartNewConnection(srcNode))
{
return false;
}
Element srcNodeModel = srcNode.getLocalModelElement();
// check the target node type
ElementType targetType = targetNode.getLocalModelElement().type();
PropertyDef connProp = srcNodeModel.property(this.propertyName).definition();
ElementType connType = connProp.getType();
PropertyDef endpointProp =
connType.property(this.bindingDef.getEndpoint2().content().getProperty().content());
if (endpointProp.getType() == null && endpointProp.hasAnnotation(Reference.class))
{
return endpointProp.getAnnotation(Reference.class).target().isAssignableFrom(targetType.getModelElementClass());
}
return false;
}
@Override
public void setSerializedEndpoint2(Element connModelElement, String endpoint2Value)
{
IDiagramConnectionEndpointBindingDef endpointDef = this.bindingDef.getEndpoint2().content();
String endpointProperty = endpointDef.getProperty().content();
setModelProperty(connModelElement, endpointProperty, endpoint2Value);
}
@Override
public StandardDiagramConnectionPart createNewDiagramConnection(DiagramNodePart srcNode,
DiagramNodePart targetNode)
{
Element srcNodeModel = srcNode.getLocalModelElement();
PropertyDef modelProperty = this.nodeTemplate.getModelProperty();
boolean found = false;
if (modelProperty instanceof ListProperty)
{
ListProperty listProperty = (ListProperty)modelProperty;
ElementList<?> list = getModelElement().property(listProperty);
for (Element listEntryModelElement : list)
{
if (listEntryModelElement == srcNodeModel)
{
found = true;
break;
}
}
}
else if (modelProperty instanceof ElementProperty)
{
ElementProperty elementProperty = (ElementProperty)modelProperty;
if (getModelElement().property(elementProperty) != null)
{
Element localModelElement = getModelElement().property(elementProperty).content();
if (localModelElement == srcNodeModel)
{
found = true;
}
}
}
if (!found)
{
throw new RuntimeException( "Cannot locate the source node element");
}
PropertyDef connProp = srcNodeModel.property(this.propertyName).definition();
Element newEndpoint = null;
if (connProp instanceof ListProperty)
{
ListProperty listProperty = (ListProperty)connProp;
ElementList<?> list = srcNodeModel.property(listProperty);
newEndpoint = list.insert();
}
// Get serialized value of endpoint
String endpointVal = getSerializedEndpoint2(targetNode);
// Set the serialized value of endpoint
setSerializedEndpoint2(newEndpoint, endpointVal);
StandardDiagramConnectionPart connPart = getConnectionPart(srcNodeModel, newEndpoint);
if (connPart == null)
{
connPart = createNewConnectionPart(newEndpoint, srcNodeModel);
}
return connPart;
}
@Override
protected StandardDiagramConnectionPart createNewConnectionPart(Element connElement, Element srcNodeElement)
{
StandardEmbeddedConnectionPart connPart =
new StandardEmbeddedConnectionPart(this.bindingDef, srcNodeElement, this.endpointPath);
connPart.init(this, connElement, this.connectionDef, Collections.<String,String>emptyMap());
connPart.initialize();
connPart.attach(this.connPartListener);
addConnectionPart(srcNodeElement, connPart);
return connPart;
}
public void addModelListener(Element srcNodeModel)
{
srcNodeModel.attach(this.modelPropertyListener, this.propertyName);
}
public void removeModelListener(Element srcNodeModel)
{
srcNodeModel.detach(this.modelPropertyListener, this.propertyName);
}
@Override
public void addModelListener()
{
ListProperty nodeProperty = (ListProperty)this.nodeTemplate.getModelProperty();
ElementList<Element> srcNodeList = getModelElement().property(nodeProperty);
for (Element srcNodeModel : srcNodeList)
{
addModelListener(srcNodeModel);
}
}
@Override
public void removeModelListener()
{
ListProperty nodeProperty = (ListProperty)this.nodeTemplate.getModelProperty();
ElementList<Element> srcNodeList = getModelElement().property(nodeProperty);
for (Element srcNodeModel : srcNodeList)
{
removeModelListener(srcNodeModel);
}
}
@Override
public List<StandardDiagramConnectionPart> getDiagramConnections(Element srcNodeModel)
{
List<StandardDiagramConnectionPart> allConnParts = new ArrayList<StandardDiagramConnectionPart>();
if (srcNodeModel != null)
{
if (this.diagramConnectionMap.get(srcNodeModel) != null)
{
allConnParts.addAll(this.diagramConnectionMap.get(srcNodeModel));
}
}
else
{
// return all the connection parts
for (List<StandardDiagramConnectionPart> connParts : this.diagramConnectionMap.values())
{
allConnParts.addAll(connParts);
}
}
return allConnParts;
}
@Override
public void addConnectionPart(Element srcNodeModel, StandardDiagramConnectionPart connPart)
{
List<StandardDiagramConnectionPart> connParts = this.diagramConnectionMap.get(srcNodeModel);
if (connParts == null)
{
connParts = new ArrayList<StandardDiagramConnectionPart>();
this.diagramConnectionMap.put(srcNodeModel, connParts);
}
connParts.add(connPart);
}
@Override
public void disposeConnectionPart(StandardDiagramConnectionPart connPart)
{
connPart.dispose();
connPart.detach(this.connPartListener);
Collection<List<StandardDiagramConnectionPart>> allConnParts = this.diagramConnectionMap.values();
for (List<StandardDiagramConnectionPart> connParts : allConnParts)
{
if (connParts.contains(connPart))
{
connParts.remove(connPart);
break;
}
}
}
public void removeConnectionParts(Element srcNodeModel)
{
List<StandardDiagramConnectionPart> connParts = new ArrayList<StandardDiagramConnectionPart>();
connParts.addAll(this.getDiagramConnections(srcNodeModel));
for (StandardDiagramConnectionPart connPart : connParts)
{
notifyConnectionDeleteEvent(new ConnectionDeleteEvent(connPart));
disposeConnectionPart(connPart);
}
}
@Override
public ConnectionType getConnectionType()
{
return ConnectionType.Embedded;
}
}