/** * 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.reflect.factory; import spoon.reflect.code.BinaryOperatorKind; import spoon.reflect.code.CtAssignment; import spoon.reflect.code.CtBinaryOperator; import spoon.reflect.code.CtBlock; import spoon.reflect.code.CtCatch; import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtFieldRead; import spoon.reflect.code.CtInvocation; import spoon.reflect.code.CtLiteral; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtNewArray; import spoon.reflect.code.CtNewClass; import spoon.reflect.code.CtStatement; import spoon.reflect.code.CtStatementList; import spoon.reflect.code.CtThisAccess; import spoon.reflect.code.CtThrow; import spoon.reflect.code.CtTypeAccess; import spoon.reflect.code.CtVariableAccess; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtNamedElement; import spoon.reflect.declaration.CtVariable; import spoon.reflect.declaration.ModifierKind; import spoon.reflect.reference.CtArrayTypeReference; import spoon.reflect.reference.CtCatchVariableReference; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.reference.CtFieldReference; import spoon.reflect.reference.CtLocalVariableReference; import spoon.reflect.reference.CtPackageReference; import spoon.reflect.reference.CtReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.reference.CtVariableReference; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; /** * This sub-factory contains utility methods to create code elements. To avoid * over-using reflection, consider using {@link spoon.template.Template}. */ public class CodeFactory extends SubFactory { /** * Creates a {@link spoon.reflect.code.CtCodeElement} sub-factory. */ public CodeFactory(Factory factory) { super(factory); } /** * Creates a binary operator. * * @param <T> * the type of the expression * @param left * the left operand * @param right * the right operand * @param kind * the operator kind * @return a binary operator expression */ public <T> CtBinaryOperator<T> createBinaryOperator(CtExpression<?> left, CtExpression<?> right, BinaryOperatorKind kind) { return factory.Core().<T>createBinaryOperator().setLeftHandOperand(left).setKind(kind).setRightHandOperand(right); } /** * Creates a accessed type. * * <p>This method sets a <i>clone</i> of the given {@code accessedType} object to the * {@linkplain CtTypeAccess#getAccessedType() accessedType} field of the returned {@link CtTypeAccess}. If the * given {@code accessedType} is unique and cloning is not needed, use * {@link #createTypeAccessWithoutCloningReference(CtTypeReference)} instead of this method.</p> * @param accessedType a type reference to the accessed type. * @param <T> the type of the expression. * @return a accessed type expression. */ public <T> CtTypeAccess<T> createTypeAccess(CtTypeReference<T> accessedType) { if (accessedType == null) { return factory.Core().createTypeAccess(); } CtTypeReference<T> access = accessedType.clone(); // a type access doesn't contain actual type parameters access.setActualTypeArguments(null); return createTypeAccessWithoutCloningReference(access); } /** * Creates a accessed type. * * <p>This method sets a <i>clone</i> of the given {@code accessedType} object to the * {@linkplain CtTypeAccess#getAccessedType() accessedType} field of the returned {@link CtTypeAccess}. If the * given {@code accessedType} is unique and cloning is not needed, use * {@link #createTypeAccessWithoutCloningReference(CtTypeReference)} instead of this method.</p> * * @param accessedType * a type reference to the accessed type. * @param isImplicit * type of the type access is implicit or not. * @param <T> * the type of the expression. * @return a accessed type expression. */ public <T> CtTypeAccess<T> createTypeAccess(CtTypeReference<T> accessedType, boolean isImplicit) { return createTypeAccess(accessedType).setImplicit(isImplicit); } /** * Creates a accessed type, see {@link #createTypeAccess(CtTypeReference)} for details. * @param accessedType a type reference to the accessed type. * @param <T> the type of the expression. * @return a accessed type expression. */ public <T> CtTypeAccess<T> createTypeAccessWithoutCloningReference(CtTypeReference<T> accessedType) { final CtTypeAccess<T> typeAccess = factory.Core().createTypeAccess(); typeAccess.setAccessedType(accessedType); return typeAccess; } /** * Creates a class access expression of the form <code>C.class</code>. * * @param <T> * the actual type of the accessed class if available * @param type * a type reference to the accessed class * @return the class access expression. */ public <T> CtFieldAccess<Class<T>> createClassAccess(CtTypeReference<T> type) { @SuppressWarnings({ "rawtypes", "unchecked" }) CtTypeReference<Class<T>> classType = (CtTypeReference) factory.Type().createReference(Class.class); CtTypeAccess<T> typeAccess = factory.Code().createTypeAccess(type); CtFieldReference<Class<T>> fieldReference = factory.Core().createFieldReference(); fieldReference.setSimpleName("class"); fieldReference.setType(classType); fieldReference.setDeclaringType(type); CtFieldRead<Class<T>> fieldRead = factory.Core().createFieldRead(); fieldRead.setType(classType.clone()); fieldRead.setVariable(fieldReference); fieldRead.setTarget(typeAccess); return fieldRead; } /** * Creates a constructor call. The correct constructor is inferred based on parameters * * @param type the decelerating type of the constructor * @param parameters the arguments of the constructor call * @param <T> the actual type of the decelerating type of the constructor if available * @return the constructor call */ public <T> CtConstructorCall<T> createConstructorCall(CtTypeReference<T> type, CtExpression<?>...parameters) { CtConstructorCall<T> constructorCall = factory.Core() .createConstructorCall(); CtExecutableReference<T> executableReference = factory.Core() .createExecutableReference(); executableReference.setType(type); executableReference.setDeclaringType(type == null ? type : type.clone()); executableReference.setSimpleName(CtExecutableReference.CONSTRUCTOR_NAME); List<CtTypeReference<?>> typeReferences = new ArrayList<>(); for (int i = 0; i < parameters.length; i++) { CtExpression<?> parameter = parameters[i]; typeReferences.add(parameter.getType()); } executableReference.setParameters(typeReferences); constructorCall.setArguments(Arrays.asList(parameters)); constructorCall.setExecutable(executableReference); return constructorCall; } /** * Creates a new class with an anonymous class. * * @param type the decelerating type of the constructor. * @param anonymousClass Anonymous class in the new class. * @param parameters the arguments of the constructor call. * @param <T> the actual type of the decelerating type of the constructor if available/ * @return the new class. */ public <T> CtNewClass<T> createNewClass(CtTypeReference<T> type, CtClass<?> anonymousClass, CtExpression<?>...parameters) { CtNewClass<T> ctNewClass = factory.Core().createNewClass(); CtExecutableReference<T> executableReference = factory.Constructor().createReference(type, parameters); ctNewClass.setArguments(Arrays.asList(parameters)); ctNewClass.setExecutable(executableReference); ctNewClass.setAnonymousClass(anonymousClass); anonymousClass.setSimpleName("0"); return ctNewClass; } /** * Creates an invocation (can be a statement or an expression). * * @param <T> * the return type of the invoked method * @param target * the target expression * @param executable * the invoked executable * @param arguments * the argument list * @return the new invocation */ public <T> CtInvocation<T> createInvocation(CtExpression<?> target, CtExecutableReference<T> executable, CtExpression<?>... arguments) { List<CtExpression<?>> ext = new ArrayList<>(arguments.length); Collections.addAll(ext, arguments); return createInvocation(target, executable, ext); } /** * Creates an invocation (can be a statement or an expression). * * @param <T> * the return type of the invoked method * @param target * the target expression (may be null for static methods) * @param executable * the invoked executable * @param arguments * the argument list * @return the new invocation */ public <T> CtInvocation<T> createInvocation(CtExpression<?> target, CtExecutableReference<T> executable, List<CtExpression<?>> arguments) { return factory.Core().<T>createInvocation().<CtInvocation<T>>setTarget(target).<CtInvocation<T>>setExecutable(executable).setArguments(arguments); } /** * Creates a literal with a given value. * * @param <T> * the type of the literal * @param value * the value of the literal * @return a new literal */ public <T> CtLiteral<T> createLiteral(T value) { CtLiteral<T> literal = factory.Core().<T>createLiteral(); literal.setValue(value); if (value != null) { literal.setType((CtTypeReference<T>) factory.Type().<T>createReference((Class<T>) value.getClass()).unbox()); } else { literal.setType((CtTypeReference<T>) factory.Type().nullType()); } return literal; } /** * Creates a one-dimension array that must only contain literals. */ @SuppressWarnings("unchecked") public <T> CtNewArray<T[]> createLiteralArray(T[] value) { if (!value.getClass().isArray()) { throw new RuntimeException("value is not an array"); } if (value.getClass().getComponentType().isArray()) { throw new RuntimeException("can only create one-dimension arrays"); } final CtTypeReference<T> componentTypeRef = factory.Type().createReference((Class<T>) value.getClass().getComponentType()); final CtArrayTypeReference<T[]> arrayReference = factory.Type().createArrayReference(componentTypeRef); CtNewArray<T[]> array = factory.Core().<T[]>createNewArray().setType(arrayReference); for (T e : value) { CtLiteral<T> l = factory.Core().createLiteral(); l.setValue(e); array.addElement(l); } return array; } /** * Creates a local variable declaration. * * @param <T> * the local variable type * @param type * the reference to the type * @param name * the name of the variable * @param defaultExpression * the assigned default expression * @return a new local variable declaration */ public <T> CtLocalVariable<T> createLocalVariable(CtTypeReference<T> type, String name, CtExpression<T> defaultExpression) { return factory.Core().<T>createLocalVariable().<CtLocalVariable<T>>setSimpleName(name).<CtLocalVariable<T>>setType(type).setDefaultExpression(defaultExpression); } /** * Creates a local variable reference that points to an existing local * variable (strong referencing). */ public <T> CtLocalVariableReference<T> createLocalVariableReference(CtLocalVariable<T> localVariable) { CtLocalVariableReference<T> ref = factory.Core().createLocalVariableReference(); ref.setType(localVariable.getType() == null ? null : localVariable.getType().clone()); ref.setSimpleName(localVariable.getSimpleName()); ref.setParent(localVariable); return ref; } /** * Creates a local variable reference with its name an type (weak * referencing). */ public <T> CtLocalVariableReference<T> createLocalVariableReference(CtTypeReference<T> type, String name) { return factory.Core().<T>createLocalVariableReference().setType(type).setSimpleName(name); } /** * Creates a catch variable declaration. * * @param <T> * the catch variable type * @param type * the reference to the type * @param name * the name of the variable * @param modifierKinds * Modifiers of the catch variable * @return a new catch variable declaration */ public <T> CtCatchVariable<T> createCatchVariable(CtTypeReference<T> type, String name, ModifierKind...modifierKinds) { return factory.Core().<T>createCatchVariable().<CtCatchVariable<T>>setSimpleName(name).<CtCatchVariable<T>>setType(type).setModifiers(new HashSet<>(Arrays.asList(modifierKinds))); } /** * Creates a catch variable reference that points to an existing catch * variable (strong referencing). */ public <T> CtCatchVariableReference<T> createCatchVariableReference(CtCatchVariable<T> catchVariable) { return factory.Core().<T>createCatchVariableReference().setType(catchVariable.getType()).<CtCatchVariableReference<T>>setSimpleName(catchVariable.getSimpleName()); } /** * Creates a new statement list from an existing block. */ public <R> CtStatementList createStatementList(CtBlock<R> block) { CtStatementList l = factory.Core().createStatementList(); for (CtStatement s : block.getStatements()) { l.addStatement(s.clone()); } return l; } /** * Creates an explicit access to a <code>this</code> variable (of the form * <code>type.this</code>). * * @param <T> * the actual type of <code>this</code> * @param type * the reference to the type that holds the <code>this</code> * variable * @return a <code>type.this</code> expression */ public <T> CtThisAccess<T> createThisAccess(CtTypeReference<T> type) { return createThisAccess(type, false); } /** * Creates an access to a <code>this</code> variable (of the form * <code>type.this</code>). * * @param <T> * the actual type of <code>this</code> * @param type * the reference to the type that holds the <code>this</code> * variable * @param isImplicit * type of the this access is implicit or not. * @return a <code>type.this</code> expression */ public <T> CtThisAccess<T> createThisAccess(CtTypeReference<T> type, boolean isImplicit) { CtThisAccess<T> thisAccess = factory.Core().<T>createThisAccess(); thisAccess.setImplicit(isImplicit); thisAccess.setType(type); CtTypeAccess<T> typeAccess = factory.Code().createTypeAccess(type); thisAccess.setTarget(typeAccess); return thisAccess; } /** * Creates a variable access. */ public <T> CtVariableAccess<T> createVariableRead(CtVariableReference<T> variable, boolean isStatic) { CtVariableAccess<T> va; if (variable instanceof CtFieldReference) { va = factory.Core().createFieldRead(); // creates a this target for non-static fields to avoid name conflicts... if (!isStatic) { ((CtFieldAccess<T>) va).setTarget(createThisAccess(((CtFieldReference<T>) variable).getDeclaringType())); } } else { va = factory.Core().createVariableRead(); } return va.setVariable(variable); } /** * Creates a list of variable accesses. * * @param variables * the variables to be accessed */ public List<CtExpression<?>> createVariableReads(List<? extends CtVariable<?>> variables) { List<CtExpression<?>> result = new ArrayList<>(variables.size()); for (CtVariable<?> v : variables) { result.add(createVariableRead(v.getReference(), v.getModifiers().contains(ModifierKind.STATIC))); } return result; } /** * Creates a variable assignment (can be an expression or a statement). * * @param <T> * the type of the assigned variable * @param variable * a reference to the assigned variable * @param isStatic * tells if the assigned variable is static or not * @param expression * the assigned expression * @return a variable assignment */ public <A, T extends A> CtAssignment<A, T> createVariableAssignment(CtVariableReference<A> variable, boolean isStatic, CtExpression<T> expression) { CtVariableAccess<A> vaccess = createVariableRead(variable, isStatic); return factory.Core().<A, T>createAssignment().<CtAssignment<A, T>>setAssignment(expression).setAssigned(vaccess); } /** * Creates a list of statements that contains the assignments of a set of * variables. * * @param variables * the variables to be assigned * @param expressions * the assigned expressions * @return a list of variable assignments */ public <T> CtStatementList createVariableAssignments(List<? extends CtVariable<T>> variables, List<? extends CtExpression<T>> expressions) { CtStatementList result = factory.Core().createStatementList(); for (int i = 0; i < variables.size(); i++) { result.addStatement(createVariableAssignment(variables.get(i).getReference(), variables.get(i).getModifiers().contains(ModifierKind.STATIC), expressions.get(i))); } return result; } /** * Creates a field. * * @param name * Name of the field. * @param type * Type of the field. * @param exp * Default expression of the field. * @param visibilities * All visibilities of the field. * @param <T> * Generic type for the type of the field. * @return a field */ public <T> CtField<T> createCtField(String name, CtTypeReference<T> type, String exp, ModifierKind... visibilities) { return factory.Core().createField().<CtField<T>>setModifiers(modifiers(visibilities)).<CtField<T>>setSimpleName(name).<CtField<T>>setType(type) .setDefaultExpression(this.<T>createCodeSnippetExpression(exp)); } /** * Creates a block. * * @param element * Statement of the block. * @param <T> * Subclasses of CtStatement. * @return a block. */ public <T extends CtStatement> CtBlock<?> createCtBlock(T element) { return factory.Core().createBlock().addStatement(element); } /** * Accepts instance of CtStatement or CtBlock. * If element is CtStatement, then it creates wrapping CtBlock, which contains the element * If element is CtBlock, then it directly returns that element * If element is null, then it returns null. * note: It must not create empty CtBlock - as expected in CtCatch, CtExecutable, CtLoop and CtTry setBody implementations * @param element * @return CtBlock instance */ public <T extends CtStatement> CtBlock<?> getOrCreateCtBlock(T element) { if (element == null) { return null; } if (element instanceof CtBlock<?>) { return (CtBlock<?>) element; } return this.createCtBlock(element); } /** * Creates a throw. * * @param thrownExp * Expression of the throw. * @return a throw. */ public CtThrow createCtThrow(String thrownExp) { return factory.Core().createThrow().setThrownExpression(this.<Throwable>createCodeSnippetExpression(thrownExp)); } /** * Creates a catch element. * * @param nameCatch * Name of the variable in the catch. * @param exception * Type of the exception. * @param ctBlock * Content of the catch. * @return a catch. */ public CtCatch createCtCatch(String nameCatch, Class<? extends Throwable> exception, CtBlock<?> ctBlock) { final CtCatchVariable<Throwable> catchVariable = factory.Core().<Throwable>createCatchVariable().<CtCatchVariable<Throwable>>setType(this.<Throwable>createCtTypeReference(exception)) .setSimpleName(nameCatch); return factory.Core().createCatch().setParameter(catchVariable).setBody(ctBlock); } /** * Creates a type reference. * * @param originalClass * Original class of the reference. * @param <T> * Type of the reference. * @return a type reference. */ public <T> CtTypeReference<T> createCtTypeReference(Class<?> originalClass) { if (originalClass == null) { return null; } CtTypeReference<T> typeReference = factory.Core().<T>createTypeReference(); typeReference.setSimpleName(originalClass.getSimpleName()); if (originalClass.isPrimitive()) { return typeReference; } return typeReference.setPackage(createCtPackageReference(originalClass.getPackage())); } /** * Creates a package reference. * * @param originalPackage * Original package of the reference. * @return a package reference. */ public CtPackageReference createCtPackageReference(Package originalPackage) { return factory.Core().createPackageReference().setSimpleName(originalPackage.getName()); } /** * Creates an annotation. * * @param annotationType * Type of the annotation. * @return an annotation. */ public <A extends Annotation> CtAnnotation<A> createAnnotation(CtTypeReference<A> annotationType) { final CtAnnotation<A> a = factory.Core().createAnnotation(); a.setAnnotationType(annotationType); return a; } /** * Gets a list of references from a list of elements. * * @param <R> * the expected reference type * @param <E> * the element type * @param elements * the element list * @return the corresponding list of references */ @SuppressWarnings("unchecked") public <R extends CtReference, E extends CtNamedElement> List<R> getReferences(List<E> elements) { List<R> refs = new ArrayList<>(elements.size()); for (E e : elements) { refs.add((R) e.getReference()); } return refs; } /** * Creates a modifier set. * * @param modifiers * to put in set * @return Set of given modifiers */ public Set<ModifierKind> modifiers(ModifierKind... modifiers) { Set<ModifierKind> ret = EnumSet.noneOf(ModifierKind.class); Collections.addAll(ret, modifiers); return ret; } /** * Creates a Code Snippet expression. * * @param <T> * The type of the expression represented by the CodeSnippet * @param expression * The string that contains the expression. * @return a new CtCodeSnippetExpression. */ public <T> CtCodeSnippetExpression<T> createCodeSnippetExpression(String expression) { CtCodeSnippetExpression<T> e = factory.Core().createCodeSnippetExpression(); e.setValue(expression); return e; } /** * Creates a Code Snippet statement. * * @param statement * The String containing the statement. * @return a new CtCodeSnippetStatement */ public CtCodeSnippetStatement createCodeSnippetStatement(String statement) { CtCodeSnippetStatement e = factory.Core().createCodeSnippetStatement(); e.setValue(statement); return e; } /** * Creates a comment * * @param content The content of the comment * @param type The comment type * @return a new CtComment */ public CtComment createComment(String content, CtComment.CommentType type) { return factory.Core().createComment().setContent(content).setCommentType(type); } /** * Creates an inline comment * * @param content The content of the comment * @return a new CtComment */ public CtComment createInlineComment(String content) { return createComment(content, CtComment.CommentType.INLINE); } }