blob: e129727595fad85692bd66c287d039f5201fe143 [file] [log] [blame]
/*******************************************************************************
* <copyright>
*
* Copyright (c) 2012, 2013 SAP AG.
* 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:
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 416708 - IdPattern canDirectEdit method disallows direct editing in case the id is set for the pictogram element
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern.id;
import org.eclipse.emf.common.util.EList;
import org.eclipse.graphiti.features.IReason;
import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.IDirectEditingContext;
import org.eclipse.graphiti.features.context.ILayoutContext;
import org.eclipse.graphiti.features.context.IUpdateContext;
import org.eclipse.graphiti.features.context.impl.LayoutContext;
import org.eclipse.graphiti.features.context.impl.UpdateContext;
import org.eclipse.graphiti.features.impl.AbstractDirectEditingFeature;
import org.eclipse.graphiti.features.impl.Reason;
import org.eclipse.graphiti.func.IDirectEditing;
import org.eclipse.graphiti.mm.Property;
import org.eclipse.graphiti.mm.PropertyContainer;
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.pattern.IPattern;
import org.eclipse.graphiti.pattern.TypedPattern;
import org.eclipse.graphiti.pattern.config.IPatternConfiguration;
import org.eclipse.graphiti.services.Graphiti;
/**
* Base class for ID patterns. The basic idea behind is to tag single parts of a
* pattern shape with IDs (using {@link Property} objects). These IDs are used
* to identify the parts of that shape and to call the update and layout methods
* for the shapes with IDs. Clients do not need to search through the shape
* hierarchy to find the shapes to update and layout.
* <p>
* Besides IDs this pattern base implementation also supports tagging
* {@link PictogramElement}s with an index property that allows to number a
* sequence of children using the same ID, e.g. a list of attributes inside a
* class.
*
* @since 0.10
* @experimental This API is in an experimental state and should be used by
* clients only with care, as it not final and can be removed or
* changed without prior notice!
*/
public abstract class IdPattern extends TypedPattern implements IPattern {
/**
* The property key that stores the information that a
* {@link PictogramElement} is the root object of a {@link IdPattern}
* subclass. The value is set by the {@link #add(IAddContext)} method.
*/
protected static final String PROPERTY_VALUE_PATTERN_TYPE_ID = "org.eclipse.graphiti.pattern.idpattern"; //$NON-NLS-1$
/**
* The property key that is used to tag individual {@link PictogramElement}s
* with a specific ID to identify them later during e.g. update or layout.
*/
protected static final String PROPERTY_KEY_ID = "org.eclipse.graphiti.pattern.id.id"; //$NON-NLS-1$
/**
* The property key that is used to tag individual {@link PictogramElement}s
* with a specific index beyond the ID to identify them later during e.g.
* update or layout. This can e.g. be used for lists of shapes like the
* attributes of a class.
*/
protected static final String PROPERTY_KEY_INDEX = "org.eclipse.graphiti.pattern.id.index"; //$NON-NLS-1$
/**
* Default constructor for a new IdPattern. Clients should call either call
* this or the constructor using a {@link IPatternConfiguration} instance
* from their subclass constructor.
*/
public IdPattern() {
super();
}
/**
* Constructor taking some pattern configuration data for the created
* IdPattern. Clients should call either call this or the null parameter
* constructor from their subclass constructor.
*
* @param patternConfiguration
* The configuration data to use
*/
public IdPattern(IPatternConfiguration patternConfiguration) {
super(patternConfiguration);
}
/*
* Base functionality
*/
/**
* Checks if the {@link PictogramElement} is controlled by the pattern. The
* default implementation simply checks if the domain object linked to the
* given {@link PictogramElement} is the one that is controlled by this
* pattern, see {@link #isMainBusinessObjectApplicable(Object)}. Especially
* the default implementation does not traverse up the hierarchy to find a
* suitable parent.
*
* @param pictogramElement
* The {@link PictogramElement} to check
* @return <code>true</code> in case the pattern controls the given
* {@link PictogramElement}, <code>false</code> otherwise.
*/
@Override
protected boolean isPatternControlled(PictogramElement pictogramElement) {
Object domainObject = getBusinessObjectForPictogramElement(pictogramElement);
return isMainBusinessObjectApplicable(domainObject);
}
/**
* Checks if the given {@link PictogramElement} is the root object of this
* pattern. The default implementation checks if the domain object linked to
* the given {@link PictogramElement} is the one that is controlled by this
* pattern, see {@link #isMainBusinessObjectApplicable(Object)}. It also
* checks if the object is controlled by an ID pattern by checking the
* according property ({@link TypedPattern#PROPERTY_KEY_PATTERN_TYPE}).
*
* @param pictogramElement
* The {@link PictogramElement} to check
* @return <code>true</code> in case the given {@link PictogramElement} is
* the root shape of this pattern, <code>false</code> otherwise.
*/
@Override
protected boolean isPatternRoot(PictogramElement pictogramElement) {
Object domainObject = getBusinessObjectForPictogramElement(pictogramElement);
if (!isMainBusinessObjectApplicable(domainObject)) {
return false;
}
String patternTypePropertyValue = Graphiti.getPeService().getPropertyValue(pictogramElement,
PROPERTY_KEY_PATTERN_TYPE);
return PROPERTY_VALUE_PATTERN_TYPE_ID.equals(patternTypePropertyValue);
}
/*
* ID handling
*/
/**
* Set the ID property ({@link #PROPERTY_KEY_ID}) for the given
* {@link PictogramElement}; it can be any {@link PropertyContainer}
* ,especially {@link Shape}s or {@link GraphicsAlgorithm}s are allowed. The
* used ID string can later be used to identify the shape, e.g. in the
* update or layout methods.
*
* @param container
* The {@link PictogramElement} to set the ID property for
* @param id
* The {@link String} ID to set.
*/
protected void setId(PropertyContainer container, String id) {
Graphiti.getPeService().setPropertyValue(container, PROPERTY_KEY_ID, id);
}
/**
* Returns any ID that has been set for the given {@link PictogramElement};
* it can be any {@link PropertyContainer}, especially {@link Shape}s or
* {@link GraphicsAlgorithm}s are allowed.
*
* @param container
* The {@link PictogramElement} to get the ID property from
* @return A {@link String} representing the value of the property or
* <code>null</code> in case the property is not set, see
* {@link #setId(PropertyContainer, String)}.
*/
protected String getId(PropertyContainer container) {
EList<Property> properties = container.getProperties();
for (Property property : properties) {
if (PROPERTY_KEY_ID.equals(property.getKey())) {
return property.getValue();
}
}
return null;
}
/**
* Searches for a {@link PictogramElement} that has the given ID starting
* from the given {@link PictogramElement}. First the given element is
* checked, then its {@link GraphicsAlgorithm}; after that the
* {@link PictogramElement} children are checked recursively and last the
* {@link GraphicsAlgorithm} children also recursively. The first
* {@link PictogramElement} that has the given ID is returned, in case none
* is found in the tree spanned by the given {@link PictogramElement},
* <code>null</code> is returned.
*
* @param pictogramElement
* The {@link PictogramElement} at which the search shall start,
* any {@link Shape}s or {@link GraphicsAlgorithm}s on top of
* this element are ignored.
* @param idToFind
* A {@link String} representing the ID to search for
* @return The {@link PictogramElement} that has the given ID property, in
* case none id found <code>null</code>.
*/
protected PropertyContainer findById(PictogramElement pictogramElement, String idToFind) {
if (idToFind == null || idToFind.length() == 0) {
return null;
}
// Check id for PE
String id = getId(pictogramElement);
if (idToFind.equals(id)) {
return pictogramElement;
}
// Check id for GA
GraphicsAlgorithm graphicsAlgorithm = pictogramElement.getGraphicsAlgorithm();
id = getId(graphicsAlgorithm);
if (idToFind.equals(id)) {
return graphicsAlgorithm;
}
// Check children of PE
if (pictogramElement instanceof ContainerShape) {
EList<Shape> children = ((ContainerShape) pictogramElement).getChildren();
for (Shape shape : children) {
PropertyContainer propertyContainer = findById(shape, idToFind);
if (propertyContainer != null) {
return propertyContainer;
}
}
}
// Check children of GA
PropertyContainer propertyContainer = findByIdInGraphicsAlgorithmChildren(graphicsAlgorithm, idToFind);
if (propertyContainer != null) {
return propertyContainer;
}
return null;
}
private PropertyContainer findByIdInGraphicsAlgorithmChildren(GraphicsAlgorithm graphicsAlgorithm, String idToFind) {
EList<GraphicsAlgorithm> graphicsAlgorithmChildren = graphicsAlgorithm.getGraphicsAlgorithmChildren();
for (GraphicsAlgorithm graphicsAlgorithmChild : graphicsAlgorithmChildren) {
String id = getId(graphicsAlgorithmChild);
if (idToFind.equals(id)) {
return graphicsAlgorithmChild;
}
PropertyContainer propertyContainer = findByIdInGraphicsAlgorithmChildren(graphicsAlgorithmChild, idToFind);
if (propertyContainer != null) {
return propertyContainer;
}
}
return null;
}
/**
* Set the index property ({@link #PROPERTY_KEY_ID}) for the given
* {@link PictogramElement}; it can be any {@link PropertyContainer}
* ,especially {@link Shape}s or {@link GraphicsAlgorithm}s are allowed. The
* used index can later - together with the ID string - be used to identify
* the concrete shape in case of a list of shapes, e.g. in the update or
* layout methods.
*
* @param container
* The {@link PictogramElement} to set the index property for
* @param id
* The {@link Integer} index to set.
*/
protected void setIndex(PropertyContainer container, int index) {
Graphiti.getPeService().setPropertyValue(container, PROPERTY_KEY_INDEX, Integer.toString(index));
}
/**
* Returns any index that has been set for the given
* {@link PictogramElement}; it can be any {@link PropertyContainer},
* especially {@link Shape}s or {@link GraphicsAlgorithm}s are allowed.
*
* @param container
* The {@link PictogramElement} to get the index property from
* @return An {@link Integer} representing the value of the property or -1
* in case the property is not set, see
* {@link #setIndex(PropertyContainer, int)}.
*/
protected int getIndex(PropertyContainer container) {
EList<Property> properties = container.getProperties();
for (Property property : properties) {
if (PROPERTY_KEY_INDEX.equals(property.getKey())) {
return Integer.valueOf(property.getValue());
}
}
return -1;
}
/*
* Add functionality
*/
/**
* Checks if adding is possible using this pattern in the given context. The
* default implementation simply checks if the new object passed in the
* context is the main domain object for the pattern, see
* {@link #isMainBusinessObjectApplicable(Object)}.
*
* @param context
* An {@link IAddContext} describing the add operation.
* @return <code>true</code> in case adding is possible, <code>false</code>
* otherwise.
*/
@Override
public boolean canAdd(IAddContext context) {
return isMainBusinessObjectApplicable(context.getNewObject());
}
/**
* Adds a {@link PictogramElement} representation for the given context to
* the diagram. The default implementation delegates to
* {@link #doAdd(IAddContext)} (which clients should primarily override) and
* updates and layouts the returned {@link PictogramElement} afterwards.
*
* @param context
* An {@link IAddContext} describing the add operation.
* @return The root object of the created {@link PictogramElement} tree.
*/
@Override
public PictogramElement add(IAddContext context) {
PictogramElement pictogramElement = doAdd(context);
setPatternType(pictogramElement, PROPERTY_VALUE_PATTERN_TYPE_ID);
update(new UpdateContext(pictogramElement));
layout(new LayoutContext(pictogramElement));
return pictogramElement;
}
/**
* Clients should primarily override this method and implement their add
* functionality here. This method is called from within
* {@link #add(IAddContext)}.
*
* @param context
* An {@link IAddContext} describing the add operation.
* @return The root object of the created {@link PictogramElement} tree.
*/
abstract protected PictogramElement doAdd(IAddContext context);
/*
* Layout functionality
*/
/**
* Checks if layouting a shape is possible using this pattern in the given
* context. The default implementation simply checks if the object passed in
* the context is the main domain object for the pattern, see
* {@link #isMainBusinessObjectApplicable(Object)} and if the
* {@link PictogramElement} given in the context is controlled by an ID
* pattern.
*
* @param context
* An {@link ILayoutContext} describing the layout operation.
* @return <code>true</code> in case layouting is possible,
* <code>false</code> otherwise.
*/
@Override
public boolean canLayout(ILayoutContext context) {
return PROPERTY_VALUE_PATTERN_TYPE_ID.equals(getPatternType(context.getPictogramElement()))
&& isMainBusinessObjectApplicable(getBusinessObjectForPictogramElement(context.getPictogramElement()));
}
/**
* Layouts a {@link PictogramElement} representation given in the context.
* The default implementation delegates to
* {@link #layout(IdLayoutContext, String)} (which clients should primarily
* override) to actually update individual {@link PictogramElement}s. This
* delegation is done for any {@link PictogramElement} tagged with an ID in
* the following order:
* <ul>
* <li>The {@link PictogramElement} given in the context itself</li>
* <li>The {@link GraphicsAlgorithm} of the {@link PictogramElement} in the
* context</li>
* <li>The {@link PictogramElement} children of the {@link PictogramElement}
* in the context</li>
* <li>The {@link GraphicsAlgorithm} children of the
* {@link GraphicsAlgorithm} of the {@link PictogramElement} in the context.
* For this the method
* {@link #layoutGraphicsAlgorithmChildren(GraphicsAlgorithm, IdLayoutContext)}
* is called which in term calles itself and this method recursively.</li>
* </ul>
*
* @param context
* An {@link ILayoutContext} describing the layout operation.
* @return <code>true</code> in case the layout operation did changes to the
* diagram, <code>false</code> otherwise.
*/
@Override
public boolean layout(ILayoutContext context) {
boolean changesDone = false;
PictogramElement rootPictogramElement;
if (context instanceof IdLayoutContext) {
rootPictogramElement = ((IdLayoutContext) context).getRootPictogramElement();
} else {
rootPictogramElement = context.getPictogramElement();
while (!isPatternRoot(rootPictogramElement)) {
if (rootPictogramElement instanceof Shape) {
ContainerShape container = ((Shape) rootPictogramElement).getContainer();
if (container != null) {
rootPictogramElement = container;
} else {
break;
}
} else {
if (rootPictogramElement instanceof GraphicsAlgorithm) {
PictogramElement pictogramElement = ((GraphicsAlgorithm) rootPictogramElement)
.getPictogramElement();
if (pictogramElement != null) {
rootPictogramElement = pictogramElement;
} else {
break;
}
}
}
}
}
// Check id for PE
PictogramElement pictogramElement = context.getPictogramElement();
String id = getId(pictogramElement);
if (id != null) {
IdLayoutContext layoutContext = new IdLayoutContext(pictogramElement,
pictogramElement.getGraphicsAlgorithm(), rootPictogramElement);
if (layout(layoutContext, id)) {
changesDone = true;
}
}
// Check id for GA
GraphicsAlgorithm graphicsAlgorithm = pictogramElement.getGraphicsAlgorithm();
id = getId(graphicsAlgorithm);
if (id != null) {
IdLayoutContext layoutContext = new IdLayoutContext(graphicsAlgorithm.getPictogramElement(),
graphicsAlgorithm, rootPictogramElement);
if (layout(layoutContext, id)) {
changesDone = true;
}
}
// Check children of PE
if (pictogramElement instanceof ContainerShape) {
EList<Shape> children = ((ContainerShape) pictogramElement).getChildren();
for (Shape shape : children) {
LayoutContext idContext = new IdLayoutContext(shape, shape.getGraphicsAlgorithm(), rootPictogramElement);
if (layout(idContext)) {
changesDone = true;
}
}
}
// Check children of GA
IdLayoutContext layoutContext;
if (context instanceof IdLayoutContext) {
layoutContext = (IdLayoutContext) context;
} else {
layoutContext = new IdLayoutContext(null, graphicsAlgorithm, rootPictogramElement);
}
if (layoutGraphicsAlgorithmChildren(graphicsAlgorithm, layoutContext)) {
changesDone = true;
}
return changesDone;
}
/**
* This method implements the part of the layout that deals with the
* {@link GraphicsAlgorithm} children, see {@link #layout(IdLayoutContext)}.
* Clients should primarily override
* {@link #layout(IdLayoutContext, String)} which is called for all found
* {@link PictogramElement}s and {@link GraphicsAlgorithm}s as described
* above that have an ID.
*
* @param graphicsAlgorithm
* The {@link GraphicsAlgorithm} to layout.
* @param context
* An {@link ILayoutContext} describing the layout operation.
* @return <code>true</code> in case the layout operation did changes to the
* diagram, <code>false</code> otherwise.
*/
protected boolean layoutGraphicsAlgorithmChildren(GraphicsAlgorithm graphicsAlgorithm, IdLayoutContext context) {
boolean changesDone = false;
EList<GraphicsAlgorithm> graphicsAlgorithmChildren = graphicsAlgorithm.getGraphicsAlgorithmChildren();
for (GraphicsAlgorithm graphicsAlgorithmChild : graphicsAlgorithmChildren) {
String id = getId(graphicsAlgorithmChild);
if (id != null) {
IdLayoutContext layoutContext = new IdLayoutContext(graphicsAlgorithmChild.getPictogramElement(),
graphicsAlgorithmChild, context.getRootPictogramElement());
if (layout(layoutContext, id)) {
changesDone = true;
}
}
if (layoutGraphicsAlgorithmChildren(graphicsAlgorithmChild, context)) {
changesDone = true;
}
}
return changesDone;
}
/**
* Clients should primarily override this method and implement their layout
* functionality here. This method is called from within
* {@link #layout(ILayoutContext)} for each of the {@link PictogramElement}s
* and {@link GraphicsAlgorithm}s that have been tagged with an ID.
*
* @param context
* An {@link IdLayoutContext} describing the layout operation.
* @return <code>true</code> in case the layout operation did changes to the
* diagram, <code>false</code> otherwise.
*/
abstract protected boolean layout(IdLayoutContext context, String id);
/*
* Update functionality
*/
/**
* Checks if updating a shape is possible using this pattern in the given
* context. The default implementation simply checks if the object passed in
* the context is the main domain object for the pattern, see
* {@link #isMainBusinessObjectApplicable(Object)} and if the
* {@link PictogramElement} given in the context is controlled by an ID
* pattern.
*
* @param context
* An {@link IUpdateContext} describing the update operation.
* @return <code>true</code> in case updating is possible,
* <code>false</code> otherwise.
*/
@Override
public boolean canUpdate(IUpdateContext context) {
return PROPERTY_VALUE_PATTERN_TYPE_ID.equals(getPatternType(context.getPictogramElement()))
&& isMainBusinessObjectApplicable(getBusinessObjectForPictogramElement(context.getPictogramElement()));
}
/**
* Checks if an updates is needed for a {@link PictogramElement}
* representation given in the context. The default implementation delegates
* to {@link #updateNeeded(IdUpdateContext, String)} (which clients should
* primarily override) to actually update individual
* {@link PictogramElement}s. This delegation is done for any
* {@link PictogramElement} tagged with an ID in the following order:
* <ul>
* <li>The {@link PictogramElement} given in the context itself</li>
* <li>The {@link GraphicsAlgorithm} of the {@link PictogramElement} in the
* context</li>
* <li>The {@link PictogramElement} children of the {@link PictogramElement}
* in the context</li>
* <li>The {@link GraphicsAlgorithm} children of the
* {@link GraphicsAlgorithm} of the {@link PictogramElement} in the context.
* For this the method
* {@link #updateNeededGraphicsAlgorithmChildren(GraphicsAlgorithm, IdUpdateContext)}
* is called which in term calls itself and this method recursively.</li>
* </ul>
*
* @param context
* An {@link IUpdateContext} describing the update operation.
* @return An {@link IReason} indicating <code>true</code> and a
* {@link String} reason in case the update operation is needed, an
* {@link IReason} indicating <code>false</code> otherwise.
*/
@Override
public IReason updateNeeded(IUpdateContext context) {
PictogramElement rootPictogramElement;
if (context instanceof IdUpdateContext) {
rootPictogramElement = ((IdUpdateContext) context).getRootPictogramElement();
} else {
rootPictogramElement = context.getPictogramElement();
}
// Check id for PE
PictogramElement pictogramElement = context.getPictogramElement();
String id = getId(pictogramElement);
if (id != null) {
IdUpdateContext updateContext = new IdUpdateContext(pictogramElement,
pictogramElement.getGraphicsAlgorithm(), rootPictogramElement,
getBusinessObjectForPictogramElement(pictogramElement));
IReason reason = updateNeeded(updateContext, id);
if (reason.toBoolean()) {
return reason;
}
}
// Check id for GA
GraphicsAlgorithm graphicsAlgorithm = pictogramElement.getGraphicsAlgorithm();
id = getId(graphicsAlgorithm);
if (id != null) {
PictogramElement element = graphicsAlgorithm.getPictogramElement();
IdUpdateContext updateContext = new IdUpdateContext(element, graphicsAlgorithm, rootPictogramElement,
getBusinessObjectForPictogramElement(element));
IReason reason = updateNeeded(updateContext, id);
if (reason.toBoolean()) {
return reason;
}
}
// Check children of PE
if (pictogramElement instanceof ContainerShape) {
EList<Shape> children = ((ContainerShape) pictogramElement).getChildren();
for (Shape shape : children) {
IdUpdateContext updateContext = new IdUpdateContext(shape, shape.getGraphicsAlgorithm(),
rootPictogramElement, getBusinessObjectForPictogramElement(shape));
IReason reason = updateNeeded(updateContext);
if (reason.toBoolean()) {
return reason;
}
}
}
// Check children of GA
IdUpdateContext updateContext;
if (context instanceof IdUpdateContext) {
updateContext = (IdUpdateContext) context;
} else {
updateContext = new IdUpdateContext(null, graphicsAlgorithm, rootPictogramElement, null);
}
IReason reason = updateNeededGraphicsAlgorithmChildren(graphicsAlgorithm, updateContext);
if (reason.toBoolean()) {
return reason;
}
return Reason.createFalseReason();
}
/**
* This method implements the part of the update needed check that deals
* with the {@link GraphicsAlgorithm} children, see
* {@link #updateNeeded(IUpdateContext)}. Clients should primarily override
* {@link #updateNeeded(IdUpdateContext, String)} which is called for all
* found {@link PictogramElement}s and {@link GraphicsAlgorithm}s as
* described above that have an ID.
*
* @param graphicsAlgorithm
* The {@link GraphicsAlgorithm} to perform the update check for.
* @param context
* An {@link IdUpdateContext} describing the update operation.
* @return An {@link IReason} indicating <code>true</code> and a
* {@link String} reason in case the update operation is needed, an
* {@link IReason} indicating <code>false</code> otherwise.
*/
protected IReason updateNeededGraphicsAlgorithmChildren(GraphicsAlgorithm graphicsAlgorithm, IdUpdateContext context) {
EList<GraphicsAlgorithm> graphicsAlgorithmChildren = graphicsAlgorithm.getGraphicsAlgorithmChildren();
for (GraphicsAlgorithm graphicsAlgorithmChild : graphicsAlgorithmChildren) {
String id = getId(graphicsAlgorithmChild);
if (id != null) {
PictogramElement pictogramElement = graphicsAlgorithmChild.getPictogramElement();
IdUpdateContext updateContext = new IdUpdateContext(pictogramElement, graphicsAlgorithmChild,
context.getRootPictogramElement(), getBusinessObjectForPictogramElement(pictogramElement));
IReason reason = updateNeeded(updateContext, id);
if (reason.toBoolean()) {
return reason;
}
}
IReason reason = updateNeededGraphicsAlgorithmChildren(graphicsAlgorithmChild, context);
if (reason.toBoolean()) {
return reason;
}
}
return Reason.createFalseReason();
}
/**
* Clients should primarily override this method and implement their update
* check functionality here. This method is called from within
* {@link #updateNeeded(IUpdateContext)} for each of the
* {@link PictogramElement}s and {@link GraphicsAlgorithm}s that have been
* tagged with an ID.
*
* @param context
* An {@link IdUpdateContext} describing the update operation.
* @return An {@link IReason} indicating <code>true</code> and a
* {@link String} reason in case the update operation is needed, an
* {@link IReason} indicating <code>false</code> otherwise.
*/
abstract protected IReason updateNeeded(IdUpdateContext context, String id);
/**
* Updates a {@link PictogramElement} representation given in the context.
* The default implementation only delegates to
* {@link #update(IUpdateContext, boolean)} setting the parameter innerCall
* to <code>false</code>.
*
* @param context
* An {@link IUpdateContext} describing the update operation.
* @return <code>true</code> in case the update operation did changes to the
* diagram, <code>false</code> otherwise.
*/
@Override
public boolean update(IUpdateContext context) {
return update(context, false);
}
/**
* Updates a {@link PictogramElement} representation given in the context
* recursively. The default implementation delegates to
* {@link #update(IdUpdateContext, String)} (which clients should primarily
* override) to actually update individual {@link PictogramElement}s. This
* delegation is done for any {@link PictogramElement} tagged with an ID in
* the following order:
* <ul>
* <li>The {@link PictogramElement} given in the context itself</li>
* <li>The {@link GraphicsAlgorithm} of the {@link PictogramElement} in the
* context</li>
* <li>The {@link PictogramElement} children of the {@link PictogramElement}
* in the context</li>
* <li>The {@link GraphicsAlgorithm} children of the
* {@link GraphicsAlgorithm} of the {@link PictogramElement} in the context.
* For this the method
* {@link #updateGraphicsAlgorithmChildren(GraphicsAlgorithm, IdUpdateContext)}
* is called which in term calls itself and this method recursively.</li>
* </ul>
*
* @param context
* An {@link IUpdateContext} describing the update operation.
* @return <code>true</code> in case the update operation did changes to the
* diagram, <code>false</code> otherwise.
*/
protected boolean update(IUpdateContext context, boolean innerCall) {
boolean result = false;
PictogramElement rootPictogramElement;
if (context instanceof IdUpdateContext) {
rootPictogramElement = ((IdUpdateContext) context).getRootPictogramElement();
} else {
rootPictogramElement = context.getPictogramElement();
}
// Check id for PE
PictogramElement pictogramElement = context.getPictogramElement();
String id = getId(pictogramElement);
if (id != null) {
IdUpdateContext updateContext = new IdUpdateContext(pictogramElement,
pictogramElement.getGraphicsAlgorithm(), rootPictogramElement,
getBusinessObjectForPictogramElement(pictogramElement));
if (update(updateContext, id)) {
result = true;
}
}
// Check id for GA
GraphicsAlgorithm graphicsAlgorithm = pictogramElement.getGraphicsAlgorithm();
id = getId(graphicsAlgorithm);
if (id != null) {
PictogramElement element = graphicsAlgorithm.getPictogramElement();
IdUpdateContext updateContext = new IdUpdateContext(element, graphicsAlgorithm, rootPictogramElement,
getBusinessObjectForPictogramElement(element));
if (update(updateContext, id)) {
result = true;
}
}
// Check children of PE
if (pictogramElement instanceof ContainerShape) {
EList<Shape> children = ((ContainerShape) pictogramElement).getChildren();
for (Shape shape : children) {
IdUpdateContext updateContext = new IdUpdateContext(shape, shape.getGraphicsAlgorithm(),
rootPictogramElement, getBusinessObjectForPictogramElement(shape));
if (update(updateContext, true)) {
result = true;
}
}
}
// Check children of GA
IdUpdateContext updateContext;
if (context instanceof IdUpdateContext) {
updateContext = (IdUpdateContext) context;
} else {
updateContext = new IdUpdateContext(null, graphicsAlgorithm, rootPictogramElement, null);
}
if (updateGraphicsAlgorithmChildren(graphicsAlgorithm, updateContext)) {
result = true;
}
if (result && !innerCall) {
layoutPictogramElement(rootPictogramElement);
}
return result;
}
/**
* This method implements the part of the update that deals with the
* {@link GraphicsAlgorithm} children, see
* {@link #update(IUpdateContext, boolean)}. Clients should primarily
* override {@link #update(IdUpdateContext, String)} which is called for all
* found {@link PictogramElement}s and {@link GraphicsAlgorithm}s as
* described above that have an ID.
*
* @param graphicsAlgorithm
* The {@link GraphicsAlgorithm} to update.
* @param context
* An {@link IdUpdateContext} describing the update operation.
* @return <code>true</code> in case the update operation did changes to the
* diagram, <code>false</code> otherwise.
*/
protected boolean updateGraphicsAlgorithmChildren(GraphicsAlgorithm graphicsAlgorithm, IdUpdateContext context) {
boolean result = false;
EList<GraphicsAlgorithm> graphicsAlgorithmChildren = graphicsAlgorithm.getGraphicsAlgorithmChildren();
for (GraphicsAlgorithm graphicsAlgorithmChild : graphicsAlgorithmChildren) {
String id = getId(graphicsAlgorithmChild);
if (id != null) {
PictogramElement pictogramElement = graphicsAlgorithmChild.getPictogramElement();
IdUpdateContext updateContext = new IdUpdateContext(pictogramElement, graphicsAlgorithmChild,
context.getRootPictogramElement(), getBusinessObjectForPictogramElement(pictogramElement));
if (update(updateContext, id)) {
result = true;
}
}
if (updateGraphicsAlgorithmChildren(graphicsAlgorithmChild, context)) {
result = true;
}
}
return result;
}
/**
* Clients should primarily override this method and implement their update
* functionality here. This method is called from within
* {@link #update(IUpdateContext, boolean)} for each of the
* {@link PictogramElement}s and {@link GraphicsAlgorithm}s that have been
* tagged with an ID.
*
* @param context
* An {@link IdUpdateContext} describing the update operation.
* @return <code>true</code> in case the update operation did changes to the
* diagram, <code>false</code> otherwise.
*/
abstract protected boolean update(IdUpdateContext context, String id);
/*
* Direct editing functionality
*/
/**
* The Graphiti framework calls this method to decide which UI to show up
* for direct editing. Corresponds to the method
* {@link AbstractDirectEditingFeature#getEditingType()}. The default
* implementation return {@link IDirectEditing#TYPE_NONE}, other valid type
* are defined by the TYPE_* constants in {@link IDirectEditing}.
*
* @return The desired editing type
*/
@Override
public int getEditingType() {
// TODO how to use different editing types for different parts of the
// pattern? No context to identify id...
return super.getEditingType();
}
/**
* Clients can override this method to indicate that the pattern allows
* direct editing for the shape described in the passed
* {@link IDirectEditingContext}, but the recommended method to override for
* {@link IdPattern} is
* {@link #canDirectEdit(IDirectEditingContext, String)}. Corresponds to the
* method
* {@link AbstractDirectEditingFeature#canDirectEdit(IDirectEditingContext)}
* . The default implementation checks if the pattern is responsible for the
* shape and an ID is set; in that case it delegates to
* {@link #canDirectEdit(IDirectEditingContext, String)}.
*
* @param context
* A context object describing the direct edit request.
* @return <code>true</code> in case direct editing shall be allowed,
* <code>false</code> otherwise.
*/
@Override
public boolean canDirectEdit(IDirectEditingContext context) {
PictogramElement pictogramElement = context.getPictogramElement();
Object domainObject = getBusinessObjectForPictogramElement(pictogramElement);
boolean patternResponsible = PROPERTY_VALUE_PATTERN_TYPE_ID.equals(getPatternType(pictogramElement))
&& isMainBusinessObjectApplicable(domainObject);
if (patternResponsible) {
String id = getId(pictogramElement);
if (id == null) {
id = getId(context.getGraphicsAlgorithm());
}
if (id != null) {
return canDirectEdit(context, id);
}
}
return false;
}
/**
* Clients can override this method to indicate that the pattern allows
* direct editing for the shape described in the passed
* {@link IDirectEditingContext} holding the given ID. Corresponds to the
* method
* {@link AbstractDirectEditingFeature#canDirectEdit(IDirectEditingContext)}
* . The default implementation simply returns <code>false</code>.
*
* @param context
* A context object describing the direct edit request.
* @param id
* The ID to check direct editing for
* @return <code>true</code> in case direct editing shall be allowed,
* <code>false</code> otherwise.
*/
protected boolean canDirectEdit(IDirectEditingContext context, String id) {
return false;
}
/**
* Provides the initial value for display in the newly opened text editing
* UI component. Corresponds to the method
* {@link AbstractDirectEditingFeature#getInitialValue(IDirectEditingContext)}
* . The default implementation checks if the pattern is responsible for the
* shape and an ID is set; in that case it delegates to
* {@link #getInitialValue(IDirectEditingContext, String)}.
*
* @param context
* A context object describing the direct edit request.
* @return The initial string value to be displayed for editing by the user.
*/
@Override
public String getInitialValue(IDirectEditingContext context) {
PictogramElement pictogramElement = context.getPictogramElement();
Object domainObject = getBusinessObjectForPictogramElement(pictogramElement);
boolean patternResponsible = PROPERTY_VALUE_PATTERN_TYPE_ID.equals(getPatternType(pictogramElement))
&& isMainBusinessObjectApplicable(domainObject);
if (patternResponsible) {
String id = getId(pictogramElement);
if (id == null) {
id = getId(context.getGraphicsAlgorithm());
}
if (id != null) {
return getInitialValue(context, id);
}
}
return "";
}
/**
* Provides the initial value for display in the newly opened text editing
* UI component. Corresponds to the method
* {@link AbstractDirectEditingFeature#getInitialValue(IDirectEditingContext)}
* . The default implementation always returns an empty string.
*
* @param context
* A context object describing the direct edit request.
* @param id
* The ID of the shape to get the initial value for
* @return The initial string value to be displayed for editing by the user.
*/
protected String getInitialValue(IDirectEditingContext context, String id) {
return "";
}
/**
* This method will be called by the framework to check if the passed String
* is valid as new value for the shape. This method's response time should
* be small since the method is queried after each change of the value in
* the direct edit UI. In case of a not valid value, the returned string
* shall indicate the reason why the value is not valid. Corresponds to the
* method
* {@link AbstractDirectEditingFeature#checkValueValid(String, IDirectEditingContext)}
* . The default implementation checks if the pattern is responsible for the
* shape and an ID is set; in that case it delegates to
* {@link #checkValueValid(String, IDirectEditingContext, String)}.
*
* @param value
* The new value to check
* @param context
* A context object describing the direct edit request.
* @return <code>null</code> in case of a valid value, a string describing
* the reason for being not valid otherwise.
*/
@Override
public String checkValueValid(String value, IDirectEditingContext context) {
PictogramElement pictogramElement = context.getPictogramElement();
Object domainObject = getBusinessObjectForPictogramElement(pictogramElement);
boolean patternResponsible = PROPERTY_VALUE_PATTERN_TYPE_ID.equals(getPatternType(pictogramElement))
&& isMainBusinessObjectApplicable(domainObject);
if (patternResponsible) {
String id = getId(pictogramElement);
if (id == null) {
id = getId(context.getGraphicsAlgorithm());
}
if (id != null) {
return checkValueValid(value, context, id);
}
}
return null;
}
/**
* This method will be called by the framework to check if the passed String
* is valid as new value for the shape. This method's response time should
* be small since the method is queried after each change of the value in
* the direct edit UI. The default implementation simply returns null to
* indicate that all values are valid. In case of a not valid value, the
* returned string shall indicate the reason why the value is not valid.
* Corresponds to the method
* {@link AbstractDirectEditingFeature#checkValueValid(String, IDirectEditingContext)}
* .
*
* @param value
* The new value to check
* @param context
* A context object describing the direct edit request.
* @param id
* The ID of the shape to check the value for
* @return <code>null</code> in case of a valid value, a string describing
* the reason for being not valid otherwise.
*/
protected String checkValueValid(String value, IDirectEditingContext context, String id) {
return null;
}
/**
* Set the new value after direct editing is finished. The value comes from
* the text editing UI component. Corresponds to the method
* {@link AbstractDirectEditingFeature#setValue(String, IDirectEditingContext)}
* . The default implementation checks if the pattern is responsible for the
* shape and an ID is set; in that case it delegates to
* {@link #setValue(String, IDirectEditingContext, String)}.
*
* @param value
* The new value to be set
* @param context
* A context object describing the direct edit request.
*/
@Override
public void setValue(String value, IDirectEditingContext context) {
PictogramElement pictogramElement = context.getPictogramElement();
Object domainObject = getBusinessObjectForPictogramElement(pictogramElement);
boolean patternResponsible = PROPERTY_VALUE_PATTERN_TYPE_ID.equals(getPatternType(pictogramElement))
&& isMainBusinessObjectApplicable(domainObject);
if (patternResponsible) {
String id = getId(pictogramElement);
if (id == null) {
id = getId(context.getGraphicsAlgorithm());
}
if (id != null) {
setValue(value, context, id);
}
}
}
/**
* Set the new value after direct editing is finished. The value comes from
* the text editing UI component. Corresponds to the method
* {@link AbstractDirectEditingFeature#setValue(String, IDirectEditingContext)}
* . The default implementation does nothing.
*
* @param value
* The new value to be set
* @param context
* A context object describing the direct edit request.
* @param id
* The ID of the shape to set the value for
*/
protected void setValue(String value, IDirectEditingContext context, String id) {
}
}