blob: 7d7c86c55aabbd0e7babe6e83e4c347fa6d90aa0 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.diagram.core.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.core.internal.commands.PersistElementCommand;
import org.eclipse.gmf.runtime.diagram.core.internal.util.MEditingDomainGetter;
import org.eclipse.gmf.runtime.emf.core.edit.MEditingDomain;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.emf.core.util.EObjectUtil;
import org.eclipse.gmf.runtime.emf.core.util.MetaModelUtil;
import org.eclipse.gmf.runtime.emf.core.util.ProxyUtil;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.gmf.runtime.notation.LayoutConstraint;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.Style;
import org.eclipse.gmf.runtime.notation.View;
/**
* provides different utility functions for the notation view
* @author mmostafa
*/
public class ViewUtil{
/**
* the append index, this is the index you should use to append a view
* to a container
*/
public static final int APPEND = -1;
/**
* create a list of View Adapters from a Notation View collection
* @param views a collection of Notation <code>View</code>s
* @return list of <code>EObjectAdapter</code>s
*/
public static List makeViewsAdaptable(Collection views){
List list = new ArrayList();
Iterator it = views.iterator();
while(it.hasNext()){
list.add(new EObjectAdapter((View)it.next()));
}
return list;
}
/**
* move the supplied view from, and all of its parents from the transient
* collections to the persisted collections. This Api will modify the model
* and make it dirty, it needs to run within a write action or unchecked
* operation.
* A view will get persisted if the following conditions are met
* <UL>
* <LI> this method is invoked inside an UNDO interval
* <LI> the supplied view is in a transient list or owned by a transient container
* </UL>
* @param view the <code>View</code> to persist
*/
public static void persistElement(View view) {
assert null != view: "null view in ViewUtil.persistElement";//$NON-NLS-1$
MEditingDomain editingDomain = MEditingDomainGetter.getMEditingDomain(view);
if (editingDomain.isUndoIntervalOpen() &&
!view.isMutable()) {
// get first view needs to get persisted
View viewToPersist = getViewToPersist(view);
if (viewToPersist!=null){
// now create a command to persisted the view and exectue it
PersistElementCommand pvc =
new PersistElementCommand(viewToPersist);
pvc.execute( new NullProgressMonitor() );
editingDomain.setCanRedoCurrentInterval( false );
CommandResult result = pvc.getCommandResult();
view = (View)result.getReturnValue();
}
}
}
/**
* helper method used to get the first view needs to be persisted,
* starting from the passed view, it could return the passed view
* itself if it is a transient view, other wise it will check its
* parent and so on ...
* @param view , view to start from
* @return first view needs to get persisted
*/
static private View getViewToPersist(View view) {
EObject container = view.eContainer();
// if the view had no container then it can not get persisted
if (container==null)
return null;
// now edges are special case, becuase they do not exist in the
// children lists, but in the edgs lists
if (view instanceof Edge){
Diagram dContainer = (Diagram)container;
// always make sure that the feature is set before calling get
// to avoid creating unwanted EList that will stay in the memory
// till the model is closed
if (dContainer.eIsSet(NotationPackage.eINSTANCE.getDiagram_TransientEdges()) &&
dContainer.getTransientEdges().size()>0)
return view;
else
return (getViewToPersist(dContainer));
}
else if (container instanceof View){
View vContainer = (View)container;
// always make sure that the feature is set before calling get
// to avoid creating unwanted EList that will stay in the memory
// till the model is closed
if (vContainer.eIsSet(NotationPackage.eINSTANCE.getView_TransientChildren()) &&
vContainer.getTransientChildren().size()>0)
return view;
else
return (getViewToPersist(vContainer));
}
return null;
}
/**
* Destroys the supplied view notational element and remove any references
* this class may have to it.
*
* @param view view to destroy
*/
public static void destroy(View view) {
if (view==null)
return;
List children = view.getChildren();
for ( int i = 0; i < children.size(); i++ ) {
View child = (View)children.get(i);
destroy(child);
}
EObjectUtil.destroy(view);
}
/**
* Returns the container view, or null if the container is not a view or null
* @param eObject a notation view
* @return the container <code>View</code>
*/
static public View getContainerView(View eObject) {
EObject container = eObject.eContainer();
if ( container instanceof View ) {
return (View)container;
}
return null;
}
/**
* inserts a child <code>View</code> in a container. the view will be inserted
* in the persisted collection if the <tt>persisted</tt> flag is <tt>true</tt>;
* otherwise it will be added to the transied collection.
* inserting a transient child does not dirty the model, inserting a persisted
* child will dirty the model
*
* @param containerView the view's container
* @param childView notation <code>View</Code> to insert
* @param index the view's position within the container's list
* @param persisted indicats the persisted state of the view
*
*/
public static void insertChildView(
View containerView,
View childView,
int index,
boolean persisted) {
if (persisted) {
insertPersistedElement(containerView,childView,index);
}
else {
insertTransientElement(containerView,childView);
}
}
/**
* inserts a child into the transient list, inserting a transient child
* does not dirty
* @param child , the child to insert
* @param container notational element's container
*/
static private void insertTransientElement(final View container,final View child) {
if (child instanceof Edge){
Diagram diagram = (Diagram)container;
diagram.insertEdge((Edge)child,false);
} else {
container.insertChild(child,false);
}
return;
}
/**
* inserts a child into the persisted list
* @param container the notational element's container
* @param child , the child to insert
* @param index the notational element's position within the container.
*/
static private void insertPersistedElement(View container,View child, int index) {
if (child instanceof Edge){
Diagram diagram = (Diagram)container;
if (index==-1)
diagram.insertEdge((Edge)child);
else
diagram.insertEdgeAt((Edge)child,index);
} else {
if (index==-1)
container.insertChild(child);
else
container.insertChildAt(child,index);
}
}
/**
* checks if the passed view is transient or exists in a transient branch
* @param view <code>View</code> to check
* @return true if transient otherwise false
*/
static public boolean isTransient(EObject view) {
EStructuralFeature sFeature = view.eContainingFeature();
// root element will have a null containing feature
if (sFeature==null)
return false;
if (sFeature.isTransient()){
return true;
}
EObject container = view.eContainer();
if (container!=null){
return isTransient(container);
}
return false;
}
/**
* gets a the first child in the passed <code>View</code> that had the same type as
* the passed semantic hint.
* @param view the view to search inside
* @param semanticHint the semantic hint to look for
* @return the found view or null if none is found
*/
public static View getChildBySemanticHint(View view,String semanticHint) {
for(Iterator children = view.getChildren().iterator();children.hasNext();) {
View child = (View)children.next();
if ( semanticHint.equals(child.getType())) {
return child ;
}
}
return null;
}
/**
* checks if the passed property is supported bythe passed view
* @param view the view to use for the search
* @param id the property to look for
* @return boolean <tt>true</tt> if supported otherwise <tt>false</tt>
*/
public static boolean isPropertySupported(View view,Object id) {
if(id instanceof String){
EStructuralFeature feature = (EStructuralFeature) MetaModelUtil.getElement((String)id);
if (feature != null) {
return isPropertySupported(view,feature, feature.getEContainingClass());
}
}
return false;
}
/**
* checks if the passed feature is supported by the passed view
* @param view the view to use for the search
* @param feature the feature to look for
* @return boolean <tt>true</tt> if supported otherwise <tt>false</tt>
*/
public static boolean isFeatureSupported(View view,EStructuralFeature feature) {
if (feature != null) {
return isPropertySupported(view,feature, feature.getEContainingClass());
}
return false;
}
/**
* checks if the passed feature is supported by the passed view
* @param view the view to use for the search
* @param feature the feature to look for
* @param featureClass the feature's <code>EClass</code>
* @return boolean <tt>true</tt> if supported otherwise <tt>false</tt>
*/
public static boolean isPropertySupported(View view,EStructuralFeature feature, EClass featureClass) {
// check if the id belongs to the view
if (view.getStyle(featureClass) != null)
return true;
if (view instanceof Node){
LayoutConstraint constraint = ((Node)view).getLayoutConstraint();
if (constraint != null && featureClass.isInstance(constraint))
return true;
}
// check if the id belongs to a style owned by the view
return featureClass.isInstance(view);
}
/**
* Returns the value of the property with the given id inside the passed view
* @param view the view to use to get the value
* @param id the id of the property to get
* @return the value of the property, or <code>null</code>
* @deprecated use {@link ViewUtil#getStructuralFeatureValue(View, EStructuralFeature)} instead
*/
static public final Object getPropertyValue(View view, Object id) {
if(id instanceof String){
EStructuralFeature feature = (EStructuralFeature) MetaModelUtil.getElement((String)id);
if (feature != null) {
return ViewUtil.getPropertyValue(view,feature, feature.getEContainingClass());
}
}
return null;
}
/**
* Returns the value of the passed feature inside the passed view
* @param view the view to use to get the value
* @param feature the feature to use
* @return the value of the property, or <code>null</code>
*/
static public final Object getStructuralFeatureValue(View view, EStructuralFeature feature) {
if (feature != null) {
return ViewUtil.getPropertyValue(view,feature, feature.getEContainingClass());
}
return null;
}
/**
* Returns the value of the featrue inside a specific EClass within the passed view
* @param view the view to use to get the value
* @param feature the featrue to use to get the value
* @param featureClass the <code>EClass</code> to use to get the feature
* @return the value of the feature, or <code>null</code>
*/
public static Object getPropertyValue(View view,EStructuralFeature feature, EClass featureClass) {
// check if the id belongs to a style owned by the view
Style style = view.getStyle(featureClass);
if (style != null)
return style.eGet(feature);
if (view instanceof Node){
LayoutConstraint constraint = ((Node)view).getLayoutConstraint();
if (constraint != null && featureClass.isInstance(constraint))
return constraint.eGet(feature);
}
// check if the id belongs to the view
if (featureClass.isInstance(view))
return view.eGet(feature);
return feature.getDefaultValue(); // for extra robustness
}
/**
* Sets the property with the given id if possible on the passed view
* to the passed value.
* @param view the view to set the value on
* @param id the id of the property being set
* @param value the value of the property being set
* @deprecated use {@link ViewUtil#setStructuralFeatureValue(View, EStructuralFeature, Object}} instead
*/
public static void setPropertyValue(View view,Object id, Object value) {
if(id instanceof String){
EStructuralFeature feature = (EStructuralFeature) MetaModelUtil.getElement((String)id);
if (feature != null) {
ViewUtil.setPropertyValue(view,feature, feature.getEContainingClass(), value);
return;
}
}
}
/**
* Sets the passed feature if possible on the passed view
* to the passed value.
* @param view the view to set the value on
* @param feature the feature to use
* @param value the value of the property being set
*/
public static void setStructuralFeatureValue(View view,EStructuralFeature feature,Object value) {
if (feature != null) {
ViewUtil.setPropertyValue(view,feature, feature.getEContainingClass(), value);
return;
}
}
/**
* Sets the passed featrue on the passed EClass inside the passed view
* to the new value if possible
* @param view the view to set the value on
* @param feature the feature to set
* @param featureClass <code> EClass </code> that owns the feature
* @param value the value of the feature being set
*/
public static void setPropertyValue(View view,EStructuralFeature feature, EClass featureClass, Object value) {
if( view == null )
return;
// check if the id belongs to a style owned by the view
Style style = view.getStyle(featureClass);
if (style != null) {
style.eSet(feature, value);
return;
}
if (view instanceof Node){
Node node = (Node)view;
LayoutConstraint constraint = node.getLayoutConstraint();
if (constraint != null & featureClass.isInstance(constraint)) {
constraint.eSet(feature, value);
return;
}
}
// check if the id belongs to the view
if (featureClass.isInstance(view)) {
view.eSet(feature, value);
return;
}
}
/**
* resolves the passed <code>View<code>'s semantic element, and returns it.
* If the semantic element is unresolvable the method will returns <code>null</code>
* @param view the view to use to get the semantic element
* @return the semanticelement or null if there is no semantic element or if it is unresolvable
*/
public static EObject resolveSemanticElement(View view) {
EObject element = view.getElement();
if (element!=null && element.eIsProxy())
return ProxyUtil.resolve(MEditingDomainGetter.getMEditingDomain(view), element);
return element;
}
/**
* resolves the passed element, and returns it.
* If the element is unresolvable the method will returns <code>null</code>
* @param the element to resolve
* @return the element or null if it is unresolvable
*/
public static EObject resolve(EObject object){
if (object!=null){
if (object.eIsProxy())
return ProxyUtil.resolve(MEditingDomainGetter.getMEditingDomain(object), object);
else
return object;
}
return null;
}
/**
* gets the <code>View</code>'s semantic element Class Id, this could be used to
* check the semantic element type
* @param view the owner of the semantic element
* @return the semantic element class Id
*/
public static String getSemanticElementClassId(View view) {
EObject element = view.getElement();
return element == null ? null : ProxyUtil.getProxyClassID(element);
}
/**
* gets all the <code>Edge</code>'s whose source is this view
* @param view the view to use
* @return List the edges list
*/
public static List getSourceConnections(View view) {
if (!view.eIsSet(NotationPackage.eINSTANCE.getView_SourceEdges()))
return Collections.EMPTY_LIST;
return view.getSourceEdges();
}
/**
* gets all the <code>Edge</code>'s whose target is this view
* @param view the view to use
* @return List the edges list
*/
public static List getTargetConnections(View view) {
if (!view.eIsSet(NotationPackage.eINSTANCE.getView_TargetEdges()))
return Collections.EMPTY_LIST;
return view.getTargetEdges();
}
/**
* return eClass Name of the view's semantic element, this method
* works only if the semantic element is a NameElement, otherwise
* it will return an Empty String
* @param view the view object
* @return the eClass name
*/
public static String getSemanticEClassName(View view){
EObject eObject = view.getElement();
if (eObject != null)
return ProxyUtil.getProxyClassID(eObject);
return ""; //$NON-NLS-1$
}
/**
* returns the unique GUID of the view
* @param view the view
* @return String the GUID of a view (constant)
*/
public static String getIdStr(View view) {
return EObjectUtil.getID( view );
}
/**
* reorders the child at the oldIndex to the newIndex
* @param container the view's container
* @param child the child to reposition
* @param newIndex (zero-based)
* @throws IndexOutOfBoundsException if index is out of bounds
* @throws IllegalArgumentException if the child is not contianed by the container,
* or if the new position is the <code>ViewUtil.APPEND</code>position
*/
static public void repositionChildAt(View container , View child, int newIndex) {
ViewUtil.persistElement(child);
if (child.eContainer() != container)
throw new IllegalArgumentException("child is not an existing child of the view"); //$NON-NLS-1$
if (newIndex == APPEND)
throw new IllegalArgumentException("append position is not allowed for reposition"); //$NON-NLS-1$
container.removeChild(child);
container.insertChildAt(child,newIndex);
}
/**
* returns the first child whose id matched the given id
* @param view the view to search in
* @param idStr the child's id
* @return View the first matching child or null if no one was found
*/
static public View getChildByIdStr(View view, String idStr) {
for(Iterator children = view.getChildren().iterator();children.hasNext();) {
View child = (View)children.next();
if ( idStr.equals( EObjectUtil.getID(child))) {
return child;
}
}
return null;
}
}