/*******************************************************************************
* Copyright © 2012, 2013 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.mof.egl.utils;
import java.util.List;
import java.util.Stack;
import org.eclipse.edt.mof.EObject;
import org.eclipse.edt.mof.egl.Annotation;
import org.eclipse.edt.mof.egl.Assignment;
import org.eclipse.edt.mof.egl.AssignmentStatement;
import org.eclipse.edt.mof.egl.BinaryExpression;
import org.eclipse.edt.mof.egl.BooleanLiteral;
import org.eclipse.edt.mof.egl.Container;
import org.eclipse.edt.mof.egl.DeclarationExpression;
import org.eclipse.edt.mof.egl.Element;
import org.eclipse.edt.mof.egl.ExceptionBlock;
import org.eclipse.edt.mof.egl.Expression;
import org.eclipse.edt.mof.egl.Field;
import org.eclipse.edt.mof.egl.ForEachStatement;
import org.eclipse.edt.mof.egl.ForStatement;
import org.eclipse.edt.mof.egl.Function;
import org.eclipse.edt.mof.egl.IfStatement;
import org.eclipse.edt.mof.egl.InvocationExpression;
import org.eclipse.edt.mof.egl.IrFactory;
import org.eclipse.edt.mof.egl.LHSExpr;
import org.eclipse.edt.mof.egl.LocalVariableDeclarationStatement;
import org.eclipse.edt.mof.egl.LoopStatement;
import org.eclipse.edt.mof.egl.MemberName;
import org.eclipse.edt.mof.egl.MofConversion;
import org.eclipse.edt.mof.egl.MultiOperandExpression;
import org.eclipse.edt.mof.egl.Part;
import org.eclipse.edt.mof.egl.Statement;
import org.eclipse.edt.mof.egl.StatementBlock;
import org.eclipse.edt.mof.egl.Type;
import org.eclipse.edt.mof.egl.UnaryExpression;
import org.eclipse.edt.mof.egl.WhileStatement;
import org.eclipse.edt.mof.impl.AbstractVisitor;
import org.eclipse.edt.mof.impl.EObjectImpl;
public class CompoundConditionExpander extends AbstractVisitor{
public static class CompoundConditionChecker extends AbstractVisitor{
private boolean needsExpanding = false;
public static boolean needsExpanding(Expression expr) {
if (expr == null) {
return false;
}
CompoundConditionChecker checker = new CompoundConditionChecker();
expr.accept(checker);
return checker.needsExpanding;
}
public CompoundConditionChecker() {
disallowRevisit();
}
public boolean visit(Expression expr) {
return true;
}
public boolean visit(BinaryExpression exp) {
if (shouldExpand(exp)) {
needsExpanding = true;
return false;
}
return true;
}
public boolean visit(EObject obj) {
return false;
}
}
public static class InvocationChecker extends AbstractVisitor{
private boolean hasInvocation = false;
public static boolean hasInvocation(Expression expr) {
InvocationChecker checker = new InvocationChecker();
expr.accept(checker);
return checker.hasInvocation;
}
public InvocationChecker() {
disallowRevisit();
}
public boolean visit(InvocationExpression expr) {
hasInvocation = true;
return false;
}
public boolean visit(Expression expr) {
return true;
}
public boolean visit(EObject obj) {
return false;
}
}
private Stack<StatementBlock> blockStack = new Stack<StatementBlock>();
private int[] tempCount = new int[1];
private Part part;
public CompoundConditionExpander(Part part) {
disallowRevisit();
allowParentTracking();
this.part = part;
part.accept(this);
}
private void setAnnotations(Element oldElem, Element newElem) {
for (Annotation ann : oldElem.getAnnotations()) {
newElem.addAnnotation(ann);
}
}
private void setNewObjectInParent(EObject obj) {
if (getParent() instanceof List)
((List<EObject>) getParent()).set(getParentSlotIndex(), obj);
else
if (getParent() instanceof EObjectImpl)
((EObjectImpl) getParent()).slotSet(getParentSlotIndex(), obj);
}
public boolean visit(Part part) {
//do not visit any parts besides the one we are interested in
return this.part == part;
}
public boolean visit(StatementBlock block) {
//create a new statement block
StatementBlock newBlock = createBlock(block, block.getContainer());
setNewObjectInParent(newBlock);
blockStack.push(newBlock);
return true;
}
private StatementBlock createBlock(Element elem, Container container) {
StatementBlock newBlock = IrFactory.INSTANCE.createStatementBlock();
newBlock.setContainer(container);
setAnnotations(elem, newBlock);
return newBlock;
}
public boolean visit(ExceptionBlock block) {
//create a new exception block
ExceptionBlock newBlock = IrFactory.INSTANCE.createExceptionBlock();
newBlock.setContainer(block.getContainer());
newBlock.setException(block.getException());
setAnnotations(block, newBlock);
setNewObjectInParent(newBlock);
blockStack.push(newBlock);
return true;
}
public boolean visit(BinaryExpression exp) {
if (shouldExpand(exp)) {
expand(exp);
}
return false;
}
private static boolean shouldExpand(BinaryExpression exp) {
if (MultiOperandExpression.Op_AND.equals(exp.getOperator()) ||
MultiOperandExpression.Op_OR.equals(exp.getOperator())) {
if (hasInvocation(exp.getLHS()) || hasInvocation(exp.getRHS())) {
return true;
}
}
return false;
}
private static boolean hasInvocation(Expression expr) {
return InvocationChecker.hasInvocation(expr);
}
private void expand(BinaryExpression exp) {
//create a temporary statementBlock to hold the new statements
StatementBlock block = IrFactory.INSTANCE.createStatementBlock();
//Create a temporary variable: boolean temp = LHS;
Field field = createTemporaryField(exp, block, getBooleanType(), exp.getLHS(), blockStack.peek().getContainer());
//create if statement...
// || : if (!temp)
// && : if (temp)
IfStatement ifStmt = IrFactory.INSTANCE.createIfStatement();
setAnnotations(exp, ifStmt);
addStatementToBlock(ifStmt, block);
StatementBlock ifBlock = createBlock(exp, blockStack.peek().getContainer());
ifStmt.setTrueBranch(ifBlock);
MemberName nameExpression = createMemberName(exp, field);
if (MultiOperandExpression.Op_OR.equals(exp.getOperator())) {
UnaryExpression unExp = IrFactory.INSTANCE.createUnaryExpression();
setAnnotations(exp, unExp);
unExp.setExpression(nameExpression);
unExp.setOperator("!");
ifStmt.setCondition(unExp);
}
else {
ifStmt.setCondition(nameExpression);
}
//create Assignment statement: temp = RHS;
createAssignentStmt(exp, ifBlock, createMemberName(exp, field), exp.getRHS());
//Expand any Compound conditions in the newly created block
Function func = IrFactory.INSTANCE.createFunction();
func.setStatementBlock(block);
func.accept(this);
//add the expanded statements to the block
for (Statement stmt : func.getStatementBlock().getStatements()) {
addStatementToBlock(stmt, blockStack.peek());
}
//create a name expression for the temporary variable and replace the binary expression with it
setNewObjectInParent(createMemberName(exp, field));
}
private String createTempVarName() {
tempCount[0] = tempCount[0] + 1;
return "eze_compound_" + tempCount[0];
}
public void endVisit(Statement stmt) {
addStatementToBlock(stmt, blockStack.peek());
}
private void addStatementToBlock(Statement stmt, StatementBlock block) {
block.getStatements().add(stmt);
stmt.setContainer(block.getContainer());
}
public void endVisit(StatementBlock block) {
StatementBlock newBlock = blockStack.pop();
if ((getParent() instanceof List)
&& (getGrandParent() instanceof StatementBlock)
&& !blockStack.isEmpty()) {
addStatementToBlock(newBlock, blockStack.peek());
}
}
private Object getGrandParent() {
if (getParents().size() > 1) {
return getParents().get(getParents().size() - 2);
}
return null;
}
public boolean visit(IfStatement stmt) {
//If the true branch is just a statement, we must expand it to be a block
if (stmt.getTrueBranch() != null && !(stmt.getTrueBranch() instanceof StatementBlock)) {
StatementBlock newBlock = createBlock(stmt, stmt.getContainer());
newBlock.getStatements().add(stmt.getTrueBranch());
stmt.setTrueBranch(newBlock);
}
//If the false branch is just a statement, we must expand it to be a block
if (stmt.getFalseBranch() != null && !(stmt.getFalseBranch() instanceof StatementBlock)) {
StatementBlock newBlock = createBlock(stmt, stmt.getContainer());
newBlock.getStatements().add(stmt.getFalseBranch());
stmt.setFalseBranch(newBlock);
}
return true;
}
public boolean visit(LoopStatement stmt) {
//If the false branch is just a statement, we must expand it to be a block
if (stmt.getBody() != null && !(stmt.getBody() instanceof StatementBlock)) {
StatementBlock newBlock = createBlock(stmt, stmt.getContainer());
newBlock.getStatements().add(stmt.getBody());
stmt.setBody(newBlock);
}
return true;
}
public boolean visit(WhileStatement stmt) {
visit((LoopStatement)stmt);
if (CompoundConditionChecker.needsExpanding(stmt.getCondition())) {
//If the condition in the while statement requires expanding, we need to turn this:
// while (condition)
// block;
// end
//into this:
// temp boolean = true;
// while (temp)
// temp = condition;
// if (temp)
// block;
// end
// end
//Then the condition will be expanded when the new statements are visited
Expression condition = stmt.getCondition();
//create a new block for the while statement
Statement oldBody = stmt.getBody();
StatementBlock newBody = createBlock(oldBody, stmt.getContainer());
stmt.setBody(newBody);
//create temp field: temp boolean = true;
BooleanLiteral boolLit = IrFactory.INSTANCE.createBooleanLiteral();
boolLit.setBooleanValue(Boolean.TRUE);
setAnnotations(condition, boolLit);
Field field = createTemporaryField(condition, blockStack.peek(), getBooleanType(), boolLit, stmt.getContainer());
//replace the conditition in the while with the temp field
stmt.setCondition(createMemberName(condition, field));
//create assignment stmt: temp = condition;
createAssignentStmt(condition, newBody, createMemberName(condition, field), condition);
//create if statement: if (temp)
IfStatement ifStmt = IrFactory.INSTANCE.createIfStatement();
setAnnotations(condition, ifStmt);
addStatementToBlock(ifStmt, newBody);
ifStmt.setCondition(createMemberName(condition, field));
ifStmt.setTrueBranch(oldBody);
}
return true;
}
public boolean visit(ForStatement stmt) {
visit((LoopStatement)stmt);
//IF the FOR stmt contains a TO expression and a DELTA that need to be expanded, we will turn this:
// for (index from start to end by inc)
// stmt;
// end
//into this:
// tempTo int = end;
// tempInc int = 0;
// for (index from start to tempTo by tempInc
// stmt;
// tempTo = end;
// tempInc = inc;
// end
Expression toExpr = stmt.getToExpression();
Expression incExpr = stmt.getDeltaExpression();
if (CompoundConditionChecker.needsExpanding(toExpr)) {
//create temp variable for TO: tempTO = end;
Field tempToField = createTemporaryField(toExpr, blockStack.peek(), toExpr.getType(), toExpr, stmt.getContainer());
stmt.setToExpression(createMemberName(toExpr, tempToField));
//add assignment statement to the end of the for body: tempTo = end;
//NOTE: must clone the TO expression, because we reference it 2 times.
Expression cloneTo = ExpressionCloner.clone(toExpr);
createAssignentStmt(toExpr, (StatementBlock)stmt.getBody(), createMemberName(toExpr, tempToField), cloneTo);
}
if (CompoundConditionChecker.needsExpanding(incExpr)) {
Field tempIncField = createTemporaryField(incExpr, blockStack.peek(), incExpr.getType(), null, stmt.getContainer());
stmt.setDeltaExpression(createMemberName(incExpr, tempIncField));
//add assignment statement to the end of the for body: tempInc = inc;
createAssignentStmt(incExpr, (StatementBlock)stmt.getBody(), createMemberName(incExpr, tempIncField), incExpr);
}
return true;
}
public boolean visit(ForEachStatement stmt) {
//If the false branch is just a statement, we must expand it to be a block
if (stmt.getBody() != null && !(stmt.getBody() instanceof StatementBlock)) {
StatementBlock newBlock = createBlock(stmt, stmt.getContainer());
newBlock.getStatements().add(stmt.getBody());
stmt.setBody(newBlock);
}
return true;
}
private Field createTemporaryField(Element annotationElem, StatementBlock block , Type type, Expression initValue, Container container) {
//Create a temporary variable: temp boolean;
LocalVariableDeclarationStatement localDeclaration = IrFactory.INSTANCE.createLocalVariableDeclarationStatement();
setAnnotations(annotationElem, localDeclaration);
addStatementToBlock(localDeclaration, block);
DeclarationExpression declarationExpression = IrFactory.INSTANCE.createDeclarationExpression();
setAnnotations(annotationElem, declarationExpression);
Field field = IrFactory.INSTANCE.createField();
field.setName(createTempVarName());
field.setType(type);
declarationExpression.getFields().add(field);
localDeclaration.setExpression(declarationExpression);
if (initValue != null) {
field.setInitializerStatements(createBlock(annotationElem, container));
//create assignment statement: temp = true;
createAssignentStmt(annotationElem, field.getInitializerStatements(), createMemberName(annotationElem, field), initValue);
}
return field;
}
private MemberName createMemberName(Element annotationElem, Field field) {
MemberName nameExpression = IrFactory.INSTANCE.createMemberName();
setAnnotations(annotationElem, nameExpression);
nameExpression.setMember(field);
nameExpression.setId(field.getName());
return nameExpression;
}
private AssignmentStatement createAssignentStmt(Element annotationElem, StatementBlock block, LHSExpr lhs, Expression rhs) {
AssignmentStatement assignStmt = IrFactory.INSTANCE.createAssignmentStatement();
setAnnotations(annotationElem, assignStmt);
addStatementToBlock(assignStmt, block);
Assignment assign = IrFactory.INSTANCE.createAssignment();
setAnnotations(annotationElem, assign);
assign.setLHS(lhs);
assign.setRHS(rhs);
assign.setOperator("=");
assignStmt.setAssignment(assign);
return assignStmt;
}
private Type getBooleanType() {
return IRUtils.getEGLPrimitiveType(MofConversion.Type_Boolean);
}
}