blob: 0a7609650ec7f45cdd345a496635b87dff18b06c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Stephen Downs, Robert Samblanet, Kevin Schilling, Jon
* Woolwine, and Chad Zamzow
* 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:
* Stephen Downs, Robert Samblanet, Kevin Schilling,
* Jon Woolwine, and Chad Zamzow
*******************************************************************************/
package org.eclipse.photran.internal.core.refactoring;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
import org.eclipse.core.internal.resources.SavedState;
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.ASTAssignmentStmtNode;
import org.eclipse.photran.internal.core.parser.ASTAttrSpecNode;
import org.eclipse.photran.internal.core.parser.ASTAttrSpecSeqNode;
import org.eclipse.photran.internal.core.parser.ASTDatalistNode;
import org.eclipse.photran.internal.core.parser.ASTDataStmtNode;
import org.eclipse.photran.internal.core.parser.ASTEntityDeclNode;
import org.eclipse.photran.internal.core.parser.ASTListNode;
import org.eclipse.photran.internal.core.parser.ASTSaveStmtNode;
import org.eclipse.photran.internal.core.parser.ASTSavedEntityNode;
import org.eclipse.photran.internal.core.parser.ASTSeparatedListNode;
import org.eclipse.photran.internal.core.parser.ASTTypeDeclarationStmtNode;
import org.eclipse.photran.internal.core.parser.ASTVariableNode;
import org.eclipse.photran.internal.core.parser.ASTVisitor;
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;
/**
* Makes all implicitly saved variables explicitly saved.
*
* @author Stephen Downs
* @author Robert Samblanet
* @author Kevin Schilling
* @author Jon Woolwine
* @author Chad Zamzow
*/
@SuppressWarnings("all")
public class MakeSaveExplicitRefactoring extends FortranResourceRefactoring
{
///////////////////////////////////////////////////////////////////////////
// Fields
///////////////////////////////////////////////////////////////////////////
@Override
public String getName()
{
return Messages.MakeSaveExplicitRefactoring_Name;
}
private IFortranAST currAST = null;
///////////////////////////////////////////////////////////////////////////
// Initial Preconditions
///////////////////////////////////////////////////////////////////////////
/**
* @see MoveSavedToCommonBlockRefactoring
*/
@Override
protected void doCheckInitialConditions(RefactoringStatus status, IProgressMonitor pm)
throws PreconditionFailure
{
ensureProjectHasRefactoringEnabled(status);
}
///////////////////////////////////////////////////////////////////////////
// Final Preconditions
///////////////////////////////////////////////////////////////////////////
@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.MakeSaveExplicitRefactoring_SelectedFileCannotBeParsed, file.getName()));
}
else
{
currAST = ast;
makeChangesTo(file, status, pm);
vpg.releaseAST(file);
}
}
}
finally
{
vpg.releaseAllASTs();
}
}
///////////////////////////////////////////////////////////////////////////
// Change
///////////////////////////////////////////////////////////////////////////
@Override
protected void doCreateChange(IProgressMonitor pm) throws CoreException, OperationCanceledException
{
}
/**
* Given an AST, makes the refactoring changes by calling the makeAllSaveAttributesExplicit function
* @param file
* @param status
* @param pm
* @throws PreconditionFailure
*/
private void makeChangesTo(IFile file, RefactoringStatus status, IProgressMonitor pm) throws PreconditionFailure
{
for (ScopingNode scope : currAST.getRoot().getAllContainedScopes())
{
SavedVariableVisitor savedVariableVisitor = new SavedVariableVisitor();
scope.accept(savedVariableVisitor);
if(!savedVariableVisitor.hasGlobalSaveStmt() && !scope.isMainProgram())
{
HashSet<String> explicitlySavedVariables = savedVariableVisitor.getExplicitlySavedVariables();
TreeSet<String> dataVariables = savedVariableVisitor.getDataBlockVariables();
makeAllSaveAttributesExplicit(scope, explicitlySavedVariables, dataVariables);
}
}
addChangeFromModifiedAST(file, pm);
}
/**
* Makes all implicit saves explicit in the given scope
* @param scope
* @param explicitlySavedVariables
* @param dataEntities
* @throws PreconditionFailure
*/
private void makeAllSaveAttributesExplicit(ScopingNode scope, HashSet<String> explicitlySavedVariables, TreeSet<String> dataEntities)
throws PreconditionFailure
{
if(scope.getBody() == null)
{
return;
}
for (IASTNode node : scope.getBody().getChildren())
{
if (node instanceof ASTTypeDeclarationStmtNode)
{
ASTTypeDeclarationStmtNode declarationNode = (ASTTypeDeclarationStmtNode)node;
makeImplicitlySavedVariablesExplicitlySaved(scope, declarationNode, dataEntities, explicitlySavedVariables);
}
}
//add all implicitly saved data block variables to a save statement
for(String variable : dataEntities)
{
if(!explicitlySavedVariables.contains(variable.toLowerCase()))
{
addVariableToSaveStmt(scope, variable);
explicitlySavedVariables.add(variable.toLowerCase());
}
}
}
/**
* Helper function that adds SAVE to a type declaration
* @param scope
* @param typeDeclaration
* @param dataEntities
* @param savedEntities
*/
private void makeImplicitlySavedVariablesExplicitlySaved(ScopingNode scope, ASTTypeDeclarationStmtNode typeDeclaration, TreeSet<String> dataEntities, HashSet<String> savedEntities)
{
IASTListNode<ASTEntityDeclNode> entityDeclList = typeDeclaration.getEntityDeclList();
boolean declContainsSavedAndUnsavedVariables = containsUnsavedAndSavedVariables(typeDeclaration, dataEntities);
for(ASTEntityDeclNode variableDeclaration : entityDeclList)
{
if (isImplicitlySaved(scope, variableDeclaration, dataEntities) &&
!savedEntities.contains(declarationVariableName(variableDeclaration).toLowerCase()))
{
if(!declContainsSavedAndUnsavedVariables)
{
String declString = SourcePrinter.getSourceCodeFromASTNode(typeDeclaration);
ASTAttrSpecSeqNode attrSpecSeqNode = createSaveAttrSpecSeqNode(!declString.contains("::"));
// if there is no attrSpecSeq, create a new one and add it to the typeDeclaration
if( typeDeclaration.getAttrSpecSeq() == null )
{
IASTListNode<ASTAttrSpecSeqNode> attrSpecSeq = new ASTListNode<ASTAttrSpecSeqNode>( 1 );
typeDeclaration.setAttrSpecSeq(attrSpecSeq);
}
// add Save attribute to attrSpecSeq
typeDeclaration.getAttrSpecSeq().add(attrSpecSeqNode);
for(ASTEntityDeclNode decl : typeDeclaration.getEntityDeclList())
{
savedEntities.add(declarationVariableName(decl).toLowerCase());
}
return;
}
else
{
String variableName = declarationVariableName(variableDeclaration);
savedEntities.add(variableName.toLowerCase());
addVariableToSaveStmt(scope, variableName);
}
}
}
}
/**
* Helper function that adds the given variable name to a global save statement in the given scope
* @param scope
* @param variableName
*/
private void addVariableToSaveStmt(ScopingNode scope, String variableName)
{
ASTSavedEntityNode savedEntity = new ASTSavedEntityNode();
Token savedEntityToken = new Token(Terminal.T_IDENT, variableName);
savedEntity.setVariableName(savedEntityToken);
for (IASTNode node : scope.getBody().getChildren())
{
if(node instanceof ASTSaveStmtNode)
{
IASTListNode<ASTSavedEntityNode> variableList = ((ASTSaveStmtNode)node).getVariableList();
ASTSeparatedListNode<ASTSavedEntityNode> astSeparatedListNode = (ASTSeparatedListNode<ASTSavedEntityNode>)variableList;
astSeparatedListNode.add(new Token(null, ", "), savedEntity);
return;
}
}
ASTSaveStmtNode newSaveStmt = (ASTSaveStmtNode)parseLiteralStatement("SAVE " + variableName);
IASTListNode body = scope.getBody();
body.add(0, newSaveStmt);
Reindenter.reindent(newSaveStmt, currAST);
}
/**
* Helper function that checks declaration lists to see if it contains both saved and unsaved variables
* @param typeDeclaration
* @param dataEntities
* @return
*/
private boolean containsUnsavedAndSavedVariables(ASTTypeDeclarationStmtNode typeDeclaration, TreeSet<String> dataEntities)
{
if(typeDeclaration.getEntityDeclList() == null) return false;
boolean containsSaved = false, containsUnSaved = false;
for(ASTEntityDeclNode decl : typeDeclaration.getEntityDeclList())
{
if (decl.getInitialization() == null
&& !dataEntities.contains(declarationVariableName(decl).toLowerCase()))
{
containsUnSaved = true;
}
else
{
containsSaved = true;
}
}
return containsSaved && containsUnSaved;
}
/**
* Helper function that extracts the declaration variable name from a declaration node
* @param decl
* @return
*/
private String declarationVariableName(ASTEntityDeclNode decl)
{
return decl.getObjectName().getObjectName().getText();
}
/**
* Helper function that checks to see if a given variable is implicitly saved
* @param scope
* @param variableDeclaration
* @param dataEntities
* @return
*/
private boolean isImplicitlySaved(ScopingNode scope, ASTEntityDeclNode variableDeclaration, TreeSet<String> dataEntities)
{
return (variableDeclaration.getInitialization() != null ||
dataEntities.contains(declarationVariableName(variableDeclaration).toLowerCase()));
}
/**
* Creates a new AttrSpecSeqNode with SAVE attribute
* @return new AttrSpecSeqNode with SAVE attribute
*/
private ASTAttrSpecSeqNode createSaveAttrSpecSeqNode(boolean addDblColon)
{
ASTAttrSpecSeqNode attrSpecSeqNode = new ASTAttrSpecSeqNode();
ASTAttrSpecNode attrSpecNode = new ASTAttrSpecNode();
Token token;
if(addDblColon)
{
token = new Token(null, ", SAVE ::" );
}
else
{
token = new Token(null, ", SAVE" );
}
attrSpecNode.setIsSave( token );
attrSpecSeqNode.setAttrSpec(attrSpecNode);
return attrSpecSeqNode;
}
/**
* Saved Variable Visitor class
*
* Iterates through all nodes in a scope. While doing this, it checks three different cases:
* 1) If the node is a save statement, it checks to see if it is a global save statement.
* If it isn't, it adds all the saved variables to the explicitlySavedVariables list.
* 2) If the node is a declaration, it checks to see if it has been explicitly saved. If it
* has, it adds the variable to the explicitlySavedVariables list.
* 3) If the node is a data statement, it adds all variables in the data block to the
* dataBlockVariables list.
*
* @author ShinSheep
*/
private class SavedVariableVisitor extends ASTVisitor
{
private boolean hasGlobalSaveStmt;
private HashSet<String> explicitlySavedVariables;
private TreeSet<String> dataBlockVariables;
private ASTSaveStmtNode saveStmt;
public TreeSet<String> getDataBlockVariables()
{
return dataBlockVariables;
}
public HashSet<String> getExplicitlySavedVariables()
{
return explicitlySavedVariables;
}
public boolean hasGlobalSaveStmt()
{
return hasGlobalSaveStmt;
}
public SavedVariableVisitor()
{
super();
this.hasGlobalSaveStmt = false;
explicitlySavedVariables = new HashSet<String>();
dataBlockVariables = new TreeSet<String>();
saveStmt = null;
}
@Override
public void visitASTSaveStmtNode(ASTSaveStmtNode node)
{
this.saveStmt = node;
if (node.getVariableList() == null)
{
hasGlobalSaveStmt = true;
}
else
{
for(ASTSavedEntityNode variable : node.getVariableList())
{
explicitlySavedVariables.add(variable.getVariableName().getText().toLowerCase());
}
}
}
@Override
public void visitASTTypeDeclarationStmtNode(ASTTypeDeclarationStmtNode node)
{
IASTListNode<ASTAttrSpecSeqNode> attrSpecSeq = node.getAttrSpecSeq();
if(attrSpecSeq != null)
{
for(ASTAttrSpecSeqNode attrSpecSeqNode : attrSpecSeq)
{
ASTAttrSpecNode attrSpecNode = attrSpecSeqNode.getAttrSpec();
if(attrSpecNode != null && attrSpecNode.isSave())
{
for(ASTEntityDeclNode variable : node.getEntityDeclList())
{
explicitlySavedVariables.add(variable.getObjectName().getObjectName().getText().toLowerCase());
}
}
}
}
}
@Override
public void visitASTDataStmtNode(ASTDataStmtNode node)
{
IASTListNode<ASTDatalistNode> dataList = ((ASTDataStmtNode)node).getDatalist();
for (IASTNode dataEntity : dataList.getChildren())
{
ASTDatalistNode dataListNode = (ASTDatalistNode)dataEntity;
for( IASTNode variableNameNode : dataListNode.getDataStmtSet().getDataStmtObjectList().getChildren())
{
if(variableNameNode instanceof ASTVariableNode)
{
ASTVariableNode variableNode = (ASTVariableNode)variableNameNode;
String variableName = variableNode.getDataRef().get(0).getName().getText();
dataBlockVariables.add(variableName.toLowerCase());
}
}
}
}
}
}