| /** |
| * Copyright (c) 2008 - 2010 OptXware Research and Development LLC. |
| * 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: |
| * Daniel Varro - Initial API and implementation |
| */ |
| /**
|
| * Copyright (c) 2008 OptXware Research and Development LLC.
|
| * 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:
|
| * Daniel Varro - Initial API and implementation
|
| */
|
| package org.eclipse.viatra2.lpgparser.typechecker;
|
|
|
| import java.util.ArrayList;
|
| import java.util.Collection;
|
| import java.util.HashSet;
|
| import java.util.Iterator;
|
| import java.util.List;
|
| import java.util.Set;
|
|
|
| import org.eclipse.viatra2.core.IModelElement;
|
| import org.eclipse.viatra2.core.IModelManager;
|
| import org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.asm.enums.ValueKind;
|
|
|
| /**
|
| * This class provides elementary boolean operations over the lattice of
|
| * VPM types.
|
| *
|
| * Valid strings of types representations are as follows
|
| * - "TOP": All concepts and terms are instances
|
| * - "MODELELEMENT": All element from the model space are instances
|
| * - "ENTITY": All entities are instances
|
| * - "RELATION": All relations are instances
|
| * - FQN of any element from the VPM model space, with special treatment for
|
| * - datatypes.String
|
| * - datatypes.Boolean
|
| * - datatypes.Integer
|
| * - datatypes.Double
|
| * - datatypes.MultiplicityKind
|
| * - "BOTTOM": The error type, no instances are available
|
| * @author varro
|
| *
|
| */
|
| public class TypeResolver {
|
|
|
| /**
|
| * Retrieves the least upper bound (LUB, or OR-join) of types
|
| * typeStr1 and typeStr2 according to the VPM type hierarchy
|
| * @param modelManager
|
| * @param typeStr1
|
| * @param typeStr2
|
| * @return String representation of the LUB type
|
| */
|
| public String leastUpperBound(IModelManager modelManager, String typeStr1, String typeStr2) {
|
| // If typeStr1 <- typeStr2, then typeStr1
|
| if (isSupertype(modelManager, typeStr1, typeStr2)) {
|
| return typeStr1;
|
| }
|
| // If typeStr2 <- typeStr1, then typeStr2
|
| else if (isSupertype(modelManager, typeStr2, typeStr1)) {
|
| return typeStr2;
|
| }
|
| // if (typeStr1 != null && typeStr2 != null) {
|
| // String result = checkOrPrimitiveTypes(typeStr1, typeStr2);
|
| // if (result != null) {
|
| // return result;
|
| // }
|
| // }
|
| // First lookup the FQN typeStr1 in the model space
|
| IModelElement me1 = modelManager.getElementByName(typeStr1);
|
| // Then lookup the FQN typeStr2 in the model space
|
| IModelElement me2 = modelManager.getElementByName(typeStr2);
|
| // Return the TOP type if any of them is unresolvable
|
| if (me1 == null || me2 == null) {
|
| return "TOP";
|
| }
|
| // If me2 -> me1, return the more general me1
|
| if (me1.isSubtypeOf(me2)) {
|
| return me1.getFullyQualifiedName();
|
| }
|
| // If me1 -> me2, return the more general me2
|
| else if (me2.isSubtypeOf(me1)) {
|
| return me2.getFullyQualifiedName();
|
| }
|
| Set<IModelElement> commonSupertypes = new HashSet<IModelElement>();
|
| traverseSupertypeHierarchy(me1, me2, commonSupertypes);
|
| if (!commonSupertypes.isEmpty()) {
|
| if (commonSupertypes.size() == 1) {
|
| return commonSupertypes.iterator().next().getFullyQualifiedName();
|
| }
|
| else {
|
| // TODO: Report error if multiple resolutions are possible
|
| return "BOTTOM";
|
| }
|
| }
|
| if (me1.isEntity() && me2.isEntity()) {
|
| return "ENTITY";
|
| }
|
| else if (me1.isRelation() && me2.isRelation()) {
|
| return "RELATION";
|
| }
|
| else {
|
| return "MODELELEMENT";
|
| }
|
| }
|
|
|
| protected void traverseSupertypeHierarchy(IModelElement current,
|
| IModelElement other, Set<IModelElement> resolutionSet) {
|
| List<IModelElement> toTraverseList = new ArrayList<IModelElement>();
|
| for (IModelElement element : current.getSupertypes()) {
|
| if (other.isSupertypeOf(element)) {
|
| boolean subtypeFound = false;
|
| Iterator<IModelElement> iter = resolutionSet.iterator();
|
| while (!subtypeFound && iter.hasNext()) {
|
| IModelElement existing = iter.next();
|
| if (element.isSubtypeOf(existing)) {
|
| subtypeFound = true;
|
| }
|
| }
|
| if (!subtypeFound) {
|
| resolutionSet.add(element);
|
| }
|
| }
|
| else {
|
| toTraverseList.add(element);
|
| }
|
| }
|
| for (IModelElement toTraverse : toTraverseList) {
|
| traverseSupertypeHierarchy(toTraverse, other, resolutionSet);
|
| }
|
| }
|
|
|
| protected String checkOrPrimitiveTypes(String typeStr1, String typeStr2) {
|
| if (typeStr1.equals("TOP") || typeStr2.equals("TOP")) {
|
| return "TOP";
|
| }
|
| // TODO: Check how to mask a bottom
|
| // else if (typeStr1.equals("BOTTOM") || typeStr2.equals("BOTTOM") ) {
|
| // return "TOP";
|
| // }
|
|
|
| else if (typeStr1.equals("BOTTOM")) {
|
| return typeStr2;
|
| }
|
| else if ( typeStr2.equals("BOTTOM")) {
|
| return typeStr1;
|
| }
|
| else if (typeStr1.equals("MODELELEMENT") &&
|
| (typeStr2.equals("ENTITY") ||
|
| typeStr2.equals("RELATION"))) {
|
| return "MODELELEMENT";
|
| }
|
| else if (typeStr2.equals("MODELELEMENT") &&
|
| (typeStr1.equals("ENTITY") ||
|
| typeStr1.equals("RELATION"))) {
|
| return "MODELELEMENT";
|
| }
|
| else if (typeStr1.equals("ENTITY") && typeStr2.equals("ENTITY")) {
|
| return "ENTITY";
|
| }
|
| else if (typeStr1.equals("RELATION") && typeStr2.equals("RELATION")) {
|
| return "RELATION";
|
| }
|
| else if (typeStr1.equals("RELATION") && typeStr2.equals("ENTITY") ||
|
| typeStr1.equals("ENTITY") && typeStr2.equals("RELATION")) {
|
| return "MODELELEMENT";
|
| }
|
| else return null;
|
|
|
| }
|
|
|
| protected String checkAndPrimitiveTypes(String typeStr1, String typeStr2) {
|
| if (typeStr1.equals("TOP") ) {
|
| return typeStr2;
|
| }
|
| else if (typeStr2.equals("TOP")) {
|
| return typeStr1;
|
| }
|
| else if (typeStr1.equals("BOTTOM") ||
|
| typeStr2.equals("BOTTOM")) {
|
| return "BOTTOM";
|
| }
|
| // else if (typeStr1.equals("MODELELEMENT") &&
|
| // (typeStr2.equals("ENTITY") ||
|
| // typeStr2.equals("RELATION"))) {
|
| // return "TOP";
|
| // }
|
| else if ( typeStr2.equals("MODELELEMENT") && typeStr1.equals("MODELELEMENT")) {
|
| return "MODELELEMENT";
|
| }
|
| else if ( typeStr2.equals("MODELELEMENT") && typeStr1.equals("ENTITY") ||
|
| typeStr1.equals("MODELELEMENT") && typeStr2.equals("ENTITY") ) {
|
| return "ENTITY";
|
| }
|
| else if (typeStr2.equals("MODELELEMENT") && typeStr1.equals("RELATION") ||
|
| typeStr1.equals("MODELELEMENT") && typeStr2.equals("RELATION") ) {
|
| return "RELATION";
|
| }
|
|
|
| else if (typeStr1.equals("ENTITY") && typeStr2.equals("RELATION") ||
|
| typeStr2.equals("ENTITY") && typeStr1.equals("RELATION")) {
|
| return "BOTTOM";
|
| }
|
|
|
| else return null;
|
|
|
| }
|
|
|
|
|
| public String greatestLowerBound(IModelManager modelManager, String typeStr1, String typeStr2) {
|
| // If typeStr1 <- typeStr2
|
| if (isSupertype(modelManager, typeStr1, typeStr2)) {
|
| return typeStr2;
|
| }
|
| // If typeStr2 <- typeStr1
|
| else if (isSupertype(modelManager, typeStr2, typeStr1)) {
|
| return typeStr1;
|
| }
|
| // // Check for primitive types
|
| // if (typeStr1 != null && typeStr2 != null) {
|
| // String result = checkAndPrimitiveTypes(typeStr1, typeStr2);
|
| // if (result != null) {
|
| // return result;
|
| // }
|
| // }
|
| // First lookup the FQN typeStr1 in the model space
|
| IModelElement me1 = modelManager.getElementByName(typeStr1);
|
| // Then lookup the FQN typeStr2 in the model space
|
| IModelElement me2 = modelManager.getElementByName(typeStr2);
|
| // Return the TOP type if any of them is unresolvable
|
| if (me1 == null || me2 == null) {
|
| return "BOTTOM";
|
| }
|
| // If me2 -> me1, return the less general me2
|
| if (me1.isSubtypeOf(me2)) {
|
| return me2.getFullyQualifiedName();
|
| }
|
| // If me1 -> me2, return the less general me1
|
| else if (me2.isSubtypeOf(me1)) {
|
| return me1.getFullyQualifiedName();
|
| }
|
| Set<IModelElement> commonSubtypes = new HashSet<IModelElement>();
|
| traverseSubtypeHierarchy(me1, me2, commonSubtypes);
|
| if (!commonSubtypes.isEmpty()) {
|
| if (commonSubtypes.size() == 1) {
|
| return commonSubtypes.iterator().next().getFullyQualifiedName();
|
| }
|
| // TODO: Report error if multiple resolutions are possible
|
| else {
|
| return "BOTTOM";
|
| }
|
| }
|
| if (me1.isEntity() && me2.isEntity()) {
|
| return "ENTITY";
|
| }
|
| else if (me1.isRelation() && me2.isRelation()) {
|
| return "RELATION";
|
| }
|
| else {
|
| return "BOTTOM";
|
| }
|
|
|
| }
|
|
|
| protected void traverseSubtypeHierarchy(IModelElement current,
|
| IModelElement other, Set<IModelElement> resolutionSet) {
|
| List<IModelElement> toTraverseList = new ArrayList<IModelElement>();
|
| for (IModelElement element : current.getSubtypes()) {
|
| if (other.isSubtypeOf(element)) {
|
| boolean supertypeFound = false;
|
| Iterator<IModelElement> iter = resolutionSet.iterator();
|
| while (!supertypeFound && iter.hasNext()) {
|
| IModelElement existing = iter.next();
|
| if (element.isSupertypeOf(existing)) {
|
| supertypeFound = true;
|
| }
|
| }
|
| if (!supertypeFound) {
|
| resolutionSet.add(current);
|
| }
|
| }
|
| else {
|
| toTraverseList.add(element);
|
| }
|
| }
|
| for (IModelElement toTraverse : toTraverseList) {
|
| traverseSubtypeHierarchy(toTraverse, other, resolutionSet);
|
| }
|
| }
|
|
|
| /**
|
| * Checks if the model element represented by supertypeStr is a supertype of
|
| * the model element represented by subtypeStr according to the model manager.
|
| * If any of the two strings are "TOP", it returns with true.
|
| * @param modelManager : {@link IModelManager} to query types
|
| * @param supertypeStr : string representation of the supertype
|
| * @param subtypeStr : string representation of the subtype
|
| * @return true, if the supertype relation holds
|
| */
|
| public boolean isSupertype(IModelManager modelManager, String supertypeStr, String subtypeStr) {
|
| // TODO: Check if the handling of TOP and BOTTOM is correct
|
| if (supertypeStr.equals("TOP") || subtypeStr.equals("TOP")) {
|
| return true;
|
| }
|
| else if (supertypeStr.equals("BOTTOM")) {
|
| return false;
|
| }
|
| else if (subtypeStr.equals("BOTTOM")) {
|
| return true;
|
| }
|
| else if (supertypeStr.equals("MODELELEMENT")) {
|
| if (subtypeStr.equals("MODELELEMENT") ||
|
| subtypeStr.equals("ENTITY") ||
|
| subtypeStr.equals("RELATION")) {
|
| return true;
|
| }
|
| else {
|
| IModelElement subElem = modelManager.getElementByName(subtypeStr);
|
| if (subElem != null) {
|
| return true;
|
| }
|
| else {
|
| return false;
|
| }
|
| }
|
|
|
| }
|
| else if (supertypeStr.equals("ENTITY")) {
|
| if (subtypeStr.equals("ENTITY")) {
|
| return true;
|
| }
|
| else if (subtypeStr.equals("MODELELEMENT") ||
|
| subtypeStr.equals("RELATION")) {
|
| return false;
|
| }
|
| else {
|
| IModelElement subElem = modelManager.getElementByName(subtypeStr);
|
| if (subElem != null && subElem.isEntity()) {
|
| return true;
|
| }
|
| else {
|
| return false;
|
| }
|
| }
|
| }
|
| else if (supertypeStr.equals("RELATION")) {
|
| if (subtypeStr.equals("RELATION")) {
|
| return true;
|
| }
|
| else if (subtypeStr.equals("MODELELEMENT") ||
|
| subtypeStr.equals("ENTITY")) {
|
| return false;
|
| }
|
| else {
|
| IModelElement subElem = modelManager.getElementByName(subtypeStr);
|
| if (subElem != null && subElem.isRelation()) {
|
| return true;
|
| }
|
| else {
|
| return false;
|
| }
|
| }
|
| }
|
| else {
|
| IModelElement superElem = modelManager.getElementByName(supertypeStr);
|
| IModelElement subElem = modelManager.getElementByName(subtypeStr);
|
| if (superElem != null && subElem != null && (subElem == superElem ||
|
| superElem.isSupertypeOf(subElem))) {
|
| return true;
|
| }
|
| else {
|
| return false;
|
| }
|
| }
|
| }
|
|
|
|
|
| /**
|
| * Returns the fully qualified name of the type of a model element
|
| * @param modelElement : the model element to be queried
|
| * @return : the FQN of the type of this model element
|
| */
|
| public String lookupType(IModelElement modelElement) {
|
| Collection<IModelElement> types = modelElement.getTypes();
|
| int size = types.size();
|
| switch (size) {
|
| case 0:
|
| // No type is found
|
| if (modelElement.isEntity()) {
|
| return "ENTITY";
|
| }
|
| else if (modelElement.isRelation()) {
|
| return "RELATION";
|
| }
|
| else return "MODELELEMENT";
|
| case 1:
|
| // A single type is found
|
| return types.iterator().next().getFullyQualifiedName();
|
|
|
| default:
|
| // More than a single type is found
|
| // TODO: Refine this if needed to more sophisticated type handling
|
| if (modelElement.isEntity()) {
|
| return "ENTITY";
|
| }
|
| else if (modelElement.isRelation()) {
|
| return "RELATION";
|
| }
|
| else return "MODELELEMENT";
|
| }
|
| }
|
|
|
|
|
| /**
|
| * Obtains the ValueKind for a given type. It resolves references to
|
| * built-in types, i.e. String, Boolean, Integer, Double
|
| * (which reside in the "datatypes" model fragment)
|
| * @param typeStr : fully qualified name of a model element
|
| * @return ValueKind
|
| */
|
| public ValueKind getValueKind(String typeStr) {
|
| if (typeStr.equals("datatypes.String")) {
|
| return ValueKind.STRING_LITERAL;
|
| }
|
| else if (typeStr.equals("datatypes.Boolean")) {
|
| return ValueKind.BOOLEAN_LITERAL;
|
| }
|
| else if (typeStr.equals("datatypes.Integer")) {
|
| return ValueKind.INTEGER_LITERAL;
|
| }
|
| else if (typeStr.equals("datatypes.Double")) {
|
| return ValueKind.DOUBLE_LITERAL;
|
| }
|
| else if (typeStr.equals("datatypes.Multiplicity")) {
|
| return ValueKind.MULTIPLICITY_LITERAL;
|
| }
|
| else if (typeStr.equals("TOP")) {
|
| return ValueKind.UNDEF_LITERAL;
|
| }
|
| else if (typeStr.equals("BOTTOM")) {
|
| return ValueKind.ERROR_LITERAL;
|
| }
|
| else {
|
| return ValueKind.MODELELEMENT_LITERAL;
|
| }
|
| }
|
|
|
|
|
| }
|