/*
Copyright (c) 2008 Arno Haase, Andr� Arnold.
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:
Arno Haase - initial API and implementation
Andr� Arnold
*/
package org.eclipse.xtend.backend.types.uml2.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
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.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.xtend.backend.common.BackendType;
import org.eclipse.xtend.backend.common.BackendTypesystem;
import org.eclipse.xtend.backend.common.ExecutionContext;
import org.eclipse.xtend.backend.types.AbstractProperty;
import org.eclipse.xtend.backend.types.AbstractType;
import org.eclipse.xtend.backend.types.builtin.CollectionType;
import org.eclipse.xtend.backend.types.uml2.UmlTypesystem;
/**
*
* @author Arno Haase (http://www.haase-consulting.com)
* @author Andr� Arnold
*/
public final class StereotypeType extends AbstractType {
private final Stereotype _stereoType;
public StereotypeType (String name, Stereotype stereoType, UmlTypesystem umlTs) {
super (name, name, superTypes (umlTs, stereoType).toArray (new BackendType[0])); //TODO uniqueRepresentation
_stereoType = stereoType;
for (StereotypeProperty stp: getProperties (this, stereoType, umlTs))
register (stp, stp.getType (umlTs));
}
private Collection<StereotypeProperty> getProperties (BackendType owningType, Stereotype stereoType, UmlTypesystem umlTs) {
final Collection<StereotypeProperty> result = new HashSet<StereotypeProperty> ();
for (Property attrib: stereoType.getAttributes()) {
if (attrib.getName() == null)
continue;
UmlTypesystem.fixName (attrib);
final Type umlType = getTypeResolveProxy (attrib);
if (umlType.getQualifiedName() == null) {
LogFactory.getLog (owningType.getClass ()).error ("qualified name is null for element " + attrib.getQualifiedName());
continue;
}
final BackendType backendType = (attrib.isMultivalued()) ? CollectionType.INSTANCE : umlTs.getTypeForStereotypeProperty (umlType);
result.add (new StereotypeProperty (attrib.getName (), backendType));
}
return result;
}
private static Collection<BackendType> superTypes (UmlTypesystem umlTs, Stereotype stereoType) {
final List<Classifier> all = new ArrayList<Classifier> (stereoType.getExtendedMetaclasses());
all.addAll (stereoType.getSuperClasses());
final List<BackendType> result = new ArrayList<BackendType>();
for (Classifier classifier : all) {
BackendType bt = umlTs.findType (UmlTypesystem.getUniqueIdentifier (classifier));
if (bt != null)
result.add (bt);
else
result.add (umlTs.getTypeForEClassifier (classifier.eClass()));
}
return result;
}
private Type getTypeResolveProxy (Property p) {
Type result = p.getType();
if (result.eIsProxy()) {
final InternalEObject proxy = (InternalEObject) result;
final URI uri = proxy.eProxyURI();
result = (Type) EcoreUtil.resolve (proxy, p);
if (result.eIsProxy())
throw new IllegalStateException ("Couldn't resolve proxy under " + uri);
}
return result;
}
@Override
public int hashCode () {
final int prime = 31;
int result = 1;
result = prime * result + ((_stereoType == null) ? 0 : _stereoType.hashCode());
return result;
}
@Override
public boolean equals (Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final StereotypeType other = (StereotypeType) obj;
if (_stereoType == null) {
if (other._stereoType != null)
return false;
} else if (!_stereoType.equals(other._stereoType))
return false;
return true;
}
private final class StereotypeProperty extends AbstractProperty {
private final BackendType _type;
public StereotypeProperty (String name, BackendType type) {
super (StereotypeType.this, Object.class, name, true, false);
_type = type;
}
@Override
public Object getRaw (ExecutionContext ctx, Object target) {
if (target instanceof Element) {
final Element ele = (Element) target;
for (Stereotype st : ele.getAppliedStereotypes()) {
if (isStereoTypeAssignable (st, _stereoType)) {
final Object value = ele.getValue (st, getName());
// custom datatypes
// see Bug#185033
if (value instanceof EList) {
final EList<?> eList = (EList<?>) value;
final Collection<Object> values = new ArrayList<Object>();
for (Object dynObject: eList) {
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());
}
public BackendType getType (BackendTypesystem ts) {
return _type;
}
private Object getDynamicValue(final Object value) {
if (value instanceof EObject) {
final EObject dynObject = (EObject) value;
final EClass dynClass = dynObject.eClass();
final EStructuralFeature baseClassFeature = dynClass.getEStructuralFeature("base_Class");
if(baseClassFeature != null){
return dynObject.eGet(baseClassFeature,true);
}
}
return null;
}
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;
}
}
}