blob: 32aaacb9ce800ad20115d2b52f9deee9977481af [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 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.parser.ASTDerivedTypeDefNode;
import org.eclipse.photran.internal.core.parser.ASTElseConstructNode;
import org.eclipse.photran.internal.core.parser.ASTElseIfConstructNode;
import org.eclipse.photran.internal.core.parser.ASTExecutableProgramNode;
import org.eclipse.photran.internal.core.parser.ASTIfConstructNode;
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;
/**
* Refactoring that converts nested if/then/else constructs into a SELECT CASE construct.
*
* @author Gustavo Risetti
*/
@SuppressWarnings("nls") // TODO: Externalize strings
public class ConvertIfThenElseToSelectCaseRefactoring extends FortranResourceRefactoring{
List<ASTIfConstructNode> ifNodes = new LinkedList<ASTIfConstructNode>();
List<ASTIfConstructNode> removeIfNodes = new LinkedList<ASTIfConstructNode>();
List<String> varNames = new LinkedList<String>();
List<String> cases = new LinkedList<String>();
List<String> bodyCases = new LinkedList<String>();
@Override
public String getName() {
return "Convert If-Then-Else To Select Case";
}
@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("One of the selected files (" + file.getName() +") cannot be parsed.");
}else {
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 {
boolean hasChanged = false;
List<ScopingNode> scopes = ast.getRoot().getAllContainedScopes();
for(ScopingNode scope : scopes){
if (!(scope instanceof ASTExecutableProgramNode) && !(scope instanceof ASTDerivedTypeDefNode)){
IASTListNode<IASTNode> body = (IASTListNode<IASTNode>)scope.getBody();
// Find nodes which contains nested if-then-else and add in a node list.
for(IASTNode node : body){
if(node instanceof ASTIfConstructNode){
ASTIfConstructNode ifNode = ((ASTIfConstructNode)node);
if(ifNode.getElseIfConstruct() != null){
ifNodes.add((ASTIfConstructNode)node);
}
}
}
}
}
// Checks that appear only allowed comparisons (==, .eq., or)
for(ASTIfConstructNode ifNode : ifNodes){
parserStmts(ifNode, null);
}
// Remove the nodes that do not satisfy the condition to be a case of a SELECT CASE
ifNodes.removeAll(removeIfNodes);
for(ASTIfConstructNode ifNode : ifNodes){
// Gets the list of values and cases for the new node to be built.
getPairs(ifNode, null);
// Checks if the same variable is evaluated in expressions.
if(hasSameVars()){
hasChanged = true;
String[] tabWithComments = ifNode.findFirstToken().getWhiteBefore().toString().split("\n"); //$NON-NLS-1$
String tab = tabWithComments[tabWithComments.length-1];
for(int i = 0; i < tab.length(); i ++){
if(tab.charAt(i) == ' ' || tab.charAt(i) == '\t'){
continue;
}else{
tab = ""; //$NON-NLS-1$
break;
}
}
// The new node is constructed.
String selectCaseNode = "SELECT CASE (" + varNames.get(0).trim() + ")\n"; //$NON-NLS-1$ //$NON-NLS-2$
for(int i = 0; i< cases.size(); i++){
String casesUnits = tab + "\tCASE " + cases.get(i).trim() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
String[] body = bodyCases.get(i).split("\n"); //$NON-NLS-1$
for(String bodyLine : body){
casesUnits += tab + "\t\t" + bodyLine.trim()+"\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
selectCaseNode += casesUnits;
}
selectCaseNode += tab + "END SELECT\n"; //$NON-NLS-1$
// Replaces the old node with the new node in AST.
ifNode.replaceWith(selectCaseNode);
varNames.clear();
cases.clear();
bodyCases.clear();
}else{
varNames.clear();
cases.clear();
bodyCases.clear();
}
}
if (hasChanged){
addChangeFromModifiedAST(file, pm);
}
}
// Checks if the same variable is evaluated in expressions.
private boolean hasSameVars(){
for(int i=0; i<varNames.size()-1; i++){
if(!(varNames.get(i).equalsIgnoreCase(varNames.get(i+1)))){
return false;
}
}
return true;
}
// Gets the list of values ​​and cases.
private void getPairs(ASTIfConstructNode ifNode, ASTElseIfConstructNode elseIfNode) {
if(elseIfNode == null){
String ifExpression = ifNode.getIfThenStmt().getGuardingExpression().toString().trim();
ifExpression = ifExpression.replaceAll(".OR.", ".or."); //$NON-NLS-1$ //$NON-NLS-2$
ifExpression = ifExpression.replaceAll(".Or.", ".or."); //$NON-NLS-1$ //$NON-NLS-2$
ifExpression = ifExpression.replaceAll(".oR.", ".or."); //$NON-NLS-1$ //$NON-NLS-2$
ifExpression = ifExpression.replaceAll(".EQ.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
ifExpression = ifExpression.replaceAll(".Eq.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
ifExpression = ifExpression.replaceAll(".eQ.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
ifExpression = ifExpression.replaceAll(".eq.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
String[] orSplit = ifExpression.split(".or."); //$NON-NLS-1$
for(String s : orSplit){
s = s.trim();
String[] eqSplit = s.split("=="); //$NON-NLS-1$
for(String seq : eqSplit){
seq = seq.trim();
}
for(int i=0; i<eqSplit.length; i+=2){
varNames.add(eqSplit[i]);
cases.add("("+eqSplit[i+1].trim()+")"); //$NON-NLS-1$ //$NON-NLS-2$
bodyCases.add(ifNode.getConditionalBody().toString());
}
}
ASTElseIfConstructNode elseIf = ifNode.getElseIfConstruct();
if(elseIf != null){
getPairs(ifNode, elseIf);
}
}else{
String elseIfExpression = elseIfNode.getElseIfStmt().getGuardingExpression().toString().trim();
elseIfExpression = elseIfExpression.replaceAll(".OR.", ".or."); //$NON-NLS-1$ //$NON-NLS-2$
elseIfExpression = elseIfExpression.replaceAll(".Or.", ".or."); //$NON-NLS-1$ //$NON-NLS-2$
elseIfExpression = elseIfExpression.replaceAll(".oR.", ".or."); //$NON-NLS-1$ //$NON-NLS-2$
elseIfExpression = elseIfExpression.replaceAll(".EQ.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
elseIfExpression = elseIfExpression.replaceAll(".Eq.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
elseIfExpression = elseIfExpression.replaceAll(".eQ.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
elseIfExpression = elseIfExpression.replaceAll(".eq.", "=="); //$NON-NLS-1$ //$NON-NLS-2$
String[] orSplit = elseIfExpression.split(".or."); //$NON-NLS-1$
for(String s : orSplit){
s = s.trim();
String[] eqSplit = s.split("=="); //$NON-NLS-1$
for(String seq : eqSplit){
seq = seq.trim();
}
for(int i=0; i<eqSplit.length; i+=2){
varNames.add(eqSplit[i]);
cases.add("("+eqSplit[i+1].trim()+")"); //$NON-NLS-1$ //$NON-NLS-2$
bodyCases.add(elseIfNode.getConditionalBody().toString());
}
}
ASTElseIfConstructNode elseIf = elseIfNode.getElseIfConstruct();
if(elseIf != null){
getPairs(ifNode, elseIf);
}else{
ASTElseConstructNode elseDefault = elseIfNode.getElseConstruct();
if(elseDefault != null){
String elseDefaultExpression = elseDefault.getConditionalBody().toString();
cases.add("DEFAULT"); //$NON-NLS-1$
bodyCases.add(elseDefaultExpression);
}
}
}
}
// Checks that appear only allowed comparisons (==, .eq., or)
private void parserStmts(ASTIfConstructNode ifNode, ASTElseIfConstructNode elseIfNode) {
String not_aloowed[] = {">", ".GT.", "<", ".LT.", ">=", ".GE.", "<=", ".LE.", "/=", ".NE.", ".AND.", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
".EQV.", ".NEQV.", ".gt.", ".lt.", ".ge.", ".le.", ".ne.", ".and.", ".eqv.", ".neqv."}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
if(elseIfNode == null){
String ifExpression = ifNode.getIfThenStmt().getGuardingExpression().toString().trim();
for(String s : not_aloowed){
if(ifExpression.contains(s)){
removeIfNodes.add(ifNode);
return;
}
}
ASTElseIfConstructNode elseIf = ifNode.getElseIfConstruct();
if(elseIf != null){
parserStmts(ifNode, elseIf);
}
}else{
String elseIfExpression = elseIfNode.getElseIfStmt().getGuardingExpression().toString().trim();
for(String s : not_aloowed){
if(elseIfExpression.contains(s)){
removeIfNodes.add(ifNode);
return;
}
}
ASTElseIfConstructNode elseIf = elseIfNode.getElseIfConstruct();
if(elseIf != null){
parserStmts(ifNode, elseIf);
}
}
}
@Override
protected void doCreateChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
// The change is made in method makeChangesTo(...).
}
}