blob: 7b175e238c0eabdf6c8378660d4e0503a059144a [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2005, 2019 SAP SE and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* SAP SE - initial API, implementation and documentation
* mwenz - Bug 340627 - Features should be able to indicate cancellation
* mwenz - Bug 356218 - Added hasDoneChanges updates to update diagram feature
* and called features via editor command stack to check it
* mwenz - Bug 367204 - Correctly return the added PE inAbstractFeatureProvider's addIfPossible method
* fvelasco - Bug 391506 - Canceling a feature with OperationCanceledException provoked an extra undo
* pjpaulin - Bug 352120 - Modified diagram linking to avoid NullPointerException. This needs to be reviewed.
* mwenz - Bug 370888 - API Access to export and print
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.graphiti.features.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.graphiti.dt.IDiagramTypeProvider;
import org.eclipse.graphiti.features.IAddBendpointFeature;
import org.eclipse.graphiti.features.IAddFeature;
import org.eclipse.graphiti.features.ICreateConnectionFeature;
import org.eclipse.graphiti.features.ICreateFeature;
import org.eclipse.graphiti.features.IDeleteFeature;
import org.eclipse.graphiti.features.IDirectEditingFeature;
import org.eclipse.graphiti.features.IDirectEditingInfo;
import org.eclipse.graphiti.features.IFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.ILayoutFeature;
import org.eclipse.graphiti.features.IMoveAnchorFeature;
import org.eclipse.graphiti.features.IMoveBendpointFeature;
import org.eclipse.graphiti.features.IMoveConnectionDecoratorFeature;
import org.eclipse.graphiti.features.IMoveShapeFeature;
import org.eclipse.graphiti.features.IPrintFeature;
import org.eclipse.graphiti.features.IReason;
import org.eclipse.graphiti.features.IReconnectionFeature;
import org.eclipse.graphiti.features.IRemoveBendpointFeature;
import org.eclipse.graphiti.features.IRemoveFeature;
import org.eclipse.graphiti.features.IResizeShapeFeature;
import org.eclipse.graphiti.features.ISaveImageFeature;
import org.eclipse.graphiti.features.IUpdateFeature;
import org.eclipse.graphiti.features.context.IAddBendpointContext;
import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.ICustomContext;
import org.eclipse.graphiti.features.context.IDeleteContext;
import org.eclipse.graphiti.features.context.IDirectEditingContext;
import org.eclipse.graphiti.features.context.ILayoutContext;
import org.eclipse.graphiti.features.context.IMoveAnchorContext;
import org.eclipse.graphiti.features.context.IMoveBendpointContext;
import org.eclipse.graphiti.features.context.IMoveConnectionDecoratorContext;
import org.eclipse.graphiti.features.context.IMoveShapeContext;
import org.eclipse.graphiti.features.context.IPictogramElementContext;
import org.eclipse.graphiti.features.context.IReconnectionContext;
import org.eclipse.graphiti.features.context.IRemoveBendpointContext;
import org.eclipse.graphiti.features.context.IRemoveContext;
import org.eclipse.graphiti.features.context.IResizeShapeContext;
import org.eclipse.graphiti.features.context.IUpdateContext;
import org.eclipse.graphiti.features.custom.ICustomFeature;
import org.eclipse.graphiti.internal.ExternalPictogramLink;
import org.eclipse.graphiti.internal.services.GraphitiInternal;
import org.eclipse.graphiti.internal.util.T;
import org.eclipse.graphiti.mm.Property;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.PictogramLink;
import org.eclipse.graphiti.mm.pictograms.PictogramsFactory;
import org.eclipse.graphiti.platform.IDiagramBehavior;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.ILinkService;
/**
* The Class AbstractFeatureProvider.
*/
public abstract class AbstractFeatureProvider implements IFeatureProvider {
private static final ICustomFeature[] ZERO_CUSTOM_FEATURES = new ICustomFeature[0];
/**
* The Constant EMPTY_REF_OBJECTS.
*/
protected static final EObject[] NO_OBJECTS = new EObject[0];
/**
* The Constant EMPTY_PICTOGRAM_ELEMENTS.
*/
protected static final PictogramElement[] EMPTY_PICTOGRAM_ELEMENTS = new PictogramElement[0];
private IDiagramTypeProvider dtp;
private IDirectEditingInfo directEditingInfo = new DefaultDirectEditingInfo();
private IIndependenceSolver independenceSolver = null;
/**
* Creates a new {@link AbstractFeatureProvider}.
*
* @param diagramTypeProvider
* the diagram type provider
*/
public AbstractFeatureProvider(IDiagramTypeProvider diagramTypeProvider) {
super();
this.dtp = diagramTypeProvider;
}
public IAddFeature getAddFeature(IAddContext context) {
return null;
}
public ICreateConnectionFeature[] getCreateConnectionFeatures() {
return new ICreateConnectionFeature[0];
}
public ICreateFeature[] getCreateFeatures() {
return new ICreateFeature[0];
}
public ICustomFeature[] getCustomFeatures(ICustomContext context) {
return ZERO_CUSTOM_FEATURES;
}
public IDeleteFeature getDeleteFeature(IDeleteContext context) {
return null;
}
public IDiagramTypeProvider getDiagramTypeProvider() {
return this.dtp;
}
public IMoveAnchorFeature getMoveAnchorFeature(IMoveAnchorContext context) {
return null;
}
public IRemoveFeature getRemoveFeature(IRemoveContext context) {
return null;
}
public IUpdateFeature getUpdateFeature(IUpdateContext context) {
return null;
}
public ILayoutFeature getLayoutFeature(ILayoutContext context) {
return null;
}
public IMoveShapeFeature getMoveShapeFeature(IMoveShapeContext context) {
return null;
}
public IMoveConnectionDecoratorFeature getMoveConnectionDecoratorFeature(IMoveConnectionDecoratorContext context) {
return null;
}
public IMoveBendpointFeature getMoveBendpointFeature(IMoveBendpointContext context) {
return null;
}
public IResizeShapeFeature getResizeShapeFeature(IResizeShapeContext context) {
return null;
}
public IAddBendpointFeature getAddBendpointFeature(IAddBendpointContext context) {
return null;
}
public IRemoveBendpointFeature getRemoveBendpointFeature(IRemoveBendpointContext context) {
return null;
}
public IDirectEditingFeature getDirectEditingFeature(IDirectEditingContext context) {
return null;
}
public IReason canUpdate(IUpdateContext context) {
final String SIGNATURE = "canUpdate(IUpdateContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
IReason ret = Reason.createFalseReason();
IUpdateFeature updateFeature = getUpdateFeature(context);
if (updateFeature != null) {
boolean b = updateFeature.canUpdate(context);
ret = new Reason(b);
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
public IReason canLayout(ILayoutContext context) {
final String SIGNATURE = "canLayout(ILayoutContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
IReason ret = Reason.createFalseReason();
ILayoutFeature layoutFeature = getLayoutFeature(context);
if (layoutFeature != null) {
boolean b = layoutFeature.canLayout(context);
ret = new Reason(b);
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
public IReason updateIfPossible(IUpdateContext context) {
final String SIGNATURE = "updateIfPossible(IUpdateContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
boolean b = false;
IUpdateFeature updateFeature = getUpdateFeature(context);
if (updateFeature != null) {
IDiagramBehavior diagramBehavior = getDiagramTypeProvider().getDiagramBehavior();
diagramBehavior.executeFeature(updateFeature, context);
b = true;
}
IReason reason = new Reason(b);
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, reason);
}
return reason;
}
public IReason layoutIfPossible(ILayoutContext context) {
final String SIGNATURE = "layoutIfPossible(ILayoutContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
boolean b = false;
ILayoutFeature layoutSemanticsFeature = getLayoutFeature(context);
if (layoutSemanticsFeature != null) {
IDiagramBehavior diagramBehavior = getDiagramTypeProvider().getDiagramBehavior();
diagramBehavior.executeFeature(layoutSemanticsFeature, context);
b = true;
}
IReason res = new Reason(b);
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, res);
}
return res;
}
public IReason updateNeeded(IUpdateContext context) {
final String SIGNATURE = "updateNeeded(IUpdateContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
IReason ret = Reason.createFalseReason();
PictogramElement pe = context.getPictogramElement();
if (pe != null && GraphitiInternal.getEmfService().isObjectAlive(pe)) {
IUpdateFeature updateFeature = getUpdateFeature(context);
if (updateFeature != null) {
ret = updateFeature.updateNeeded(context);
}
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
public IReason updateIfPossibleAndNeeded(IUpdateContext context) {
final String SIGNATURE = "updateIfPossibleAndNeeded(IUpdateContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
IReason ret = canUpdate(context);
if (ret.toBoolean()) {
ret = updateNeeded(context);
if (ret.toBoolean()) {
ret = updateIfPossible(context);
}
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
public PictogramElement addIfPossible(IAddContext context) {
final String SIGNATURE = "addIfPossible(IAddContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
PictogramElement returnValue = null;
if (canAdd(context).toBoolean()) {
IAddFeature feature = getAddFeature(context);
IDiagramBehavior diagramBehavior = getDiagramTypeProvider().getDiagramBehavior();
Object result = diagramBehavior.executeFeature(feature, context);
if (result instanceof PictogramElement) {
returnValue = (PictogramElement) result;
}
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, returnValue);
}
return returnValue;
}
public IReason canAdd(IAddContext context) {
final String SIGNATURE = "canAdd(IAddContext)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { context });
}
IReason ret = Reason.createFalseReason();
IAddFeature feature = getAddFeature(context);
if (feature != null) {
boolean b = feature.canAdd(context);
ret = new Reason(b);
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
final public IDirectEditingInfo getDirectEditingInfo() {
return this.directEditingInfo;
}
public IReconnectionFeature getReconnectionFeature(IReconnectionContext context) {
return new DefaultReconnectionFeature(this);
}
public abstract IPrintFeature getPrintFeature();
public abstract ISaveImageFeature getSaveImageFeature();
public IFeature[] getDragAndDropFeatures(IPictogramElementContext context) {
return new IFeature[0];
}
private class StringTransformer {
private final static String marker = "__independentN"; //$NON-NLS-1$
String[] decode(String value) {
if (!value.startsWith(marker)) {
return new String[] { value };
} else {
value = value.substring(marker.length(), value.length());
return value.split(marker);
}
}
String encode(String[] segments) {
if (segments.length == 1) {
return segments[0];
}
StringBuffer sb = new StringBuffer();
for (String string : segments) {
sb.append(marker);
sb.append(string);
}
return sb.toString();
}
}
StringTransformer st = new StringTransformer();
public Object[] getAllBusinessObjectsForPictogramElement(PictogramElement pictogramElement) {
final String SIGNATURE = "getAllBusinessObjectsForPictogramElement(PictogramElement)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { pictogramElement });
}
Object[] ret = new Object[0];
List<Object> retList = new ArrayList<Object>();
if (getIndependenceSolver() != null) {
Property property = Graphiti.getPeService().getProperty(pictogramElement, ExternalPictogramLink.KEY_INDEPENDENT_PROPERTY);
if (property != null && property.getValue() != null) {
String value = property.getValue();
String[] values = getValues(value);
for (String v : values) {
retList.add(getIndependenceSolver().getBusinessObjectForKey(v));
}
}
}
EObject[] allBusinessObjectsForLinkedPictogramElement = getLinkService().getAllBusinessObjectsForLinkedPictogramElement(
pictogramElement);
for (EObject eObject : allBusinessObjectsForLinkedPictogramElement) {
retList.add(eObject);
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return retList.toArray(ret);
}
private String[] getValues(String value) {
if (value.length() == 0) {
return new String[0];
} else {
return st.decode(value);
}
}
public Object getBusinessObjectForPictogramElement(PictogramElement pictogramElement) {
final String SIGNATURE = "getBusinessObjectForPictogramElement(PictogramElement)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { pictogramElement });
}
Object ret = null;
if (getIndependenceSolver() != null) {
Property property = Graphiti.getPeService().getProperty(pictogramElement, ExternalPictogramLink.KEY_INDEPENDENT_PROPERTY);
if (property != null && property.getValue() != null) {
String[] values = getValues(property.getValue());
if (values.length > 0)
ret = getIndependenceSolver().getBusinessObjectForKey(values[0]);
if (ret != null) {
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
}
}
ret = getLinkService().getBusinessObjectForLinkedPictogramElement(pictogramElement);
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, ret);
}
return ret;
}
public PictogramElement[] getAllPictogramElementsForBusinessObject(Object businessObject) {
final String SIGNATURE = "getAllPictogramElementsForBusinessObject(Object)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { businessObject });
}
List<PictogramElement> retList = new ArrayList<PictogramElement>();
IIndependenceSolver solver = getIndependenceSolver();
if (solver != null) {
String keyForBusinessObject = solver.getKeyForBusinessObject(businessObject);
if (keyForBusinessObject != null) {
Collection<PictogramElement> allContainedPictogramElements = Graphiti.getPeService().getAllContainedPictogramElements(
getDiagramTypeProvider().getDiagram());
for (PictogramElement pe : allContainedPictogramElements) {
Property property = Graphiti.getPeService().getProperty(pe, ExternalPictogramLink.KEY_INDEPENDENT_PROPERTY);
if (property != null && Arrays.asList(getValues(property.getValue())).contains(keyForBusinessObject)) {
retList.add(pe);
}
}
}
}
if (businessObject instanceof EObject) {
Diagram diagram = getDiagramTypeProvider().getDiagram();
if (diagram != null) {
Collection<PictogramLink> pictogramLinks = diagram.getPictogramLinks();
for (PictogramLink pictogramLink : pictogramLinks) {
List<EObject> businessObjects = pictogramLink.getBusinessObjects();
for (EObject obj : businessObjects) {
if (getDiagramTypeProvider().getCurrentToolBehaviorProvider().equalsBusinessObjects(businessObject, obj)) {
PictogramElement pe = pictogramLink.getPictogramElement();
if (pe != null) {
retList.add(pe);
}
break;
}
}
}
}
}
PictogramElement[] res = retList.toArray(new PictogramElement[0]);
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, res);
}
return res;
}
/**
* Provides the first pictogram element which represents the given business
* object.
*
* @param businessObject
* the business object
* @return the pictogram element for business object
*/
public PictogramElement getPictogramElementForBusinessObject(Object businessObject) {
final String SIGNATURE = "getPictogramElementForBusinessObject(Object)"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { businessObject });
}
PictogramElement result = null;
if (businessObject instanceof EObject) {
Diagram diagram = getDiagramTypeProvider().getDiagram();
if (diagram != null) {
Collection<PictogramLink> pictogramLinks = diagram.getPictogramLinks();
for (PictogramLink pictogramLink : pictogramLinks) {
List<EObject> businessObjects = pictogramLink.getBusinessObjects();
for (EObject obj : businessObjects) {
if (getDiagramTypeProvider().getCurrentToolBehaviorProvider().equalsBusinessObjects(businessObject, obj)) {
PictogramElement pe = pictogramLink.getPictogramElement();
if (pe != null) {
result = pe;
}
break;
}
}
if (result != null) {
break;
}
}
}
} else {
IIndependenceSolver solver = getIndependenceSolver();
if (solver != null) {
String keyForBusinessObject = solver.getKeyForBusinessObject(businessObject);
if (keyForBusinessObject != null) {
Collection<PictogramElement> allContainedPictogramElements = Graphiti.getPeService().getAllContainedPictogramElements(
getDiagramTypeProvider().getDiagram());
for (PictogramElement pe : allContainedPictogramElements) {
Property property = Graphiti.getPeService().getProperty(pe, ExternalPictogramLink.KEY_INDEPENDENT_PROPERTY);
if (property != null && Arrays.asList(getValues(property.getValue())).contains(keyForBusinessObject)) {
result = pe;
break;
}
}
}
}
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE, result);
}
return result;
}
/**
* Check does there have pictogram element linked to this business object.
*
* @param businessObject
* the business object
* @return true when at least one pictogram element is linked, otherwise
* return false.
*/
public boolean hasPictogramElementForBusinessObject(Object businessObject) {
return getPictogramElementForBusinessObject(businessObject) != null;
}
public void link(PictogramElement pictogramElement, Object businessObject) {
link(pictogramElement, new Object[] { businessObject });
}
public void link(PictogramElement pictogramElement, Object[] businessObjects) {
final String SIGNATURE = "link(PictogramElement, Object[])"; //$NON-NLS-1$
boolean info = T.racer().info();
if (info) {
T.racer().entering(AbstractFeatureProvider.class, SIGNATURE, new Object[] { pictogramElement, businessObjects });
}
IIndependenceSolver is = getIndependenceSolver();
if (is == null) {
PictogramLink link = createOrGetPictogramLink(pictogramElement);
if (link != null) {
// remove currently linked BOs and add new BOs
link.getBusinessObjects().clear();
if (businessObjects != null) {
for (int i = 0; i < businessObjects.length; i++) {
EObject bo = (EObject) businessObjects[i];
/*
* TODO For our use case with DiagramComposite the
* resource was being returned as null. This is one area
* where I need some guidance from the Graphiti team.
*/
// Resource resource = bo.eResource();
// Assert.isNotNull(resource, " Business object " + bo + " is not contained in a resource"); //$NON-NLS-1$ //$NON-NLS-2$
// ResourceSet resourceSet = resource.getResourceSet();
// Assert.isNotNull(resourceSet, " Resource " + resource + " is not contained in a resource set"); //$NON-NLS-1$ //$NON-NLS-2$
// TransactionalEditingDomain editingDomain =
// getDiagramTypeProvider().getDiagramEditor().getEditingDomain();
// ResourceSet editorResourceSet =
// editingDomain.getResourceSet();
// if (!resourceSet.equals(editorResourceSet)) {
// URI boUri = EcoreUtil.getURI(bo);
// bo = editorResourceSet.getEObject(boUri, true);
// }
if (bo != null) {
link.getBusinessObjects().add(bo);
}
}
}
}
} else {
List<String> values = new ArrayList<String>();
for (Object bo : businessObjects) {
String propertyValue = is.getKeyForBusinessObject(bo);
if (propertyValue != null)
values.add(propertyValue);
}
String encodedValues = st.encode(values.toArray(new String[] {}));
Graphiti.getPeService().setPropertyValue(pictogramElement, ExternalPictogramLink.KEY_INDEPENDENT_PROPERTY, encodedValues);
}
if (info) {
T.racer().exiting(AbstractFeatureProvider.class, SIGNATURE);
}
}
private PictogramLink createPictogramLink(PictogramElement pe) {
PictogramLink ret = null;
Diagram diagram = getDiagramTypeProvider().getDiagram();
if (diagram != null) {
// create new link
ret = PictogramsFactory.eINSTANCE.createPictogramLink();
ret.setPictogramElement(pe);
// add new link to diagram
diagram.getPictogramLinks().add(ret);
}
return ret;
}
private PictogramLink createOrGetPictogramLink(PictogramElement pe) {
PictogramLink link = pe.getLink();
if (link == null) {
link = createPictogramLink(pe);
}
return link;
}
/**
* Gets the independence solver.
*
* @return the independence solver
*/
protected final IIndependenceSolver getIndependenceSolver() {
return this.independenceSolver;
}
/**
* Sets the independence solver.
*
* @param independenceSolver
* the new independence solver
*/
protected final void setIndependenceSolver(IIndependenceSolver independenceSolver) {
this.independenceSolver = independenceSolver;
}
public void dispose() {
}
protected ILinkService getLinkService() {
return Graphiti.getLinkService();
}
}