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.collect.FluentIterable; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.Lists; 22 import com.google.common.collect.Maps; 23 import com.google.common.collect.Sets; 24 import java.io.IOException; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.Set; 29 import javax.lang.model.element.Modifier; 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 EnumWriter extends TypeWriter { 37 private final Map<String, ConstantWriter> constantWriters = Maps.newLinkedHashMap(); 38 private final List<ConstructorWriter> constructorWriters = Lists.newArrayList(); 39 EnumWriter(ClassName name)40 EnumWriter(ClassName name) { 41 super(name); 42 } 43 addConstant(String name)44 public ConstantWriter addConstant(String name) { 45 ConstantWriter constantWriter = new ConstantWriter(name); 46 constantWriters.put(name, constantWriter); 47 return constantWriter; 48 } 49 addConstructor()50 public ConstructorWriter addConstructor() { 51 ConstructorWriter constructorWriter = new ConstructorWriter(name.simpleName()); 52 constructorWriters.add(constructorWriter); 53 return constructorWriter; 54 } 55 56 @Override write(Appendable appendable, Context context)57 public Appendable write(Appendable appendable, Context context) throws IOException { 58 context = context.createSubcontext(FluentIterable.from(nestedTypeWriters) 59 .transform(new Function<TypeWriter, ClassName>() { 60 @Override public ClassName apply(TypeWriter input) { 61 return input.name; 62 } 63 }) 64 .toSet()); 65 writeAnnotations(appendable, context); 66 writeModifiers(appendable).append("enum ").append(name.simpleName()); 67 Iterator<TypeName> implementedTypesIterator = implementedTypes.iterator(); 68 if (implementedTypesIterator.hasNext()) { 69 appendable.append(" implements "); 70 implementedTypesIterator.next().write(appendable, context); 71 while (implementedTypesIterator.hasNext()) { 72 appendable.append(", "); 73 implementedTypesIterator.next().write(appendable, context); 74 } 75 } 76 appendable.append(" {"); 77 78 checkState(!constantWriters.isEmpty(), "Cannot write an enum with no constants."); 79 appendable.append('\n'); 80 ImmutableList<ConstantWriter> constantWriterList = 81 ImmutableList.copyOf(constantWriters.values()); 82 for (ConstantWriter constantWriter 83 : constantWriterList.subList(0, constantWriterList.size() - 1)) { 84 constantWriter.write(appendable, context); 85 appendable.append(",\n"); 86 } 87 constantWriterList.get(constantWriterList.size() - 1).write(appendable, context); 88 appendable.append(";\n"); 89 90 if (!fieldWriters.isEmpty()) { 91 appendable.append('\n'); 92 } 93 for (VariableWriter fieldWriter : fieldWriters.values()) { 94 fieldWriter.write(new IndentingAppendable(appendable), context).append("\n"); 95 } 96 for (ConstructorWriter constructorWriter : constructorWriters) { 97 appendable.append('\n'); 98 if (!isDefaultConstructor(constructorWriter)) { 99 constructorWriter.write(new IndentingAppendable(appendable), context); 100 } 101 } 102 for (MethodWriter methodWriter : methodWriters) { 103 appendable.append('\n'); 104 methodWriter.write(new IndentingAppendable(appendable), context); 105 } 106 for (TypeWriter nestedTypeWriter : nestedTypeWriters) { 107 appendable.append('\n'); 108 nestedTypeWriter.write(new IndentingAppendable(appendable), context); 109 } 110 appendable.append("}\n"); 111 return appendable; 112 } 113 114 private static final Set<Modifier> VISIBILIY_MODIFIERS = 115 Sets.immutableEnumSet(PUBLIC, PROTECTED, PRIVATE); 116 isDefaultConstructor(ConstructorWriter constructorWriter)117 private boolean isDefaultConstructor(ConstructorWriter constructorWriter) { 118 return Sets.intersection(VISIBILIY_MODIFIERS, modifiers) 119 .equals(Sets.intersection(VISIBILIY_MODIFIERS, constructorWriter.modifiers)) 120 && constructorWriter.body().isEmpty(); 121 } 122 123 @Override referencedClasses()124 public Set<ClassName> referencedClasses() { 125 return FluentIterable.from(ImmutableList.<HasClassReferences>of()) 126 .append(nestedTypeWriters) 127 .append(constantWriters.values()) 128 .append(fieldWriters.values()) 129 .append(constructorWriters) 130 .append(methodWriters) 131 .append(implementedTypes) 132 .append(annotations) 133 .transformAndConcat(HasClassReferences.COMBINER) 134 .toSet(); 135 } 136 137 public static final class ConstantWriter implements Writable, HasClassReferences { 138 private final String name; 139 private final List<Snippet> constructorSnippets; 140 ConstantWriter(String name)141 private ConstantWriter(String name) { 142 this.name = name; 143 this.constructorSnippets = Lists.newArrayList(); 144 } 145 addArgument(Snippet snippet)146 ConstantWriter addArgument(Snippet snippet) { 147 constructorSnippets.add(snippet); 148 return this; 149 } 150 151 @Override write(Appendable appendable, Context context)152 public Appendable write(Appendable appendable, Context context) throws IOException { 153 appendable.append(name); 154 Iterator<Snippet> snippetIterator = constructorSnippets.iterator(); 155 if (snippetIterator.hasNext()) { 156 appendable.append('('); 157 snippetIterator.next().write(appendable, context); 158 while (snippetIterator.hasNext()) { 159 appendable.append(", "); 160 snippetIterator.next().write(appendable, context); 161 } 162 appendable.append(')'); 163 } 164 return appendable; 165 } 166 167 @Override referencedClasses()168 public Set<ClassName> referencedClasses() { 169 return FluentIterable.from(constructorSnippets) 170 .transformAndConcat(HasClassReferences.COMBINER) 171 .toSet(); 172 } 173 } 174 } 175