/**
* Copyright (C) 2006-2017 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.support.reflect.code;
import spoon.SpoonException;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtCase;
import spoon.reflect.code.CtIf;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLoop;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtStatementList;
import spoon.reflect.code.CtSwitch;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.visitor.CtInheritanceScanner;
import java.util.ArrayList;
import java.util.List;
public abstract class CtStatementImpl extends CtCodeElementImpl implements CtStatement {
private static final long serialVersionUID = 1L;
public static void insertAfter(CtStatement target, CtStatement statement)
throws ParentNotInitializedException {
CtStatementList sts = target.getFactory().Core().createStatementList();
sts.addStatement(statement);
insertAfter(target, sts);
}
public static void insertAfter(CtStatement target, CtStatementList statements)
throws ParentNotInitializedException {
CtElement e = target.getParent();
if (e instanceof CtExecutable) {
throw new RuntimeException("cannot insert in this context (use insertEnd?)");
}
new InsertVisitor(target, statements, InsertType.AFTER).scan(e);
}
public static void insertBefore(CtStatement target, CtStatement statement)
throws ParentNotInitializedException {
CtStatementList sts = target.getFactory().Core().createStatementList();
sts.addStatement(statement);
insertBefore(target, sts);
}
public static void insertBefore(CtStatement target, CtStatementList statementsToBeInserted)
throws ParentNotInitializedException {
CtElement targetParent = target.getParent();
if (targetParent instanceof CtExecutable) {
throw new SpoonException("cannot insert in this context (use insertEnd?)");
}
try {
if (target.getParent(CtConstructor.class) != null) {
if (target instanceof CtInvocation && ((CtInvocation<?>) target).getExecutable().getSimpleName().startsWith("<init>")) {
throw new SpoonException("cannot insert a statement before a super or this invocation.");
}
}
} catch (ParentNotInitializedException ignore) {
// no parent set somewhere
}
new InsertVisitor(target, statementsToBeInserted, InsertType.BEFORE).scan(targetParent);
}
private static class InsertVisitor extends CtInheritanceScanner {
private final CtStatement target;
private final CtStatementList statementsToBeInserted;
private final InsertType insertType;
InsertVisitor(CtStatement target, CtStatementList statementsToBeInserted, InsertType insertType) {
this.target = target;
this.statementsToBeInserted = statementsToBeInserted;
this.insertType = insertType;
}
@Override
public <R> void visitCtBlock(CtBlock<R> e) {
super.visitCtBlock(e);
insertType.insertFromFirstStatement(e, target, statementsToBeInserted);
}
@Override
public void visitCtIf(CtIf e) {
super.visitCtIf(e);
boolean inThen = true;
CtStatement stat = e.getThenStatement();
if (stat != target) {
stat = e.getElseStatement();
inThen = false;
}
if (stat != target) {
throw new IllegalArgumentException("should not happen");
}
if (stat instanceof CtBlock) {
insertType.insert((CtBlock<?>) stat, statementsToBeInserted);
} else {
CtBlock<?> block = insertNewBlock(stat);
if (inThen) {
e.setThenStatement(block);
} else {
e.setElseStatement(block);
}
}
}
@Override
public <E> void visitCtSwitch(CtSwitch<E> e) {
super.visitCtSwitch(e);
for (CtStatement s : statementsToBeInserted) {
if (!(s instanceof CtCase)) {
throw new RuntimeException("cannot insert something that is not case in a switch");
}
}
e.setCases(insertType.insertFromLastStatement(e.getCases(), target, statementsToBeInserted));
}
@Override
public <E> void visitCtCase(CtCase<E> e) {
super.visitCtCase(e);
target.setParent(e);
e.setStatements(insertType.insertFromLastStatement(e.getStatements(), target, statementsToBeInserted));
}
@Override
public void scanCtLoop(CtLoop loop) {
super.scanCtLoop(loop);
CtStatement stat = loop.getBody();
if (stat instanceof CtBlock) {
insertType.insert((CtBlock<?>) stat, statementsToBeInserted);
} else {
CtBlock<?> block = insertNewBlock(stat);
target.setParent(block);
loop.setBody(block);
}
}
private CtBlock<?> insertNewBlock(CtStatement stat) {
CtBlock<?> block = target.getFactory().Core().createBlock();
block.addStatement(stat);
insertType.insertFromFirstStatement(block, target, statementsToBeInserted);
return block;
}
}
private enum InsertType {
BEFORE {
@Override
void insert(CtBlock<?> block, CtStatementList statementsToBeInserted) {
block.insertBegin(statementsToBeInserted);
}
@Override
void insertFromFirstStatement(CtBlock<?> block, CtStatement target, CtStatementList statementsToBeInserted) {
final List<CtStatement> copy = new ArrayList<>(block.getStatements());
int indexOfTargetElement = indexOfReference(block.getStatements(), target);
for (CtStatement ctStatement : statementsToBeInserted) {
copy.add(indexOfTargetElement++, ctStatement);
}
block.setStatements(copy);
}
@Override
<T extends CtElement> List<T> insertFromLastStatement(List<T> statements, CtStatement target, CtStatementList statementsToBeInserted) {
final List<T> copy = new ArrayList<>(statements);
int indexOfTargetElement = indexOfReference(statements, target);
for (int j = statementsToBeInserted.getStatements().size() - 1; j >= 0; j--) {
copy.add(indexOfTargetElement, (T) statementsToBeInserted.getStatements().get(j));
}
return copy;
}
},
AFTER {
@Override
void insert(CtBlock<?> block, CtStatementList statementsToBeInserted) {
block.insertEnd(statementsToBeInserted);
}
@Override
void insertFromFirstStatement(CtBlock<?> block, CtStatement target, CtStatementList statementsToBeInserted) {
final List<CtStatement> copy = new ArrayList<>(block.getStatements());
int indexOfTargetElement = indexOfReference(block.getStatements(), target);
for (CtStatement s : statementsToBeInserted) {
copy.add(++indexOfTargetElement, s);
}
block.setStatements(copy);
}
@Override
<T extends CtElement> List<T> insertFromLastStatement(List<T> statements, CtStatement target, CtStatementList statementsToBeInserted) {
final List<T> copy = new ArrayList<>(statements);
int indexOfTargetElement = indexOfReference(copy, target) + 1;
for (int j = statementsToBeInserted.getStatements().size() - 1; j >= 0; j--) {
copy.add(indexOfTargetElement, (T) statementsToBeInserted.getStatements().get(j));
}
return copy;
}
};
public int indexOfReference(List statements, CtElement target) {
int indexOfTargetElement = -1;
// check the reference not the equality
for (int i = 0; i < statements.size(); i++) {
if (statements.get(i) == target) {
indexOfTargetElement = i;
break;
}
}
return indexOfTargetElement;
}
abstract void insert(CtBlock<?> block, CtStatementList statementsToBeInserted);
abstract void insertFromFirstStatement(CtBlock<?> block, CtStatement target, CtStatementList statementsToBeInserted);
abstract <T extends CtElement> List<T> insertFromLastStatement(List<T> statements, CtStatement target, CtStatementList statementsToBeInserted);
}
@Override
public <T extends CtStatement> T insertBefore(CtStatement statement) throws ParentNotInitializedException {
insertBefore(this, statement);
return (T) this;
}
@Override
public <T extends CtStatement> T insertBefore(CtStatementList statements) throws ParentNotInitializedException {
insertBefore(this, statements);
return (T) this;
}
@Override
public <T extends CtStatement> T insertAfter(CtStatement statement) throws ParentNotInitializedException {
insertAfter(this, statement);
return (T) this;
}
@Override
public <T extends CtStatement> T insertAfter(CtStatementList statements) throws ParentNotInitializedException {
insertAfter(this, statements);
return (T) this;
}
String label;
@Override
public String getLabel() {
return label;
}
@Override
public <T extends CtStatement> T setLabel(String label) {
this.label = label;
return (T) this;
}
@Override
public void replace(CtStatement element) {
replace((CtElement) element);
}
@Override
public CtStatement clone() {
return (CtStatement) super.clone();
}
}