| /* |
| * 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; |
| } |
| |
| } |