1 /* 2 * Copyright (C) 2014 Google, 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 dagger.internal.codegen.writer; 17 18 import com.google.common.base.Function; 19 import com.google.common.base.Optional; 20 import com.google.common.collect.FluentIterable; 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.Iterables; 23 import com.google.common.collect.Lists; 24 import com.google.common.collect.Sets; 25 import java.io.IOException; 26 import java.util.List; 27 import java.util.Set; 28 import javax.lang.model.element.Modifier; 29 import javax.lang.model.element.TypeElement; 30 31 import static com.google.common.base.Preconditions.checkState; 32 import static javax.lang.model.element.Modifier.PRIVATE; 33 import static javax.lang.model.element.Modifier.PROTECTED; 34 import static javax.lang.model.element.Modifier.PUBLIC; 35 36 public final class ClassWriter extends TypeWriter { 37 private Optional<TypeName> superclass; 38 private final List<ConstructorWriter> constructorWriters; 39 private final List<TypeVariableName> typeParameters; 40 ClassWriter(ClassName className)41 ClassWriter(ClassName className) { 42 super(className); 43 this.superclass = Optional.absent(); 44 this.constructorWriters = Lists.newArrayList(); 45 this.typeParameters = Lists.newArrayList(); 46 } 47 setSuperclass(TypeName typeReference)48 public void setSuperclass(TypeName typeReference) { 49 checkState(!superclass.isPresent()); 50 superclass = Optional.of(typeReference); 51 } 52 53 /** 54 * If {@code supertype} is a class, makes this class extend it; if it is an interface, makes this 55 * class implement it. 56 */ setSupertype(TypeElement supertype)57 public void setSupertype(TypeElement supertype) { 58 switch (supertype.getKind()) { 59 case CLASS: 60 setSuperclass(ClassName.fromTypeElement(supertype)); 61 break; 62 case INTERFACE: 63 addImplementedType(supertype); 64 break; 65 default: 66 throw new IllegalArgumentException(supertype + " must be a class or interface"); 67 } 68 } 69 addConstructor()70 public ConstructorWriter addConstructor() { 71 ConstructorWriter constructorWriter = new ConstructorWriter(name.simpleName()); 72 constructorWriters.add(constructorWriter); 73 return constructorWriter; 74 } 75 addTypeParameter(TypeVariableName typeVariableName)76 public void addTypeParameter(TypeVariableName typeVariableName) { 77 this.typeParameters.add(typeVariableName); 78 } 79 addTypeParameters(Iterable<TypeVariableName> typeVariableNames)80 public void addTypeParameters(Iterable<TypeVariableName> typeVariableNames) { 81 Iterables.addAll(typeParameters, typeVariableNames); 82 } 83 typeParameters()84 public List<TypeVariableName> typeParameters() { 85 return ImmutableList.copyOf(typeParameters); 86 } 87 88 @Override write(Appendable appendable, Context context)89 public Appendable write(Appendable appendable, Context context) throws IOException { 90 context = context.createSubcontext(FluentIterable.from(nestedTypeWriters) 91 .transform(new Function<TypeWriter, ClassName>() { 92 @Override public ClassName apply(TypeWriter input) { 93 return input.name; 94 } 95 }) 96 .toSet()); 97 writeAnnotations(appendable, context); 98 writeModifiers(appendable).append("class ").append(name.simpleName()); 99 Writables.join(", ", typeParameters, "<", ">", appendable, context); 100 if (superclass.isPresent()) { 101 appendable.append(" extends "); 102 superclass.get().write(appendable, context); 103 } 104 Writables.join(", ", implementedTypes, " implements ", "", appendable, context); 105 appendable.append(" {"); 106 if (!fieldWriters.isEmpty()) { 107 appendable.append('\n'); 108 } 109 for (VariableWriter fieldWriter : fieldWriters.values()) { 110 fieldWriter.write(new IndentingAppendable(appendable), context).append("\n"); 111 } 112 for (ConstructorWriter constructorWriter : constructorWriters) { 113 appendable.append('\n'); 114 if (!isDefaultConstructor(constructorWriter)) { 115 constructorWriter.write(new IndentingAppendable(appendable), context); 116 } 117 } 118 for (MethodWriter methodWriter : methodWriters) { 119 appendable.append('\n'); 120 methodWriter.write(new IndentingAppendable(appendable), context); 121 } 122 for (TypeWriter nestedTypeWriter : nestedTypeWriters) { 123 appendable.append('\n'); 124 nestedTypeWriter.write(new IndentingAppendable(appendable), context); 125 } 126 appendable.append("}\n"); 127 return appendable; 128 } 129 130 private static final Set<Modifier> VISIBILIY_MODIFIERS = 131 Sets.immutableEnumSet(PUBLIC, PROTECTED, PRIVATE); 132 isDefaultConstructor(ConstructorWriter constructorWriter)133 private boolean isDefaultConstructor(ConstructorWriter constructorWriter) { 134 return Sets.intersection(VISIBILIY_MODIFIERS, modifiers) 135 .equals(Sets.intersection(VISIBILIY_MODIFIERS, constructorWriter.modifiers)) 136 && constructorWriter.body().isEmpty(); 137 } 138 139 @Override referencedClasses()140 public Set<ClassName> referencedClasses() { 141 return FluentIterable.from(ImmutableList.<HasClassReferences>of()) 142 .append(nestedTypeWriters) 143 .append(fieldWriters.values()) 144 .append(constructorWriters) 145 .append(methodWriters) 146 .append(implementedTypes) 147 .append(superclass.asSet()) 148 .append(annotations) 149 .append(typeParameters) 150 .transformAndConcat(HasClassReferences.COMBINER) 151 .toSet(); 152 } 153 } 154