/**
* 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.compiler;
import spoon.SpoonModelBuilder;
import spoon.compiler.ModelBuildingException;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtCodeSnippetExpression;
import spoon.reflect.code.CtCodeSnippetStatement;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtReturn;
import spoon.reflect.code.CtStatement;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtTypeReference;
import spoon.support.compiler.jdt.JDTSnippetCompiler;
import spoon.support.reflect.declaration.CtElementImpl;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Helper class for working with snippets */
public class SnippetCompilationHelper {
private SnippetCompilationHelper() { }
private static final String WRAPPER_CLASS_NAME = "Wrapper";
private static final String WRAPPER_METHOD_NAME = "wrap";
public static void compileAndReplaceSnippetsIn(CtType<?> c) {
Factory f = c.getFactory();
CtType<?> workCopy = c;
Set<ModifierKind> backup = EnumSet.noneOf(ModifierKind.class);
backup.addAll(workCopy.getModifiers());
workCopy.removeModifier(ModifierKind.PUBLIC);
try {
build(f, workCopy.toString());
} finally {
// restore modifiers
c.setModifiers(backup);
}
}
public static CtStatement compileStatement(CtCodeSnippetStatement st)
throws SnippetCompilationError {
return internalCompileStatement(st, st.getFactory().Type().VOID_PRIMITIVE);
}
public static CtStatement compileStatement(CtCodeSnippetStatement st, CtTypeReference returnType)
throws SnippetCompilationError {
return internalCompileStatement(st, returnType);
}
private static CtStatement internalCompileStatement(CtElement st, CtTypeReference returnType) {
Factory f = st.getFactory();
String contents = createWrapperContent(st, f, returnType);
build(f, contents);
CtType<?> c = f.Type().get(WRAPPER_CLASS_NAME);
// Get the part we want
CtMethod<?> wrapper = c.getMethod(WRAPPER_METHOD_NAME);
List<CtStatement> statements = wrapper.getBody().getStatements();
CtStatement ret = statements.get(statements.size() - 1);
// Clean up
c.getPackage().getTypes().remove(c);
if (ret instanceof CtClass) {
CtClass klass = (CtClass) ret;
ret.getFactory().Package().getRootPackage().addType(klass);
klass.setSimpleName(klass.getSimpleName().replaceAll("^[0-9]*", ""));
}
return ret;
}
@SuppressWarnings("unchecked")
public static <T> CtExpression<T> compileExpression(
CtCodeSnippetExpression<T> expr) throws SnippetCompilationError {
CtReturn<T> ret = (CtReturn<T>) internalCompileStatement(expr, expr.getFactory().Type().OBJECT);
return ret.getReturnedExpression();
}
private static void build(Factory f, String contents) {
// Build contents
SpoonModelBuilder builder = new JDTSnippetCompiler(f, contents);
try {
builder.build();
} catch (Exception e) {
throw new ModelBuildingException("snippet compilation error while compiling: " + contents, e);
}
}
private static String createWrapperContent(final CtElement element, final Factory f, final CtTypeReference returnType) {
CtClass<?> w = f.Class().create(WRAPPER_CLASS_NAME);
CtBlock body = f.Core().createBlock();
if (element instanceof CtStatement) {
body.addStatement((CtStatement) element);
} else if (element instanceof CtExpression) {
CtReturn ret = f.Core().createReturn();
ret.setReturnedExpression((CtExpression) element);
body.addStatement(ret);
}
Set<ModifierKind> modifiers = EnumSet.of(ModifierKind.STATIC);
Set<CtTypeReference<? extends Throwable>> thrownTypes = new HashSet<>();
thrownTypes.add(f.Class().<Throwable>get(Throwable.class).getReference());
f.Method().create(
w,
modifiers,
returnType,
WRAPPER_METHOD_NAME,
CtElementImpl.<CtParameter<?>>emptyList(),
thrownTypes,
body);
String contents = w.toString();
// Clean up (delete wrapper from factory) after it is printed. The DefaultJavaPrettyPrinter needs w in model to be able to print it correctly
w.getPackage().getTypes().remove(w);
return contents;
}
}