blob: 95c05a148a34f0b34211cfe9589c8fa987d160f9 [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.diagram.core.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.AdapterFactoryTreeIterator;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.epf.diagram.core.DiagramCoreResources;
import org.eclipse.epf.diagram.core.bridge.BridgeHelper;
import org.eclipse.epf.diagram.core.bridge.NodeAdapter;
import org.eclipse.epf.diagram.core.services.DiagramHelper;
import org.eclipse.epf.diagram.model.ActivityDetailDiagram;
import org.eclipse.epf.diagram.model.ActivityDiagram;
import org.eclipse.epf.diagram.model.Link;
import org.eclipse.epf.diagram.model.RoleNode;
import org.eclipse.epf.diagram.model.TaskNode;
import org.eclipse.epf.diagram.model.TypedNode;
import org.eclipse.epf.diagram.model.WorkBreakdownElementNode;
import org.eclipse.epf.diagram.model.util.GraphicalDataHelper;
import org.eclipse.epf.library.edit.TngAdapterFactory;
import org.eclipse.epf.library.edit.process.IBSItemProvider;
import org.eclipse.epf.library.edit.util.IDiagramManager;
import org.eclipse.epf.library.edit.util.MethodElementPropertyHelper;
import org.eclipse.epf.library.edit.util.ProcessUtil;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.WorkBreakdownElement;
import org.eclipse.epf.uma.WorkOrder;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.uml2.uml.ActivityEdge;
import org.eclipse.uml2.uml.ActivityNode;
import org.eclipse.uml2.uml.ControlNode;
import org.eclipse.uml2.uml.FinalNode;
import org.eclipse.uml2.uml.ForkNode;
import org.eclipse.uml2.uml.InitialNode;
import org.eclipse.uml2.uml.JoinNode;
/**
* Validating routines for diagram editing
*
* @author Phong Nguyen Le
* @author Shashidhar Kannoori
* @since 1.0
*/
public class DiagramCoreValidation {
private static final String errMsg_CanNotConnect = DiagramCoreResources.DiagramValidation_err_cannot_connect_text; //$NON-NLS-1$
private static final String errMsg_SamePredAndSucc = DiagramCoreResources.DiagramValidation_err_samepredandsuc_text; //$NON-NLS-1$
private static final String errMsg_CanNotDelete = DiagramCoreResources.DiagramValidation_err_cannot_delete_text; //$NON-NLS-1$
public static boolean isConnectionToReadOnlyTargetAllowed() {
// return DiagramPreferences.getADAllowConnectionToReadOnlyElements();
return true;
}
public static String isTargetReadonly(EditPart part){
View view = (View)part.getModel();
if(BridgeHelper.isReadOnly(view)) return errMsg_CanNotConnect;
return null;
}
public static String canConnect(EditPart targetEditPart, EObject sourceElement, EObject targetElement){
if(targetEditPart == null) return errMsg_CanNotConnect;
EditPart part = targetEditPart.getParent();
Diagram diagram = null;
while(part != null && !(part instanceof DiagramEditPart)){
part = part.getParent();
}
if(part instanceof DiagramEditPart){
diagram = (Diagram)((DiagramEditPart)part).getModel();
}
if(diagram == null) return errMsg_CanNotConnect;
return checkConnect(diagram, sourceElement, targetElement);
}
public static String checkConnect(Diagram diagram, EObject sourceElement, EObject targetElement){
switch(DiagramHelper.getDiagramType(diagram)){
case IDiagramManager.ACTIVITY_DIAGRAM:
if(alwaysAllowed(sourceElement, targetElement)) {
return null;
}
// If target Diagram is read-only - do not connect
if(BridgeHelper.isReadOnly(diagram)) return errMsg_CanNotConnect;
NodeAdapter sourceAdapter = BridgeHelper.getNodeAdapter(sourceElement);
NodeAdapter targetAdapter = BridgeHelper.getNodeAdapter(targetElement);
// if adapters of source and target is null - no connect.
if(sourceAdapter == null || targetAdapter == null) return errMsg_CanNotConnect;
Diagram sourceDiagram = sourceAdapter.getView().getDiagram();
// If source and target diagrams not equal - do not connect.
if (!diagram.equals(sourceDiagram))
return errMsg_CanNotConnect;
AdapterFactory adapterFactory = TngAdapterFactory.INSTANCE
.getWBS_ComposedAdapterFactory();
Object adapter = adapterFactory.adapt(BridgeHelper.getMethodElement(sourceDiagram)
, ITreeItemContentProvider.class);
Object proc = null;
if (adapter instanceof IBSItemProvider) {
proc = ((IBSItemProvider) adapter).getTopItem();
}
// get all breakdown elements in this process
//
List<Object> allElements = new ArrayList<Object>();
for (Iterator iter = new AdapterFactoryTreeIterator(adapterFactory,
proc); iter.hasNext();) {
Object obj = iter.next();
allElements.add(TngUtil.unwrap(obj));
}
if(proc != null){
EObject source = sourceAdapter.getElement();
EObject target = null;
if (targetAdapter != null) {
target = targetAdapter.getElement();
}
if (source instanceof
WorkBreakdownElement) {
if (target instanceof WorkBreakdownElement) {
if (ProcessUtil.checkCircular(
(WorkBreakdownElement) target,
(WorkBreakdownElement) source, allElements))
return errMsg_SamePredAndSucc;
if (!isConnectionToReadOnlyTargetAllowed() && targetAdapter.isTargetReadOnly())
return errMsg_CanNotConnect;
} else {
// target node might be control node
Collection<ActivityNode> actNodes = new ArrayList<ActivityNode>();
BridgeHelper.getSuccessorNodes(actNodes,
(ActivityNode) targetElement);
for (ActivityNode node : actNodes) {
if (BridgeHelper.getNodeAdapter(node).isTargetReadOnly() && !isConnectionToReadOnlyTargetAllowed())
return errMsg_CanNotConnect;
if (ProcessUtil.checkCircular(
(WorkBreakdownElement) BridgeHelper
.getMethodElement(node),
(WorkBreakdownElement) source, allElements)) {
return errMsg_SamePredAndSucc;
}
}
}
} else {
if (target instanceof WorkBreakdownElement) {
Collection<ActivityNode> srcNodes = new ArrayList<ActivityNode>();
BridgeHelper.getPredecessorNodes(srcNodes,
(ActivityNode) sourceElement);
if (!srcNodes.isEmpty()) {
if (targetAdapter.isTargetReadOnly() && !isConnectionToReadOnlyTargetAllowed())
return errMsg_CanNotConnect;
for (ActivityNode predNode : srcNodes) {
MethodElement pred = BridgeHelper
.getMethodElement(predNode);
if (ProcessUtil.checkCircular(
(WorkBreakdownElement) target,
(WorkBreakdownElement) pred,
allElements)) {
return errMsg_SamePredAndSucc;
}
}
}
} else {
if (checkSyncBarCircularLooop((ActivityNode)sourceElement,
(ActivityNode) targetElement) != null) {
return errMsg_CanNotConnect;
}
// should not allow incoming connection to start node
// and FreeText.
if (targetElement instanceof InitialNode) {
return errMsg_CanNotConnect;
}
Collection<ActivityNode> srcNodes = new ArrayList<ActivityNode>();
BridgeHelper.getPredecessorNodes(srcNodes, (ActivityNode) sourceElement);
if (!srcNodes.isEmpty()) {
Collection<ActivityNode> targetNodes = new ArrayList<ActivityNode>();
BridgeHelper.getSuccessorNodes(targetNodes,
(ActivityNode) target);
for (ActivityNode node : targetNodes) {
NodeAdapter nodeAdapter = BridgeHelper
.getNodeAdapter(node);
if (nodeAdapter.isTargetReadOnly() && !isConnectionToReadOnlyTargetAllowed())
return errMsg_CanNotConnect;
WorkBreakdownElement succ = (WorkBreakdownElement) nodeAdapter
.getElement();
for (ActivityNode prednode : srcNodes) {
NodeAdapter predAdapter = BridgeHelper
.getNodeAdapter(prednode);
WorkBreakdownElement pred = (WorkBreakdownElement) predAdapter
.getElement();
if (ProcessUtil.checkCircular(succ, pred,
allElements)) {
return errMsg_SamePredAndSucc;
}
}
}
}
}
}
}
}
return null;
}
/*
* To avoid circular looping between synchronization bars. If SycnBar1 ->
* SyncBar2 (connected) then SyncBar2 -> SyncBar1 cannot connect. if
* syncbar1 -> syncbar2->syncbar3 then syncbar3 -> syncbar1 disallowed.
*
*/
public static String checkSyncBarCircularLooop(ActivityNode sNode,
ActivityNode tNode) {
List list = tNode.getOutgoings();
if (!list.isEmpty() && list.size() > 0) {
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
ActivityEdge link = (ActivityEdge) iterator.next();
ActivityNode typednode = link.getTarget();
if (sNode.equals(typednode))
return errMsg_CanNotConnect;
if (typednode instanceof ControlNode) {
return checkSyncBarCircularLooop(sNode,
(ControlNode)typednode);
}
}
}
return null;
}
/*
* Method to Check if SyncBar inComming connections have
* any source is a readonly.
* @return
*/
public static String checkSyncBarIncomingLinks(ActivityNode typedNode){
for (Iterator iter = typedNode.getIncomings().iterator(); iter.hasNext();) {
ActivityEdge link = (ActivityEdge) iter.next();
ActivityNode source = link.getSource();
NodeAdapter adapter = BridgeHelper.getNodeAdapter(source);
if (adapter != null) {
if(adapter.getElement() instanceof WorkBreakdownElement){
if(adapter.isTargetReadOnly())
return errMsg_CanNotDelete;
}else if(source instanceof ControlNode){
if(source instanceof ForkNode || source instanceof JoinNode)
if(checkSyncBarIncomingLinks(source)!= null)
return errMsg_CanNotDelete;
}
}
}
return null;
}
/*
*Method to check if synchronization bar outgoing connection
*has any target is readonly.
*/
public static String checkSyncBarOutgoingLinks(ActivityNode typedNode){
for (Iterator iter = typedNode.getOutgoings().iterator(); iter.hasNext();) {
ActivityEdge link = (ActivityEdge) iter.next();
ActivityNode target = link.getTarget();
NodeAdapter adapter = BridgeHelper.getNodeAdapter(target);
if (adapter != null) {
if(adapter.getElement() instanceof WorkBreakdownElement){
if(adapter.isTargetReadOnly())
return errMsg_CanNotDelete;
}else if(target instanceof ControlNode){
if(target instanceof ForkNode || target instanceof JoinNode)
if(checkSyncBarOutgoingLinks(target)!= null)
return errMsg_CanNotDelete;
}
}
}
return null;
}
public static String checkDelete(Node node) {
if (BridgeHelper.isReadOnly(node))
return errMsg_CanNotDelete;
Diagram diagram = node.getDiagram();
if (diagram instanceof ActivityDiagram) {
if (node.getElement() instanceof ControlNode) {
Collection<ActivityNode> targetNodes = new ArrayList<ActivityNode>();
BridgeHelper.getSuccessorNodes(targetNodes, (ActivityNode)node.getElement());
for (ActivityNode targetNode : targetNodes) {
NodeAdapter adapter = BridgeHelper.getNodeAdapter(targetNode);
if(adapter != null && adapter.isTargetReadOnly()){
return errMsg_CanNotDelete;
}
}
}
} else if (diagram instanceof ActivityDetailDiagram) {
if (node.getElement() instanceof RoleNode) {
return errMsg_CanNotDelete;
}
}
return null;
}
public static String checkReconnect(Node source, Node target, Edge link) {
if (source.getDiagram() instanceof ActivityDiagram) {
if (link.getTarget() != null) {
if (BridgeHelper.isReadOnly(link.getTarget())) {
return errMsg_CanNotConnect;
} else {
if (link.getTarget() instanceof TypedNode) {
Collection targetNodes = GraphicalDataHelper
.getTargetNodes((TypedNode) link.getTarget(),
WorkBreakdownElementNode.class);
for (Iterator iter = targetNodes.iterator(); iter
.hasNext();) {
Node node = (Node) iter.next();
if (BridgeHelper.isReadOnly(node)) {
return errMsg_CanNotConnect;
}
}
}
}
}
}
return checkConnect(source, target, link);
}
/*
* Method will not allow multiple link between two nodes. Needs to call
* CreakLinkCommand and ReconnectLinkCommand. Any action on a Link should
* call this checkConnect(Node source, Node target, Link link) before allow
* to connect.
*/
public static String checkConnect(Node source, Node target, Edge link) {
if (source == target && !(BridgeHelper.getMethodElement(source) instanceof Activity)) {
return errMsg_CanNotConnect;
}
List links = source.getTargetEdges();
if (links != null) {
// This is need for reconnect to same target node.
if (links.contains(link)) {
if (link.getTarget().equals(target)) {
return null;
}
}
// This is need for new connect and reconnect to different target node.
for (Iterator iter = links.iterator(); iter.hasNext();) {
Link linkx = (Link) iter.next();
Object linkxtarget = linkx.getTarget();
if (linkxtarget != null && linkxtarget.equals(target)) {
return errMsg_CanNotConnect;
}
}
}
return checkConnect(source.getDiagram(), source.getElement(), target.getElement());
}
private static boolean alwaysAllowed(Object sourceElement, Object targetElement) {
return sourceElement instanceof InitialNode
|| targetElement instanceof FinalNode
|| (sourceElement instanceof ControlNode && !BridgeHelper.isSynchBar((ControlNode)sourceElement))
|| (targetElement instanceof ControlNode && !BridgeHelper.isSynchBar((ControlNode)targetElement));
}
/*
* Checks if the given edge is a regular edge, means, it is not a special
* edge that is created in extended diagram with a inherited, read-only
* target.
*/
private static boolean isRegularEdge(Edge edge) {
MethodElement workOrder = BridgeHelper.getMethodElement(edge);
return workOrder == null || MethodElementPropertyHelper.getProperty(workOrder, MethodElementPropertyHelper.WORK_ORDER__SUCCESSOR) == null;
}
public static String checkDelete(Edge edge) {
View source = edge.getSource();
View target = edge.getTarget();
if(target != null){
Diagram diagram = target.getDiagram();
switch(DiagramHelper.getDiagramType(diagram)){
case IDiagramManager.ACTIVITY_DIAGRAM:
EObject sourceElement = source.getElement();
EObject targetElement = target.getElement();
if(alwaysAllowed(sourceElement, targetElement)) {
return null;
}
if(BridgeHelper.isInherited(edge)) {
return errMsg_CanNotDelete;
} else if(BridgeHelper.isReadOnly(source) && BridgeHelper.isReadOnly(target)) {
// check if this edge is a custom one
//
MethodElement me = BridgeHelper.getMethodElement(edge);
if(me instanceof WorkOrder && ProcessUtil.isCustomWorkOrder((WorkOrder) me)) {
return null;
} else {
// edge is automatically created for 2 inherited
// predecessor/successor and cannot be deleted
//
return errMsg_CanNotDelete;
}
}
// Since making connection to a inherited, read-only target is
// allowed now, the check logic below is no longer needed. We
// only need to prevented inherited connections and nodes from
// deletion.
// // check if this edge is representing any work order
// //
// if (sourceElement instanceof ActivityNode
// && BridgeHelper.getMethodElement(sourceElement) == null) {
// Collection<ActivityNode> actNodes = new ArrayList<ActivityNode>();
// BridgeHelper.getSourceNodes(actNodes,
// (ActivityNode) sourceElement,
// WorkBreakdownElement.class);
// if (actNodes.isEmpty()) {
// return null;
// }
// }
//
// // the edge does represent a work order
//
// if(BridgeHelper.isReadOnly(target)) {
// if(BridgeHelper.getMethodElement(sourceElement) instanceof WorkBreakdownElement)
// return errMsg_CanNotDelete;
// }
//
// if(targetElement instanceof ActivityNode) {
// NodeAdapter adapter = BridgeHelper.getNodeAdapter(targetElement);
// if(adapter != null && adapter.getElement() instanceof WorkBreakdownElement) {
// return null;
// }
//// // target does not represent a work breakdown element
//// // disallow deletion of the edge if one of the target nodes of the target is read-only or inherited
//// //
//// Collection<ActivityNode> actNodes = new ArrayList<ActivityNode>();
//// BridgeHelper.getSuccessorNodes(actNodes, (ActivityNode) targetElement);
//// for (ActivityNode activityNode : actNodes) {
//// View view = BridgeHelper.getView(diagram, activityNode);
//// if(view != null && BridgeHelper.isReadOnly(view)) {
//// return errMsg_CanNotDelete;
//// }
//// }
//
// if(BridgeHelper.isSynchBar((ActivityNode)targetElement)) {
// return checkSyncBarOutgoingLinks((ActivityNode)targetElement);
// }
//
// }
return null;
case IDiagramManager.ACTIVITY_DETAIL_DIAGRAM:
return errMsg_CanNotDelete;
case IDiagramManager.WORK_PRODUCT_DEPENDENCY_DIAGRAM:
if(BridgeHelper.isReadOnly(target))
return errMsg_CanNotDelete;
return null;
}
}
return null;
}
/**
* Check to see link is readonly.
*/
public static boolean isReadOnly(Edge edge) {
View target = edge.getTarget();
View source = edge.getSource();
if (target != null
&& target.getDiagram().getElement() instanceof ActivityDetailDiagram) {
if(source != null){
Object sourceElement = source.getElement();
if(sourceElement != null && sourceElement instanceof TaskNode
&& ((TaskNode)sourceElement).isReadOnly()){
return true;
}
}
if(target != null){
Object targetElement = target.getElement();
if(targetElement != null && targetElement instanceof TaskNode
&& ((TaskNode)targetElement).isReadOnly()){
return true;
}
}
} else {
return checkDelete(edge) != null;
}
return false;
}
/**
* Check if any duplicate links between two ends already exists.
*
* */
public static boolean isDuplicateRelationship(EObject source, EObject target) {
EObject container = source.eContainer();
if (container instanceof org.eclipse.uml2.uml.Activity) {
org.eclipse.uml2.uml.Activity activity = (org.eclipse.uml2.uml.Activity) container;
List<ActivityEdge> list = activity.getEdges();
for (Iterator<ActivityEdge> iter = list.iterator(); iter.hasNext();) {
ActivityEdge element = (ActivityEdge) iter.next();
if ((element.getSource() == source)
&& element.getTarget() == target) {
return true;
}
}
}
return false;
}
public static boolean hasVisibleTarget(ActivityNode source) {
List<ActivityEdge> list = source.getOutgoings();
if (list != null && list.size() >= 1) {
// ignore outgoing connections from invisible nodes
//
for (ActivityEdge edge : list) {
ActivityNode node = edge.getTarget();
NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node);
if(nodeAdapter != null) {
View view = nodeAdapter.getView();
if(view != null && view.isVisible()) {
return true;
}
}
}
}
return false;
}
public static boolean hasVisibleSource(ActivityNode target) {
List<ActivityEdge> list = target.getIncomings();
if (list != null && list.size() >= 1) {
// ignore incoming connection from invisible nodes
//
for (ActivityEdge conn : list) {
ActivityNode node = conn.getSource();
NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node);
if(nodeAdapter != null) {
View view = nodeAdapter.getView();
if(view != null && view.isVisible()) {
return true;
}
}
}
}
return false;
}
}