blob: c527384b2023e23f8f075a6c3a2b9e474388207f [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.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.gmf.internal.xpand.ResourceManager;
import org.eclipse.gmf.internal.xpand.ast.AbstractDefinition;
import org.eclipse.gmf.internal.xpand.ast.Advice;
import org.eclipse.gmf.internal.xpand.ast.Definition;
import org.eclipse.gmf.internal.xpand.ast.ErrorStatement;
import org.eclipse.gmf.internal.xpand.ast.ExpandStatement;
import org.eclipse.gmf.internal.xpand.ast.ExpressionStatement;
import org.eclipse.gmf.internal.xpand.ast.FileStatement;
import org.eclipse.gmf.internal.xpand.ast.ForEachStatement;
import org.eclipse.gmf.internal.xpand.ast.IfStatement;
import org.eclipse.gmf.internal.xpand.ast.ImportDeclaration;
import org.eclipse.gmf.internal.xpand.ast.LetStatement;
import org.eclipse.gmf.internal.xpand.ast.NamespaceImport;
import org.eclipse.gmf.internal.xpand.ast.Statement;
import org.eclipse.gmf.internal.xpand.ast.Template;
import org.eclipse.gmf.internal.xpand.expression.AnalysationIssue;
import org.eclipse.gmf.internal.xpand.expression.ExecutionContext;
import org.eclipse.gmf.internal.xpand.expression.SyntaxConstants;
import org.eclipse.gmf.internal.xpand.expression.ast.DeclaredParameter;
import org.eclipse.gmf.internal.xpand.expression.ast.Expression;
import org.eclipse.gmf.internal.xpand.expression.ast.Identifier;
import org.eclipse.gmf.internal.xpand.expression.ast.SyntaxElement;
import org.eclipse.gmf.internal.xpand.migration.MigrationException.Type;
import org.eclipse.gmf.internal.xpand.model.XpandAdvice;
import org.eclipse.gmf.internal.xpand.model.XpandDefinition;
import org.eclipse.gmf.internal.xpand.model.XpandResource;
import org.eclipse.gmf.internal.xpand.util.CompositeXpandResource;
import org.eclipse.gmf.internal.xpand.xtend.ast.XtendResource;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
public class XpandMigrationFacade {
private ResourceManager resourceManager;
private String resourceName;
private Document document;
private MigrationExecutionContext ctx;
private MultiTextEdit edit;
private ModelManager modelManager;
private TypeManager typeManager;
private OclKeywordManager oclKeywordManager;
private MigrationExecutionContext rootExecutionContext;
public XpandMigrationFacade(ResourceManager resourceManager, String xtendResourceName, MigrationExecutionContext executionContext) {
this(resourceManager, xtendResourceName);
rootExecutionContext = executionContext;
}
public XpandMigrationFacade(ResourceManager resourceManager, String xtendResourceName) {
this.resourceManager = resourceManager;
resourceName = xtendResourceName;
}
public String migrateXpandResource() throws MigrationException {
StringBuilder originalContent = new StringBuilder();
try {
Reader[] readers = resourceManager.resolveMultiple(resourceName, XpandResource.TEMPLATE_EXTENSION);
assert readers.length > 0;
Reader mainReader = readers[0];
for (int ch = mainReader.read(); ch != -1; ch = mainReader.read()) {
originalContent.append((char) ch);
}
} catch (IOException e) {
throw new MigrationException(Type.RESOURCE_NOT_FOUND, resourceName, "Unable to load resource: " + resourceName);
}
XpandResource xpandResource = resourceManager.loadXpandResource(resourceName);
if (xpandResource == null) {
throw new MigrationException(Type.RESOURCE_NOT_FOUND, resourceName, "Unable to load resource: " + resourceName);
}
ctx = rootExecutionContext != null ? rootExecutionContext : (MigrationExecutionContext) new MigrationExecutionContextImpl(resourceManager).cloneWithResource(xpandResource);
Set<AnalysationIssue> issues = new HashSet<AnalysationIssue>();
xpandResource.analyze(ctx, issues);
if (MigrationException.hasErrors(issues)) {
throw new MigrationException(issues, resourceName);
}
Template xpandTemplate = getFirstTemplate(xpandResource);
document = new Document(originalContent.toString());
edit = new MultiTextEdit();
migrate(xpandTemplate);
try {
edit.apply(document);
} catch (MalformedTreeException e) {
throw new MigrationException(Type.UNABLE_TO_APPLY_EDIT, resourceName, e.getMessage());
} catch (BadLocationException e) {
throw new MigrationException(Type.UNABLE_TO_APPLY_EDIT, resourceName, e.getMessage());
}
return document.get();
}
private Template getFirstTemplate(XpandResource xpandResource) throws MigrationException {
// TODO: there should be more generic way to get first definition..
while (xpandResource instanceof CompositeXpandResource) {
xpandResource = ((CompositeXpandResource) xpandResource).getFirstDefinition();
}
if (false == xpandResource instanceof Template) {
throw new MigrationException(Type.UNSUPPORTED_XPAND_RESOURCE, resourceName, "Only Template instances are supported, but loaded: " + xpandResource);
}
return (Template) xpandResource;
}
private void migrate(Template xpandTemplate) throws MigrationException {
StandardLibraryImports stdLibImportsManager = new StandardLibraryImports(getStdLibImportsPosition(xpandTemplate));
oclKeywordManager = new OclKeywordManager();
modelManager = new ModelManager(stdLibImportsManager, oclKeywordManager);
modelManager.registerSelfAlias(ExecutionContext.IMPLICIT_VARIABLE);
typeManager = new TypeManager(oclKeywordManager);
for (NamespaceImport namespaceImport : xpandTemplate.getImports()) {
migrateExpression(namespaceImport.getStringLiteral(), EcorePackage.eINSTANCE.getEString(), Collections.<String, EClassifier> emptyMap(), new VariableNameDispatcher());
}
for (XpandDefinition definition : xpandTemplate.getDefinitions()) {
assert definition instanceof AbstractDefinition;
migrateDefinition((AbstractDefinition) definition);
}
for (XpandAdvice advice : xpandTemplate.getAdvices()) {
assert advice instanceof Advice;
migrateDefinition((Advice) advice);
}
injectStdlibImports(stdLibImportsManager, getAdditionalLibraries(xpandTemplate));
}
// TODO: use RangeMarker instead?
private int getStdLibImportsPosition(Template xpandTemplate) {
int offset = 0;
if (xpandTemplate.getExtensions().length > 0) {
ImportDeclaration[] extensions = xpandTemplate.getExtensions();
offset = extensions[extensions.length - 1].getEndOffset();
} else if (xpandTemplate.getImports().length > 0) {
NamespaceImport[] imports = xpandTemplate.getImports();
offset = imports[imports.length - 1].getEndOffset();
}
if (offset > 0) {
try {
for (; !"»".equals(document.get(offset, 1)); offset++) {
}
offset++;
} catch (BadLocationException e) {
offset = 0;
}
}
return offset;
}
private List<String> getAdditionalLibraries(Template xpandTemplate) {
List<String> result = new ArrayList<String>();
for (ImportDeclaration extension : xpandTemplate.getExtensions()) {
XtendResource xtendResource = resourceManager.loadXtendResource(extension.getImportString().getValue());
if (xtendResource != null) {
result.addAll(getReexportedExtensions(xtendResource));
}
}
return result;
}
private List<String> getReexportedExtensions(XtendResource xtendResource) {
List<String> result = new ArrayList<String>();
for (String extension : xtendResource.getImportedExtensions()) {
if (xtendResource.isReexported(extension)) {
result.add(extension);
XtendResource extensionResource = resourceManager.loadXtendResource(extension);
result.addAll(getReexportedExtensions(extensionResource));
}
}
return result;
}
private void injectStdlibImports(StandardLibraryImports stdLibImportsManager, List<String> list) {
list.addAll(Arrays.asList(stdLibImportsManager.getLibraries()));
if (list.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder();
if (stdLibImportsManager.getPlaceholderIndex() > 0) {
sb.append(ExpressionMigrationFacade.LF);
}
for (int i = 0; i < list.size(); i++) {
if (i > 0) {
sb.append(ExpressionMigrationFacade.LF);
}
sb.append("«EXTENSION ");
sb.append(list.get(i));
sb.append("»");
}
if (stdLibImportsManager.getPlaceholderIndex() == 0) {
sb.append(ExpressionMigrationFacade.LF);
}
insert(stdLibImportsManager.getPlaceholderIndex(), sb);
}
private void migrateDefinition(AbstractDefinition definition) throws MigrationException {
assert definition instanceof Definition || definition instanceof Advice;
migrateIdentifier(definition instanceof Definition ? ((Definition) definition).getDefName() : ((Advice) definition).getPointCut());
Map<String, EClassifier> envVariables = new HashMap<String, EClassifier>();
for (DeclaredParameter parameter : definition.getParams()) {
envVariables.put(parameter.getName().getValue(), migrateParameter(parameter));
}
Identifier targetType = definition.getType();
EClassifier qvtType = ctx.getTypeForName(targetType.getValue());
replace(targetType, typeManager.getQvtFQName(qvtType));
envVariables.put(ExecutionContext.IMPLICIT_VARIABLE, qvtType);
VariableNameDispatcher variableNameDispatcher = new VariableNameDispatcher(definition);
for (Statement statement : definition.getBody()) {
migrateStatement(statement, variableNameDispatcher, envVariables);
}
}
private void migrateIdentifier(Identifier definitionName) {
if (oclKeywordManager.isOclKeyword(definitionName)) {
replace(definitionName, oclKeywordManager.getValidIdentifierValue(definitionName));
}
}
private void migrateStatement(Statement statement, VariableNameDispatcher variableNameDispatcher, Map<String, EClassifier> envVariables) throws MigrationException {
if (statement instanceof ExpressionStatement) {
ExpressionStatement expressionStatement = (ExpressionStatement) statement;
migrateExpression(expressionStatement.getExpression(), EcorePackage.eINSTANCE.getEString(), envVariables, variableNameDispatcher);
} else if (statement instanceof ErrorStatement) {
ErrorStatement errorStatement = (ErrorStatement) statement;
migrateExpression(errorStatement.getMessage(), EcorePackage.eINSTANCE.getEString(), envVariables, variableNameDispatcher);
} else if (statement instanceof ExpandStatement) {
ExpandStatement expandStatement = (ExpandStatement) statement;
migrateExpandStatementDefinition(expandStatement);
ExpressionAnalyzeTrace trace = ctx.getTraces().get(expandStatement);
assert trace instanceof ExpandAnalyzeTrace;
ExpandAnalyzeTrace expTrace = (ExpandAnalyzeTrace) trace;
for (Expression parameter : expandStatement.getParameters()) {
migrateExpression(parameter, expTrace.getParameterType(parameter), envVariables, variableNameDispatcher);
}
if (expandStatement.getTarget() != null) {
migrateExpression(expandStatement.getTarget(), expTrace.getResultType(), envVariables, variableNameDispatcher);
}
if (expandStatement.getSeparator() != null) {
migrateExpression(expandStatement.getSeparator(), expTrace.getSeparatorType(), envVariables, variableNameDispatcher);
}
} else if (statement instanceof FileStatement) {
FileStatement fileStatement = (FileStatement) statement;
migrateExpression(fileStatement.getTargetFileName(), EcorePackage.eINSTANCE.getEString(), envVariables, variableNameDispatcher);
for (Statement bodyStatement : fileStatement.getBody()) {
migrateStatement(bodyStatement, variableNameDispatcher, envVariables);
}
} else if (statement instanceof ForEachStatement) {
ForEachStatement forEach = (ForEachStatement) statement;
ExpressionAnalyzeTrace trace = ctx.getTraces().get(forEach);
assert trace instanceof ForEachAnalyzeTrace;
ForEachAnalyzeTrace forEachTrace = (ForEachAnalyzeTrace) trace;
migrateExpression(forEach.getTarget(), forEachTrace.getResultType(), envVariables, variableNameDispatcher);
if (forEach.getSeparator() != null) {
migrateExpression(forEach.getSeparator(), forEachTrace.getSeparatorType(), envVariables, variableNameDispatcher);
}
for (Statement bodyStatement : forEach.getBody()) {
migrateStatement(bodyStatement, variableNameDispatcher, envVariables);
}
} else if (statement instanceof IfStatement) {
IfStatement ifStatement = (IfStatement) statement;
if (ifStatement.getCondition() != null) {
ExpressionAnalyzeTrace trace = ctx.getTraces().get(ifStatement);
migrateExpression(ifStatement.getCondition(), trace.getResultType(), envVariables, variableNameDispatcher);
}
for (Statement thenStatement : ifStatement.getThenPart()) {
migrateStatement(thenStatement, variableNameDispatcher, envVariables);
}
if (ifStatement.getElseIf() != null) {
migrateStatement(ifStatement.getElseIf(), variableNameDispatcher, envVariables);
}
} else if (statement instanceof LetStatement) {
LetStatement letStatement = (LetStatement) statement;
migrateIdentifier(letStatement.getVarName());
ExpressionAnalyzeTrace trace = ctx.getTraces().get(letStatement);
migrateExpression(letStatement.getVarValue(), trace.getResultType(), envVariables, variableNameDispatcher);
envVariables.put(letStatement.getVarName().getValue(), trace.getResultType());
try {
for (Statement bodyStatement : letStatement.getBody()) {
migrateStatement(bodyStatement, variableNameDispatcher, envVariables);
}
} finally {
envVariables.remove(letStatement.getVarName().getValue());
}
}
}
private void migrateExpandStatementDefinition(ExpandStatement expandStatement) {
Identifier definition = expandStatement.getDefinition();
String fullQualifiedDefinitionName = definition.getValue();
int lastSeparatorIndex = fullQualifiedDefinitionName.lastIndexOf(SyntaxConstants.NS_DELIM);
if (lastSeparatorIndex == -1) {
migrateIdentifier(definition);
return;
}
// fullName
String namePrefix = fullQualifiedDefinitionName.substring(0, lastSeparatorIndex);
String shortName = fullQualifiedDefinitionName.substring(lastSeparatorIndex + SyntaxConstants.NS_DELIM.length());
if (oclKeywordManager.isOclKeyword(shortName)) {
replace(definition, namePrefix + SyntaxConstants.NS_DELIM + oclKeywordManager.getValidIdentifierValue(shortName));
}
}
private EClassifier migrateParameter(DeclaredParameter parameter) throws MigrationException {
EClassifier parameterType = ctx.getTypeForName(parameter.getType().getValue());
replace(parameter, oclKeywordManager.getValidIdentifierValue(parameter.getName()) + " : " + typeManager.getQvtFQName(parameterType));
return parameterType;
}
private void migrateExpression(Expression expression, EClassifier expectedExpressionType, Map<String, EClassifier> envVariables, VariableNameDispatcher variableNameDispatcher) throws MigrationException {
ExpressionMigrationFacade expressionMF = new ExpressionMigrationFacade(expression, expectedExpressionType, envVariables, typeManager, modelManager, variableNameDispatcher, ctx, resourceName);
StringBuilder result = expressionMF.migrate();
replace(expression, result.toString());
}
private void replace(SyntaxElement syntaxElement, CharSequence replacement) {
ReplaceEdit replaceEdit = new ReplaceEdit(syntaxElement.getStartOffset(), syntaxElement.getEndOffset() + 1 - syntaxElement.getStartOffset(), replacement.toString());
edit.addChild(replaceEdit);
}
private void insert(int position, CharSequence text) {
InsertEdit insertEdit = new InsertEdit(position, text.toString());
edit.addChild(insertEdit);
}
}