/*******************************************************************************
* Copyright (c) 2005, 2006 committers of openArchitectureWare 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:
* committers of openArchitectureWare - initial API and implementation
*******************************************************************************/
package org.eclipse.xtend.typesystem.uml2.profile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.internal.xtend.expression.parser.SyntaxConstants;
import org.eclipse.internal.xtend.type.baseimpl.PropertyImpl;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.xtend.expression.TypeSystem;
import org.eclipse.xtend.typesystem.AbstractTypeImpl;
import org.eclipse.xtend.typesystem.Feature;
import org.eclipse.xtend.typesystem.Type;
public class StereotypeType extends AbstractTypeImpl {
private static final Log LOG = LogFactory.getLog(StereotypeType.class);
Stereotype stereoType;
public StereotypeType(TypeSystem typeSystem, String name, Stereotype stereoType) {
super(typeSystem, name);
this.stereoType = stereoType;
}
@Override
public Feature[] getContributedFeatures() {
List<?> children = stereoType.getAttributes();
List<Feature> features = new ArrayList<Feature>();
for (Iterator<?> iter = children.iterator(); iter.hasNext();) {
Object obj = iter.next();
if (obj instanceof Property) {
Property p = (Property) obj;
String name = normalizedName(p.getName());
if (name != null) {
org.eclipse.uml2.uml.Type type = p.getType();
if (type.eIsProxy()) {
InternalEObject proxy = (InternalEObject) type;
URI uri = proxy.eProxyURI();
type = (org.eclipse.uml2.uml.Type) EcoreUtil.resolve(proxy, p);
if (type.eIsProxy()) {
LOG.error("Couldn't resolve proxy under " + uri);
}
}
String fqn = getFullName(type);
if (fqn == null) {
LOG.error("element : " + p.getQualifiedName());
LOG.error("Type : " + type.getQualifiedName());
}
if (fqn != null) {
// We cannot use typeSystem.getTypeForName here, since
// some UML tools have there own UML profiles (e.g.
// MagicDraw 11.5)
// so this is a work around
Type rt = null;
if (fqn.toLowerCase().endsWith("::string")) {
rt = getTypeSystem().getStringType();
} else if (fqn.toLowerCase().endsWith("::boolean")) {
rt = getTypeSystem().getBooleanType();
} else if (fqn.toLowerCase().endsWith("::integer") || fqn.toLowerCase().endsWith("::int")) {
rt = getTypeSystem().getIntegerType();
} else if (fqn.toLowerCase().endsWith("::real")) {
rt = getTypeSystem().getRealType();
} else {
rt = getTypeSystem().getTypeForName(fqn);
}
// mapping of multivalued tagged values
// see Bug#168076
if(p.isMultivalued()){
rt = getTypeSystem().getCollectionType(rt);
}
if (rt != null) {
PropertyImpl prop = new PropertyImpl(StereotypeType.this, name, rt) {
@SuppressWarnings("unchecked")
public Object get(Object target) {
if (target instanceof Element) {
Element ele = (Element) target;
List<Stereotype> all = ele.getAppliedStereotypes();
for (Stereotype st : all) {
if (isStereoTypeAssignable(st, StereotypeType.this.stereoType)) {
final Object value = ele.getValue(st, getName());
// custom datatypes
// see Bug#185033
if (value instanceof EList) {
final EList<Object> eList = (EList<Object>) value;
final Collection<Object> values = new ArrayList<Object>();
for (Iterator<Object> iterator = eList.iterator(); iterator.hasNext();) {
final Object dynObject = iterator.next();
final Object dynValue = getDynamicValue(dynObject);
if(dynValue != null){
values.add(dynValue);
}
}
if(!values.isEmpty()){
return values;
}
}else if (value instanceof EObject) {
final Object dynValue = getDynamicValue(value);
if(dynValue != null){
return dynValue;
}
}
return value;
}
}
}
throw new IllegalArgumentException("uml2 Element expected but was " + target.getClass().getName());
}
private Object getDynamicValue(final Object value) {
if (value instanceof EObject) {
final EObject dynObject = (EObject) value;
final EClass dynClass = dynObject.eClass();
final EList<EStructuralFeature> eStructuralFeatures = dynClass.getEAllStructuralFeatures();
for (EStructuralFeature feature : eStructuralFeatures) {
if(feature.getName().startsWith("base_")) {
return dynObject.eGet(feature,true);
}
}
}
return null;
}
};
features.add(prop);
} else {
LOG.error("Couldn't find type for " + fqn);
}
}
}
}
}
return features.toArray(new Feature[features.size()]);
}
private boolean isStereoTypeAssignable(Stereotype st1, Stereotype st2) {
if (st1.getQualifiedName().equals(st2.getQualifiedName())) {
return true;
}
List<Generalization> gs = st1.getGeneralizations();
for (Generalization g : gs) {
if (g.getGeneral() instanceof Stereotype && isStereoTypeAssignable((Stereotype) g.getGeneral(), st2))
return true;
}
return false;
}
public boolean isInstance(Object o) {
if (o instanceof Element) {
Element ele = (Element) o;
List<Stereotype> all = ele.getAppliedStereotypes();
for (Stereotype st : all) {
if(isStereoTypeAssignable(st, StereotypeType.this.stereoType)){
return true;
}
}
}
return false;
}
public Object newInstance() {
throw new UnsupportedOperationException();
}
private Set<Type> superTypes = null;
@Override
public Set<Type> getSuperTypes() {
if (superTypes == null) {
superTypes = new HashSet<Type>();
List<Classifier> all = new ArrayList<Classifier>(stereoType.getExtendedMetaclasses());
all.addAll(stereoType.getSuperClasses());
for (Classifier classifier : all) {
String superTypeName = getFullName(classifier);
Type t = getTypeSystem().getTypeForName(superTypeName);
if (t == null) {
LOG.warn("Couldn't resolve super type " + superTypeName);
} else {
superTypes.add(t);
}
}
}
return Collections.unmodifiableSet(superTypes);
}
public Stereotype getStereoType () {
return stereoType;
}
private String getFullName(Object object) {
if (object instanceof NamedElement) {
return normalizedName(((NamedElement) object).getQualifiedName());
} else if (object instanceof EClassifier) {
return getFullyQualifiedName((EClassifier) object);
}
if (LOG.isWarnEnabled()) {
LOG.warn("Cannot resolve names for " + object.getClass().getName());
}
return null;
}
private String getFullyQualifiedName(final ENamedElement ele) {
return getFqnRec(ele.eContainer(), ele.getName());
}
private String getFqnRec(final EObject ele, final String suffix) {
if (ele == null || !(ele instanceof ENamedElement)) {
return suffix;
}
else {
return getFqnRec(ele.eContainer(), ((ENamedElement) ele).getName() + SyntaxConstants.NS_DELIM + suffix);
}
}
@Override
protected boolean internalIsAssignableFrom(Type t) {
if (super.internalIsAssignableFrom(t)) {
return true;
} else if (t instanceof StereotypeType) {
return ((StereotypeType)t).isCompatible(this);
} else {
return false;
}
}
protected boolean isCompatible (Type t) {
return false;
}
/**
* It is not allowed to have whitespaces in profile, stereotype or tagged value names. Therefore we need to replace them w
* All non-word characters are replaced by underscore.
* @param name An element's name
* @return All non-word characters are replaced by underscores
*/
private static String normalizedName (String name) {
String[] fragments = name.split(SyntaxConstants.NS_DELIM);
StringBuffer result = new StringBuffer(name.length());
result.append(fragments[0].replaceAll("\\W", "_"));
for (int i=1; i<fragments.length; i++) {
result.append(SyntaxConstants.NS_DELIM);
result.append(fragments[1].replaceAll("\\W", "_"));
}
return result.toString();
}
}