/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright (C) 2007 Matthias Braeuer (braeuer.matthias@web.de). *
* All rights reserved. *
* *
* This work was done as a project at the Chair for Software Technology, *
* Dresden University Of Technology, Germany (http://st.inf.tu-dresden.de). *
* It is understood that any modification not identified as such is not *
* covered by the preceding statement. *
* *
* This work is free software; you can redistribute it and/or modify it *
* under the terms of the GNU Library General Public License as published *
* by the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This work is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public *
* License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; if not, you can view it online at *
* http://www.fsf.org/licensing/licenses/gpl.html. *
* *
* To submit a bug report, send a comment, or get the latest news on this *
* project, please visit the website: http://dresden-ocl.sourceforge.net. *
* For more information on OCL and related projects visit the OCL Portal: *
* http://st.inf.tu-dresden.de/ocl *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* $Id$
*/
package org.dresdenocl.essentialocl.types.impl;
import java.util.List;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.dresdenocl.essentialocl.expressions.CollectionKind;
import org.dresdenocl.essentialocl.types.CollectionType;
import org.dresdenocl.essentialocl.types.OclLibrary;
import org.dresdenocl.essentialocl.types.TypesFactory;
import org.dresdenocl.pivotmodel.Type;
import org.dresdenocl.pivotmodel.TypeParameter;
import org.dresdenocl.pivotmodel.impl.TypeImpl;
import org.dresdenocl.pivotmodel.util.ListUtil;
/**
* <!-- begin-user-doc --> An implementation of the model object '
* <em><b>Collection Type</b></em>'. <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link org.dresdenocl.essentialocl.types.impl.CollectionTypeImpl#getElementType <em>Element Type</em>}</li>
* <li>{@link org.dresdenocl.essentialocl.types.impl.CollectionTypeImpl#getOclLibrary <em>Ocl Library</em>}</li>
* <li>{@link org.dresdenocl.essentialocl.types.impl.CollectionTypeImpl#getKind <em>Kind</em>}</li>
* </ul>
* </p>
*
* @generated
*/
public class CollectionTypeImpl extends TypeImpl implements CollectionType {
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(CollectionTypeImpl.class);
/**
* The cached value of the '{@link #getElementType() <em>Element Type</em>}' reference.
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @see #getElementType()
* @generated
* @ordered
*/
protected Type elementType;
/**
* The cached value of the '{@link #getOclLibrary() <em>Ocl Library</em>}' reference.
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @see #getOclLibrary()
* @generated
* @ordered
*/
protected OclLibrary oclLibrary;
/**
* The default value of the '{@link #getKind() <em>Kind</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getKind()
* @generated
* @ordered
*/
protected static final CollectionKind KIND_EDEFAULT = CollectionKind.COLLECTION;
/**
* The cached value of the '{@link #getKind() <em>Kind</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getKind()
* @generated
* @ordered
*/
protected CollectionKind kind = KIND_EDEFAULT;
/**
* The EMF implementation is adapted in order to set the element type of the
* collection type to <code>OclAny</code> which is the default.
*
* @generated NOT
*/
protected CollectionTypeImpl() {
super();
}
/**
* Overridden to comply with the wellformedness rules defined in the OCL 2.0
* specification, Section 8.2.2.:
*
* <p>
* The name of a collection type is �Collection� followed by the element
* type�s name in parentheses.
*
* <pre>
* context CollectionType
* inv: self.name = �Collection(� + self.elementType.name + �)�
* </pre>
*
* </p>
*
* @see org.dresdenocl.pivotmodel.impl.NamedElementImpl#getName()
*/
@Override
public String getName() {
// check whether the name has already been determined
if (name == NAME_EDEFAULT) {
// we cannot determine the name until the element type is set
if (elementType == null) {
return getCollectionTypeName();
}
name = determineCollectionTypeName();
}
return name;
}
/**
* Helper method to determine the name of this collection type.
*
* @return a <code>String</code> representing the name
*/
protected final String determineCollectionTypeName() {
if (logger.isDebugEnabled()) {
logger.debug("determineCollectionTypeName() - enter"); //$NON-NLS-1$
}
StringBuilder name;
// initialize with the collection-specific name
name = new StringBuilder(getCollectionTypeName());
// only add the element type if it is set
if (getElementType() != null) {
name.append('(').append(getElementType().getName()).append(')');
}
if (logger.isDebugEnabled()) {
logger.debug("determineCollectionTypeName() - exit - return value=" + name); //$NON-NLS-1$
}
return name.toString();
}
/**
* Helper method to return the literal for this type of collection. This
* simply returns the string value of the {@link #getKind() kind} of this
* collection type as modelled in the Standard Library.
*
* @return a <code>String</code> representing the type of collection
*/
protected String getCollectionTypeName() {
return getKind().toString();
}
/**
* Overridden to prevent clients from changing the name of the
* <code>CollectionType</code>. This method will throw an
* {@link UnsupportedOperationException}.
*
* @see org.dresdenocl.pivotmodel.impl.NamedElementImpl#setName(java.lang.String)
*/
@Override
public final void setName(String newName) {
throw new UnsupportedOperationException(
"The name of a collection type cannot be changed"); //$NON-NLS-1$
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public Type getElementType() {
return elementType;
}
/**
* This method acts as a decorator for {@link #setElementTypeGen(Type)} to
* prevent that the element type of a collection type is changed after it has
* been set the first time. An <code>IllegalStateException</code> will be
* thrown if clients attempt to set the element type again.
*
* @generated NOT
*/
public void setElementType(Type newElementType) {
if (logger.isDebugEnabled()) {
logger.debug("setElementType(newElementType=" + newElementType //$NON-NLS-1$
+ ") - enter"); //$NON-NLS-1$
}
// the element type must not have been set before
if (elementType != null) {
throw new IllegalStateException(
"The element type of a collection type cannot be changed once it has been set."); //$NON-NLS-1$
}
// set the type using the generated method
setElementTypeGen(newElementType);
if (logger.isDebugEnabled()) {
logger.debug("setElementType() - exit"); //$NON-NLS-1$
}
}
/**
* <!-- begin-user-doc -->The code for {@link #setElementType(Type)} is
* forwarded to this method. <!-- end-user-doc -->
* @generated
*/
public void setElementTypeGen(Type newElementType) {
Type oldElementType = elementType;
elementType = newElementType;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET,
TypesPackageImpl.COLLECTION_TYPE__ELEMENT_TYPE,
oldElementType, elementType));
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public OclLibrary getOclLibrary() {
return oclLibrary;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public void setOclLibrary(OclLibrary newOclLibrary) {
OclLibrary oldOclLibrary = oclLibrary;
oclLibrary = newOclLibrary;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET,
TypesPackageImpl.COLLECTION_TYPE__OCL_LIBRARY,
oldOclLibrary, oclLibrary));
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public CollectionKind getKind() {
return kind;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public void setKind(CollectionKind newKind) {
CollectionKind oldKind = kind;
kind = newKind == null ? KIND_EDEFAULT : newKind;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET,
TypesPackageImpl.COLLECTION_TYPE__KIND, oldKind, kind));
}
/**
* Overrides the implementation in {@link Type}. A <code>CollectionType</code>
* is only conformant to another type if:
*
* <ul>
* <li>the other type is indeed a <code>CollectionType</code>, i.e. has the
* same metatype
* <li>the element type is conformant to the element type of the other
* collection type
* </ul>
*
* <p>
* Note that this definition of conformance for collection types is only valid
* because OCL collections are value types, i.e. they are immutable. That's
* why a <code>Collection(Real)</code> is conformant to a
* <code>Collection(Integer)</code>. This is NOT the case in Java where
* collections may be altered.
* </p>
*
* <p>
* This implementation regards a collection with meta type
* <code>SetType</code> as conformant to a collection with meta type
* <code>CollectionType</code>. The old OCL Toolkit implementation returned
* <code>false</code> because it checked for metatype equality.
* </p>
*
* <p>
* The corresponding invariants from the OCL spec are as follows:
*
* <pre>
* [1] Specific collection types conform to collection type.
*
* context CollectionType
* inv: -- all instances of SetType, SequenceType, BagType conform to a
* -- CollectionType if the elementTypes conform
* CollectionType.allInstances()->forAll (c |
* c.oclIsTypeOf(CollectionType) and
* self.elementType.conformsTo(c.elementType) implies
* self.conformsTo(c))
*
*
* [2] Collections do not conform to any primitive type.
*
* context CollectionType
* inv: PrimitiveType.allInstances()->forAll (p | not self.conformsTo(p))
*
*
* [3] Collections of non-conforming types do not conform.
*
* context CollectionType
* inv: CollectionType.allInstances()->forAll (c |
* (not self.elementType.conformsTo (c.elementType)) implies (not self.conformsTo (c)))
* </pre>
*
* </p>
*
* @see org.dresdenocl.pivotmodel.impl.TypeImpl#conformsTo(org.dresdenocl.pivotmodel.Type)
*/
@Override
public final boolean conformsTo(Type other) {
if (logger.isDebugEnabled()) {
logger.debug("conformsTo(other=" + other + ") - enter"); //$NON-NLS-1$ //$NON-NLS-2$
}
boolean conformant;
// by default the other type is not conformant
conformant = false;
// the other type must be a collection type, too
if (other instanceof CollectionType) {
CollectionType otherCollectionType = (CollectionType) other;
// collection types conform to a type of the same kind or to Collection
if (getKind() == otherCollectionType.getKind()
|| otherCollectionType.getKind() == CollectionKind.COLLECTION) {
Type otherElementType = otherCollectionType.getElementType();
// collection types always conform to unbound collection types
// (comparable with a wildcard collection type in Java 5), this is
// necessary to ensure that operations with unbound collection
// types as parameters (e.g., Collection::product) can be looked up;
// unbound collection types themselves do not conform to other types
conformant = otherElementType == null ? true
: (this.elementType != null ? this.elementType
.conformsTo(otherElementType) : false);
}
}
if (logger.isDebugEnabled()) {
logger.debug("conformsTo() - exit - return value=" + conformant); //$NON-NLS-1$
}
return conformant;
}
/**
* Overridden to implement special behaviour for collection types. The common
* super type of a <code>CollectionType</code> with another type is:
*
* <ul>
* <li><code>null</code>, if the other type is no collection type or there is
* no common supertype for the element types of two collection types
* <li>the most specific collection type with the common element type
* </ul>
*
* For example, the common supertype of <code>Set(Integer)</code> and
* <code>Sequence(Real)</code> is <code>Collection(Real)</code>, but together
* with <code>Sequence(String)</code> the common super type evaluates to
* <code>Collection(OclAny)</code>.
*
* @see org.dresdenocl.pivotmodel.impl.TypeImpl#commonSuperType(org.dresdenocl.pivotmodel.Type)
*/
@Override
public final Type commonSuperType(Type other) {
if (logger.isDebugEnabled()) {
logger.debug("commonSuperType(other=" + other + ") - enter"); //$NON-NLS-1$ //$NON-NLS-2$
}
Type commonSuperType;
// check invariant
if (getElementType() == null) {
throw new IllegalStateException(
"Unable to determine common super type if element type is null!"); //$NON-NLS-1$
}
// by default no common supertype
commonSuperType = null;
// common supertype can only be determined if the other type is a collection
// type
if (other instanceof CollectionType) {
CollectionType otherCollectionType = (CollectionType) other;
// find the common super type of the element types
Type commonElementType = getElementType().commonSuperType(
otherCollectionType.getElementType());
if (commonElementType != null) {
commonSuperType = getCommonCollectionType(otherCollectionType,
commonElementType);
}
}
if (logger.isDebugEnabled()) {
logger.debug("commonSuperType() - exit - return value=" + commonSuperType); //$NON-NLS-1$
}
return commonSuperType;
}
/**
* Helper method to determine the common type of this
* <code>CollectionType</code> and another <code>CollectionType</code> for a
* given element type. This should be overridden in subclasses.
*
* @param otherCollectionType
* the other <code>CollectionType</code>
* @param commonElementType
* the common element type
*
* @return the most specific <code>CollectionType</code> that is commmon for
* this and the other <code>CollectionType</code>
*/
protected CollectionType getCommonCollectionType(
CollectionType otherCollectionType, Type commonElementType) {
// check invariant
if (getOclLibrary() == null) {
throw new IllegalStateException(
"The reference to the OCL library was null for " + this + "."); //$NON-NLS-1$ //$NON-NLS-2$
}
return getOclLibrary().getCollectionType(commonElementType);
}
/**
* Overridden to set the element type of the bound collection type.
*
* @see org.dresdenocl.pivotmodel.impl.TypeImpl#bindTypeParameter(java.util.List,
* java.util.List)
*/
@Override
public CollectionType bindTypeParameter(List<TypeParameter> parameters,
List<? extends Type> types) {
CollectionType boundType;
// bind the collection type with the given parameters
boundType = (CollectionType) super.bindTypeParameter(parameters, types);
// set the element type if it has not yet been set and if the given bindings
// contain the type parameter of this collection type
if (boundType.getElementType() == null) {
Type elementType = null;
int index;
// find the TypeParameter that corresponds to the element type
index = ListUtil
.indexOf(parameters, getOwnedTypeParameter().get(0));
if (index != -1) {
elementType = types.get(index);
}
// set the element type if it was found
if (elementType != null) {
boundType.setElementType(elementType);
}
}
return boundType;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public CollectionType clone() {
return initialize(TypesFactory.INSTANCE.createCollectionType());
}
/**
* Overridden to additionally set the kind, the element type and the reference
* to the {@link OclLibrary}.
*
* @see org.dresdenocl.pivotmodel.impl.TypeImpl#initialize(org.dresdenocl.pivotmodel.Type)
*/
protected CollectionType initialize(CollectionType clone) {
super.initialize(clone);
clone.setKind(getKind());
if (elementType != null) {
clone.setElementType(elementType);
}
clone.setOclLibrary(getOclLibrary());
return clone;
}
/**
* Overridden to return <code>true</code> as a hint that the name of
* collection types is determined automatically.
*
* @see org.dresdenocl.pivotmodel.impl.NamedElementImpl#hasVolatileName()
*/
@Override
protected boolean hasVolatileName() {
return true;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case TypesPackageImpl.COLLECTION_TYPE__ELEMENT_TYPE:
return getElementType();
case TypesPackageImpl.COLLECTION_TYPE__OCL_LIBRARY:
return getOclLibrary();
case TypesPackageImpl.COLLECTION_TYPE__KIND:
return getKind();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case TypesPackageImpl.COLLECTION_TYPE__ELEMENT_TYPE:
setElementType((Type) newValue);
return;
case TypesPackageImpl.COLLECTION_TYPE__OCL_LIBRARY:
setOclLibrary((OclLibrary) newValue);
return;
case TypesPackageImpl.COLLECTION_TYPE__KIND:
setKind((CollectionKind) newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case TypesPackageImpl.COLLECTION_TYPE__ELEMENT_TYPE:
setElementType((Type) null);
return;
case TypesPackageImpl.COLLECTION_TYPE__OCL_LIBRARY:
setOclLibrary((OclLibrary) null);
return;
case TypesPackageImpl.COLLECTION_TYPE__KIND:
setKind(KIND_EDEFAULT);
return;
}
super.eUnset(featureID);
}
/**
* The EMF implementation is adapted to prevent that the name and element type
* of the <code>CollectionType</code> are serialized to XMI. This is necessary
* to prevent setting these properties upon loading the document which would
* cause an exception.
*
* @generated NOT
*
* @see #setName(String)
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case TypesPackageImpl.COLLECTION_TYPE__ELEMENT_TYPE:
case TypesPackageImpl.COLLECTION_TYPE__NAME:
return false;
case TypesPackageImpl.COLLECTION_TYPE__OCL_LIBRARY:
return oclLibrary != null;
case TypesPackageImpl.COLLECTION_TYPE__KIND:
return kind != KIND_EDEFAULT;
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass() {
return TypesPackageImpl.Literals.COLLECTION_TYPE;
}
/**
* Adapted the EMF implementation for consistent strings.
*
* @generated NOT
*/
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.appendSuper(super.toString())
.append("elementType", elementType) //$NON-NLS-1$
.append("kind", kind).toString(); //$NON-NLS-1$
}
} // CollectionTypeImpl