| /********************************************************************* |
| * Copyright (c) 2012, 2019 SAP SE |
| * |
| * 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 416708 - IdPattern canDirectEdit method disallows direct editing in case the id is set for the pictogram element |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| **********************************************************************/ |
| 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. |
| * @deprecated Use {@link IdPatternHelper.setId(container, id)} instead. |
| */ |
| protected void setId(PropertyContainer container, String id) { |
| IdPatternHelper.setId(container, 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)}. |
| * @deprecated Use {@link IdPatternHelper.getId(container)} instead. |
| */ |
| protected String getId(PropertyContainer container) { |
| return IdPatternHelper.getId(container); |
| } |
| |
| /** |
| * 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>. |
| * @deprecated Use {@link IdPatternHelper.findById(pictogramElement, |
| * idToFind)} instead. |
| */ |
| protected PropertyContainer findById(PictogramElement pictogramElement, String idToFind) { |
| return IdPatternHelper.findById(pictogramElement, idToFind); |
| } |
| |
| /** |
| * 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) { |
| } |
| } |