/*
* Copyright 2012 PRODYNA AG
*
* Licensed under the Eclipse Public License (EPL), Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php or
* http://www.nabucco.org/License.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nabucco.framework.generator.compiler.transformation.java.common.ast;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotation;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotationGroupType;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotationMapper;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotationType;
import org.nabucco.framework.generator.compiler.transformation.common.collection.CollectionImplementationType;
import org.nabucco.framework.generator.compiler.transformation.common.collection.CollectionType;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.container.JavaAstContainter;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.container.JavaAstMethodStatementContainer;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.container.JavaAstType;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.util.FieldOptions;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.util.JavaAstGetterSetterProducer;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.util.equals.JavaAstObjectMethodFactory;
import org.nabucco.framework.generator.compiler.transformation.java.common.basetype.BasetypeFacade;
import org.nabucco.framework.generator.compiler.transformation.java.common.basetype.BasetypeMapping;
import org.nabucco.framework.generator.compiler.transformation.java.common.javadoc.NabuccoJavadocTransformationException;
import org.nabucco.framework.generator.compiler.transformation.java.common.javadoc.NabuccoToJavaJavadocCreator;
import org.nabucco.framework.generator.compiler.transformation.java.constants.CollectionConstants;
import org.nabucco.framework.generator.compiler.transformation.java.visitor.NabuccoToJavaVisitor;
import org.nabucco.framework.generator.compiler.transformation.util.mapper.NabuccoModifierComponentMapper;
import org.nabucco.framework.generator.compiler.visitor.NabuccoVisitorException;
import org.nabucco.framework.generator.parser.model.modifier.NabuccoModifierType;
import org.nabucco.framework.generator.parser.syntaxtree.AnnotationDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.Node;
import org.nabucco.framework.generator.parser.syntaxtree.Parameter;
import org.nabucco.framework.generator.parser.syntaxtree.ParameterList;
import org.nabucco.framework.mda.logger.MdaLogger;
import org.nabucco.framework.mda.logger.MdaLoggingFactory;
import org.nabucco.framework.mda.model.java.JavaCompilationUnit;
import org.nabucco.framework.mda.model.java.JavaModelException;
import org.nabucco.framework.mda.model.java.ast.JavaAstField;
import org.nabucco.framework.mda.model.java.ast.JavaAstMethod;
import org.nabucco.framework.mda.model.java.ast.JavaAstUnit;
import org.nabucco.framework.mda.model.java.ast.element.JavaAstElementFactory;
import org.nabucco.framework.mda.model.java.ast.element.method.JavaAstMethodSignature;
import org.nabucco.framework.mda.model.java.ast.produce.JavaAstModelProducer;
import org.nabucco.framework.mda.template.java.JavaTemplateException;
/**
* JavaAstSupport
* <p/>
* Support class for {@link NabuccoToJavaVisitor} instances. Creates and modifies Java AST elements.
*
* @author Nicolas Moser, PRODYNA AG
*/
public final class JavaAstSupport implements CollectionConstants {
private static MdaLogger logger = MdaLoggingFactory.getInstance().getLogger(JavaAstSupport.class);
/**
* Private constructor must not be invoked
*/
private JavaAstSupport() {
}
/**
* Converts and adds a list of {@link JavaAstContainter} instances into java ASTNodes and
* related ImportReferences.
*
* @param unit
* the unit to add the nodes
* @param containers
* the container holding the nodes
* @param nabuccoImports
* the NABUCCO imports
*
* @throws JavaModelException
*/
public static void convertAstNodes(JavaCompilationUnit unit, List<JavaAstContainter<? extends ASTNode>> containers,
List<String> nabuccoImports) throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
TypeDeclaration type = javaFactory.getJavaAstUnit().getPublicJavaClass(unit.getUnitDeclaration());
Set<String> convertedImports = new HashSet<String>();
for (JavaAstContainter<? extends ASTNode> container : containers) {
ASTNode astNode = container.getAstNode();
switch (container.getType()) {
case IMPORT:
ImportReference importRef = (ImportReference) astNode;
javaFactory.getJavaAstUnit().addImport(unit.getUnitDeclaration(), importRef);
break;
case INTERFACE:
TypeReference intf = (TypeReference) astNode;
javaFactory.getJavaAstType().addInterface(type, intf);
break;
case SUPER_CLASS:
TypeReference superClass = (TypeReference) astNode;
javaFactory.getJavaAstType().setSuperClass(type, superClass);
break;
case FIELD:
FieldDeclaration field = (FieldDeclaration) astNode;
javaFactory.getJavaAstType().addField(type, field);
break;
case METHOD:
MethodDeclaration method = (MethodDeclaration) astNode;
javaFactory.getJavaAstType().addMethod(type, method);
break;
case METHOD_STATEMENT:
convertMethodStatement(container, type);
break;
default:
throw new IllegalArgumentException("Java AST type '" + container.getType() + "' is not supported.");
}
JavaAstSupport.createImports(unit.getUnitDeclaration(), container, nabuccoImports, convertedImports);
}
}
/**
* Creates import references for a {@link JavaAstContainter} instance.
*
* @param unit
* the compilation unit to add the imports
* @param container
* the container for the import references
* @param nabuccoImports
* the list of NABUCCO imports
* @param convertedImports
* the already converted imports
*
* @throws JavaModelException
*/
private static void createImports(CompilationUnitDeclaration unit, JavaAstContainter<? extends ASTNode> container,
List<String> nabuccoImports, Set<String> convertedImports) throws JavaModelException {
JavaAstUnit unitFactory = JavaAstElementFactory.getInstance().getJavaAstUnit();
for (String containerImport : container.getImports()) {
// Final import string.
String importName = null;
if (containerImport.contains(PKG_SEPARATOR)) {
// Import is already qualified.
importName = containerImport;
} else {
// Import must be resolved.
String resolvedImport = null;
for (String nabuccoImport : nabuccoImports) {
if (nabuccoImport.endsWith(containerImport)) {
String[] importToken = nabuccoImport.split("\\.");
if (importToken[importToken.length - 1].equals(containerImport)) {
resolvedImport = nabuccoImport;
break;
}
}
}
importName = resolvedImport;
}
if (importName != null && !convertedImports.contains(importName)) {
ImportReference importReference = JavaAstModelProducer.getInstance().createImportReference(importName);
convertedImports.add(importName);
unitFactory.addImport(unit, importReference);
}
}
}
/**
* Converts a {@link JavaAstMethodStatementContainer} into a method statement of the particular
* method.
*
* @param container
* the container to add
*
* @param type
* the type of the method
*
* @throws JavaModelException
*/
private static void convertMethodStatement(JavaAstContainter<? extends ASTNode> container, TypeDeclaration type)
throws JavaModelException {
if (container instanceof JavaAstMethodStatementContainer<?>) {
JavaAstMethodStatementContainer<?> statementContainer = (JavaAstMethodStatementContainer<?>) container;
JavaAstMethodSignature signature = statementContainer.getSignature();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
AbstractMethodDeclaration method;
if (statementContainer.isConstructor()) {
method = javaFactory.getJavaAstType().getConstructor(type, signature);
} else {
method = javaFactory.getJavaAstType().getMethod(type, signature);
}
javaFactory.getJavaAstMethod().addStatement(method, (Statement) container.getAstNode());
}
}
/**
* Creates and adds 'equals' and 'hashCode' for the javaFields contained by the type.
*
* @param type
* the type to add the methods for
* @param collectionsEnabled
* depending on the flag, collections are considered or not
*
* @throws JavaTemplateException
*/
public static void createObjectMethods(TypeDeclaration type, boolean collectionsEnabled)
throws JavaTemplateException {
if (collectionsEnabled) {
JavaAstObjectMethodFactory.getInstance().getDefaultStrategy().createAllObjectMethods(type);
} else {
JavaAstObjectMethodFactory.getInstance().getNoCollectionStrategy().createAllObjectMethods(type);
}
}
/**
* Converts NABUCCO documentation annotations (like @Description and @Author) to Javadoc.
*
* @param nabuccoAnnotations
* the annotation declaration containing all NABUCCO annotations
* @param type
* the java type to add the javadoc
*/
public static void convertJavadocAnnotations(AnnotationDeclaration nabuccoAnnotations, TypeDeclaration type)
throws NabuccoVisitorException {
try {
List<NabuccoAnnotation> javaAnnotations = NabuccoAnnotationMapper.getInstance().mapToAnnotationList(
nabuccoAnnotations, NabuccoAnnotationGroupType.DOCUMENTATION);
NabuccoToJavaJavadocCreator.createJavadoc(javaAnnotations, type);
} catch (NabuccoJavadocTransformationException je) {
logger.error(je, "Error converting type annotations to javadoc.");
throw new NabuccoVisitorException("Error converting type annotations to javadoc.", je);
}
}
/**
* Converts NABUCCO documentation annotations (like @Description and @Author) to Javadoc.
*
* @param nabuccoAnnotations
* the annotation declaration containing all NABUCCO annotations
* @param method
* the java method to add the javadoc
*/
public static void convertJavadocAnnotations(AnnotationDeclaration nabuccoAnnotations, MethodDeclaration method)
throws NabuccoVisitorException {
try {
List<NabuccoAnnotation> javaAnnotations = NabuccoAnnotationMapper.getInstance().mapToAnnotationList(
nabuccoAnnotations, NabuccoAnnotationGroupType.DOCUMENTATION);
NabuccoToJavaJavadocCreator.createJavadoc(javaAnnotations, method);
} catch (NabuccoJavadocTransformationException je) {
logger.error(je, "Error converting method annotations to javadoc.");
throw new NabuccoVisitorException("Error converting method annotations to javadoc.", je);
}
}
/**
* Converts NABUCCO documentation annotations (like @Description and @Author) to Javadoc.
*
* @param nabuccoAnnotations
* the annotation declaration containing all NABUCCO annotations
* @param field
* the java field to add the javadoc
*/
public static void convertJavadocAnnotations(AnnotationDeclaration nabuccoAnnotations, FieldDeclaration field)
throws NabuccoVisitorException {
try {
List<NabuccoAnnotation> javaAnnotations = NabuccoAnnotationMapper.getInstance().mapToAnnotationList(
nabuccoAnnotations, NabuccoAnnotationGroupType.DOCUMENTATION);
NabuccoToJavaJavadocCreator.createJavadoc(javaAnnotations, field);
} catch (NabuccoJavadocTransformationException je) {
logger.error(je, "Error converting field annotations to javadoc.");
throw new NabuccoVisitorException("Error converting field annotations to javadoc.", je);
}
}
/**
* Creates a new container for a Java AST Superclass.
*
* @param className
* given name
* @return new {@link JavaAstContainter} for an Superclass
*
* @throws JavaModelException
*/
public static JavaAstContainter<TypeReference> createSuperClass(String className) throws JavaModelException {
TypeReference superClass = JavaAstModelProducer.getInstance().createTypeReference(className, false);
JavaAstContainter<TypeReference> container = new JavaAstContainter<TypeReference>(superClass,
JavaAstType.SUPER_CLASS);
if (BasetypeFacade.isBasetype(className)) {
container.getImports().add(BasetypeFacade.getBasetype(className));
} else {
container.getImports().add(className);
}
return container;
}
/**
* Creates a new container for a Java AST Interface.
*
* @param interfaceName
* given name
* @return new {@link JavaAstContainter} for an Interface
*
* @throws JavaModelException
*/
public static JavaAstContainter<TypeReference> createInterface(String interfaceName) throws JavaModelException {
TypeReference superClass = JavaAstModelProducer.getInstance().createTypeReference(interfaceName, false);
JavaAstContainter<TypeReference> container = new JavaAstContainter<TypeReference>(superClass,
JavaAstType.INTERFACE);
return container;
}
/**
* Creates a {@link FieldDeclaration} and adds it into a JavaAstContainer.
* <p/>
*
* @param type
* type of the field
* @param name
* name of the field
* @param modifier
* modifier of the field
* @param collectionType
* type of the collection, if null no collection is created
*
* @return a container holding the field declaration and related imports
*/
public static JavaAstContainter<FieldDeclaration> createField(String type, String name, NabuccoModifierType modifier) {
return JavaAstSupport.createField(type, name, modifier, CollectionType.NONE,
CollectionImplementationType.DEFAULT);
}
/**
* Creates a {@link FieldDeclaration} and adds it into a JavaAstContainer.
* <p/>
*
* @param type
* type of the field
* @param name
* name of the field
* @param modifier
* modifier of the field
* @param collectionType
* type of the collection, if null no collection is created
*
* @return a container holding the field declaration and related imports
*/
public static JavaAstContainter<FieldDeclaration> createField(String type, String name,
NabuccoModifierType modifier, CollectionType collectionType, CollectionImplementationType collectionImplType) {
JavaAstField fieldFactory = JavaAstElementFactory.getInstance().getJavaAstField();
try {
FieldDeclaration field = JavaAstModelProducer.getInstance().createFieldDeclaration(name);
JavaAstContainter<FieldDeclaration> container = new JavaAstContainter<FieldDeclaration>(field,
JavaAstType.FIELD);
TypeReference typeRef = JavaAstModelProducer.getInstance().createTypeReference(type, false);
if (BasetypeMapping.N_TYPE.getName().equals(type)) {
container.getImports().add(BasetypeMapping.N_TYPE.getPrimitiveType());
} else {
container.getImports().add(type);
}
if (collectionType != null) {
switch (collectionType) {
case LIST:
typeRef = JavaAstModelProducer.getInstance().createParameterizedTypeReference(
collectionImplType.getList(), false, Arrays.asList(typeRef));
container.getImports().add(IMPORT_LIST);
break;
case SET:
typeRef = JavaAstModelProducer.getInstance().createParameterizedTypeReference(
collectionImplType.getSet(), false, Arrays.asList(typeRef));
container.getImports().add(IMPORT_SET);
break;
case MAP:
typeRef = JavaAstModelProducer.getInstance().createParameterizedTypeReference(
collectionImplType.getMap(), false, Arrays.asList(typeRef));
container.getImports().add(IMPORT_MAP);
break;
}
}
fieldFactory.setFieldType(field, typeRef);
fieldFactory.setModifier(field, NabuccoModifierComponentMapper.mapModifierToJava(modifier));
return container;
} catch (JavaModelException jme) {
throw new NabuccoVisitorException("Error during Java AST field modification.", jme);
}
}
/**
* Creates a getter for a field declaration.
*
* @param field
* the field declaration
*
* @return the getter method declaration
*/
public static JavaAstContainter<MethodDeclaration> createGetter(FieldDeclaration field) {
return JavaAstSupport.createGetter(field, null);
}
/**
* Creates a getter for a field declaration.
*
* @param field
* the field declaration
* @param options
* additional options
*
* @return the getter method declaration
*/
public static JavaAstContainter<MethodDeclaration> createGetter(FieldDeclaration field, FieldOptions options) {
if (options == null) {
options = new FieldOptions();
}
try {
MethodDeclaration getter = JavaAstGetterSetterProducer.getInstance().produceGetter(field, options);
JavaAstContainter<MethodDeclaration> container = new JavaAstContainter<MethodDeclaration>(getter,
JavaAstType.METHOD);
addCollectionImports(field, container, options);
return container;
} catch (JavaModelException jme) {
throw new NabuccoVisitorException("Error creating Java AST Getter.", jme);
}
}
/**
* Adds the collection imports to the container.
*
* @param field
* the field
* @param container
* the method container
* @param options
* the options
*/
private static void addCollectionImports(FieldDeclaration field, JavaAstContainter<MethodDeclaration> container,
FieldOptions options) {
char[] type = field.type.getLastToken();
if (Arrays.equals(type, LIST.toCharArray())) {
container.getImports().add(IMPORT_LIST);
container.getImports().add(IMPORT_DEFAULT_LIST);
} else if (Arrays.equals(type, SET.toCharArray())) {
container.getImports().add(IMPORT_SET);
container.getImports().add(IMPORT_DEFAULT_SET);
} else if (Arrays.equals(type, MAP.toCharArray())) {
container.getImports().add(IMPORT_MAP);
container.getImports().add(IMPORT_DEFAULT_MAP);
} else if (Arrays.equals(type, NABUCCO_LIST.toCharArray())) {
container.getImports().add(IMPORT_NABUCCO_LIST);
container.getImports().add(IMPORT_NABUCCO_LIST_IMPL);
container.getImports().add(IMPORT_NABUCCO_COLLECTION_STATE);
} else if (Arrays.equals(type, NABUCCO_SET.toCharArray())) {
container.getImports().add(IMPORT_NABUCCO_SET);
container.getImports().add(IMPORT_NABUCCO_SET_IMPL);
container.getImports().add(IMPORT_NABUCCO_COLLECTION_STATE);
} else if (Arrays.equals(type, NABUCCO_MAP.toCharArray())) {
container.getImports().add(IMPORT_NABUCCO_MAP);
container.getImports().add(IMPORT_NABUCCO_MAP_IMPL);
}
}
/**
* Creates a setter for a field declaration with a fetch type LAZY.
*
* @param field
* the field declaration
*
* @return the setter method declaration
*/
public static JavaAstContainter<MethodDeclaration> createSetter(FieldDeclaration field) {
try {
return new JavaAstContainter<MethodDeclaration>(JavaAstGetterSetterProducer.getInstance().produceSetter(
field), JavaAstType.METHOD);
} catch (JavaModelException jme) {
throw new NabuccoVisitorException("Error creating Java AST Setter.", jme);
}
}
/**
* Creates a {@link MethodDeclaration} and adds it to the list of method declarations in the
* visitor context.
*
* @param returnType
* return type of the method
* @param name
* name of the method
* @param modifier
* modifier of the method
* @param parameterList
* parameters of the method
* @param isAbstract
* whether a method is abstract or not
*/
public static JavaAstContainter<MethodDeclaration> createMethod(String returnType, String name,
NabuccoModifierType modifier, ParameterList parameterList, boolean isAbstract) {
try {
JavaAstMethod methodFactory = JavaAstElementFactory.getInstance().getJavaAstMethod();
MethodDeclaration method = JavaAstModelProducer.getInstance().createMethodDeclaration(name, null,
NabuccoModifierComponentMapper.mapModifierToJava(modifier, isAbstract));
JavaAstContainter<MethodDeclaration> container = new JavaAstContainter<MethodDeclaration>(method,
JavaAstType.METHOD);
TypeReference typeRef = JavaAstModelProducer.getInstance().createTypeReference(returnType, false);
container.getImports().add(returnType);
methodFactory.setReturnType(method, typeRef);
if (parameterList != null) {
for (Node node : parameterList.nodeListOptional.nodes) {
if (node instanceof Parameter) {
Parameter param = (Parameter) node;
String argumentType = param.nodeToken.tokenImage;
String argumentName = param.nodeToken1.tokenImage;
TypeReference type = JavaAstModelProducer.getInstance()
.createTypeReference(argumentType, false);
Argument argument = JavaAstModelProducer.getInstance().createArgument(argumentName, type);
container.getImports().add(argumentType);
methodFactory.addArgument(method, argument);
}
}
}
return container;
} catch (JavaModelException jme) {
logger.error(jme, "Error during Java AST method modification.");
throw new NabuccoVisitorException("Error during Java AST method modification.", jme);
}
}
/**
* Creates a java.util.Map {@link FieldDeclaration} and adds it into a JavaAstContainer.
*
* @param key
* the map's key type
* @param value
* the map's value type
* @param name
* name of the map field
* @param modifier
* modifier of the map
*
* @return the container holding the field
*/
public static JavaAstContainter<FieldDeclaration> createMap(String key, String value, String name,
NabuccoModifierType modifier) {
JavaAstField fieldFactory = JavaAstElementFactory.getInstance().getJavaAstField();
try {
int javaModifier = NabuccoModifierComponentMapper.mapModifierToJava(modifier);
// Transient fields cannot be serialized.
// javaModifier |= ClassFileConstants.AccTransient;
FieldDeclaration field = JavaAstModelProducer.getInstance().createFieldDeclaration(name, javaModifier);
JavaAstContainter<FieldDeclaration> fieldContainer = new JavaAstContainter<FieldDeclaration>(field,
JavaAstType.FIELD);
TypeReference keyType = JavaAstModelProducer.getInstance().createTypeReference(key, false);
TypeReference valueType = JavaAstModelProducer.getInstance().createTypeReference(value, false);
fieldContainer.getImports().add(key);
fieldContainer.getImports().add(value);
TypeReference mapType = JavaAstModelProducer.getInstance().createParameterizedTypeReference(MAP, false,
Arrays.asList(keyType, valueType));
fieldContainer.getImports().add(IMPORT_MAP);
fieldContainer.getImports().add(IMPORT_NABUCCO_MAP_IMPL);
fieldFactory.setFieldType(field, mapType);
return fieldContainer;
} catch (JavaModelException jme) {
logger.error(jme, "Error during Java AST field modification.");
throw new NabuccoVisitorException("Error during Java AST field modification.", jme);
}
}
/**
* Converts all annotations of a {@link AnnotationDeclaration} to a list of
* {@link NabuccoAnnotation}.
*
* @param nabuccoAnnotations
* the annotation declaration
* @param types
* the optional annotation types to filter for
*
* @return the converted list of annotations
*/
public static List<NabuccoAnnotation> convertAnnotations(AnnotationDeclaration nabuccoAnnotations,
NabuccoAnnotationGroupType... types) {
return NabuccoAnnotationMapper.getInstance().mapToAnnotationList(nabuccoAnnotations, types);
}
/**
* Checks whether an {@link AnnotationDeclaration} does contain one of the given annotations.
*
* @see NabuccoAnnotationConstants
*
* @param annotationDeclaration
* the annotation declaration
* @param types
* the annotation types to check for
*
* @return <b>true</b> if an annotation does exist, <b>false</b> if not
*/
public static boolean hasAnnotation(AnnotationDeclaration annotationDeclaration, NabuccoAnnotationType... types) {
return NabuccoAnnotationMapper.getInstance().hasAnnotation(annotationDeclaration, types);
}
}