/**
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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.
*/
package gov.redhawk.core.graphiti.ui.diagram.patterns;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.graphiti.features.IReason;
import org.eclipse.graphiti.features.context.IDirectEditingContext;
import org.eclipse.graphiti.features.context.impl.UpdateContext;
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.AbstractPattern;
import org.eclipse.graphiti.pattern.config.IPatternConfiguration;
import gov.redhawk.core.graphiti.ui.util.DUtil;
import mil.jpeojtrs.sca.util.CollectionUtils;
public abstract class AbstractContainerPattern extends AbstractPattern {
public AbstractContainerPattern(IPatternConfiguration patternConfiguration) {
super(null);
}
/**
* Unless overridden, this method ensures consistency of the validation logic
* and associated error messages for Component, FindBy, and HostCollocation names.
*/
@Override
public String checkValueValid(String value, IDirectEditingContext context) {
return validate(getCheckValueValidName(), value);
}
/**
* If <code>getCreateName</code> does not return the desired name for an error
* message, this method can be overridden.
*/
protected String getCheckValueValidName() {
return getCreateName();
}
protected Map<EObject, UpdateAction> getChildrenToUpdate(ContainerShape containerShape, List< ? extends EObject> modelChildren) {
// Put the model children into a set for tracking
Set<EObject> expectedChildren = new HashSet<EObject>(modelChildren);
// Record the actions for each port shape or model stub
Map<EObject, UpdateAction> actions = new HashMap<EObject, UpdateAction>();
// First, check the existing shapes for removal or update
for (Shape child : containerShape.getChildren()) {
// Check the existence of the child business object, and try to remove it from the set of expected
// children. This lets us know if the shape should exist (remove returns true), and any objects still
// left in the set after checking need to be added
EObject bo = (EObject) getBusinessObjectForPictogramElement(child);
if (bo == null || !expectedChildren.remove(bo)) {
// Delete non-existent or stale (no longer contained in model) child
actions.put(child, UpdateAction.REMOVE);
} else {
// Ask the feature provider if the child needs an update
IReason reason = getFeatureProvider().updateNeeded(new UpdateContext(child));
if (reason.toBoolean()) {
actions.put(child, UpdateAction.UPDATE);
}
}
}
// Add shapes for missing children
for (EObject instantiation : expectedChildren) {
actions.put(instantiation, UpdateAction.ADD);
}
return actions;
}
protected void updateChildren(ContainerShape containerShape, Map<EObject, UpdateAction> actions) {
for (Map.Entry<EObject, UpdateAction> entry : actions.entrySet()) {
switch (entry.getValue()) {
case ADD:
DUtil.addShapeViaFeature(getFeatureProvider(), containerShape, entry.getKey());
break;
case REMOVE:
DUtil.removeShapeViaFeature(getFeatureProvider(), (Shape) entry.getKey());
break;
case UPDATE:
updatePictogramElement((PictogramElement) entry.getKey());
break;
default:
break;
}
}
}
/**
* Checks to see if the given String <code>value</code> is valid. Returns an error
* message if invalid and <code>null</code> if valid.
* @param valueType - should be capitalized (e.g. Component, Host Collocation, etc.)
* @param value
* @return error message
*/
public static String validate(String valueType, String value) {
if (value.length() < 1) {
return valueType + " Name must not be empty";
}
if (value.contains(" ")) {
return valueType + " Name must not include spaces";
}
if (value.contains("\n")) {
return valueType + " Name must not include line breaks";
}
// null means, that the value is valid
return null;
}
/**
* Nested helper class for sorting child shapes based on the order of their business objects in their original
* containing list.
*/
protected class BusinessObjectListComparator implements Comparator<Shape> {
private final List< ? > list;
public BusinessObjectListComparator(List< ? > list) {
this.list = list;
}
@Override
public int compare(Shape o1, Shape o2) {
return Integer.compare(getIndex(o1), getIndex(o2));
}
private int getIndex(Shape shape) {
Object bo = getBusinessObjectForPictogramElement(shape);
return list.indexOf(bo);
}
}
protected boolean isSortedByBusinessObject(EList<Shape> children, List< ? > list) {
return CollectionUtils.isSorted(children, new BusinessObjectListComparator(list));
}
protected boolean sortByBusinessObject(EList<Shape> children, List< ? > list) {
Comparator<Shape> comparator = new BusinessObjectListComparator(list);
if (!CollectionUtils.isSorted(children, comparator)) {
ECollections.sort(children, comparator);
return true;
}
return false;
}
/**
* Checks whether the provided pictogram element is linked to exactly the set of objects given. It is assumed that
* order does not matter and there are no duplicates.
*
* @param pictogramElement
* @param businessObjects
* @return true if the set of linked objects is exactly the set of business objects
*/
protected boolean isLinked(PictogramElement pictogramElement, List< ? > businessObjects) {
Object[] linkedObjects = getMappingProvider().getAllBusinessObjectsForPictogramElement(pictogramElement);
if (businessObjects.size() != linkedObjects.length) {
return false;
}
return businessObjects.containsAll(Arrays.asList(linkedObjects));
}
}