blob: 0c5aed94c75b9c8bf0d31a229e3a09fa8332a7c2 [file] [log] [blame]
/*
* Copyright (c) 2003, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*
* $Id: ElementOperations.java,v 1.7 2004/05/18 21:00:48 khussey Exp $
*/
package org.eclipse.uml2.internal.operation;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.AbstractTreeIterator;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EContentsEList;
import org.eclipse.emf.ecore.util.ECrossReferenceEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.uml2.Element;
import org.eclipse.uml2.Model;
import org.eclipse.uml2.util.UML2Validator;
import org.eclipse.uml2.UML2Plugin;
/**
* A static utility class that provides operations related to elements.
*/
public final class ElementOperations
extends UML2Operations {
protected static class ChangeableECrossReferenceEList
extends ECrossReferenceEList {
protected static class ChangeableFeatureIteratorImpl
extends ECrossReferenceEList.FeatureIteratorImpl {
public ChangeableFeatureIteratorImpl(EObject eObject) {
super(eObject);
}
public ChangeableFeatureIteratorImpl(EObject eObject,
EStructuralFeature[] eStructuralFeatures) {
super(eObject, eStructuralFeatures);
}
protected boolean isIncluded(EStructuralFeature eStructuralFeature) {
return super.isIncluded(eStructuralFeature)
&& eStructuralFeature.isChangeable();
}
protected boolean isIncludedEntry(
EStructuralFeature eStructuralFeature) {
return super.isIncludedEntry(eStructuralFeature)
&& eStructuralFeature.isChangeable();
}
}
protected static class ChangeableResolvingFeatureIteratorImpl
extends ChangeableFeatureIteratorImpl {
public ChangeableResolvingFeatureIteratorImpl(EObject eObject) {
super(eObject);
}
public ChangeableResolvingFeatureIteratorImpl(EObject eObject,
EStructuralFeature[] eStructuralFeatures) {
super(eObject, eStructuralFeatures);
}
protected boolean resolve() {
return true;
}
}
protected ChangeableECrossReferenceEList(EObject eObject) {
super(eObject);
}
protected ChangeableECrossReferenceEList(EObject eObject,
EStructuralFeature[] eStructuralFeatures) {
super(eObject, eStructuralFeatures);
}
protected boolean isIncluded(EStructuralFeature eStructuralFeature) {
return super.isIncluded(eStructuralFeature)
&& eStructuralFeature.isChangeable();
}
protected ListIterator newListIterator() {
return this.resolve()
? new ChangeableResolvingFeatureIteratorImpl(eObject,
eStructuralFeatures)
: new ChangeableFeatureIteratorImpl(eObject,
eStructuralFeatures);
}
public List basicList() {
return new ChangeableECrossReferenceEList(eObject,
eStructuralFeatures) {
protected boolean resolve() {
return false;
}
};
}
public Iterator basicIterator() {
if (eStructuralFeatures == null) {
return ChangeableFeatureIteratorImpl.EMPTY_ITERATOR;
}
return new ChangeableFeatureIteratorImpl(eObject,
eStructuralFeatures);
}
public ListIterator basicListIterator() {
if (eStructuralFeatures == null) {
return FeatureIteratorImpl.EMPTY_ITERATOR;
}
return new ChangeableFeatureIteratorImpl(eObject,
eStructuralFeatures);
}
public ListIterator basicListIterator(int index) {
if (eStructuralFeatures == null) {
if (index < 0 || index > 1) {
throw new IndexOutOfBoundsException("index = " + index //$NON-NLS-1$
+ ", size = 0"); //$NON-NLS-1$
}
return ChangeableFeatureIteratorImpl.EMPTY_ITERATOR;
}
ListIterator result = new ChangeableFeatureIteratorImpl(eObject,
eStructuralFeatures);
for (int i = 0; i < index; ++i) {
result.next();
}
return result;
}
}
public static class ChangeableUsageCrossReferencer
extends EcoreUtil.UsageCrossReferencer {
public static Collection find(EObject eObject, ResourceSet resourceSet) {
return new ChangeableUsageCrossReferencer(resourceSet)
.findUsage(eObject);
}
protected ChangeableUsageCrossReferencer(ResourceSet resourceSet) {
super(resourceSet);
}
protected void handleCrossReference(EObject eObject) {
InternalEList eCrossReferences = new ChangeableECrossReferenceEList(
eObject);
EContentsEList.FeatureIterator crossReferences = (EContentsEList.FeatureIterator) (resolve()
? eCrossReferences.iterator() : eCrossReferences
.basicIterator());
while (crossReferences.hasNext()) {
EObject crossReferencedEObject = (EObject) crossReferences
.next();
EReference eReference = (EReference) crossReferences.feature();
if (crossReference(eObject, eReference, crossReferencedEObject)) {
getCollection(crossReferencedEObject).add(
((InternalEObject) eObject).eSetting(eReference));
}
}
}
}
/**
* The source for the keywords annotation on elements.
*/
public static final String ANNOTATION_SOURCE__KEYWORDS = "keywords"; //$NON-NLS-1$
/**
* Constructs a new Element Operations. This constructor should never be
* called because this is a static utility class.
*/
private ElementOperations() {
super();
}
public static Set allOwnedElements(Element element) {
Set allOwnedElements = new HashSet();
allOwnedElements.addAll(element.getOwnedElements());
for (Iterator ownedElements = element.getOwnedElements().iterator(); ownedElements
.hasNext();) {
allOwnedElements.addAll(((Element) ownedElements.next())
.allOwnedElements());
}
return allOwnedElements;
}
public static boolean mustBeOwned(Element element) {
return true;
}
/**
* Retrieves the model that contains (either directly or indirectly) the
* specified element.
*
* @param element
* The element for which to retrieve the model.
* @return The model containing the element, or <code>null</code>.
*/
public static Model getModel(Element element) {
if (null == element) {
return null;
}
return Model.class.isInstance(element)
? (Model) element : (Element.class.isInstance(element.eContainer())
? getModel((Element) element.eContainer()) : null);
}
/**
* Retrieves the nearest package that contains (either directly or
* indirectly) the specified element, or the element itself (if it is a
* package).
*
* @param element
* The element for which to retrieve the nearest package.
* @return The nearest package containing the element, the element itself,
* or <code>null</code>.
*/
public static org.eclipse.uml2.Package getNearestPackage(Element element) {
if (null == element) {
return null;
}
return org.eclipse.uml2.Package.class.isInstance(element)
? (org.eclipse.uml2.Package) element : (Element.class
.isInstance(element.eContainer())
? getNearestPackage((Element) element.eContainer()) : null);
}
/**
* Destroys the specified element by removing all usage cross references to
* it (from within its resource set) and removing it from its containing
* resource or object.
* <p>
* Note that this operation can be expensive (since it traverses all
* containment hierarchies) and doesn't guarantee that all references will
* be removed (since references can exist in other resources that don't
* belong to the same resource set).
*
* @param element
* The element to be destroyed.
*/
public static void destroy(Element element) {
if (null == element) {
throw new IllegalArgumentException(String.valueOf(element));
}
ResourceSet resourceSet = null == element.eResource()
? null : element.eResource().getResourceSet();
if (null != resourceSet) {
TreeIterator eAllContents = new AbstractTreeIterator(element, true) {
public Iterator getChildren(Object parent) {
return ((EObject) parent).eContents().iterator();
}
};
while (eAllContents.hasNext()) {
EObject eObject = (EObject) eAllContents.next();
for (Iterator settings = ChangeableUsageCrossReferencer.find(
eObject, resourceSet).iterator(); settings.hasNext();) {
EcoreUtil.remove((EStructuralFeature.Setting) settings
.next(), eObject);
}
}
}
EcoreUtil.remove(element);
}
/**
* Retrieves the set of keywords for the specified element.
*
* @param element
* The element for which to retrieve keywords.
* @return The keywords for the specified element.
*/
public static Set getKeywords(Element element) {
if (null == element) {
return Collections.EMPTY_SET;
}
return Collections.unmodifiableSet(getEAnnotation(
ANNOTATION_SOURCE__KEYWORDS, element).getDetails().keySet());
}
/**
* Determines whether the specified element has the specified keyword.
*
* @param element
* The element in question.
* @param keyword
* The keyword in question.
* @return <code>true</code> if the specified element has the specified
* keyword; <code>false</code> otherwise.
*/
public static boolean hasKeyword(Element element, String keyword) {
if (null == element) {
return false;
}
if (isEmpty(keyword)) {
return false;
}
return getEAnnotation(ANNOTATION_SOURCE__KEYWORDS, element)
.getDetails().containsKey(keyword);
}
/**
* Adds the specified keyword to the specified element.
*
* @param element
* The element to which to add the keyword.
* @param keyword
* The keyword to be added.
* @throws IllegalArgumentException
* If the keyword is invalid (i.e. empty).
*/
public static void addKeyword(Element element, String keyword) {
if (null == element) {
throw new IllegalArgumentException(String.valueOf(element));
}
if (isEmpty(keyword)) {
throw new IllegalArgumentException(String.valueOf(keyword));
}
getOrCreateEAnnotation(ANNOTATION_SOURCE__KEYWORDS, element)
.getDetails().put(keyword, null);
}
/**
* Removes the specified keyword from the specified element.
*
* @param element
* The element from which to remove the keyword.
* @param keyword
* The keyword to be removed.
* @throws IllegalArgumentException
* If the element does not have the keyword.
*/
public static void removeKeyword(Element element, String keyword) {
if (null == element) {
throw new IllegalArgumentException(String.valueOf(element));
}
if (!hasKeyword(element, keyword)) {
throw new IllegalArgumentException(String.valueOf(keyword));
}
getEAnnotation(ANNOTATION_SOURCE__KEYWORDS, element).getDetails()
.removeKey(keyword);
}
/**
* An element may not directly or indirectly own itself.
*
*/
public static boolean validateNotOwnSelf(Element element,
DiagnosticChain diagnostics, Map context) {
boolean result = true;
if (element.allOwnedElements().contains(element)) {
result = false;
if (null != diagnostics) {
diagnostics.add(new BasicDiagnostic(Diagnostic.WARNING,
UML2Validator.DIAGNOSTIC_SOURCE,
UML2Validator.ELEMENT__NOT_OWN_SELF,
UML2Plugin.INSTANCE.getString(
"_UI_Element_NotOwnSelf_diagnostic", //$NON-NLS-1$
getMessageSubstitutions(context, element)),
new Object[] {element}));
}
}
return result;
}
/**
* Elements that must be owned must have an owner.
*
*/
public static boolean validateHasOwner(Element element,
DiagnosticChain diagnostics, Map context) {
boolean result = true;
if (element.mustBeOwned() && null == element.getOwner()) {
result = false;
if (null != diagnostics) {
diagnostics.add(new BasicDiagnostic(Diagnostic.WARNING,
UML2Validator.DIAGNOSTIC_SOURCE,
UML2Validator.ELEMENT__HAS_OWNER,
UML2Plugin.INSTANCE.getString(
"_UI_Element_HasOwner_diagnostic", //$NON-NLS-1$
getMessageSubstitutions(context, element)),
new Object[] {element}));
}
}
return result;
}
}