blob: 516fe660fabcba5ccc83c409fa8a8d66253d9979 [file] [log] [blame]
/**
* Copyright (c) 2008 Borland Software Corp.
*
* 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:
* Alexander Shatalin (Borland) - initial API and implementation
*/
package org.eclipse.gmf.internal.xpand.migration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Map.Entry;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.gmf.internal.xpand.BuiltinMetaModel;
import org.eclipse.gmf.internal.xpand.BuiltinMetaModelExt;
import org.eclipse.gmf.internal.xpand.ResourceManager;
import org.eclipse.gmf.internal.xpand.ResourceMarker;
import org.eclipse.gmf.internal.xpand.eval.EvaluationListener;
import org.eclipse.gmf.internal.xpand.expression.AnalysationIssue;
import org.eclipse.gmf.internal.xpand.expression.ExecutionContext;
import org.eclipse.gmf.internal.xpand.expression.ExecutionContextImpl;
import org.eclipse.gmf.internal.xpand.expression.Variable;
import org.eclipse.gmf.internal.xpand.expression.ast.BooleanLiteral;
import org.eclipse.gmf.internal.xpand.expression.ast.BooleanOperation;
import org.eclipse.gmf.internal.xpand.expression.ast.Case;
import org.eclipse.gmf.internal.xpand.expression.ast.Cast;
import org.eclipse.gmf.internal.xpand.expression.ast.ChainExpression;
import org.eclipse.gmf.internal.xpand.expression.ast.CollectionExpression;
import org.eclipse.gmf.internal.xpand.expression.ast.ConstructorCallExpression;
import org.eclipse.gmf.internal.xpand.expression.ast.Expression;
import org.eclipse.gmf.internal.xpand.expression.ast.FeatureCall;
import org.eclipse.gmf.internal.xpand.expression.ast.IfExpression;
import org.eclipse.gmf.internal.xpand.expression.ast.IntegerLiteral;
import org.eclipse.gmf.internal.xpand.expression.ast.LetExpression;
import org.eclipse.gmf.internal.xpand.expression.ast.ListLiteral;
import org.eclipse.gmf.internal.xpand.expression.ast.NullLiteral;
import org.eclipse.gmf.internal.xpand.expression.ast.OperationCall;
import org.eclipse.gmf.internal.xpand.expression.ast.RealLiteral;
import org.eclipse.gmf.internal.xpand.expression.ast.StringLiteral;
import org.eclipse.gmf.internal.xpand.expression.ast.SwitchExpression;
import org.eclipse.gmf.internal.xpand.expression.ast.TypeSelectExpression;
import org.eclipse.gmf.internal.xpand.expression.parser.ExpressionLexer;
import org.eclipse.gmf.internal.xpand.expression.parser.ExpressionParser;
import org.eclipse.gmf.internal.xpand.migration.MigrationException.Type;
import org.eclipse.gmf.internal.xpand.util.ClassLoadContext;
import org.eclipse.gmf.internal.xpand.xtend.ast.Extension;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
public class ExpressionMigrationFacade {
public static final String LF = System.getProperty("line.separator");
static final EcoreEnvironmentFactory ECORE_ENV_FACTORY = new EcoreEnvironmentFactory(null);
private static final Set<EOperation> infixOperations = new HashSet<EOperation>(Arrays.asList(new EOperation[] {
BuiltinMetaModel.Boolean_NE,
BuiltinMetaModel.Int_Unary_Minus,
BuiltinMetaModel.Double_Unary_Minus,
BuiltinMetaModel.Int_Minus_Double,
BuiltinMetaModel.Int_Minus_Int,
BuiltinMetaModel.Double_Minus_Double,
BuiltinMetaModel.Double_Minus_Int,
BuiltinMetaModel.Int_Plus_Double,
BuiltinMetaModel.Int_Plus_Int,
BuiltinMetaModel.Double_Plus_Double,
BuiltinMetaModel.Double_Plus_Int,
BuiltinMetaModel.Int_Mult_Double,
BuiltinMetaModel.Int_Mult_Int,
BuiltinMetaModel.Double_Mult_Double,
BuiltinMetaModel.Double_Mult_Int,
BuiltinMetaModel.Int_Div_Double,
BuiltinMetaModel.Double_Div_Double,
BuiltinMetaModel.Double_Div_Int,
BuiltinMetaModel.Int_Less,
BuiltinMetaModel.Int_LessOrEqual,
BuiltinMetaModel.Int_Greater,
BuiltinMetaModel.Int_GreatOrEqual,
BuiltinMetaModel.EString_Plus_EJavaObject,
BuiltinMetaModel.Object_EQ,
BuiltinMetaModel.Object_NotEQ
}));
private static final Set<EOperation> collectionOperations = new HashSet<EOperation>(Arrays.asList(new EOperation[] {
BuiltinMetaModel.Collection_Add,
BuiltinMetaModel.Collection_AddAll,
BuiltinMetaModel.Collection_Clear,
BuiltinMetaModel.Collection_Contains,
BuiltinMetaModel.Collection_ContainsAll,
BuiltinMetaModel.Collection_Flatten,
BuiltinMetaModel.Collection_Intersect,
BuiltinMetaModel.Collection_IsEmpty,
BuiltinMetaModel.Collection_Size,
BuiltinMetaModel.Collection_ToList,
BuiltinMetaModel.Collection_ToSet,
BuiltinMetaModel.Collection_Union,
BuiltinMetaModel.Collection_Without,
BuiltinMetaModel.List_First,
BuiltinMetaModel.List_Get,
BuiltinMetaModel.List_IndexOf,
BuiltinMetaModel.List_Last,
BuiltinMetaModel.List_PurgeDups,
BuiltinMetaModel.List_WithoutFirst,
BuiltinMetaModel.List_WithoutLast
}));
private Stack<Expression> expressionsStack = new Stack<Expression>();
private StringBuilder output = new StringBuilder();
private MigrationExecutionContext ctx;
private int returnPosition;
private VariableNameDispatcher variableDispatcher;
private Expression rootExpression;
private TypeManager typeManager;
private ModelManager modelManager;
private Stack<QvtExecutionContext> qvtContexts = new Stack<QvtExecutionContext>();
private EClassifier rootExpressionType;
private String resourceName;
private HashMap<String, EClassifier> envVariables;
public ExpressionMigrationFacade(String expression, EClassifier requiredType, Map<String, EClassifier> envVariables, TypeManager typeManager, ModelManager modelManager, VariableNameDispatcher variableDispatcher,
MigrationExecutionContext context, String resourceName) throws MigrationException {
this(parseXtend(expression), requiredType, envVariables, typeManager, modelManager, variableDispatcher, context, resourceName);
Set<AnalysationIssue> issues = new HashSet<AnalysationIssue>();
rootExpression.analyze(ctx, issues);
if (MigrationException.hasErrors(issues)) {
throw new MigrationException(issues, resourceName);
}
}
public ExpressionMigrationFacade(Expression expression, EClassifier requiredType, Map<String, EClassifier> envVariables, TypeManager typeManager, ModelManager modelManager, VariableNameDispatcher variableDispatcher,
MigrationExecutionContext context, String resourceName) {
rootExpression = expression;
rootExpressionType = requiredType;
this.envVariables = new HashMap<String, EClassifier>(envVariables);
this.typeManager = typeManager;
this.modelManager = modelManager;
this.variableDispatcher = variableDispatcher;
this.resourceName = resourceName;
ctx = context;
markReturnPosition();
}
private static Expression parseXtend(final String expression) {
final ExpressionLexer scanner = new ExpressionLexer(expression.toCharArray(), "nofile");
final ExpressionParser parser = new ExpressionParser(scanner);
scanner.lexer(parser);
return parser.parser();
}
public Expression getRootExpression() {
return rootExpression;
}
public StringBuilder migrate() throws MigrationException {
qvtContexts.push(QvtExecutionContext.createNewContext(envVariables));
try {
EClassifier expressionQvtType = migrateExpression(rootExpression);
internalConvertTypes(expressionQvtType, rootExpressionType);
} finally {
qvtContexts.pop();
}
return output;
}
// TODO: similar to internalMigrateParameterCollectionToMain() ?
private void internalConvertTypes(EClassifier actualType, EClassifier expectedType) {
if (expectedType != BuiltinMetaModel.VOID && BuiltinMetaModel.isCollectionType(expectedType)) {
assert BuiltinMetaModel.isCollectionType(actualType);
if (BuiltinMetaModelExt.isListType(expectedType)) {
if (BuiltinMetaModelExt.isSetType(actualType) || BuiltinMetaModelExt.isOrderedSetType(actualType) || BuiltinMetaModelExt.isBagType(actualType)) {
write("->asSequence()");
} else if (BuiltinMetaModelExt.isAbstractCollectionType(actualType)) {
internalMigrateCollectionToBag();
write("->asSequence()");
}
} else if (BuiltinMetaModelExt.isSetType(expectedType)) {
if (BuiltinMetaModelExt.isListType(actualType)) {
write("->asOrderedSet()");
} else if (BuiltinMetaModelExt.isBagType(actualType)) {
write("->asSet()");
} else if (BuiltinMetaModelExt.isAbstractCollectionType(actualType)) {
internalMigrateCollectionToBag();
write("->asSet()");
}
}
// Abstract collection should be compatible with any other kind
// on collections
}
// else if (EcorePackage.eINSTANCE.getEString() == expectedType && actualType != EcorePackage.eINSTANCE.getEString()) {
// write(".repr()");
// }
}
int getReturnPosition() {
return returnPosition;
}
// Returning xpand types from here. This method can be modified to make use
// of QVT type system instead.
private EClassifier migrateExpression(Expression expression) throws MigrationException {
expressionsStack.push(expression);
try {
if (expression instanceof BooleanOperation) {
return migrateBooleanOperation((BooleanOperation) expression);
} else if (expression instanceof Cast) {
return migrateCast((Cast) expression);
} else if (expression instanceof ChainExpression) {
return migrateChainExpression((ChainExpression) expression);
} else if (expression instanceof ConstructorCallExpression) {
return migrateConstructorCallExpression((ConstructorCallExpression) expression);
} else if (expression instanceof CollectionExpression) {
return migrateCollectionExpression((CollectionExpression) expression);
} else if (expression instanceof OperationCall) {
return migrateOperationCall((OperationCall) expression);
} else if (expression instanceof TypeSelectExpression) {
return migrateTypeSelectExpression((TypeSelectExpression) expression);
} else if (expression instanceof FeatureCall) {
return migrateFeatureCall((FeatureCall) expression);
} else if (expression instanceof IfExpression) {
return migrateIfExpression((IfExpression) expression);
} else if (expression instanceof LetExpression) {
return migrateLetExpression((LetExpression) expression);
} else if (expression instanceof ListLiteral) {
return migrateListLiteral((ListLiteral) expression);
} else if (expression instanceof BooleanLiteral) {
return migrateBooleanLiteral((BooleanLiteral) expression);
} else if (expression instanceof IntegerLiteral) {
return migrateIntegerLiteral((IntegerLiteral) expression);
} else if (expression instanceof NullLiteral) {
return migrateNullLiteral((NullLiteral) expression);
} else if (expression instanceof RealLiteral) {
return migrateRealLiteral((RealLiteral) expression);
} else if (expression instanceof StringLiteral) {
return migrateStringLiteral((StringLiteral) expression);
} else if (expression instanceof SwitchExpression) {
return migrateSwitchExpression((SwitchExpression) expression);
} else {
throw new MigrationException(Type.UNSUPPORTED_EXPRESSION, resourceName, expression, expression.getClass().getName());
}
} finally {
expressionsStack.pop();
}
}
private EClassifier migrateSwitchExpression(SwitchExpression switchExpression) throws MigrationException {
Collection<EClassifier> expressionTypes = new ArrayList<EClassifier>();
if (switchExpression.getCases().size() == 0) {
expressionTypes.add(migrateExpression(switchExpression.getDefaultExpr()));
} else {
// TODO: define additional variable here.
write("switch { ");
for (Case caseExpression : switchExpression.getCases()) {
write("case (");
migrateExpression(switchExpression.getSwitchExpr());
write(" = ");
migrateExpression(caseExpression.getCondition());
write(") ");
expressionTypes.add(migrateExpression(caseExpression.getThenPart()));
write("; ");
}
write("else ");
expressionTypes.add(migrateExpression(switchExpression.getDefaultExpr()));
write("; }");
}
// TODO: check different types of collections was produced
return BuiltinMetaModelExt.getCommonSuperType(expressionTypes);
}
private EClassifier migrateStringLiteral(StringLiteral expression) {
write("'");
write(escape(expression.getValue()));
write("'");
return EcorePackage.eINSTANCE.getEString();
}
private String escape(String value) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < value.length(); i++) {
char nextChar = value.charAt(i);
if (nextChar == '\b') {
sb.append("\\b");
// leaving tab char without escaping
// } else if (nextChar == '\t') {
// sb.append("\\t");
} else if (nextChar == '\n') {
sb.append("\\n");
} else if (nextChar == '\f') {
sb.append("\\f");
} else if (nextChar == '\r') {
sb.append("\\r");
} else if (nextChar == '\"') {
sb.append("\\\"");
} else if (nextChar == '\'') {
sb.append("\\\'");
} else if (nextChar == '\\') {
sb.append("\\\\");
} else {
sb.append(nextChar);
}
}
return sb.toString();
}
private EClassifier migrateRealLiteral(RealLiteral realLiteral) {
write(new Double(realLiteral.getLiteralValue()).toString());
return EcorePackage.eINSTANCE.getEDouble();
}
private EClassifier migrateNullLiteral(NullLiteral expression) {
write("null");
return BuiltinMetaModel.VOID;
}
private EClassifier migrateIntegerLiteral(IntegerLiteral integerLiteral) {
write(new Integer(integerLiteral.getLiteralValue()).toString());
return EcorePackage.eINSTANCE.getEInt();
}
private EClassifier migrateBooleanLiteral(BooleanLiteral booleanLiteral) {
write(Boolean.valueOf(booleanLiteral.getLiteralValue()) ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
return EcorePackage.eINSTANCE.getEBoolean();
}
private EClassifier migrateListLiteral(ListLiteral listLiteral) throws MigrationException {
Collection<EClassifier> expressionTypes = new ArrayList<EClassifier>();
write("Sequence { ");
for (int i = 0; i < listLiteral.getElements().length; i++) {
if (i > 0) {
write(", ");
}
expressionTypes.add(migrateExpression(listLiteral.getElements()[i]));
}
write(" }");
return BuiltinMetaModelExt.getListType(BuiltinMetaModelExt.getCommonSuperType(expressionTypes));
}
private EClassifier migrateLetExpression(LetExpression letExpression) throws MigrationException {
String varName = modelManager.getOclKeywordManager().getValidIdentifierValue(letExpression.getVarName());
write("let ");
write(varName);
write(" = ");
pushContextWithVariable(varName, migrateExpression(letExpression.getVarExpression()));
try {
write(" in ");
return migrateExpression(letExpression.getTargetExpression());
} finally {
qvtContexts.pop();
}
}
private EClassifier migrateIfExpression(IfExpression ifExpression) throws MigrationException {
write("(if ");
migrateExpression(ifExpression.getCondition());
write(" then ");
EClassifier thenType = migrateExpression(ifExpression.getThenPart());
write(" else ");
EClassifier elseType = migrateExpression(ifExpression.getElsePart());
write(" endif)");
// TODO: check if then/else produces different types of collections..
return BuiltinMetaModelExt.getCommonSuperType(thenType, elseType);
}
private EClassifier migrateConstructorCallExpression(ConstructorCallExpression constructorCall) throws MigrationException {
write("object ");
EClassifier type = ctx.getTypeForName(constructorCall.getType().getValue());
if (type == null) {
throw new MigrationException(Type.TYPE_NOT_FOUND, resourceName, constructorCall.getType(), constructorCall.getType().getValue());
}
write(typeManager.getQvtFQName(type));
write(" {}");
return type;
}
private EClassifier migrateChainExpression(ChainExpression chainExpression) throws MigrationException {
write("compute (");
String varName = variableDispatcher.getNextVariableName();
write(varName);
write(" : ");
int typePosition = getCurrentPosition();
write(") {");
internalMigrateChainExpressionFirstArg(chainExpression.getFirst());
write(varName);
write(" = ");
EClassifier varType = migrateExpression(chainExpression.getNext());
write(typeManager.getQvtFQName(varType), typePosition);
write("}");
return varType;
}
private void internalMigrateChainExpressionFirstArg(Expression first) throws MigrationException {
if (first instanceof ChainExpression) {
ChainExpression innerChain = (ChainExpression) first;
internalMigrateChainExpressionFirstArg(innerChain.getFirst());
migrateExpression(innerChain.getNext());
} else {
migrateExpression(first);
}
write("; ");
}
private EClassifier migrateBooleanOperation(BooleanOperation booleanOperation) throws MigrationException {
int operationPrecedence = getPrecedence(booleanOperation);
int leftPosition = getCurrentPosition();
migrateExpression(booleanOperation.getLeft());
encloseExpressionIntoParenthesis(booleanOperation.getLeft(), operationPrecedence, leftPosition, false);
if (booleanOperation.isAndOperation()) {
write(" and ");
} else if (booleanOperation.isOrOperation()) {
write(" or ");
} else if (booleanOperation.isImpliesOperation()) {
write(" implies ");
} else {
throw new MigrationException(Type.UNSUPPORTED_BOOLEAN_OPERATION, resourceName, booleanOperation, booleanOperation.getOperator());
}
int rightPosition = getCurrentPosition();
migrateExpression(booleanOperation.getRight());
encloseExpressionIntoParenthesis(booleanOperation.getRight(), operationPrecedence, rightPosition, true);
return EcorePackage.eINSTANCE.getEBoolean();
}
private EClassifier migrateCast(Cast cast) throws MigrationException {
EClassifier migratedExpressionType = migrateExpression(cast.getTarget());
EClassifier type = ctx.getTypeForName(cast.getType().getValue());
if (type == null) {
throw new MigrationException(Type.TYPE_NOT_FOUND, resourceName, cast.getType(), cast.getType().getValue());
}
if (BuiltinMetaModel.isCollectionType(type)) {
// This operation is not supported now.
return migratedExpressionType;
}
write(".oclAsType(");
write(typeManager.getQvtFQName(type));
write(")");
return type;
}
private EClassifier migrateTypeSelectExpression(TypeSelectExpression typeSelectExpression) throws MigrationException {
int placeholder = getCurrentPosition();
EClassifier targetQvtType = migrateExpression(typeSelectExpression.getTarget());
EClassifier type = ctx.getTypeForName(typeSelectExpression.getTypeLiteral().getValue());
if (type == null) {
throw new MigrationException(Type.TYPE_NOT_FOUND, resourceName, typeSelectExpression.getTypeLiteral(), typeSelectExpression.getTypeLiteral().getValue());
}
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(typeSelectExpression);
if (false == expressionTrace instanceof TypeSelectExpressionTrace) {
throw new MigrationException(Type.UNSUPPORTED_TYPE_SELECT_EXPRESSION_TRACE, resourceName, typeSelectExpression, expressionTrace);
}
TypeSelectExpressionTrace trace = (TypeSelectExpressionTrace) expressionTrace;
if (!trace.isValid()) {
throw new MigrationException(Type.UNSUPPORTED_TYPE_SELECT_EXPRESSION, resourceName, typeSelectExpression, trace);
}
internalMigrateTypeSelectCastingCollectionToBag(targetQvtType, typeManager.getQvtFQName(type), placeholder);
if (!BuiltinMetaModelExt.isListType(targetQvtType) && !BuiltinMetaModelExt.isOrderedSetType(targetQvtType)) {
write("->asSequence()");
}
return BuiltinMetaModelExt.isOrderedSetType(targetQvtType) ? BuiltinMetaModelExt.getOrderedSetType(type) : BuiltinMetaModelExt.getListType(type);
}
private void internalMigrateTypeSelectCastingCollectionToBag(EClassifier collectionType, String typeName, int placeholder) {
assert BuiltinMetaModel.isCollectionType(collectionType);
if (BuiltinMetaModelExt.isAbstractCollectionType(collectionType)) {
internalMigrateCollectionToBag();
}
internalMigrateTypeSelect(typeName, placeholder, getCurrentPosition());
}
private void internalMigrateTypeSelect(String typeName, int expressionStartPosition, int expressionEndPosition) {
// TODO: This method should write braces around expression starting
// at placeholder position conditionally depending on the last char
// in output sequence.
StringBuilder sb = new StringBuilder();
sb.append(")[");
sb.append(typeName);
sb.append("]");
write(sb, expressionEndPosition);
write("(", expressionStartPosition);
}
// TODO: use ->asSequence() here in addition?
private void internalMigrateCollectionToBag() {
String iteratorName = variableDispatcher.getNextIteratorName();
write("->collect(");
write(iteratorName);
write(" | ");
write(iteratorName);
write(")");
}
private EClassifier migrateCollectionExpression(CollectionExpression collectionExpression) throws MigrationException {
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(collectionExpression);
if (false == expressionTrace instanceof CollectionExpressionTrace) {
throw new MigrationException(Type.UNSUPPORTED_COLLECTION_EXPRESSION_TRACE, resourceName, collectionExpression, expressionTrace);
}
CollectionExpressionTrace trace = (CollectionExpressionTrace) expressionTrace;
int placeholder = getCurrentPosition();
boolean hasNegation = false;
EClassifier targetQvtType;
if (collectionExpression.getTarget() != null) {
targetQvtType = migrateExpression(collectionExpression.getTarget());
} else {
write(Environment.SELF_VARIABLE_NAME);
targetQvtType = getEnvVariableType(ExecutionContext.IMPLICIT_VARIABLE);
}
assert BuiltinMetaModel.isCollectionType(targetQvtType);
EClassifier innerTargetQvtType = BuiltinMetaModel.getInnerType(targetQvtType);
write("->");
switch (trace.getType()) {
case NOTEXISTS_REF:
hasNegation = true;
write("not ", placeholder);
write("exists");
break;
case COLLECT_REF:
case SELECT_REF:
case REJECT_REF:
case EXISTS_REF:
case FORALL_REF:
write(collectionExpression.getName().getValue());
break;
case INCORRECT_EXPRESSION_TYPE:
case UNDESOLVED_TARGET_TYPE:
throw new MigrationException(Type.UNSUPPORTED_COLLECTION_EXPRESSION, resourceName, collectionExpression, trace);
default:
throw new MigrationException(Type.UNSUPPORTED_COLLECTION_EXPRESSION_TRACE, resourceName, collectionExpression, trace);
}
write("(");
String varName = collectionExpression.getElementName();
write(modelManager.getOclKeywordManager().getValidIdentifierValue(varName));
pushContextWithVariable(varName, innerTargetQvtType);
EClassifier expressionType;
try {
write(" | ");
expressionType = migrateExpression(collectionExpression.getClosure());
} finally {
qvtContexts.pop();
}
write(")");
try {
// Determining actual result type
switch (trace.getType()) {
case NOTEXISTS_REF:
case EXISTS_REF:
case FORALL_REF:
return EcorePackage.eINSTANCE.getEBoolean();
case COLLECT_REF:
if (BuiltinMetaModelExt.isSetType(trace.getResultType())) {
// Does not work now due to the bug in xpand implementation
// - see "TODO [AS]" comment in CollectionExpression
write("->asSet()");
// TODO: add flatten here?
return BuiltinMetaModel.getSetType(expressionType);
}else if (BuiltinMetaModelExt.isListType(trace.getResultType())) {
return BuiltinMetaModelExt.getListType(expressionType);
} else {
return BuiltinMetaModelExt.getBagType(expressionType);
}
case SELECT_REF:
case REJECT_REF:
return targetQvtType;
}
// Unreachable
return null;
} finally {
if (hasNegation) {
addNegationBraces(placeholder);
}
}
}
private void addNegationBraces(int placeholder) {
if (expressionsStack.size() == 1) {
return;
}
// TODO: check for the type of parent expression here + add braces
// conditionaly
// Expression parentExpression =
// expressionsStack.get(expressionsStack.size() - 2);
// check for the type of parent expression;
write("(", placeholder);
write(")");
}
private EClassifier migrateOperationCall(OperationCall operationCall) throws MigrationException {
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(operationCall);
if (false == expressionTrace instanceof OperationCallTrace) {
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL_TRACE, resourceName, operationCall, expressionTrace);
}
OperationCallTrace trace = (OperationCallTrace) expressionTrace;
switch (trace.getType()) {
case UNDESOLVED_PARAMETER_TYPE:
case UNDESOLVED_TARGET_TYPE:
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL, resourceName, operationCall, trace.toString());
case STATIC_EXTENSION_REF:
if (trace.isStaticQvtoCall()) {
write(modelManager.getName(operationCall, trace));
write("(");
internalMigrateOperationCallParameters(operationCall, trace.getParamTypes());
write(")");
} else {
List<EClassifier> expectedParameterTypes = trace.getParamTypes();
assert operationCall.getParams().length > 0;
assert expectedParameterTypes == null || operationCall.getParams().length == expectedParameterTypes.size();
EClassifier actualParameterType = migrateExpression(operationCall.getParams()[0]);
if (expectedParameterTypes != null) {
internalConvertTypes(actualParameterType, expectedParameterTypes.get(0));
}
if (BuiltinMetaModel.isCollectionType(actualParameterType)) {
write("->");
} else {
write(".");
}
write(modelManager.getName(operationCall, trace));
write("(");
for (int i = 1; i < operationCall.getParams().length; i++) {
if (i > 1) {
write(", ");
}
actualParameterType = migrateExpression(operationCall.getParams()[i]);
if (expectedParameterTypes != null) {
internalConvertTypes(actualParameterType, expectedParameterTypes.get(i));
}
}
write(")");
}
return trace.getResultType();
case OPERATION_REF:
if (isInfixOperation(trace)) {
return internalMigrateInfixOperation(trace, operationCall);
} else if (isCollectionOperation(trace)) {
return internalMigrateCollectionOperationCall(trace, operationCall);
} else if (isEClassOperationOnTypeLiteral(trace, operationCall)) {
return internalMigrateEClassOperationOnTypeLiteral(trace, operationCall);
}
// else same as IMPLICIT_COLLECT_OPERATION_REF:
case IMPLICIT_COLLECT_OPERATION_REF:
EOperation eOperation = trace.getEOperation();
assert eOperation != null;
EClassifier targetQvtType = trace.getTargetType();
// getTarget can be null for implicit call to "self" variable in xpand
if (operationCall.getTarget() != null) {
targetQvtType = migrateExpression(operationCall.getTarget());
write(".");
}
write(modelManager.getName(operationCall, trace));
write("(");
if (BuiltinMetaModel.EString_SubString_StartEnd == eOperation) {
write("1 + ");
}
List<EClassifier> parameterTypes = internalMigrateOperationCallParameters(operationCall, trace.getParamTypes());
if (BuiltinMetaModel.EString_Plus_EJavaObject == eOperation) {
assert parameterTypes.size() == 1;
if (parameterTypes.get(0) != EcorePackage.eINSTANCE.getEString()) {
write(".repr()");
}
}
write(")");
if (trace.getType() == OperationCallTrace.Type.OPERATION_REF) {
return getTypedElementQvtType(eOperation);
} else {
if (!BuiltinMetaModel.isParameterizedType(targetQvtType)) {
throw new MigrationException(Type.UNSUPPORTED_EXPRESSION, resourceName, operationCall.getTarget(), "Implicit collect is not supported for simple types: " + targetQvtType.toString() + "." + operationCall.getName().getValue());
}
convertImplicitCollectProduct(targetQvtType);
return BuiltinMetaModelExt.getListType(BuiltinMetaModel.getInnerType(targetQvtType));
}
case EXTENSION_REF:
if (!trace.isStaticQvtoCall()) {
if (operationCall.getTarget() != null) {
migrateExpression(operationCall.getTarget());
} else {
// in case of xpand migration substituting implicit target of static extension call
write(Environment.SELF_VARIABLE_NAME);
}
if (BuiltinMetaModel.isCollectionType(trace.getParamTypes().get(0))) {
write("->");
} else {
write(".");
}
}
write(modelManager.getName(operationCall, trace));
write("(");
if (trace.isStaticQvtoCall()) {
if (operationCall.getTarget() != null) {
migrateExpression(operationCall.getTarget());
} else {
// in case of xpand migration substituting implicit target of static extension call
write(Environment.SELF_VARIABLE_NAME);
}
if (operationCall.getParams().length > 0) {
write(", ");
}
}
internalMigrateOperationCallParameters(operationCall, withoutFirst(trace.getParamTypes()));
write(")");
return trace.getResultType();
case IMPLICIT_COLLECT_EXTENSION_REF:
assert operationCall.getTarget() != null;
EClassifier implicitCollectTargetQvtType = migrateExpression(operationCall.getTarget());
if (!BuiltinMetaModel.isParameterizedType(implicitCollectTargetQvtType)) {
throw new MigrationException(Type.UNSUPPORTED_EXPRESSION, resourceName, operationCall.getTarget(), "Implicit collect is not supported for simple types: " + implicitCollectTargetQvtType.toString() + "." + operationCall.getName().getValue());
}
String iteratorName = variableDispatcher.getNextIteratorName();
write("->collect(");
write(iteratorName);
write(" | ");
if (!trace.isStaticQvtoCall()) {
write(iteratorName);
if (BuiltinMetaModel.isCollectionType(BuiltinMetaModel.getInnerType(implicitCollectTargetQvtType))) {
write("->");
} else {
write(".");
}
}
write(modelManager.getName(operationCall, trace));
write("(");
if (trace.isStaticQvtoCall()) {
write(iteratorName);
if (operationCall.getParams().length > 0) {
write(", ");
}
}
internalMigrateOperationCallParameters(operationCall, withoutFirst(trace.getParamTypes()));
write(")");
write(")");
convertImplicitCollectProduct(implicitCollectTargetQvtType);
return trace.getResultType();
default:
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL_TRACE, resourceName, operationCall, trace);
}
}
private EClassifier internalMigrateEClassOperationOnTypeLiteral(OperationCallTrace trace, OperationCall operationCall) throws MigrationException {
EOperation eOperation = trace.getEOperation();
assert eOperation != null;
if ("isSuperTypeOf".equals(eOperation.getName())) {
assert operationCall.getParams().length == 1;
Expression parameter = operationCall.getParams()[0];
if (parameter instanceof OperationCall) {
OperationCall parameterOpCall = (OperationCall) parameter;
if ("eClass".equals(parameterOpCall.getName().getValue()) && parameterOpCall.getTarget() != null && parameterOpCall.getParams().length == 0) {
migrateExpression(parameterOpCall.getTarget());
write(".oclIsKindOf(");
Expression target = operationCall.getTarget();
assert target instanceof FeatureCall;
EClassifier type = ctx.getTypeForName(((FeatureCall) target).getName().getValue());
write(typeManager.getQvtFQName(type));
write(")");
return EcorePackage.eINSTANCE.getEBoolean();
}
}
}
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL, resourceName, operationCall.getName(), "Migration of operation \"" + eOperation.getName()
+ "\" is not supported for TypeLiteral expressions now");
}
private boolean isEClassOperationOnTypeLiteral(OperationCallTrace trace, OperationCall operationCall) throws MigrationException {
if (trace.getTargetType() == EcorePackage.eINSTANCE.getEClass()) {
Expression target = operationCall.getTarget();
if (target instanceof FeatureCall) {
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(target);
if (false == expressionTrace instanceof FeatureCallTrace) {
throw new MigrationException(Type.UNSUPPORTED_FEATURE_CALL_TRACE, resourceName, target, expressionTrace);
}
return ((FeatureCallTrace) expressionTrace).getType() == FeatureCallTrace.Type.UNSUPPORTED_CLASSIFIER_REF;
}
}
return false;
}
private static <T> List<T> withoutFirst(List<T> parameters) {
assert parameters.size() > 0;
return parameters.subList(1, parameters.size());
}
private EClassifier internalMigrateInfixOperation(OperationCallTrace trace, OperationCall operationCall) throws MigrationException {
EOperation eOperation = trace.getEOperation();
assert eOperation != null;
TypeSelectExpression typeSelect = getInfixInstanceOfTypeSelect(eOperation, operationCall);
if (typeSelect != null) {
return internalMigrateInfixInstanceof(typeSelect);
}
typeSelect = getInfixNotInstanceOfTypeSelect(eOperation, operationCall);
if (typeSelect != null) {
write("not ");
return internalMigrateInfixInstanceof(typeSelect);
}
OperationCall indexOfOperation = getInfixContains(eOperation, operationCall);
if (indexOfOperation != null) {
return internalMigrateInfixContains(indexOfOperation);
}
indexOfOperation = getInfixNotContains(eOperation, operationCall);
if (indexOfOperation != null) {
write("not ");
return internalMigrateInfixContains(indexOfOperation);
}
int operationPrecedence = getPrecedence(operationCall);
int targetStartPosition = getCurrentPosition();
internalMigrateOperationCallTarget(operationCall);
encloseExpressionIntoParenthesis(operationCall.getTarget(), operationPrecedence, targetStartPosition, false);
String opName = eOperation.getName();
if (BuiltinMetaModel.Boolean_NE == eOperation) {
write("not ", targetStartPosition);
} else if (BuiltinMetaModel.Int_Unary_Minus == eOperation || BuiltinMetaModel.Double_Unary_Minus == eOperation) {
write(opName, targetStartPosition);
} else if (BuiltinMetaModel.Int_Minus_Int == eOperation || BuiltinMetaModel.Int_Minus_Double == eOperation || BuiltinMetaModel.Double_Minus_Int == eOperation
|| BuiltinMetaModel.Double_Minus_Double == eOperation || BuiltinMetaModel.Int_Plus_Int == eOperation || BuiltinMetaModel.Int_Plus_Double == eOperation
|| BuiltinMetaModel.Double_Plus_Int == eOperation || BuiltinMetaModel.Double_Plus_Double == eOperation || BuiltinMetaModel.Int_Mult_Int == eOperation
|| BuiltinMetaModel.Int_Mult_Double == eOperation || BuiltinMetaModel.Double_Mult_Int == eOperation || BuiltinMetaModel.Double_Mult_Double == eOperation
|| BuiltinMetaModel.Int_Div_Double == eOperation || BuiltinMetaModel.Double_Div_Double == eOperation || BuiltinMetaModel.Double_Div_Int == eOperation
|| BuiltinMetaModel.Int_Less == eOperation || BuiltinMetaModel.Int_LessOrEqual == eOperation || BuiltinMetaModel.Int_Greater == eOperation
|| BuiltinMetaModel.Int_GreatOrEqual == eOperation || BuiltinMetaModel.EString_Plus_EJavaObject == eOperation) {
write(" ");
write(opName);
write(" ");
} else if (BuiltinMetaModel.Object_EQ == eOperation) {
write(" = ");
} else if (BuiltinMetaModel.Object_NotEQ == eOperation) {
write(" <> ");
} else {
throw new MigrationException(Type.UNSUPPORTED_EXPRESSION, resourceName, operationCall.getName(), "Incorrect infix operation: " + opName);
}
int parameterStartPosition = getCurrentPosition();
List<EClassifier> parameterTypes = internalMigrateOperationCallParameters(operationCall, null);
if (operationCall.getParams().length == 1) {
encloseExpressionIntoParenthesis(operationCall.getParams()[0], operationPrecedence, parameterStartPosition, true);
}
if (BuiltinMetaModel.EString_Plus_EJavaObject == eOperation) {
assert parameterTypes.size() == 1;
if (parameterTypes.get(0) != EcorePackage.eINSTANCE.getEString()) {
write(".repr()");
}
}
return getTypedElementQvtType(eOperation);
}
private OperationCall getInfixNotContains(EOperation eOperation, OperationCall operationCall) throws MigrationException {
if (operationCall.getParams().length == 1 && operationCall.getTarget() instanceof OperationCall) {
Integer intLiteral = getIntLiteralValue(operationCall.getParams()[0]);
if (intLiteral != null && ((eOperation == BuiltinMetaModel.Object_EQ && intLiteral == -1) || (eOperation == BuiltinMetaModel.Int_Less && intLiteral == 0))) {
return getIndexOfOpCall((OperationCall) operationCall.getTarget());
}
}
return null;
}
private Integer getIntLiteralValue(Expression expression) throws MigrationException {
if (expression instanceof IntegerLiteral) {
return new Integer(((IntegerLiteral) expression).getLiteralValue());
}
if (expression instanceof OperationCall) {
OperationCall operationCall = (OperationCall) expression;
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(operationCall);
if (false == expressionTrace instanceof OperationCallTrace) {
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL_TRACE, resourceName, operationCall, expressionTrace);
}
if (((OperationCallTrace) expressionTrace).getEOperation() == BuiltinMetaModel.Int_Unary_Minus) {
assert operationCall.getTarget() instanceof IntegerLiteral;
return new Integer("-" + ((IntegerLiteral) operationCall.getTarget()).getLiteralValue());
}
}
return null;
}
private OperationCall getIndexOfOpCall(OperationCall operationCall) throws MigrationException {
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(operationCall);
if (false == expressionTrace instanceof OperationCallTrace) {
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL_TRACE, resourceName, operationCall, expressionTrace);
}
OperationCallTrace trace = (OperationCallTrace) expressionTrace;
return trace.getEOperation() == BuiltinMetaModel.List_IndexOf ? operationCall : null;
}
private EClassifier internalMigrateInfixContains(OperationCall indexOfOperation) throws MigrationException {
assert indexOfOperation.getParams().length == 1;
EClassifier targetType = internalMigrateOperationCallTarget(indexOfOperation);
assert BuiltinMetaModelExt.isListType(targetType) || BuiltinMetaModelExt.isOrderedSetType(targetType);
write("->includes(");
internalMigrateOperationCallParameters(indexOfOperation, null);
write(")");
return EcorePackage.eINSTANCE.getEBoolean();
}
private OperationCall getInfixContains(EOperation eOperation, OperationCall operationCall) throws MigrationException {
if (operationCall.getParams().length == 1 && operationCall.getTarget() instanceof OperationCall) {
Integer intLiteral = getIntLiteralValue(operationCall.getParams()[0]);
if (intLiteral != null
&& ((eOperation == BuiltinMetaModel.Object_NotEQ && intLiteral == -1) || (eOperation == BuiltinMetaModel.Int_GreatOrEqual && intLiteral == 0) || (eOperation == BuiltinMetaModel.Int_Greater && intLiteral == -1))) {
return getIndexOfOpCall((OperationCall) operationCall.getTarget());
}
}
return null;
}
private EClassifier internalMigrateInfixInstanceof(TypeSelectExpression typeSelect) throws MigrationException {
assert typeSelect.getTarget() instanceof ListLiteral;
ListLiteral listLiteral = (ListLiteral) typeSelect.getTarget();
assert listLiteral.getElements().length == 1;
migrateExpression(listLiteral.getElements()[0]);
write(".oclIsKindOf(");
write(typeManager.getQvtFQName(ctx.getTypeForName(typeSelect.getTypeLiteral().getValue())));
write(")");
return EcorePackage.eINSTANCE.getEBoolean();
}
private TypeSelectExpression getInfixNotInstanceOfTypeSelect(EOperation eOperation, OperationCall operationCall) {
if (operationCall.getParams().length == 1 && operationCall.getTarget() instanceof OperationCall) {
Expression paramExpression = operationCall.getParams()[0];
OperationCall target = (OperationCall) operationCall.getTarget();
if (eOperation == BuiltinMetaModel.Object_EQ && paramExpression instanceof IntegerLiteral && "0".equals(((IntegerLiteral) paramExpression).getLiteralValue())
&& "size".equals(target.getName().getValue()) && target.getTarget() instanceof TypeSelectExpression) {
TypeSelectExpression result = (TypeSelectExpression) target.getTarget();
if (isInstanceofTypeselect(result)) {
return result;
}
} else if (eOperation == BuiltinMetaModel.Int_Less && paramExpression instanceof IntegerLiteral && "1".equals(((IntegerLiteral) paramExpression).getLiteralValue())
&& "size".equals(target.getName().getValue()) && target.getTarget() instanceof TypeSelectExpression) {
TypeSelectExpression result = (TypeSelectExpression) target.getTarget();
if (isInstanceofTypeselect(result)) {
return result;
}
}
}
return null;
}
private TypeSelectExpression getInfixInstanceOfTypeSelect(EOperation eOperation, OperationCall operationCall) {
if (operationCall.getTarget() instanceof OperationCall) {
OperationCall target = (OperationCall) operationCall.getTarget();
if (operationCall.getParams().length == 0) {
if (eOperation == BuiltinMetaModel.Boolean_NE && "isEmpty".equals(target.getName().getValue()) && target.getTarget() instanceof TypeSelectExpression) {
TypeSelectExpression result = (TypeSelectExpression) target.getTarget();
if (isInstanceofTypeselect(result)) {
return result;
}
}
} else if (operationCall.getParams().length == 1) {
Expression paramExpression = operationCall.getParams()[0];
if (eOperation == BuiltinMetaModel.Int_Greater && paramExpression instanceof IntegerLiteral && "0".equals(((IntegerLiteral) paramExpression).getLiteralValue())
&& "size".equals(target.getName().getValue()) && target.getTarget() instanceof TypeSelectExpression) {
TypeSelectExpression result = (TypeSelectExpression) target.getTarget();
if (isInstanceofTypeselect(result)) {
return result;
}
} else if (eOperation == BuiltinMetaModel.Object_EQ && paramExpression instanceof BooleanLiteral && !Boolean.valueOf(((BooleanLiteral) paramExpression).getLiteralValue())
&& "isEmpty".equals(target.getName().getValue()) && target.getTarget() instanceof TypeSelectExpression) {
TypeSelectExpression result = (TypeSelectExpression) target.getTarget();
if (isInstanceofTypeselect(result)) {
return result;
}
}
}
}
return null;
}
/**
* @return true if passed OperationCall represents typeSelect operation
* called for the single element list literal expression like:
*
* {<var>}.typeSelect(<TypeLiteral>)
*
* usually repressenting .oclIsKindOf() operation.
*/
private boolean isInstanceofTypeselect(TypeSelectExpression typeSelect) {
if (typeSelect.getTarget() instanceof ListLiteral) {
ListLiteral listLiteral = (ListLiteral) typeSelect.getTarget();
return listLiteral.getElements().length == 1;
}
return false;
}
/**
* @param expression can be null
*/
private void encloseExpressionIntoParenthesis(Expression expression, int parentPrecedence, int placeholder, boolean ecloseIfEqualPrecenence) throws MigrationException {
int expressionPrecedence = getPrecedence(expression);
if (ecloseIfEqualPrecenence ? parentPrecedence <= expressionPrecedence : parentPrecedence < expressionPrecedence) {
write("(", placeholder);
write(")");
}
}
/**
* @return integer value representing position of this operation in a list
* from OCL specification (see 7.4.7. "Precedence Rules")
*/
private int getPrecedence(Expression expression) throws MigrationException {
if (expression instanceof OperationCall) {
OperationCall operationCall = (OperationCall) expression;
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(operationCall);
if (false == expressionTrace instanceof OperationCallTrace) {
throw new MigrationException(Type.UNSUPPORTED_OPERATION_CALL_TRACE, resourceName, expression, expressionTrace);
}
OperationCallTrace trace = (OperationCallTrace) expressionTrace;
if (getInfixNotInstanceOfTypeSelect(trace.getEOperation(), operationCall) != null) {
/*
* this operation will be migrated as
*
* not <var>.oclIsKindOf(<TypeLiteral>)
*
* so having Boolean_NE precedence
*/
return 2;
}
if (BuiltinMetaModel.Collection_IsEmpty == trace.getEOperation() && operationCall.getTarget() instanceof TypeSelectExpression
&& isInstanceofTypeselect((TypeSelectExpression) operationCall.getTarget())) {
/*
* this operation will be migrated as
*
* not <var>.oclIsKindOf(<TypeLiteral>)
*
* so having Boolean_NE precedence
*/
return 2;
}
if (getInfixNotContains(trace.getEOperation(), operationCall) != null) {
/*
* this operation will be migrated as
*
* not <var>.oclIsKindOf(<TypeLiteral>)
*
* so having Boolean_NE precedence
*/
return 2;
}
if (trace.getEOperation() != null && infixOperations.contains(trace.getEOperation())) {
EOperation eOperation = trace.getEOperation();
if (BuiltinMetaModel.Boolean_NE == eOperation || BuiltinMetaModel.Int_Unary_Minus == eOperation || BuiltinMetaModel.Double_Unary_Minus == eOperation) {
return 2;
} else if (BuiltinMetaModel.Int_Mult_Int == eOperation || BuiltinMetaModel.Int_Mult_Double == eOperation || BuiltinMetaModel.Double_Mult_Int == eOperation
|| BuiltinMetaModel.Double_Mult_Double == eOperation || BuiltinMetaModel.Int_Div_Int == eOperation || BuiltinMetaModel.Int_Div_Double == eOperation
|| BuiltinMetaModel.Double_Div_Int == eOperation || BuiltinMetaModel.Double_Div_Double == eOperation) {
return 3;
} else if (BuiltinMetaModel.Int_Minus_Int == eOperation || BuiltinMetaModel.Int_Minus_Double == eOperation || BuiltinMetaModel.Double_Minus_Int == eOperation
|| BuiltinMetaModel.Double_Minus_Double == eOperation || BuiltinMetaModel.Int_Plus_Int == eOperation || BuiltinMetaModel.Int_Plus_Double == eOperation
|| BuiltinMetaModel.Double_Plus_Int == eOperation || BuiltinMetaModel.Double_Plus_Double == eOperation || BuiltinMetaModel.EString_Plus_EJavaObject == eOperation) {
return 4;
} else if (BuiltinMetaModel.Int_Less == eOperation || BuiltinMetaModel.Int_LessOrEqual == eOperation || BuiltinMetaModel.Int_Greater == eOperation
|| BuiltinMetaModel.Int_GreatOrEqual == eOperation) {
return 6;
} else if (BuiltinMetaModel.Object_EQ == eOperation || BuiltinMetaModel.Object_NotEQ == eOperation) {
return 7;
}
}
} else if (expression instanceof BooleanOperation) {
// and/or/xor is on the 9-th row in the list from OCL spec
return 8;
}
// highest precedence
return 0;
}
private EClassifier internalMigrateOperationCallTarget(OperationCall operationCall) throws MigrationException {
if (operationCall.getTarget() != null) {
return migrateExpression(operationCall.getTarget());
} else {
// getTarget() == null if it is an implicit self operation.
// TODO: check if it is working with XPand
write(Environment.SELF_VARIABLE_NAME);
return null;
}
}
// TODO: check if "internalMigrateOperationCallParameters()" can be called
// with non-null parameters here (used to transform parameter collection
// types too)
private EClassifier internalMigrateCollectionOperationCall(OperationCallTrace trace, OperationCall operationCall) throws MigrationException {
EOperation eOperation = trace.getEOperation();
assert eOperation != null;
EClassifier targetType = trace.getTargetType();
assert targetType != null;
assert BuiltinMetaModel.isCollectionType(targetType);
EClassifier elementType = BuiltinMetaModel.getInnerType(targetType);
if (BuiltinMetaModel.Collection_IsEmpty == eOperation && operationCall.getTarget() instanceof TypeSelectExpression && isInstanceofTypeselect((TypeSelectExpression) operationCall.getTarget())) {
write("not ");
return internalMigrateInfixInstanceof((TypeSelectExpression) operationCall.getTarget());
}
int expressionStartPosition = getCurrentPosition();
if (BuiltinMetaModel.Collection_Clear != eOperation && BuiltinMetaModel.List_WithoutFirst != eOperation && BuiltinMetaModel.List_WithoutLast != eOperation) {
EClassifier targetQvtType = internalMigrateOperationCallTarget(operationCall);
if (targetQvtType != null && BuiltinMetaModel.isCollectionType(targetQvtType)) {
targetType = targetQvtType;
elementType = BuiltinMetaModel.getInnerType(targetQvtType);
}
}
if (BuiltinMetaModel.Collection_Add == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->including(");
EClassifier parameterType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
write(")");
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterType);
return internalMigrateToConcreteCollection(targetType, commonSuperType, expressionStartPosition, operationStartPosition);
} else if (BuiltinMetaModel.Collection_AddAll == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->union(");
EClassifier parameterCollectionType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
EClassifier parameterCollectionElementType = getCollectionElementType(parameterCollectionType);
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterCollectionElementType);
internalMigrateParameterCollectionToMain(parameterCollectionType, targetType);
write(")");
return internalMigrateToConcreteCollection(targetType, commonSuperType, expressionStartPosition, operationStartPosition);
} else if (BuiltinMetaModel.Collection_Union == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->union(");
EClassifier parameterCollectionType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
EClassifier parameterCollectionElementType = getCollectionElementType(parameterCollectionType);
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterCollectionElementType);
internalMigrateParameterCollectionToList(parameterCollectionType);
write(")->asOrderedSet()->asSequence()");
return internalMigrateToList(targetType, commonSuperType, expressionStartPosition, operationStartPosition);
} else if (BuiltinMetaModel.Collection_Intersect == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->intersection(");
EClassifier parameterCollectionType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
EClassifier parameterCollectionElementType = getCollectionElementType(parameterCollectionType);
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterCollectionElementType);
internalMigrateParameterCollectionToSet(parameterCollectionType);
write(")");
return internalMigrateToSet(targetType, commonSuperType, expressionStartPosition, operationStartPosition);
} else if (BuiltinMetaModel.Collection_Without == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->-(");
EClassifier parameterCollectionType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
EClassifier parameterCollectionElementType = getCollectionElementType(parameterCollectionType);
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterCollectionElementType);
internalMigrateParameterCollectionToSet(parameterCollectionType);
write(")");
return internalMigrateToSet(targetType, commonSuperType, expressionStartPosition, operationStartPosition);
} else if (BuiltinMetaModel.Collection_Contains == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->includes(");
EClassifier parameterType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
write(")");
if (!BuiltinMetaModel.isAssignableFrom(elementType, parameterType)) {
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterType);
internalMigrateTypeSelect(typeManager.getQvtFQName(commonSuperType), expressionStartPosition, operationStartPosition);
}
return EcorePackage.eINSTANCE.getEBoolean();
} else if (BuiltinMetaModel.Collection_ContainsAll == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->includesAll(");
EClassifier parameterCollectionType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
EClassifier parameterCollectionElementType = getCollectionElementType(parameterCollectionType);
write(")");
if (!BuiltinMetaModel.isAssignableFrom(elementType, parameterCollectionElementType)) {
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterCollectionElementType);
internalMigrateTypeSelect(typeManager.getQvtFQName(commonSuperType), expressionStartPosition, operationStartPosition);
}
return EcorePackage.eINSTANCE.getEBoolean();
} else if (BuiltinMetaModel.List_IndexOf == eOperation) {
int operationStartPosition = getCurrentPosition();
write("->indexOf(");
EClassifier parameterType = getSingleParameterType(internalMigrateOperationCallParameters(operationCall, null));
write(")");
if (!BuiltinMetaModel.isAssignableFrom(elementType, parameterType)) {
EClassifier commonSuperType = BuiltinMetaModelExt.getCommonSuperType(elementType, parameterType);
internalMigrateTypeSelect(typeManager.getQvtFQName(commonSuperType), expressionStartPosition, operationStartPosition);
}
write("(", expressionStartPosition);
write(" - 1)");
return EcorePackage.eINSTANCE.getEInt();
} else if (BuiltinMetaModel.Collection_Clear == eOperation) {
EClassifier resultType;
if (BuiltinMetaModelExt.isSetType(targetType)) {
write("Set{}");
resultType = BuiltinMetaModelExt.getSetType(elementType);
} else if (BuiltinMetaModelExt.isListType(targetType) || BuiltinMetaModelExt.isOrderedSetType(targetType)) {
write("Sequence{}");
resultType = BuiltinMetaModelExt.getListType(elementType);
} else {
write("Bag{}");
resultType = BuiltinMetaModelExt.getBagType(elementType);
}
// write("Bag{}");
if (elementType != EcorePackage.eINSTANCE.getEJavaObject()) {
write("[");
write(typeManager.getQvtFQName(elementType));
write("]");
}
return resultType;
} else if (BuiltinMetaModel.Collection_Flatten == eOperation) {
EClassifier resultType = internalMigrateToConcreteCollection(targetType, elementType, expressionStartPosition, getCurrentPosition());
write("->flatten()");
if (BuiltinMetaModel.isCollectionType(elementType)) {
elementType = BuiltinMetaModel.getInnerType(elementType);
}
if (BuiltinMetaModelExt.isSetType(resultType) || BuiltinMetaModelExt.isOrderedSetType(resultType)) {
return BuiltinMetaModelExt.getSetType(elementType);
} else if (BuiltinMetaModelExt.isListType(resultType)) {
return BuiltinMetaModelExt.getListType(elementType);
} else {
return BuiltinMetaModelExt.getBagType(elementType);
}
} else if (BuiltinMetaModel.Collection_ToSet == eOperation) {
return internalMigrateToSet(targetType, elementType, expressionStartPosition, getCurrentPosition());
} else if (BuiltinMetaModel.Collection_ToList == eOperation) {
return internalMigrateToList(targetType, elementType, expressionStartPosition);
} else if (BuiltinMetaModel.List_Get == eOperation) {
write("->at(");
internalMigrateOperationCallParameters(operationCall, null);
write(" + 1)");
return elementType;
} else if (BuiltinMetaModel.List_WithoutFirst == eOperation) {
String varName = variableDispatcher.getNextVariableName();
write("let ");
write(varName);
write(" = ");
targetType = internalMigrateOperationCallTarget(operationCall);
assert BuiltinMetaModelExt.isListType(targetType) || BuiltinMetaModelExt.isOrderedSetType(targetType);
boolean isOrderedSet = BuiltinMetaModelExt.isOrderedSetType(targetType);
write(" in ");
write("if ");
write(varName);
write("->size() < 2 then ");
if (isOrderedSet) {
write("OrderedSet");
} else {
write("Sequence");
}
write("{}");
if (elementType != EcorePackage.eINSTANCE.getEJavaObject()) {
write("[");
write(typeManager.getQvtFQName(elementType));
write("]");
}
write(" else ");
write(varName);
write("->");
if (isOrderedSet) {
write("subOrderedSet");
} else {
write("subSequence");
}
write("(2, ");
write(varName);
write("->size()) endif");
return targetType;
} else if (BuiltinMetaModel.List_WithoutLast == eOperation) {
String varName = variableDispatcher.getNextVariableName();
write("let ");
write(varName);
write(" = ");
targetType = internalMigrateOperationCallTarget(operationCall);
assert BuiltinMetaModelExt.isListType(targetType) || BuiltinMetaModelExt.isOrderedSetType(targetType);
boolean isOrderedSet = BuiltinMetaModelExt.isOrderedSetType(targetType);
write(" in ");
write("if ");
write(varName);
write("->size() < 2 then ");
if (isOrderedSet) {
write("OrderedSet");
} else {
write("Sequence");
}
write("{}");
if (elementType != EcorePackage.eINSTANCE.getEJavaObject()) {
write("[");
write(typeManager.getQvtFQName(elementType));
write("]");
}
write(" else ");
write(varName);
write("->");
if (isOrderedSet) {
write("subOrderedSet");
} else {
write("subSequence");
}
write("(1, ");
write(varName);
write("->size() - 1) endif");
return targetType;
} else if (BuiltinMetaModel.List_PurgeDups == eOperation) {
if (BuiltinMetaModelExt.isListType(targetType)) {
write("->asOrderedSet()->asSequence()");
}
return targetType;
} else {
/**
* .isEmpty() .size() .first() .last()
*/
assert operationCall.getParams().length == 0;
write("->");
write(eOperation.getName());
write("(");
internalMigrateOperationCallParameters(operationCall, null);
write(")");
if (BuiltinMetaModel.Collection_IsEmpty == eOperation) {
return EcorePackage.eINSTANCE.getEBoolean();
} else if (BuiltinMetaModel.Collection_Size == eOperation) {
return EcorePackage.eINSTANCE.getEInt();
} else if (BuiltinMetaModel.List_First == eOperation || BuiltinMetaModel.List_Last == eOperation) {
return elementType;
} else {
throw new MigrationException(Type.UNSUPPORTED_COLLECTION_OPERATION, resourceName, operationCall.getName(), eOperation.getName());
}
}
}
private EClassifier getSingleParameterType(List<EClassifier> parameterTypes) {
assert parameterTypes.size() == 1;
return parameterTypes.get(0);
}
private EClassifier getCollectionElementType(EClassifier collectionType) {
assert BuiltinMetaModel.isCollectionType(collectionType);
return BuiltinMetaModel.getInnerType(collectionType);
}
private EClassifier internalMigrateToConcreteCollection(EClassifier collectionType, EClassifier requestedElementType, int expressionStartPosition, int expressionEndPosition) throws MigrationException {
assert BuiltinMetaModel.isCollectionType(collectionType);
EClassifier elementType = BuiltinMetaModel.getInnerType(collectionType);
if (BuiltinMetaModelExt.isAbstractCollectionType(collectionType)) {
String iteratorName = variableDispatcher.getNextIteratorName();
StringBuilder sb = new StringBuilder();
sb.append("->collect(");
sb.append(iteratorName);
sb.append(" | ");
sb.append(iteratorName);
if (requestedElementType != elementType) {
sb.append(".oclAsType(");
sb.append(typeManager.getQvtFQName(requestedElementType));
sb.append(")");
}
sb.append(")");
write(sb, expressionEndPosition);
return BuiltinMetaModelExt.getBagType(requestedElementType);
} else if (requestedElementType != elementType) {
internalMigrateTypeSelect(typeManager.getQvtFQName(requestedElementType), expressionStartPosition, expressionEndPosition);
return BuiltinMetaModelExt.replaceCollectionElementType(collectionType, requestedElementType);
}
return collectionType;
}
private EClassifier internalMigrateToSet(EClassifier collectionType, EClassifier elementSuperType, int expressionStartPosition, int expressionEndPosition) throws MigrationException {
if (!BuiltinMetaModelExt.isSetType(collectionType) && !BuiltinMetaModelExt.isOrderedSetType(collectionType)) {
if (BuiltinMetaModelExt.isListType(collectionType)) {
write("->asOrderedSet()", expressionEndPosition);
} else {
write("->asSet()", expressionEndPosition);
}
}
internalMigrateToConcreteCollection(collectionType, elementSuperType, expressionStartPosition, expressionEndPosition);
return BuiltinMetaModelExt.isOrderedSetType(collectionType) || BuiltinMetaModelExt.isListType(collectionType) ? BuiltinMetaModelExt.getOrderedSetType(elementSuperType) : BuiltinMetaModelExt.getSetType(elementSuperType);
}
private EClassifier internalMigrateToList(EClassifier collectionType, EClassifier elementSuperType, int placeholder) throws MigrationException {
return internalMigrateToList(collectionType, elementSuperType, placeholder, getCurrentPosition());
}
private EClassifier internalMigrateToList(EClassifier collectionType, EClassifier elementSuperType, int expressionStartPosition, int expressionEndPosition) throws MigrationException {
if (!BuiltinMetaModelExt.isListType(collectionType)) {
write("->asSequence()", expressionEndPosition);
}
internalMigrateToConcreteCollection(collectionType, elementSuperType, expressionStartPosition, expressionEndPosition);
return BuiltinMetaModelExt.getListType(elementSuperType);
}
private void internalMigrateParameterCollectionToMain(EClassifier parameterCollectionType, EClassifier mainCollectionType) {
assert BuiltinMetaModel.isCollectionType(parameterCollectionType);
assert BuiltinMetaModel.isCollectionType(mainCollectionType);
if (BuiltinMetaModelExt.isListType(mainCollectionType)) {
if (BuiltinMetaModelExt.isSetType(parameterCollectionType) || BuiltinMetaModelExt.isOrderedSetType(parameterCollectionType)) {
write("->asSequence()");
} else if (BuiltinMetaModelExt.isAbstractCollectionType(parameterCollectionType)) {
internalMigrateCollectionToBag();
write("->asSequence()");
}
} else if (BuiltinMetaModelExt.isSetType(mainCollectionType) || BuiltinMetaModelExt.isOrderedSetType(mainCollectionType)) {
if (BuiltinMetaModelExt.isListType(parameterCollectionType) || BuiltinMetaModelExt.isBagType(parameterCollectionType)) {
write("->asSet()");
} else if (BuiltinMetaModelExt.isAbstractCollectionType(parameterCollectionType)) {
internalMigrateCollectionToBag();
write("->asSet()");
}
} else { //For Bag/AbstractCollection (should be transformed to Bag)
if (BuiltinMetaModelExt.isListType(parameterCollectionType)) {
write("->asBag()");
} else if (BuiltinMetaModelExt.isAbstractCollectionType(parameterCollectionType)) {
internalMigrateCollectionToBag();
}
}
}
private void internalMigrateParameterCollectionToSet(EClassifier parameterCollectionType) {
assert BuiltinMetaModel.isCollectionType(parameterCollectionType);
if (BuiltinMetaModelExt.isListType(parameterCollectionType) || BuiltinMetaModelExt.isBagType(parameterCollectionType)) {
write("->asSet()");
} else if (BuiltinMetaModelExt.isAbstractCollectionType(parameterCollectionType)) {
internalMigrateCollectionToBag();
write("->asSet()");
}
}
private void internalMigrateParameterCollectionToList(EClassifier parameterCollectionType) {
assert BuiltinMetaModel.isCollectionType(parameterCollectionType);
if (BuiltinMetaModelExt.isAbstractCollectionType(parameterCollectionType)) {
internalMigrateCollectionToBag();
}
if (!BuiltinMetaModelExt.isListType(parameterCollectionType)) {
write("->asSequence()");
}
}
private boolean isCollectionOperation(OperationCallTrace trace) {
EOperation eOperation = trace.getEOperation();
assert eOperation != null;
return collectionOperations.contains(eOperation);
}
private EClassifier getTypedElementQvtType(ETypedElement typedElement) {
EClassifier type = typedElement.getEType();
if (typedElement.isMany()) {
if (typedElement.isOrdered() && typedElement.isUnique()) {
type = BuiltinMetaModelExt.getOrderedSetType(type);
} else if (typedElement.isOrdered()) {
type = BuiltinMetaModelExt.getListType(type);
} else if (typedElement.isUnique()) {
type = BuiltinMetaModelExt.getSetType(type);
} else {
type = BuiltinMetaModelExt.getBagType(type);
}
}
return type == null ? BuiltinMetaModel.VOID : type;
}
private void convertImplicitCollectProduct(EClassifier targetType) {
assert targetType != null;
if (!BuiltinMetaModelExt.isListType(targetType) && !BuiltinMetaModelExt.isOrderedSetType(targetType)) {
write("->asSequence()");
}
}
private List<EClassifier> internalMigrateOperationCallParameters(OperationCall operationCall, List<EClassifier> expectedParameterTypes) throws MigrationException {
assert expectedParameterTypes == null || operationCall.getParams().length == expectedParameterTypes.size();
List<EClassifier> parameterTypes = new ArrayList<EClassifier>();
for (int i = 0; i < operationCall.getParams().length; i++) {
if (i > 0) {
write(", ");
}
Expression parameter = operationCall.getParams()[i];
if (operationCall.getTarget() != null && operationCall.getTarget().getLine() != parameter.getLine()) {
write(ExpressionMigrationFacade.LF);
}
parameterTypes.add(migrateExpression(parameter));
if (expectedParameterTypes != null) {
internalConvertTypes(parameterTypes.get(i), expectedParameterTypes.get(i));
}
}
return parameterTypes;
}
private boolean isInfixOperation(OperationCallTrace trace) {
EOperation eOperation = trace.getEOperation();
assert eOperation != null;
return infixOperations.contains(eOperation);
}
private EClassifier migrateFeatureCall(FeatureCall featureCall) throws MigrationException {
ExpressionAnalyzeTrace expressionTrace = ctx.getTraces().get(featureCall);
if (false == expressionTrace instanceof FeatureCallTrace) {
throw new MigrationException(Type.UNSUPPORTED_FEATURE_CALL_TRACE, resourceName, featureCall, expressionTrace);
}
FeatureCallTrace trace = (FeatureCallTrace) expressionTrace;
switch (trace.getType()) {
case ENUM_LITERAL_REF:
EEnumLiteral enumLiteral = trace.getEnumLiteral();
assert enumLiteral != null;
write(typeManager.getQvtFQName(enumLiteral));
return enumLiteral.getEEnum();
case ENV_VAR_REF:
write(modelManager.getName(featureCall, trace));
EClassifier variableType = getEnvVariableType(featureCall.getName().getValue());
return variableType != null ? variableType : trace.getResultType();
case UNDESOLVED_TARGET_TYPE:
case UNSUPPORTED_CLASSIFIER_REF:
throw new MigrationException(Type.UNSUPPORTED_FEATURE_CALL, resourceName, featureCall.getName(), trace);
}
EClassifier targetType = trace.getTargetType();
// featureCall.getTarget() == null for FeatureCall of implicit variable
// feature
if (featureCall.getTarget() != null) {
migrateExpression(featureCall.getTarget());
// Skipping EnumLiteral.value/EnumLiteral.literal features
if (targetType instanceof EEnum && trace.getType() == FeatureCallTrace.Type.FEATURE_REF) {
if (skipEnumLiteralFeature(trace.getFeature())) {
return targetType;
} else if (addEnumLiteralStringRepresentation(trace.getFeature())) {
write(".repr()");
return EcorePackage.eINSTANCE.getEString();
}
}
write(".");
} else {
if (targetType instanceof EEnum && trace.getType() == FeatureCallTrace.Type.FEATURE_REF) {
if (skipEnumLiteralFeature(trace.getFeature())) {
write(Environment.SELF_VARIABLE_NAME);
return targetType;
} else if (addEnumLiteralStringRepresentation(trace.getFeature())) {
write(Environment.SELF_VARIABLE_NAME);
write(".repr()");
return EcorePackage.eINSTANCE.getEString();
}
}
if (FeatureCallTrace.Type.IMPLICIT_COLLECT_FEATURE_REF == trace.getType()) {
// implicit collect call without explicitly specified target -
// writing "self" to make it compilable
write(Environment.SELF_VARIABLE_NAME);
write(".");
}
}
write(modelManager.getName(featureCall, trace));
assert targetType != null;
switch (trace.getType()) {
case FEATURE_REF:
if (BuiltinMetaModel.isParameterizedType(targetType)) {
throw new MigrationException(Type.UNSUPPORTED_EXPRESSION, resourceName, featureCall, "Attribute call is not supported for the collection types: " + targetType.toString() + "." + featureCall.getName().getValue());
}
return getTypedElementQvtType(trace.getFeature());
case IMPLICIT_COLLECT_FEATURE_REF:
if (!BuiltinMetaModel.isParameterizedType(targetType)) {
throw new MigrationException(Type.UNSUPPORTED_EXPRESSION, resourceName, featureCall, "Implicit collect is not supported for simple types: " + targetType.toString() + "." + featureCall.getName().getValue());
}
convertImplicitCollectProduct(targetType);
return trace.getResultType();
default:
throw new MigrationException(Type.UNSUPPORTED_FEATURE_CALL_TRACE, resourceName, featureCall,trace);
}
}
private boolean addEnumLiteralStringRepresentation(EStructuralFeature feature) {
return EcorePackage.eINSTANCE.getENamedElement_Name() == feature;
}
private boolean skipEnumLiteralFeature(EStructuralFeature feature) {
return EcorePackage.eINSTANCE.getEEnumLiteral_Value() == feature || EcorePackage.eINSTANCE.getEEnumLiteral_Literal() == feature;
}
private void markReturnPosition() {
returnPosition = getCurrentPosition();
}
private int getCurrentPosition() {
return output.length();
}
private void write(CharSequence cs, int index) {
output.insert(index, cs);
}
private void write(CharSequence cs) {
output.append(cs);
}
private void pushContextWithVariable(String varName, EClassifier varType) {
qvtContexts.push(qvtContexts.peek().cloneWithVariable(varName, varType));
}
private EClassifier getEnvVariableType(String varName) {
return qvtContexts.peek().getVariableType(varName);
}
/**
* This class was intended to store QVT env. variable types during
* expression migration. It is necessary for more intelligent QVT
* (collections) types transformation during expression migration.
*
* Only following methods can be used by clients:
* {@link ExecutionContext#cloneWithVariable(java.util.Collection)}
* {@link ExecutionContext#cloneWithVariable(org.eclipse.gmf.internal.xpand.expression.Variable...)}
* {@link ExecutionContext#cloneContext()}
*/
private static class QvtExecutionContext extends ExecutionContextImpl {
public static QvtExecutionContext createNewContext(HashMap<String, EClassifier> envVariables) {
QvtExecutionContext result = new QvtExecutionContext();
for (Entry<String, EClassifier> envVar : envVariables.entrySet()) {
result = result.cloneWithVariable(envVar.getKey(), envVar.getValue());
}
return result;
}
private QvtExecutionContext() {
super((ResourceManager) null);
}
private QvtExecutionContext(QvtExecutionContext original) {
super(original);
}
public QvtExecutionContext cloneWithVariable(String name, EClassifier type) {
return (QvtExecutionContext) super.cloneWithVariable(new Variable(name, type));
}
public EClassifier getVariableType(String name) {
Variable var = getVariable(name);
if (var != null) {
return (EClassifier) var.getValue();
}
return null;
}
@Override
public QvtExecutionContext cloneContext() {
return new QvtExecutionContext(this);
}
@Override
public EClassifier[] findTypesForPrefix(String prefix) {
throw new UnsupportedOperationException();
}
@Override
public void setContextClassLoader(ClassLoadContext classLoadContext) {
throw new UnsupportedOperationException();
}
@Override
public Class<?> loadClass(String value) {
throw new UnsupportedOperationException();
}
@Override
public EClassifier getTypeForName(String name) {
throw new UnsupportedOperationException();
}
@Override
public ExecutionContext cloneWithResource(ResourceMarker ns) {
throw new UnsupportedOperationException();
}
@Override
public ResourceMarker currentResource() {
throw new UnsupportedOperationException();
}
@Override
public Set<Extension> getAllExtensions() {
throw new UnsupportedOperationException();
}
@Override
public Extension getExtension(String functionName, EClassifier[] parameterTypes) {
throw new UnsupportedOperationException();
}
@Override
public EvaluationListener getEvaluationListener() {
throw new UnsupportedOperationException();
}
@Override
public void setEvaluationListener(EvaluationListener listener) {
throw new UnsupportedOperationException();
}
}
}