1 /* 2 * Copyright (C) 2015 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.squareup.javapoet; 17 18 import java.io.IOException; 19 import java.lang.reflect.Type; 20 import java.util.ArrayList; 21 import java.util.Collections; 22 import java.util.List; 23 import java.util.Set; 24 import javax.lang.model.SourceVersion; 25 import javax.lang.model.element.ElementKind; 26 import javax.lang.model.element.ExecutableElement; 27 import javax.lang.model.element.Modifier; 28 import javax.lang.model.element.VariableElement; 29 30 import static com.squareup.javapoet.Util.checkArgument; 31 import static com.squareup.javapoet.Util.checkNotNull; 32 33 /** A generated parameter declaration. */ 34 public final class ParameterSpec { 35 public final String name; 36 public final List<AnnotationSpec> annotations; 37 public final Set<Modifier> modifiers; 38 public final TypeName type; 39 public final CodeBlock javadoc; 40 ParameterSpec(Builder builder)41 private ParameterSpec(Builder builder) { 42 this.name = checkNotNull(builder.name, "name == null"); 43 this.annotations = Util.immutableList(builder.annotations); 44 this.modifiers = Util.immutableSet(builder.modifiers); 45 this.type = checkNotNull(builder.type, "type == null"); 46 this.javadoc = builder.javadoc.build(); 47 } 48 hasModifier(Modifier modifier)49 public boolean hasModifier(Modifier modifier) { 50 return modifiers.contains(modifier); 51 } 52 emit(CodeWriter codeWriter, boolean varargs)53 void emit(CodeWriter codeWriter, boolean varargs) throws IOException { 54 codeWriter.emitAnnotations(annotations, true); 55 codeWriter.emitModifiers(modifiers); 56 if (varargs) { 57 TypeName.asArray(type).emit(codeWriter, true); 58 } else { 59 type.emit(codeWriter); 60 } 61 codeWriter.emit(" $L", name); 62 } 63 equals(Object o)64 @Override public boolean equals(Object o) { 65 if (this == o) return true; 66 if (o == null) return false; 67 if (getClass() != o.getClass()) return false; 68 return toString().equals(o.toString()); 69 } 70 hashCode()71 @Override public int hashCode() { 72 return toString().hashCode(); 73 } 74 toString()75 @Override public String toString() { 76 StringBuilder out = new StringBuilder(); 77 try { 78 CodeWriter codeWriter = new CodeWriter(out); 79 emit(codeWriter, false); 80 return out.toString(); 81 } catch (IOException e) { 82 throw new AssertionError(); 83 } 84 } 85 get(VariableElement element)86 public static ParameterSpec get(VariableElement element) { 87 checkArgument(element.getKind().equals(ElementKind.PARAMETER), "element is not a parameter"); 88 89 TypeName type = TypeName.get(element.asType()); 90 String name = element.getSimpleName().toString(); 91 // Copying parameter annotations can be incorrect so we're deliberately not including them. 92 // See https://github.com/square/javapoet/issues/482. 93 return ParameterSpec.builder(type, name) 94 .addModifiers(element.getModifiers()) 95 .build(); 96 } 97 parametersOf(ExecutableElement method)98 static List<ParameterSpec> parametersOf(ExecutableElement method) { 99 List<ParameterSpec> result = new ArrayList<>(); 100 for (VariableElement parameter : method.getParameters()) { 101 result.add(ParameterSpec.get(parameter)); 102 } 103 return result; 104 } 105 isValidParameterName(String name)106 private static boolean isValidParameterName(String name) { 107 // Allow "this" for explicit receiver parameters 108 // See https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.1. 109 if (name.endsWith(".this")) { 110 return SourceVersion.isIdentifier(name.substring(0, name.length() - ".this".length())); 111 } 112 return name.equals("this") || SourceVersion.isName(name); 113 } 114 builder(TypeName type, String name, Modifier... modifiers)115 public static Builder builder(TypeName type, String name, Modifier... modifiers) { 116 checkNotNull(type, "type == null"); 117 checkArgument(isValidParameterName(name), "not a valid name: %s", name); 118 return new Builder(type, name) 119 .addModifiers(modifiers); 120 } 121 builder(Type type, String name, Modifier... modifiers)122 public static Builder builder(Type type, String name, Modifier... modifiers) { 123 return builder(TypeName.get(type), name, modifiers); 124 } 125 toBuilder()126 public Builder toBuilder() { 127 return toBuilder(type, name); 128 } 129 toBuilder(TypeName type, String name)130 Builder toBuilder(TypeName type, String name) { 131 Builder builder = new Builder(type, name); 132 builder.annotations.addAll(annotations); 133 builder.modifiers.addAll(modifiers); 134 return builder; 135 } 136 137 public static final class Builder { 138 private final TypeName type; 139 private final String name; 140 private final CodeBlock.Builder javadoc = CodeBlock.builder(); 141 142 public final List<AnnotationSpec> annotations = new ArrayList<>(); 143 public final List<Modifier> modifiers = new ArrayList<>(); 144 Builder(TypeName type, String name)145 private Builder(TypeName type, String name) { 146 this.type = type; 147 this.name = name; 148 } 149 addJavadoc(String format, Object... args)150 public Builder addJavadoc(String format, Object... args) { 151 javadoc.add(format, args); 152 return this; 153 } 154 addJavadoc(CodeBlock block)155 public Builder addJavadoc(CodeBlock block) { 156 javadoc.add(block); 157 return this; 158 } 159 addAnnotations(Iterable<AnnotationSpec> annotationSpecs)160 public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) { 161 checkArgument(annotationSpecs != null, "annotationSpecs == null"); 162 for (AnnotationSpec annotationSpec : annotationSpecs) { 163 this.annotations.add(annotationSpec); 164 } 165 return this; 166 } 167 addAnnotation(AnnotationSpec annotationSpec)168 public Builder addAnnotation(AnnotationSpec annotationSpec) { 169 this.annotations.add(annotationSpec); 170 return this; 171 } 172 addAnnotation(ClassName annotation)173 public Builder addAnnotation(ClassName annotation) { 174 this.annotations.add(AnnotationSpec.builder(annotation).build()); 175 return this; 176 } 177 addAnnotation(Class<?> annotation)178 public Builder addAnnotation(Class<?> annotation) { 179 return addAnnotation(ClassName.get(annotation)); 180 } 181 addModifiers(Modifier... modifiers)182 public Builder addModifiers(Modifier... modifiers) { 183 Collections.addAll(this.modifiers, modifiers); 184 return this; 185 } 186 addModifiers(Iterable<Modifier> modifiers)187 public Builder addModifiers(Iterable<Modifier> modifiers) { 188 checkNotNull(modifiers, "modifiers == null"); 189 for (Modifier modifier : modifiers) { 190 if (!modifier.equals(Modifier.FINAL)) { 191 throw new IllegalStateException("unexpected parameter modifier: " + modifier); 192 } 193 this.modifiers.add(modifier); 194 } 195 return this; 196 } 197 build()198 public ParameterSpec build() { 199 return new ParameterSpec(this); 200 } 201 } 202 } 203