blob: cc8f5954d2d386133d446df76a85a7be0cf81fef [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 UFSM - Universidade Federal de Santa Maria (www.ufsm.br).
* 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
*******************************************************************************/
package org.eclipse.photran.internal.core.refactoring;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.photran.core.IFortranAST;
import org.eclipse.photran.internal.core.analysis.binding.ScopingNode;
import org.eclipse.photran.internal.core.lexer.Terminal;
import org.eclipse.photran.internal.core.lexer.Token;
import org.eclipse.photran.internal.core.parser.ASTDerivedTypeDefNode;
import org.eclipse.photran.internal.core.parser.ASTEntityDeclNode;
import org.eclipse.photran.internal.core.parser.ASTExecutableProgramNode;
import org.eclipse.photran.internal.core.parser.ASTTypeDeclarationStmtNode;
import org.eclipse.photran.internal.core.parser.ASTTypeSpecNode;
import org.eclipse.photran.internal.core.parser.IASTListNode;
import org.eclipse.photran.internal.core.parser.IASTNode;
import org.eclipse.photran.internal.core.refactoring.infrastructure.FortranResourceRefactoring;
import org.eclipse.photran.internal.core.refactoring.infrastructure.SourcePrinter;
import org.eclipse.photran.internal.core.reindenter.Reindenter;
/**
* Standardize Statements: refactoring that rewrites variables declarations,
* transforming them into simple statements, always containing the two points (::)
* that are characteristic of Fortran, making the code more readable.
*
* @author Gustavo Rissetti
* @author Timofey Yuvashev
* @author Jeff Overbey
* @author Ashley Kasza - externalized strings
**/
public class StandardizeStatementsRefactoring extends FortranResourceRefactoring{
@Override
public String getName()
{
return Messages.StandardizeStatementsRefactoring_Name;
}
@Override
protected void doCheckInitialConditions(RefactoringStatus status, IProgressMonitor pm) throws PreconditionFailure
{
ensureProjectHasRefactoringEnabled(status);
removeFixedFormFilesFrom(this.selectedFiles, status);
removeCpreprocessedFilesFrom(this.selectedFiles, status);
}
@Override
protected void doCheckFinalConditions(RefactoringStatus status, IProgressMonitor pm) throws PreconditionFailure
{
try
{
for (IFile file : selectedFiles)
{
IFortranAST ast = vpg.acquirePermanentAST(file);
if (ast == null)
{
status.addError(Messages.bind(Messages.StandardizeStatementsRefactoring_SelectedFileCannotBeParsed, file.getName()));
}
makeChangesTo(file, ast, status, pm);
vpg.releaseAST(file);
}
}
finally
{
vpg.releaseAllASTs();
}
}
@SuppressWarnings("unchecked")
private void makeChangesTo(IFile file, IFortranAST ast, RefactoringStatus status, IProgressMonitor pm) throws PreconditionFailure
{
List<ScopingNode> scopes = ast.getRoot().getAllContainedScopes();
for (ScopingNode scope : scopes)
if (!(scope instanceof ASTExecutableProgramNode) && !(scope instanceof ASTDerivedTypeDefNode))
standardizeStmtsInScope((IASTListNode<IASTNode>)scope.getBody(), ast);
addChangeFromModifiedAST(file, pm);
}
private void standardizeStmtsInScope(IASTListNode<IASTNode> body, IFortranAST ast)
{
List<ASTTypeDeclarationStmtNode> typeDeclStmts = createTypeDeclStmtList(body);
insertNewStmts(typeDeclStmts, body, ast);
removeOldStmts(typeDeclStmts, body);
}
/**
* @return a list of {@link ASTTypeDeclarationStmtNode}s where the nodes at odd-numbered
* indices are old statements to remove and those at even-numbered indices are the new,
* standardized statements to insert
*/
private List<ASTTypeDeclarationStmtNode> createTypeDeclStmtList(IASTListNode<IASTNode> body)
{
List<ASTTypeDeclarationStmtNode> statements = new LinkedList<ASTTypeDeclarationStmtNode>();
for (IASTNode node : body)
if (node instanceof ASTTypeDeclarationStmtNode)
standardizeTypeDeclStmt((ASTTypeDeclarationStmtNode)node, statements);
return statements;
}
private void standardizeTypeDeclStmt(ASTTypeDeclarationStmtNode typeDeclStmt, List<ASTTypeDeclarationStmtNode> statements)
{
IASTListNode<ASTEntityDeclNode> variables = typeDeclStmt.getEntityDeclList();
for (int i=0; i<variables.size(); i++)
{
ASTTypeDeclarationStmtNode newStmt = createNewVariableDeclaration(typeDeclStmt, i);
// Add a reference to the old statement (this will have an even-numbered index in the list)
statements.add((ASTTypeDeclarationStmtNode)typeDeclStmt);
// Then add the new declaration (this will have an odd-numbered index in the list)
statements.add(newStmt);
}
}
@SuppressWarnings("unchecked")
private ASTTypeDeclarationStmtNode createNewVariableDeclaration(ASTTypeDeclarationStmtNode typeDeclStmt, int i)
{
IASTListNode<ASTEntityDeclNode> variables = typeDeclStmt.getEntityDeclList();
ASTTypeDeclarationStmtNode newStmt = (ASTTypeDeclarationStmtNode)typeDeclStmt.clone();
if (i>0) newStmt.setTypeSpec(createTypeSpecNodeFrom(typeDeclStmt));
IASTListNode<ASTEntityDeclNode> newVariable = (IASTListNode<ASTEntityDeclNode>)variables.clone();
List<ASTEntityDeclNode> listOfVariablesToRemove = new LinkedList<ASTEntityDeclNode>();
for (int j=0; j<variables.size(); j++)
if (j != i)
listOfVariablesToRemove.add(newVariable.get(j));
newVariable.removeAll(listOfVariablesToRemove);
newStmt.setEntityDeclList(newVariable);
// Insert "::" if the original statement does not contain that already
String source = addTwoColons(newStmt);
newStmt = (ASTTypeDeclarationStmtNode)parseLiteralStatement(source);
return newStmt;
}
private ASTTypeSpecNode createTypeSpecNodeFrom(ASTTypeDeclarationStmtNode typeDeclStmt)
{
ASTTypeSpecNode typeNode = new ASTTypeSpecNode();
String type = typeDeclStmt.getTypeSpec().toString().trim();
String[] typeWithoutComments = type.split("\n"); //$NON-NLS-1$
type = typeWithoutComments[typeWithoutComments.length - 1].trim();
typeNode.setIsInteger(new Token(Terminal.T_INTEGER, type));
return typeNode;
}
private String addTwoColons(ASTTypeDeclarationStmtNode newStmt)
{
String source = SourcePrinter.getSourceCodeFromASTNode(newStmt);
int position_type = newStmt.getTypeSpec().toString().length();
String twoPoints = ""; //$NON-NLS-1$
String source_1 = source.substring(0, position_type);
String source_2 = source.substring(position_type,source.length());
if (!containsColonColon(source_2))
{
twoPoints = " :: "; //$NON-NLS-1$
}
// New statement, with the two points (::).
source = source_1+twoPoints+source_2.trim();
return source;
}
/** @return true iff <code>s</code> contains :: outside a comment */
private boolean containsColonColon(String s)
{
for (int i=0; i<s.length()-1; i++)
{
char p1 = s.charAt(i);
char p2 = s.charAt(i+1);
if (p1 == '!' || p2 == '!')
{
return false;
}
else if (p1 == ':' && p2 ==':')
{
return true;
}
}
return false;
}
/**
* Insert the new, standardized statements in the AST.
* <p>
* These have odd-numbered indices in the list (see {@link #createTypeDeclStmtList(IASTListNode)})
*/
private void insertNewStmts(List<ASTTypeDeclarationStmtNode> typeDeclStmts,
IASTListNode<IASTNode> body,
IFortranAST ast)
{
for (int i = 0; i<typeDeclStmts.size(); i+=2)
{
body.insertBefore(typeDeclStmts.get(i), typeDeclStmts.get(i+1));
Reindenter.reindent(typeDeclStmts.get(i+1), ast);
}
}
/**
* Removes the old statements from the AST.
* <p>
* These have even-numbered indices in the list (see {@link #createTypeDeclStmtList(IASTListNode)})
*/
private void removeOldStmts(List<ASTTypeDeclarationStmtNode> typeDeclStmts,
IASTListNode<IASTNode> body)
{
for (int i = 0; i<typeDeclStmts.size(); i+=2)
{
ASTTypeDeclarationStmtNode delete = typeDeclStmts.get(i);
if (body.contains(delete))
{
delete.removeFromTree();
}
}
}
@Override
protected void doCreateChange(IProgressMonitor pm) throws CoreException, OperationCanceledException
{
// The change is made in method makeChangesTo(...).
}
}