/** * 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); } }