blob: a3079bbca7eaf6646710218d17079bca2a347c95 [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: PropertyOperations.java,v 1.8 2004/06/17 03:20:09 khussey Exp $
*/
package org.eclipse.uml2.internal.operation;
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.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.util.UML2Validator;
/**
* A static utility class that provides operations related to properties.
*/
public final class PropertyOperations
extends UML2Operations {
/**
* Constructs a new Property Operations. This constructor should never be
* called because this is a static utility class.
*/
private PropertyOperations() {
super();
}
protected static Property getOtherEnd(Property property) {
Association association = property.getAssociation();
if (null != association) {
List memberEnds = association.getMemberEnds();
if (2 == memberEnds.size()) {
return (Property) memberEnds.get(Math.abs(memberEnds
.indexOf(property) - 1));
}
}
return null;
}
public static boolean isConsistentWith(Property property,
RedefinableElement redefinee) {
if (redefinee.isRedefinitionContextValid(property)
&& Property.class.isInstance(redefinee)) {
Property prop = (Property) redefinee;
return (null == property.getType()
? null == prop.getType()
: prop.getType().conformsTo(property.getType()))
&& prop.lowerBound() >= property.lowerBound()
&& (MultiplicityElement.UNLIMITED_UPPER_BOUND == property
.upperBound() || (prop.upperBound() != MultiplicityElement.UNLIMITED_UPPER_BOUND && prop
.upperBound() <= property.upperBound()))
&& (property.isDerived()
? prop.isDerived()
: true);
}
return false;
}
public static Property opposite(Property property) {
if (null == property.getOwningAssociation()) {
Property otherEnd = getOtherEnd(property);
if (null != otherEnd && null == otherEnd.getOwningAssociation()) {
return otherEnd;
}
}
return null;
}
public static Set subsettingContext(Property property) {
Set subsettingContext = new HashSet();
if (null == property.getAssociation()) {
if (null != property.getType()) {
subsettingContext.add(property.getType());
}
} else {
subsettingContext.addAll(property.getAssociation().getEndTypes());
}
return subsettingContext;
}
/**
* 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 || 2 != association.getEndTypes().size()) {
throw new IllegalArgumentException(String.valueOf(property));
}
if (navigable != isNavigable(property)) {
if (navigable) {
List ownedAttributes = getOwnedAttributes((Type) association
.getEndTypes().get(
association.getMemberEnds().indexOf(property)));
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));
}
((LiteralBoolean) (LiteralBoolean.class.isInstance(property
.getDefaultValue())
? property.getDefaultValue()
: 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));
}
((LiteralInteger) (LiteralInteger.class.isInstance(property
.getDefaultValue())
? property.getDefaultValue()
: 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));
}
((LiteralString) (LiteralString.class.isInstance(property
.getDefaultValue())
? property.getDefaultValue()
: 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));
}
((LiteralUnlimitedNatural) (LiteralUnlimitedNatural.class
.isInstance(property.getDefaultValue())
? property.getDefaultValue()
: property.createDefaultValue(UML2Package.eINSTANCE
.getLiteralUnlimitedNatural()))).setValue(value);
}
public static String getDefault(Property property) {
return null == property.getDefaultValue()
? EMPTY_STRING
: property.getDefaultValue().stringValue();
}
/**
* 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.
*
*/
public static boolean validateOppositeIsOtherEnd(Property property,
DiagnosticChain diagnostics, Map context) {
boolean result = true;
Property opposite = null;
if (null == property.getOwningAssociation()) {
Property otherEnd = getOtherEnd(property);
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;
}
/**
* A multiplicity of a composite aggregation must not have an upper bound
* greater than 1.
*
*/
public static boolean validateMultiplicityOfComposite(Property property,
DiagnosticChain diagnostics, Map context) {
boolean result = true;
if (property.isComposite()) {
Property otherEnd = getOtherEnd(property);
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;
}
/**
* Subsetting may only occur when the context of the subsetting property
* conforms to the context of the subsetted property.
*
*/
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;
}
/**
* A navigable property (one that is owned by a class) can only be redefined
* or subsetted by a navigable property.
*
*/
public static boolean validateNavigablePropertyRedefinition(
Property property, DiagnosticChain diagnostics, Map context) {
boolean result = true;
for (Iterator subsettedProperties = property.getSubsettedProperties()
.iterator(); subsettedProperties.hasNext();) {
Property subsettedProperty = (Property) subsettedProperties.next();
if (subsettedProperty.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_NavigablePropertyRedefinition_diagnostic", //$NON-NLS-1$
getMessageSubstitutions(context, property,
subsettedProperty)), new Object[]{
property, subsettedProperty}));
}
}
}
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_NavigablePropertyRedefinition_diagnostic", //$NON-NLS-1$
getMessageSubstitutions(context, property,
redefinedProperty)), new Object[]{
property, redefinedProperty}));
}
}
}
return result;
}
/**
* A subsetting property may strengthen the type of the subsetted property,
* and its upper bound may be less.
*
*/
public static boolean validateSubsettingRules(Property property,
DiagnosticChain diagnostics, Map context) {
boolean result = true;
for (Iterator subsettedProperties = property.getSubsettedProperties()
.iterator(); subsettedProperties.hasNext();) {
Property subsettedProperty = (Property) subsettedProperties.next();
if (!property.getType().conformsTo(subsettedProperty.getType())
|| (MultiplicityElement.UNLIMITED_UPPER_BOUND != subsettedProperty
.upperBound() && (property.upperBound() == MultiplicityElement.UNLIMITED_UPPER_BOUND || property
.upperBound() > subsettedProperty.upperBound()))) {
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;
}
/**
* Only a navigable property can be marked as read-only.
*
*/
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;
}
/**
* A derived union is derived.
*
*/
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;
}
}