| /* |
| * Copyright (c) 2003, 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 - initial API and implementation |
| * |
| * $Id: PropertyOperations.java,v 1.18 2005/10/19 19:44:48 khussey Exp $ |
| */ |
| package org.eclipse.uml2.internal.operation; |
| |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| 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.ecore.EObject; |
| |
| import org.eclipse.uml2.Association; |
| import org.eclipse.uml2.Classifier; |
| import org.eclipse.uml2.LiteralBoolean; |
| import org.eclipse.uml2.LiteralInteger; |
| import org.eclipse.uml2.LiteralString; |
| import org.eclipse.uml2.LiteralUnlimitedNatural; |
| import org.eclipse.uml2.MultiplicityElement; |
| import org.eclipse.uml2.Property; |
| import org.eclipse.uml2.RedefinableElement; |
| import org.eclipse.uml2.Type; |
| import org.eclipse.uml2.UML2Package; |
| import org.eclipse.uml2.UML2Plugin; |
| import org.eclipse.uml2.ValueSpecification; |
| |
| import org.eclipse.uml2.util.UML2Validator; |
| |
| /** |
| * <!-- begin-user-doc --> |
| * A static utility class that provides operations related to '<em><b>Property</b></em>' model objects. |
| * <!-- end-user-doc --> |
| * |
| * <p> |
| * The following operations are supported: |
| * <ul> |
| * <li>{@link org.eclipse.uml2.Property#validateOppositeIsOtherEnd(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Opposite Is Other End</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#opposite() <em>Opposite</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#validateMultiplicityOfComposite(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Multiplicity Of Composite</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#validateSubsettingContext(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Subsetting Context</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#validateNavigablePropertyRedefinition(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Navigable Property Redefinition</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#validateSubsettingRules(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Subsetting Rules</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#validateNavigableReadonly(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Navigable Readonly</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#validateDerivedUnionIsDerived(org.eclipse.emf.common.util.DiagnosticChain, java.util.Map) <em>Validate Derived Union Is Derived</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#subsettingContext() <em>Subsetting Context</em>}</li> |
| * <li>{@link org.eclipse.uml2.Property#isConsistentWith(org.eclipse.uml2.RedefinableElement) <em>Is Consistent With</em>}</li> |
| * </ul> |
| * </p> |
| * |
| * @generated not |
| */ |
| public final class PropertyOperations extends UML2Operations { |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| public static final String copyright = "Copyright (c) IBM Corporation and others."; //$NON-NLS-1$ |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * @generated |
| */ |
| private PropertyOperations() { |
| super(); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * If this property is owned by a class, associated with a binary |
| * association, and the other end of the association is also owned by a |
| * class, then opposite gives the other end. |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * opposite = |
| * if owningAssociation->notEmpty() and association.memberEnd->size() = 2 then |
| * let otherEnd = (association.memberEnd - self)->any() in |
| * if otherEnd.owningAssociation->notEmpty then otherEnd else Set{} endif |
| * else Set {} |
| * endif |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateOppositeIsOtherEnd(Property property, DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| Property opposite = null; |
| |
| if (null == property.getOwningAssociation()) { |
| Property otherEnd = property.getOtherEnd(); |
| |
| if (null != otherEnd && null == otherEnd.getOwningAssociation()) { |
| opposite = otherEnd; |
| } |
| } |
| |
| if (!safeEquals(property.getOpposite(), opposite)) { |
| result = false; |
| |
| if (null != diagnostics) { |
| diagnostics.add(new BasicDiagnostic(Diagnostic.ERROR, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__OPPOSITE_IS_OTHER_END, |
| UML2Plugin.INSTANCE.getString( |
| "_UI_Property_OppositeIsOtherEnd_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, property)), |
| new Object[]{property, opposite})); |
| |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * A query based on the following OCL expression: |
| * <code> |
| * if owningAssociation->notEmpty() and association.memberEnd->size() = 2 then |
| * let otherEnd = (association.memberEnd - self)->any() in |
| * if otherEnd.owningAssociation->notEmpty then otherEnd else Set{} endif |
| * else Set {} |
| * endif |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static Property opposite(Property property) { |
| |
| if (null == property.getOwningAssociation()) { |
| Property otherEnd = property.getOtherEnd(); |
| |
| if (null != otherEnd && null == otherEnd.getOwningAssociation()) { |
| return otherEnd; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * A multiplicity of a composite aggregation must not have an upper bound |
| * greater than 1. |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * isComposite implies (upperBound()->isEmpty() or upperBound() <= 1) |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateMultiplicityOfComposite(Property property, DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| if (property.isComposite()) { |
| Property otherEnd = property.getOtherEnd(); |
| |
| if (null != otherEnd) { |
| int upperBound = otherEnd.upperBound(); |
| |
| if (MultiplicityElement.UNLIMITED_UPPER_BOUND == upperBound |
| || 1 < upperBound) { |
| |
| result = false; |
| |
| if (null != diagnostics) { |
| diagnostics |
| .add(new BasicDiagnostic( |
| Diagnostic.WARNING, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__MULTIPLICITY_OF_COMPOSITE, |
| UML2Plugin.INSTANCE |
| .getString( |
| "_UI_Property_MultiplicityOfComposite_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, |
| property)), new Object[]{property, |
| new Integer(upperBound)})); |
| } |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * subsettedProperty->notEmpty() implies |
| * (subsettingContext()->notEmpty() and subsettingContext()->forAll (sc | |
| * subsettedProperty->forAll(sp | |
| * sp.subsettingContext()->exists(c | sc.conformsTo(c))))) |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateSubsettingContext(Property property, DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| spLoop : for (Iterator sp = property.getSubsettedProperties() |
| .iterator(); sp.hasNext();) { |
| |
| Property subsettedProperty = (Property) sp.next(); |
| |
| for (Iterator sc = property.subsettingContext().iterator(); sc |
| .hasNext();) { |
| |
| Classifier subsettingContext = (Classifier) sc.next(); |
| |
| for (Iterator c = subsettedProperty.subsettingContext() |
| .iterator(); c.hasNext();) { |
| |
| if (subsettingContext.conformsTo((Classifier) c.next())) { |
| continue spLoop; |
| } |
| } |
| } |
| |
| result = false; |
| |
| if (null == diagnostics) { |
| return result; |
| } else { |
| diagnostics.add(new BasicDiagnostic(Diagnostic.WARNING, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__SUBSETTING_CONTEXT, |
| UML2Plugin.INSTANCE.getString( |
| "_UI_Property_SubsettingContext_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, property, |
| subsettedProperty)), new Object[]{property, |
| subsettedProperty})); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * A navigable property (one that is owned by a class) can only be redefined |
| * by a navigable property. |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * (redefinedProperty->exists(rp | rp.class->notEmpty()) |
| * implies class->notEmpty()) |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateNavigablePropertyRedefinition(Property property, DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| for (Iterator redefinedProperties = property.getRedefinedProperties() |
| .iterator(); redefinedProperties.hasNext();) { |
| |
| Property redefinedProperty = (Property) redefinedProperties.next(); |
| |
| if (redefinedProperty.isNavigable() && !property.isNavigable()) { |
| result = false; |
| |
| if (null == diagnostics) { |
| return result; |
| } else { |
| diagnostics |
| .add(new BasicDiagnostic( |
| Diagnostic.WARNING, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__NAVIGABLE_PROPERTY_REDEFINITION, |
| UML2Plugin.INSTANCE |
| .getString( |
| "_UI_Property_NavigablePropertyRedefined_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, property, |
| redefinedProperty)), new Object[]{ |
| property, redefinedProperty})); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * A subsetting property may strengthen the type of the subsetted property, |
| * and its upper bound may be less. |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * subsettedProperty->forAll(sp | |
| * type.conformsTo(sp.type) and |
| * ((upperBound()->notEmpty() and sp.upperBound()->notEmpty()) implies |
| * upperBound()<=sp.upperBound() )) |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateSubsettingRules(Property property, |
| DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| Type type = property.getType(); |
| int upperBound = property.upperBound(); |
| |
| for (Iterator subsettedProperties = property.getSubsettedProperties() |
| .iterator(); subsettedProperties.hasNext();) { |
| |
| Property subsettedProperty = (Property) subsettedProperties.next(); |
| int subsettedUpperBound = subsettedProperty.upperBound(); |
| |
| if (!type.conformsTo(subsettedProperty.getType()) |
| || (MultiplicityElement.UNLIMITED_UPPER_BOUND != subsettedUpperBound && (upperBound == MultiplicityElement.UNLIMITED_UPPER_BOUND || upperBound > subsettedUpperBound))) { |
| |
| result = false; |
| |
| if (null == diagnostics) { |
| return result; |
| } else { |
| diagnostics.add(new BasicDiagnostic(Diagnostic.WARNING, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__SUBSETTING_RULES, |
| UML2Plugin.INSTANCE.getString( |
| "_UI_Property_SubsettingRules_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, property, |
| subsettedProperty)), new Object[]{property, |
| subsettedProperty})); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * Only a navigable property can be marked as read-only. |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * isReadOnly implies class->notEmpty() |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateNavigableReadonly(Property property, DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| if (property.isReadOnly() && null != property.getAssociation() |
| && !property.isNavigable()) { |
| |
| result = false; |
| |
| if (null != diagnostics) { |
| diagnostics.add(new BasicDiagnostic(Diagnostic.WARNING, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__NAVIGABLE_READONLY, |
| UML2Plugin.INSTANCE.getString( |
| "_UI_Property_NavigableReadOnly_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, property)), |
| new Object[]{property})); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * A derived union is derived. |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * An invariant constraint based on the following OCL expression: |
| * <code> |
| * isDerivedUnion implies isDerived |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean validateDerivedUnionIsDerived(Property property, DiagnosticChain diagnostics, Map context) { |
| boolean result = true; |
| |
| if (property.isDerivedUnion() && !property.isDerived()) { |
| result = false; |
| |
| if (null != diagnostics) { |
| diagnostics.add(new BasicDiagnostic(Diagnostic.WARNING, |
| UML2Validator.DIAGNOSTIC_SOURCE, |
| UML2Validator.PROPERTY__DERIVED_UNION_IS_DERIVED, |
| UML2Plugin.INSTANCE.getString( |
| "_UI_Property_DerivedUnionIsDerived_diagnostic", //$NON-NLS-1$ |
| getMessageSubstitutions(context, property)), |
| new Object[]{property})); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * A query based on the following OCL expression: |
| * <code> |
| * if association->notEmpty() |
| * then association.endType-type |
| * else if classifier->notEmpty then Set{classifier} else Set{} endif |
| * endif |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static Set subsettingContext(Property property) { |
| Set subsettingContext = new HashSet(); |
| |
| Association association = property.getAssociation(); |
| |
| if (null == association) { |
| EObject eContainer = property.eContainer(); |
| |
| if (Classifier.class.isInstance(eContainer)) { |
| subsettingContext.add(eContainer); |
| } |
| } else { |
| |
| for (Iterator memberEnds = association.getMemberEnds().iterator(); memberEnds |
| .hasNext();) { |
| |
| Property memberEnd = (Property) memberEnds.next(); |
| |
| if (memberEnd != property) { |
| Type memberEndType = memberEnd.getType(); |
| |
| if (Classifier.class.isInstance(memberEndType)) { |
| subsettingContext.add(memberEndType); |
| } |
| } |
| } |
| } |
| |
| return Collections.unmodifiableSet(subsettingContext); |
| } |
| |
| /** |
| * <!-- begin-user-doc --> |
| * <!-- end-user-doc --> |
| * <!-- begin-model-doc --> |
| * A query based on the following OCL expression: |
| * <code> |
| * (redefinee.oclIsKindOf(Property) and |
| * let prop: Property = redefinee.oclAsType(Property) in |
| * type.conformsTo(prop.type) and |
| * (lowerBound()->notEmpty and prop.lowerBound()->notEmpty() implies lowerBound() >= prop.lowerBound()) |
| * and |
| * (upperBound()->notEmpty and prop.upperBound()->notEmpty() implies upperBound() <= prop.upperBound()) |
| * and |
| * (prop.isDerived implies isDerived)) |
| * </code> |
| * <!-- end-model-doc --> |
| * @generated NOT |
| */ |
| public static boolean isConsistentWith(Property property, |
| RedefinableElement redefinee) { |
| |
| if (redefinee.isRedefinitionContextValid(property) |
| && Property.class.isInstance(redefinee)) { |
| |
| Property prop = (Property) redefinee; |
| |
| Type type = property.getType(); |
| int upperBound = property.upperBound(); |
| |
| Type propType = prop.getType(); |
| int propUpperBound = prop.upperBound(); |
| |
| return (null == type |
| ? null == propType |
| : propType.conformsTo(type)) |
| && prop.lowerBound() >= property.lowerBound() |
| && (MultiplicityElement.UNLIMITED_UPPER_BOUND == upperBound || (propUpperBound != MultiplicityElement.UNLIMITED_UPPER_BOUND && propUpperBound <= upperBound)) |
| && (property.isDerived() |
| ? prop.isDerived() |
| : true); |
| } |
| |
| return false; |
| } |
| |
| // <!-- begin-custom-operations --> |
| |
| /** |
| * Retrieves the other end of the (binary) association in which the |
| * specified property is a member end. |
| * |
| * @param property |
| * The property for which to retrieve the other end. |
| * @return The other end. |
| */ |
| public static Property getOtherEnd(Property property) { |
| Association association = property.getAssociation(); |
| |
| if (null != association && association.isBinary()) { |
| List memberEnds = association.getMemberEnds(); |
| |
| return (Property) memberEnds.get(Math.abs(memberEnds |
| .indexOf(property) - 1)); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Determines whether the specified property is navigable, i.e. it is part |
| * of an assocation and owned by one its the end types. |
| * |
| * @param property |
| * The property for which to determine navigability. |
| * @return <code>true</code> if the property is navigable; |
| * <code>false</code> otherwise. |
| */ |
| public static boolean isNavigable(Property property) { |
| |
| if (null == property) { |
| return false; |
| } |
| |
| return null != property.getAssociation() |
| && null == property.getOwningAssociation(); |
| } |
| |
| /** |
| * Sets the navigability of the specified property as specified. |
| * |
| * @param property |
| * The property for which to set the navigability. |
| * @param navigable |
| * Whether the property should be navigable. |
| * @exception IllegalArgumentException |
| * If the specified property is not an association end or if |
| * the specified navigability does not apply. |
| */ |
| public static void setNavigable(Property property, boolean navigable) { |
| |
| if (null == property) { |
| throw new IllegalArgumentException(String.valueOf(property)); |
| } |
| |
| Association association = property.getAssociation(); |
| |
| if (null == association || !association.isBinary()) { |
| throw new IllegalArgumentException(String.valueOf(property)); |
| } |
| |
| if (navigable != isNavigable(property)) { |
| |
| if (navigable) { |
| List ownedAttributes = getOwnedAttributes(property |
| .getOtherEnd().getType()); |
| |
| if (null == ownedAttributes) { |
| throw new IllegalArgumentException(String |
| .valueOf(navigable)); |
| } else { |
| ownedAttributes.add(property); |
| } |
| } else { |
| association.getOwnedEnds().add(property); |
| } |
| } |
| } |
| |
| /** |
| * Sets the default of the specified property to the specified boolean |
| * value. |
| * |
| * @param property |
| * The property whose default to set. |
| * @param value |
| * The new value of the default. |
| */ |
| public static void setBooleanDefault(Property property, boolean value) { |
| |
| if (null == property) { |
| throw new IllegalArgumentException(String.valueOf(property)); |
| } |
| |
| ValueSpecification defaultValue = property.getDefaultValue(); |
| |
| ((LiteralBoolean) (LiteralBoolean.class.isInstance(defaultValue) |
| ? defaultValue |
| : property.createDefaultValue(UML2Package.eINSTANCE |
| .getLiteralBoolean()))).setValue(value); |
| } |
| |
| /** |
| * Sets the default of the specified property to the specified integer |
| * value. |
| * |
| * @param property |
| * The property whose default to set. |
| * @param value |
| * The new value of the default. |
| */ |
| public static void setIntegerDefault(Property property, int value) { |
| |
| if (null == property) { |
| throw new IllegalArgumentException(String.valueOf(property)); |
| } |
| |
| ValueSpecification defaultValue = property.getDefaultValue(); |
| |
| ((LiteralInteger) (LiteralInteger.class.isInstance(defaultValue) |
| ? defaultValue |
| : property.createDefaultValue(UML2Package.eINSTANCE |
| .getLiteralInteger()))).setValue(value); |
| } |
| |
| /** |
| * Sets the default of the specified property to the specified string value. |
| * |
| * @param property |
| * The property whose default to set. |
| * @param value |
| * The new value of the default. |
| */ |
| public static void setStringDefault(Property property, String value) { |
| |
| if (null == property) { |
| throw new IllegalArgumentException(String.valueOf(property)); |
| } |
| |
| ValueSpecification defaultValue = property.getDefaultValue(); |
| |
| ((LiteralString) (LiteralString.class.isInstance(defaultValue) |
| ? defaultValue |
| : property.createDefaultValue(UML2Package.eINSTANCE |
| .getLiteralString()))).setValue(value); |
| } |
| |
| /** |
| * Sets the default of the specified property to the specified unlimited |
| * natural value. |
| * |
| * @param property |
| * The property whose default to set. |
| * @param value |
| * The new value of the default. |
| */ |
| public static void setUnlimitedNaturalDefault(Property property, int value) { |
| |
| if (null == property) { |
| throw new IllegalArgumentException(String.valueOf(property)); |
| } |
| |
| ValueSpecification defaultValue = property.getDefaultValue(); |
| |
| ((LiteralUnlimitedNatural) (LiteralUnlimitedNatural.class |
| .isInstance(defaultValue) |
| ? defaultValue |
| : property.createDefaultValue(UML2Package.eINSTANCE |
| .getLiteralUnlimitedNatural()))).setValue(value); |
| } |
| |
| public static String getDefault(Property property) { |
| ValueSpecification defaultValue = property.getDefaultValue(); |
| return null == defaultValue |
| ? EMPTY_STRING |
| : defaultValue.stringValue(); |
| } |
| |
| // <!-- end-custom-operations --> |
| |
| } // PropertyOperations |