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(); } } }