/**
* Copyright (c) 2014 committers of YAKINDU 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 YAKINDU - initial API and implementation
*
*/
package org.yakindu.base.types.typesystem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.yakindu.base.types.ComplexType;
import org.yakindu.base.types.Operation;
import org.yakindu.base.types.PrimitiveType;
import org.yakindu.base.types.Property;
import org.yakindu.base.types.Type;
import org.yakindu.base.types.TypesFactory;
import org.yakindu.base.types.annotations.TypeAnnotations;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
/**
* Abstract base implementation if {@link ITypeSystem}. Provides convenience
* methods to determine type compatibility.
*
* @author andreas muelder - Initial contribution and API
*
*/
public abstract class AbstractTypeSystem implements ITypeSystem {
protected Map<String, Type> typeRegistry = new HashMap<String, Type>();
protected ListMultimap<Type, Type> extendsRegistry = ArrayListMultimap.create();
protected ListMultimap<Type, Operation> extensionOperationRegistry = ArrayListMultimap.create();
protected ListMultimap<Type, Property> extensionPropertyRegistry = ArrayListMultimap.create();
protected Map<Type, Type> conversionRegistry = new HashMap<Type, Type>();
protected abstract void initRegistries();
protected Resource resource;
protected TypeAnnotations typeAnnotations;
public AbstractTypeSystem() {
resource = new ResourceImpl(URI.createURI("types"));
typeAnnotations = new TypeAnnotations();
initRegistries();
}
protected void reset() {
typeRegistry.clear();
extendsRegistry.clear();
conversionRegistry.clear();
}
public Type getType(String type) {
Type result = typeRegistry.get(type);
return result;
}
public List<Type> getSuperTypes(Type type) {
List<Type> superTypes = new ArrayList<Type>();
for (Entry<Type, Type> entry : extendsRegistry.entries()) {
if (isSame(type, entry.getKey())) {
superTypes.add(entry.getValue());
}
}
if (type instanceof ComplexType) {
ComplexType complexType = (ComplexType) type;
superTypes.addAll(complexType.getSuperTypes());
}
return superTypes;
}
public boolean isSuperType(Type subtype, Type supertype) {
List<Type> typehierachy = new ArrayList<Type>();
typehierachy.add(subtype);
collectSupertypes(subtype, typehierachy);
for (Type eObject : typehierachy) {
if (isSame(eObject, supertype))
return true;
}
return false;
}
private void collectSupertypes(Type subtypeClass, List<Type> typeHierachy) {
if (subtypeClass == null)
return;
List<Type> superTypes = getSuperTypes(subtypeClass);
for (Type superType : superTypes) {
typeHierachy.add(superType);
collectSupertypes(superType, typeHierachy);
}
}
public Collection<Type> getTypes() {
return Collections.unmodifiableCollection(typeRegistry.values());
}
public Collection<Type> getConcreteTypes() {
List<Type> result = new ArrayList<Type>();
for (Type type : getTypes()) {
if (!type.isAbstract())
result.add(type);
}
return result;
}
protected Type declarePrimitive(String name) {
PrimitiveType primitive = TypesFactory.eINSTANCE.createPrimitiveType();
primitive.setName(name);
declareType(primitive, name);
resource.getContents().add(primitive);
return primitive;
}
public void declareType(Type type, String name) {
typeRegistry.put(name, type);
}
public void removeType(String name) {
Type type = typeRegistry.get(name);
if (type != null) {
extendsRegistry.removeAll(type);
resource.getContents().remove(type);
typeRegistry.remove(type);
}
}
public void declareSuperType(Type subType, Type superType) {
extendsRegistry.put(subType, superType);
}
public void declareConversion(Type baseType, Type targetType) {
conversionRegistry.put(baseType, targetType);
}
public boolean haveCommonType(Type type1, Type type2) {
return getCommonType(type1, type2) != null;
}
public boolean isSame(Type type1, Type type2) {
return EcoreUtil.equals(type1, type2);
}
public Type getCommonType(Type type1, Type type2) {
Type result = getCommonTypeInternal(type1, type2);
if (result != null)
return result;
return null;
}
@Override
public boolean haveCommonTypeWithConversion(Type type1, Type type2) {
return getCommonTypeWithConversion(type1, type2) != null;
}
public Type getCommonTypeWithConversion(Type type1, Type type2) {
Type result = getCommonType(type1, type2);
if (result != null)
return result;
Type conversionType1 = getConversionType(type1);
if (conversionType1 != null) {
result = getCommonTypeInternal(conversionType1, type2);
if (result != null)
return result;
}
Type conversionType2 = getConversionType(type2);
if (conversionType2 != null)
return getCommonTypeInternal(type1, conversionType2);
return null;
}
private Type getCommonTypeInternal(Type type1, Type type2) {
if (isSame(type1, type2))
return type1;
if (isSuperType(type1, type2)) {
return type2;
}
if (isSuperType(type2, type1))
return type1;
List<Type> typehierachy1 = new ArrayList<Type>();
collectSupertypes(type1, typehierachy1);
List<Type> typehierachy2 = new ArrayList<Type>();
collectSupertypes(type2, typehierachy2);
for (Type type : typehierachy1) {
if (typehierachy2.contains(type)) {
return type;
}
}
for (Type type : typehierachy2) {
if (typehierachy1.contains(type)) {
return type;
}
}
return null;
}
protected Type getConversionType(Type sourceType) {
return conversionRegistry.get(sourceType);
}
public Resource getResource() {
return resource;
}
@Override
public boolean isBuiltInType(Type type) {
return typeAnnotations.hasBuiltInTypeAnnotation(type);
}
@Override
public List<Operation> getOperationExtensions(Type type) {
List<Operation> result = new ArrayList<>();
result.addAll(extensionOperationRegistry.get(type));
List<Type> superTypes = getSuperTypes(type);
for (Type superType : superTypes) {
result.addAll(extensionOperationRegistry.get(superType));
}
return result;
}
@Override
public boolean isExtensionOperation(Operation op) {
return extensionOperationRegistry.containsValue(op);
}
@Override
public List<Property> getPropertyExtensions(Type type) {
List<Property> result = new ArrayList<>();
result.addAll(extensionPropertyRegistry.get(type));
List<Type> superTypes = getSuperTypes(type);
for (Type superType : superTypes) {
result.addAll(extensionPropertyRegistry.get(superType));
}
return result;
}
@Override
public boolean isExtensionProperty(Property prop) {
return extensionPropertyRegistry.containsValue(prop);
}
}