package org.springframework.roo.classpath.itd;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.classpath.PhysicalTypeCategory;
import org.springframework.roo.classpath.details.AnnotationMetadataUtils;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ConstructorMetadata;
import org.springframework.roo.classpath.details.DeclaredFieldAnnotationDetails;
import org.springframework.roo.classpath.details.DeclaredMethodAnnotationDetails;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.ItdTypeDetails;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.comments.AbstractComment;
import org.springframework.roo.classpath.details.comments.CommentStructure;
import org.springframework.roo.classpath.details.comments.CommentStructure.CommentLocation;
import org.springframework.roo.classpath.details.comments.JavadocComment;
import org.springframework.roo.model.ImportRegistrationResolver;
import org.springframework.roo.model.ImportRegistrationResolverImpl;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
/**
* A simple way of producing an inter-type declaration source file.
*
* @author Ben Alex
* @author Stefan Schmidt
* @author Alan Stewart
* @author Juan Carlos GarcĂa
* @author Sergio Clares
* @since 1.0
*/
public class ItdSourceFileComposer {
private final JavaType aspect;
private boolean content;
private int indentLevel = 0;
private final JavaType introductionTo;
private final ItdTypeDetails itdTypeDetails;
private StringBuilder pw = new StringBuilder();
private final ImportRegistrationResolver resolver;
/**
* Constructs an {@link ItdSourceFileComposer} containing the members that
* were requested in the passed object.
*
* @param itdTypeDetails to construct (required)
*/
public ItdSourceFileComposer(final ItdTypeDetails itdTypeDetails) {
Validate.notNull(itdTypeDetails, "ITD type details required");
Validate.notNull(itdTypeDetails.getName(), "Introduction to is required");
this.itdTypeDetails = itdTypeDetails;
introductionTo = itdTypeDetails.getName();
aspect = itdTypeDetails.getAspect();
// Create my own resolver, so we can add items to it as we process
resolver = new ImportRegistrationResolverImpl(itdTypeDetails.getAspect().getPackage());
resolver.addImport(introductionTo); // ROO-2932
SortedMap<JavaType, Boolean> imports = itdTypeDetails.getRegisteredImports();
for (final Entry<JavaType, Boolean> entry : imports.entrySet()) {
JavaType registeredImport = entry.getKey();
boolean asStatic = entry.getValue();
// Do a sanity check in case the user misused it
if (resolver.isAdditionLegal(registeredImport)) {
resolver.addImport(registeredImport, asStatic);
}
}
appendTypeDeclaration();
appendDeclarePrecedence();
appendExtendsTypes();
appendImplementsTypes();
appendTypeAnnotations();
appendFieldAnnotations();
appendMethodAnnotations();
appendFields();
appendConstructors();
appendMethods(itdTypeDetails.getGovernor().getPhysicalTypeCategory()
.equals(PhysicalTypeCategory.INTERFACE));
appendInnerTypes();
appendTerminator();
// Now prepend the package declaration and any imports
// We need to do this ** at the end ** so we can ensure our compilation
// unit imports are correct, as they're built as we traverse over the
// other members
prependCompilationUnitDetails();
}
/**
* Prints the message, WITHOUT ANY INDENTATION.
*/
private ItdSourceFileComposer append(final String message) {
if (message != null && !"".equals(message)) {
pw.append(message);
content = true;
}
return this;
}
private void appendConstructors() {
final List<? extends ConstructorMetadata> constructors =
itdTypeDetails.getDeclaredConstructors();
if (constructors == null || constructors.isEmpty()) {
return;
}
content = true;
for (final ConstructorMetadata constructor : constructors) {
Validate.isTrue(constructor.getParameterTypes().size() == constructor.getParameterNames()
.size(), "Mismatched parameter names against parameter types");
// ROO-3447: Append comments if exists
CommentStructure commentStructure = constructor.getCommentStructure();
if (commentStructure != null && commentStructure.getBeginComments() != null) {
List<AbstractComment> constructorComments = commentStructure.getBeginComments();
String comment = "";
boolean missingComponentsAdded = false;
for (AbstractComment constructorComment : constructorComments) {
// Join all JavadocComment's
if (constructorComment instanceof JavadocComment) {
// Add JavaDoc missing components
if (!missingComponentsAdded) {
if (!constructor.getParameterNames().isEmpty()
&& ((JavadocComment) constructorComment).getParamsInfo() == null) {
List<String> paramsInfo = new ArrayList<String>();
for (JavaSymbolName name : constructor.getParameterNames()) {
paramsInfo.add(name.getSymbolName());
}
((JavadocComment) constructorComment).setParamsInfo(paramsInfo);
}
}
missingComponentsAdded = true;
comment =
comment.concat(constructorComment.getComment()).concat(IOUtils.LINE_SEPARATOR);
} else {
// Not JavadocComment, write comment as it is, unchanged
appendFormalLine(constructorComment.getComment());
}
}
// Write JavadocComment's to ITD, in a single JavadocComment instance
String[] commentLines = comment.split(IOUtils.LINE_SEPARATOR);
for (String commentLine : commentLines) {
appendFormalLine(commentLine);
}
} else {
// ROO-3834: Append default Javadoc if not exists a comment structure,
// including constructor params
CommentStructure defaultCommentStructure = new CommentStructure();
List<String> parameterNames = new ArrayList<String>();
for (JavaSymbolName name : constructor.getParameterNames()) {
parameterNames.add(name.getSymbolName());
}
JavadocComment javadocComment =
new JavadocComment("TODO Auto-generated constructor documentation", parameterNames,
null, null);
defaultCommentStructure.addComment(javadocComment, CommentLocation.BEGINNING);
constructor.setCommentStructure(defaultCommentStructure);
// Now lines should be formatted, so write them
String[] comment = javadocComment.getComment().split(IOUtils.LINE_SEPARATOR);
for (String line : comment) {
appendFormalLine(line);
}
}
// Append annotations
for (final AnnotationMetadata annotation : constructor.getAnnotations()) {
appendIndent();
outputAnnotation(annotation);
this.newLine(false);
}
// Append "<modifier> <TargetOfIntroduction>.new" portion
appendIndent();
if (constructor.getModifier() != 0) {
append(Modifier.toString(constructor.getModifier()));
append(" ");
}
append(introductionTo.getSimpleTypeName());
append(".");
append("new");
// Append parameter types and names
append("(");
final List<AnnotatedJavaType> parameterTypes = constructor.getParameterTypes();
final List<JavaSymbolName> parameterNames = constructor.getParameterNames();
for (int i = 0; i < parameterTypes.size(); i++) {
final AnnotatedJavaType paramType = parameterTypes.get(i);
final JavaSymbolName paramName = parameterNames.get(i);
for (final AnnotationMetadata methodParameterAnnotation : paramType.getAnnotations()) {
append(AnnotationMetadataUtils.toSourceForm(methodParameterAnnotation, resolver));
append(" ");
}
append(paramType.getJavaType().getNameIncludingTypeParameters(false, resolver));
append(" ");
append(paramName.getSymbolName());
if (i < parameterTypes.size() - 1) {
append(", ");
}
}
append(") {");
this.newLine(false);
indent();
// Add body
append(constructor.getBody());
indentRemove();
appendFormalLine("}");
this.newLine(false);
}
}
private void appendExtendsTypes() {
final List<JavaType> extendsTypes = itdTypeDetails.getExtendsTypes();
if (extendsTypes == null || extendsTypes.isEmpty()) {
return;
}
content = true;
for (final JavaType extendsType : extendsTypes) {
appendIndent();
append("declare parents: ");
append(introductionTo.getSimpleTypeName());
append(" extends ");
if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(extendsType)) {
append(extendsType.getNameIncludingTypeParameters());
} else {
append(extendsType.getNameIncludingTypeParameters(false, resolver));
}
append(";");
this.newLine(false);
this.newLine();
}
}
private void appendDeclarePrecedence() {
final Set<JavaType> aspects = itdTypeDetails.getDeclarePrecedence();
if (aspects == null || aspects.isEmpty()) {
return;
}
content = true;
// If only exists one aspect, means that
// it should take the lower precedence.
if (aspects.size() == 1) {
appendFormalLine("/*");
appendFormalLine(" * This Aspect takes the lower precedence");
appendFormalLine(" */");
appendIndent();
append("declare precedence: *, ");
} else {
appendIndent();
append("declare precedence: ");
}
List<String> aspectNames = new ArrayList<String>(aspects.size());
for (final JavaType aspect : aspects) {
if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(aspect)) {
aspectNames.add(aspect.getNameIncludingTypeParameters());
} else {
aspectNames.add(aspect.getNameIncludingTypeParameters(false, resolver));
}
}
append(StringUtils.join(aspectNames, ", "));
append(";");
this.newLine(false);
this.newLine();
}
private void appendFieldAnnotations() {
final List<DeclaredFieldAnnotationDetails> fieldAnnotations =
itdTypeDetails.getFieldAnnotations();
if (fieldAnnotations == null || fieldAnnotations.isEmpty()) {
return;
}
content = true;
for (final DeclaredFieldAnnotationDetails fieldDetails : fieldAnnotations) {
appendIndent();
append("declare @field: * ");
append(introductionTo.getSimpleTypeName());
append(".");
append(fieldDetails.getField().getFieldName().getSymbolName());
append(": ");
if (fieldDetails.isRemoveAnnotation()) {
append("-");
}
outputAnnotation(fieldDetails.getFieldAnnotation());
append(";");
this.newLine(false);
this.newLine();
}
}
private void appendFields() {
final List<? extends FieldMetadata> fields = itdTypeDetails.getDeclaredFields();
if (fields == null || fields.isEmpty()) {
return;
}
content = true;
for (final FieldMetadata field : fields) {
// ROO-3447: Append comments if exists
CommentStructure commentStructure = field.getCommentStructure();
if (commentStructure != null && commentStructure.getBeginComments() != null) {
List<AbstractComment> comments = commentStructure.getBeginComments();
String commentString = "";
for (AbstractComment comment : comments) {
// Join all JavadocComment's
if (comment instanceof JavadocComment) {
commentString =
commentString.concat(comment.getComment()).concat(IOUtils.LINE_SEPARATOR);
} else {
// Not JavadocComment, write comment as it is, unchanged
appendFormalLine(comment.getComment());
}
}
// Write JavadocComment's to ITD, in a single JavadocComment instance
String[] commentLines = commentString.split(IOUtils.LINE_SEPARATOR);
for (String commentLine : commentLines) {
appendFormalLine(commentLine);
}
} else {
// ROO-3834: Append default Javadoc if not exists a comment structure
JavadocComment javadocComment =
new JavadocComment("TODO Auto-generated attribute documentation");
// Now lines should be formatted, so write them
String[] comment = javadocComment.getComment().split(IOUtils.LINE_SEPARATOR);
for (String line : comment) {
appendFormalLine(line);
}
}
// Append annotations
for (final AnnotationMetadata annotation : field.getAnnotations()) {
appendIndent();
outputAnnotation(annotation);
this.newLine(false);
}
// Append "<modifier> <fieldType> <fieldName>" portion
appendIndent();
if (field.getModifier() != 0) {
append(Modifier.toString(field.getModifier()));
append(" ");
}
append(field.getFieldType().getNameIncludingTypeParameters(false, resolver));
append(" ");
append(introductionTo.getSimpleTypeName());
append(".");
append(field.getFieldName().getSymbolName());
// Append initializer, if present
if (field.getFieldInitializer() != null) {
append(" = ");
append(field.getFieldInitializer());
}
// Complete the field declaration
append(";");
this.newLine(false);
this.newLine();
}
}
/**
* Prints the message, after adding indents and returns to a new line. This
* is the most commonly used method.
*/
private ItdSourceFileComposer appendFormalLine(final String message) {
appendIndent();
if (message != null && !"".equals(message)) {
pw.append(message);
content = true;
}
return newLine(false);
}
private void appendImplementsTypes() {
final List<JavaType> implementsTypes = itdTypeDetails.getImplementsTypes();
if (implementsTypes == null || implementsTypes.isEmpty()) {
return;
}
content = true;
for (final JavaType extendsType : implementsTypes) {
appendIndent();
append("declare parents: ");
append(introductionTo.getSimpleTypeName());
append(" implements ");
if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(extendsType)) {
append(extendsType.getNameIncludingTypeParameters());
} else {
append(extendsType.getNameIncludingTypeParameters(false, resolver));
}
append(";");
this.newLine(false);
this.newLine();
}
}
/**
* Prints the relevant number of indents.
*/
private ItdSourceFileComposer appendIndent() {
for (int i = 0; i < indentLevel; i++) {
pw.append(" ");
}
return this;
}
/**
* supports static inner types with static field definitions only at this
* point
*/
private void appendInnerTypes() {
final List<ClassOrInterfaceTypeDetails> innerTypes = itdTypeDetails.getInnerTypes();
for (final ClassOrInterfaceTypeDetails innerType : innerTypes) {
content = true;
appendIndent();
if (innerType.getModifier() != 0) {
append(Modifier.toString(innerType.getModifier()));
append(" ");
}
append("class ");
append(introductionTo.getNameIncludingTypeParameters());
append(".");
append(innerType.getName().getSimpleTypeName());
if (innerType.getExtendsTypes().size() > 0) {
append(" extends ");
// There should only be one extends type for inner classes
final JavaType extendsType = innerType.getExtendsTypes().get(0);
if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(extendsType)) {
append(extendsType.getNameIncludingTypeParameters());
} else {
append(extendsType.getNameIncludingTypeParameters(false, resolver));
}
append(" ");
}
final List<JavaType> implementsTypes = innerType.getImplementsTypes();
if (implementsTypes.size() > 0) {
append(" implements ");
for (int i = 0; i < implementsTypes.size(); i++) {
final JavaType implementsType = implementsTypes.get(i);
if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(implementsType)) {
append(implementsType.getNameIncludingTypeParameters());
} else {
append(implementsType.getNameIncludingTypeParameters(false, resolver));
}
if (i != implementsTypes.size() - 1) {
append(", ");
} else {
append(" ");
}
}
}
append("{");
this.newLine(false);
// Write out fields
for (final FieldMetadata field : innerType.getDeclaredFields()) {
indent();
this.newLine(false);
// Append annotations
for (final AnnotationMetadata annotation : field.getAnnotations()) {
appendIndent();
outputAnnotation(annotation);
this.newLine(false);
}
appendIndent();
if (field.getModifier() != 0) {
append(Modifier.toString(field.getModifier()));
append(" ");
}
append(field.getFieldType().getNameIncludingTypeParameters(false, resolver));
append(" ");
append(field.getFieldName().getSymbolName());
// Append initializer, if present
if (field.getFieldInitializer() != null) {
append(" = ");
append(field.getFieldInitializer());
}
// Complete the field declaration
append(";");
this.newLine(false);
indentRemove();
}
this.newLine(false);
// Write out constructors
indent();
writeInnerTypeConstructors(innerType.getName(), innerType.getDeclaredConstructors(), false,
false);
indentRemove();
// Write out methods
indent();
writeMethods(innerType.getDeclaredMethods(), false, false);
indentRemove();
appendIndent();
append("}");
this.newLine(false);
this.newLine();
}
}
private void appendMethodAnnotations() {
final List<DeclaredMethodAnnotationDetails> methodAnnotations =
itdTypeDetails.getMethodAnnotations();
if (methodAnnotations == null || methodAnnotations.isEmpty()) {
return;
}
content = true;
for (final DeclaredMethodAnnotationDetails methodDetails : methodAnnotations) {
appendIndent();
append("declare @method: ");
append(Modifier.toString(methodDetails.getMethodMetadata().getModifier()));
append(" ");
append(methodDetails.getMethodMetadata().getReturnType()
.getNameIncludingTypeParameters(false, resolver));
append(" ");
append(introductionTo.getSimpleTypeName());
append(".");
append(methodDetails.getMethodMetadata().getMethodName().getSymbolName());
append("(");
for (int i = 0; i < methodDetails.getMethodMetadata().getParameterTypes().size(); i++) {
append(methodDetails.getMethodMetadata().getParameterTypes().get(i).getJavaType()
.getNameIncludingTypeParameters(false, resolver));
if (i != methodDetails.getMethodMetadata().getParameterTypes().size() - 1) {
append(",");
}
}
append("): ");
outputAnnotation(methodDetails.getMethodAnnotation());
append(";");
this.newLine(false);
this.newLine();
}
}
private void appendMethods(final boolean interfaceMethod) {
final List<? extends MethodMetadata> methods = itdTypeDetails.getDeclaredMethods();
if (methods == null || methods.isEmpty()) {
return;
}
content = true;
writeMethods(methods, true, interfaceMethod);
}
private void appendTerminator() {
Validate.isTrue(indentLevel == 1, "Indent level must be 1 (not %d) to conclude", indentLevel);
indentRemove();
// Ensure we present the content flag, as it will be set true during the
// formal line append
final boolean contentBefore = content;
appendFormalLine("}");
content = contentBefore;
}
private void appendTypeAnnotations() {
final List<? extends AnnotationMetadata> typeAnnotations = itdTypeDetails.getAnnotations();
if (typeAnnotations == null || typeAnnotations.isEmpty()) {
return;
}
content = true;
for (final AnnotationMetadata typeAnnotation : typeAnnotations) {
appendIndent();
append("declare @type: ");
append(introductionTo.getSimpleTypeName());
append(": ");
outputAnnotation(typeAnnotation);
append(";");
this.newLine(false);
this.newLine();
}
}
private void appendTypeDeclaration() {
Validate.isTrue(introductionTo.getPackage().equals(aspect.getPackage()),
"Aspect and introduction must be in identical packages");
appendIndent();
if (itdTypeDetails.isPrivilegedAspect()) {
append("privileged ");
}
append("aspect " + aspect.getSimpleTypeName() + " {");
this.newLine(false);
indent();
this.newLine();
// Set to false, as it was set true during the above operations
content = false;
}
private String getNewLine() {
// We use \n for consistency with JavaParser's DumpVisitor, which always
// uses \n
return "\n";
}
public String getOutput() {
return pw.toString();
}
/**
* Increases the indent by one level.
*/
private ItdSourceFileComposer indent() {
indentLevel++;
return this;
}
/**
* Decreases the indent by one level.
*/
private ItdSourceFileComposer indentRemove() {
indentLevel--;
return this;
}
/**
* Indicates whether any content was added to the ITD, aside from the formal
* ITD declaration.
*
* @return true if there is actual content in the ITD, false otherwise
*/
public boolean isContent() {
return content;
}
/**
* Prints a blank line, ensuring any indent is included before doing so.
*/
private ItdSourceFileComposer newLine() {
return newLine(true);
}
/**
* Prints a blank line, ensuring any indent is included before doing so.
*/
private ItdSourceFileComposer newLine(final boolean indent) {
if (indent) {
appendIndent();
}
// We use \n for consistency with JavaParser's DumpVisitor, which always
// uses \n
pw.append(getNewLine());
// pw.append(StringUtils.LINE_SEPARATOR);
return this;
}
private void outputAnnotation(final AnnotationMetadata annotation) {
append(AnnotationMetadataUtils.toSourceForm(annotation, resolver));
}
private void prependCompilationUnitDetails() {
final StringBuilder topOfFile = new StringBuilder();
topOfFile.append("// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO.")
.append(getNewLine());
topOfFile
.append(
"// You may push code into the target .java compilation unit if you wish to edit any member(s).")
.append(getNewLine()).append(getNewLine());
// Note we're directly interacting with the top of file string builder
if (!aspect.isDefaultPackage()) {
topOfFile.append("package ").append(aspect.getPackage().getFullyQualifiedPackageName())
.append(";").append(getNewLine());
topOfFile.append(getNewLine());
}
// Ordered to ensure consistency of output
final SortedMap<JavaType, Boolean> types = new TreeMap<JavaType, Boolean>();
types.putAll(resolver.getRegisteredImports());
if (!types.isEmpty()) {
// First of all include the static imports
boolean hasStaticImports = false;
for (final Entry<JavaType, Boolean> entry : types.entrySet()) {
JavaType importType = entry.getKey();
boolean asStatic = entry.getValue();
if (introductionTo.equals(importType.getEnclosingType())) {
// We don't "import" types defined within governor, as they
// already have scope and this causes AJDT warnings (see
// ROO-1686)
continue;
}
if (asStatic) {
topOfFile.append("import static ").append(importType.getFullyQualifiedTypeName())
.append(";").append(getNewLine());
hasStaticImports = true;
}
}
// If some static field has been included, append a new line after the static imports
if (hasStaticImports) {
topOfFile.append(getNewLine());
}
// After static imports, include the other ones
for (final Entry<JavaType, Boolean> entry : types.entrySet()) {
JavaType importType = entry.getKey();
boolean asStatic = entry.getValue();
if (introductionTo.equals(importType.getEnclosingType())) {
// We don't "import" types defined within governor, as they
// already have scope and this causes AJDT warnings (see
// ROO-1686)
continue;
}
if (!asStatic) {
topOfFile.append("import ").append(importType.getFullyQualifiedTypeName()).append(";")
.append(getNewLine());
}
}
topOfFile.append(getNewLine());
}
// Now append the normal file to the bottom
topOfFile.append(pw.toString());
// Replace the old writer with out new writer
pw = topOfFile;
}
private void writeMethods(final List<? extends MethodMetadata> methods,
final boolean defineTarget, final boolean isInterfaceMethod) {
for (final MethodMetadata method : methods) {
Validate.isTrue(method.getParameterTypes().size() == method.getParameterNames().size(),
"Method %s has mismatched parameter names against parameter types", method
.getMethodName().getSymbolName());
// ROO-3447: Append comments if exists
CommentStructure commentStructure = method.getCommentStructure();
if (commentStructure != null && commentStructure.getBeginComments() != null) {
List<AbstractComment> comments = commentStructure.getBeginComments();
String commentString = "";
boolean missingComponentsAdded = false;
for (AbstractComment comment : comments) {
// Join all JavadocComment's
if (comment instanceof JavadocComment) {
// Add JavaDoc missing components
if (!missingComponentsAdded) {
// Check params info
if (!method.getParameterNames().isEmpty()
&& ((JavadocComment) comment).getParamsInfo() == null) {
List<String> paramsInfo = new ArrayList<String>();
for (JavaSymbolName name : method.getParameterNames()) {
paramsInfo.add(name.getSymbolName());
}
((JavadocComment) comment).setParamsInfo(paramsInfo);
}
// Check return info
if (!method.getReturnType().equals(JavaType.VOID_OBJECT)
&& !method.getReturnType().equals(JavaType.VOID_PRIMITIVE)
&& ((JavadocComment) comment).getReturnInfo() == null) {
((JavadocComment) comment)
.setReturnInfo(method.getReturnType().getSimpleTypeName());
}
// Check throws info
if (!method.getThrowsTypes().isEmpty()
&& ((JavadocComment) comment).getThrowsInfo() == null) {
List<String> throwsInfo = new ArrayList<String>();
for (JavaType throwsType : method.getThrowsTypes()) {
throwsInfo.add(throwsType.getSimpleTypeName());
}
((JavadocComment) comment).setThrowsInfo(throwsInfo);
}
missingComponentsAdded = true;
}
commentString =
commentString.concat(comment.getComment()).concat(IOUtils.LINE_SEPARATOR);
} else {
// Not JavadocComment, write comment as it is, unchanged
appendFormalLine(comment.getComment());
}
}
// Write JavadocComment's to ITD, in a single JavadocComment instance
String[] commentLines = commentString.split(IOUtils.LINE_SEPARATOR);
for (String commentLine : commentLines) {
appendFormalLine(commentLine);
}
} else {
// ROO-3834: Append default Javadoc if not exists a comment structure,
// including params, return and throws
CommentStructure defaultCommentStructure = new CommentStructure();
List<String> parameterNames = new ArrayList<String>();
for (JavaSymbolName name : method.getParameterNames()) {
parameterNames.add(name.getSymbolName());
}
List<String> throwsTypesNames = new ArrayList<String>();
for (JavaType type : method.getThrowsTypes()) {
throwsTypesNames.add(type.getSimpleTypeName());
}
String returnInfo = null;
JavaType returnType = method.getReturnType();
if (!returnType.equals(JavaType.VOID_OBJECT) && !returnType.equals(JavaType.VOID_PRIMITIVE)) {
returnInfo = returnType.getSimpleTypeName();
}
JavadocComment javadocComment =
new JavadocComment("TODO Auto-generated method documentation", parameterNames,
returnInfo, throwsTypesNames);
defaultCommentStructure.addComment(javadocComment, CommentLocation.BEGINNING);
method.setCommentStructure(defaultCommentStructure);
// Now lines should be formatted, so write them
String[] comment = javadocComment.getComment().split(IOUtils.LINE_SEPARATOR);
for (String line : comment) {
appendFormalLine(line);
}
}
// Append annotations
for (final AnnotationMetadata annotation : method.getAnnotations()) {
appendIndent();
outputAnnotation(annotation);
this.newLine(false);
}
// Append "<modifier> <genericDefinition> <returnType> <methodName>" portion
appendIndent();
// modifier
if (method.getModifier() != 0)
{
append(Modifier.toString(method.getModifier()));
append(" ");
}
// ROO-3648: genericDefinition
if (method.getGenericDefinition() != null && !method.getGenericDefinition().trim().equals("")) {
append("<".concat(method.getGenericDefinition()).concat(">"));
append(" ");
}
// return type
append(method.getReturnType().getNameIncludingTypeParameters(false, resolver));
append(" ");
if (defineTarget) {
append(introductionTo.getSimpleTypeName());
append(".");
}
append(method.getMethodName().getSymbolName());
// Append parameter types and names
append("(");
final List<AnnotatedJavaType> parameterTypes = method.getParameterTypes();
final List<JavaSymbolName> parameterNames = method.getParameterNames();
for (int i = 0; i < parameterTypes.size(); i++) {
final AnnotatedJavaType paramType = parameterTypes.get(i);
final JavaSymbolName paramName = parameterNames.get(i);
for (final AnnotationMetadata methodParameterAnnotation : paramType.getAnnotations()) {
outputAnnotation(methodParameterAnnotation);
append(" ");
}
append(paramType.getJavaType().getNameIncludingTypeParameters(false, resolver));
append(" ");
append(paramName.getSymbolName());
if (i < parameterTypes.size() - 1) {
append(", ");
}
}
// Add exceptions to be thrown
final List<JavaType> throwsTypes = method.getThrowsTypes();
if (throwsTypes.size() > 0) {
append(") throws ");
for (int i = 0; i < throwsTypes.size(); i++) {
append(throwsTypes.get(i).getNameIncludingTypeParameters(false, resolver));
if (throwsTypes.size() > i + 1) {
append(", ");
}
}
} else {
append(")");
}
if (isInterfaceMethod) {
append(";");
this.newLine(false);
} else {
append(" {");
this.newLine(false);
// Add body
indent();
append(method.getBody());
indentRemove();
appendFormalLine("}");
}
this.newLine();
}
}
private void writeInnerTypeConstructors(final JavaType innerType,
final List<? extends ConstructorMetadata> constructors, final boolean defineTarget,
final boolean isInterfaceMethod) {
for (final ConstructorMetadata constructor : constructors) {
Validate.isTrue(constructor.getParameterTypes().size() == constructor.getParameterNames()
.size(), "One constructor has mismatched parameter names against parameter types");
// Append annotations
for (final AnnotationMetadata annotation : constructor.getAnnotations()) {
appendIndent();
outputAnnotation(annotation);
this.newLine(false);
}
// Append "<modifier> <methodName>" portion
appendIndent();
if (constructor.getModifier() != 0) {
append(Modifier.toString(constructor.getModifier()));
append(" ");
}
append(innerType.getSimpleTypeName());
// Append parameter types and names
append("(");
final List<AnnotatedJavaType> parameterTypes = constructor.getParameterTypes();
final List<JavaSymbolName> parameterNames = constructor.getParameterNames();
for (int i = 0; i < parameterTypes.size(); i++) {
final AnnotatedJavaType paramType = parameterTypes.get(i);
final JavaSymbolName paramName = parameterNames.get(i);
for (final AnnotationMetadata methodParameterAnnotation : paramType.getAnnotations()) {
outputAnnotation(methodParameterAnnotation);
append(" ");
}
append(paramType.getJavaType().getNameIncludingTypeParameters(false, resolver));
append(" ");
append(paramName.getSymbolName());
if (i < parameterTypes.size() - 1) {
append(", ");
}
}
// Add exceptions to be thrown
final List<JavaType> throwsTypes = constructor.getThrowsTypes();
if (throwsTypes.size() > 0) {
append(") throws ");
for (int i = 0; i < throwsTypes.size(); i++) {
append(throwsTypes.get(i).getNameIncludingTypeParameters(false, resolver));
if (throwsTypes.size() > i + 1) {
append(", ");
}
}
} else {
append(")");
}
if (isInterfaceMethod) {
append(";");
} else {
append(" {");
this.newLine(false);
// Add body
indent();
append(constructor.getBody());
indentRemove();
appendFormalLine("}");
}
this.newLine();
}
}
}