blob: a5fabf5d9effb06014745d96b7f8660a4d00d639 [file] [log] [blame]
/*
* Copyright (C) 2013 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.auto.factory;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map.Entry;
import javax.annotation.Generated;
import javax.annotation.processing.Filer;
import javax.inject.Inject;
import javax.lang.model.element.Modifier;
import javax.tools.JavaFileObject;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.squareup.javawriter.JavaWriter;
final class FactoryWriter {
private final Filer filer;
@Inject FactoryWriter(Filer filer) {
this.filer = filer;
}
private static final Joiner argumentJoiner = Joiner.on(", ");
void writeFactory(final FactoryDescriptor descriptor)
throws IOException {
JavaFileObject sourceFile = filer.createSourceFile(descriptor.name());
JavaWriter writer = new JavaWriter(sourceFile.openWriter());
String packageName = getPackage(descriptor.name()).toString();
writer.emitPackage(packageName)
.emitImports("javax.annotation.Generated");
writer.emitImports("javax.inject.Inject");
if (!descriptor.providerNames().isEmpty()) {
writer.emitImports("javax.inject.Provider");
}
for (String implementingType : descriptor.implementingTypes()) {
String implementingPackageName = getPackage(implementingType).toString();
if (!"java.lang".equals(implementingPackageName)
&& !packageName.equals(implementingPackageName)) {
writer.emitImports(implementingType);
}
}
String[] implementedClasses = FluentIterable.from(descriptor.implementingTypes())
.transform(new Function<String, String>() {
@Override public String apply(String implemetingClass) {
return getSimpleName(implemetingClass).toString();
}
})
.toSortedSet(Ordering.natural())
.toArray(new String[0]);
String factoryName = getSimpleName(descriptor.name()).toString();
writer.emitAnnotation(Generated.class,
ImmutableMap.of("value", "\"" + AutoFactoryProcessor.class.getName() + "\""));
EnumSet<Modifier> modifiers = EnumSet.of(FINAL);
if (descriptor.publicType()) {
modifiers.add(PUBLIC);
}
writer.beginType(factoryName, "class", modifiers, null, implementedClasses);
ImmutableList.Builder<String> constructorTokens = ImmutableList.builder();
for (Entry<Key, String> entry : descriptor.providerNames().entrySet()) {
Key key = entry.getKey();
String providerName = entry.getValue();
writer.emitField("Provider<" + key.getType() + ">", providerName, EnumSet.of(PRIVATE, FINAL));
Optional<String> qualifier = key.getQualifier();
String qualifierPrefix = qualifier.isPresent() ? "@" + qualifier.get() + " " : "";
constructorTokens.add(qualifierPrefix + "Provider<" + key.getType() + ">").add(providerName);
}
writer.emitAnnotation("Inject");
writer.beginMethod(null, factoryName,
descriptor.publicType() ? EnumSet.of(PUBLIC) : EnumSet.noneOf(Modifier.class),
constructorTokens.build().toArray(new String[0]));
for (String providerName : descriptor.providerNames().values()) {
writer.emitStatement("this.%1$s = %1$s", providerName);
}
writer.endMethod();
for (final FactoryMethodDescriptor methodDescriptor : descriptor.methodDescriptors()) {
writer.beginMethod(methodDescriptor.returnType(), methodDescriptor.name(),
methodDescriptor.publicMethod() ? EnumSet.of(PUBLIC) : EnumSet.noneOf(Modifier.class),
parameterTokens(methodDescriptor.passedParameters()));
FluentIterable<String> creationParameterNames =
FluentIterable.from(methodDescriptor.creationParameters())
.transform(new Function<Parameter, String>() {
@Override public String apply(Parameter parameter) {
return methodDescriptor.passedParameters().contains(parameter)
? parameter.name()
: descriptor.providerNames().get(parameter.asKey()) + ".get()";
}
});
writer.emitStatement("return new %s(%s)", writer.compressType(methodDescriptor.returnType()),
argumentJoiner.join(creationParameterNames));
writer.endMethod();
}
for (ImplemetationMethodDescriptor methodDescriptor
: descriptor.implementationMethodDescriptors()) {
writer.emitAnnotation(Override.class);
writer.beginMethod(methodDescriptor.returnType(), methodDescriptor.name(),
methodDescriptor.publicMethod() ? EnumSet.of(PUBLIC) : EnumSet.noneOf(Modifier.class),
parameterTokens(methodDescriptor.passedParameters()));
FluentIterable<String> creationParameterNames =
FluentIterable.from(methodDescriptor.passedParameters())
.transform(new Function<Parameter, String>() {
@Override public String apply(Parameter parameter) {
return parameter.name();
}
});
writer.emitStatement("return create(%s)", argumentJoiner.join(creationParameterNames));
writer.endMethod();
}
writer.endType();
writer.close();
}
private static String[] parameterTokens(Collection<Parameter> parameters) {
List<String> parameterTokens =
Lists.newArrayListWithCapacity(parameters.size());
for (Parameter parameter : parameters) {
parameterTokens.add(parameter.type());
parameterTokens.add(parameter.name());
}
return parameterTokens.toArray(new String[0]);
}
private static CharSequence getSimpleName(CharSequence fullyQualifiedName) {
int lastDot = lastIndexOf(fullyQualifiedName, '.');
return fullyQualifiedName.subSequence(lastDot + 1, fullyQualifiedName.length());
}
private static CharSequence getPackage(CharSequence fullyQualifiedName) {
int lastDot = lastIndexOf(fullyQualifiedName, '.');
return fullyQualifiedName.subSequence(0, lastDot);
}
private static int lastIndexOf(CharSequence charSequence, char c) {
for (int i = charSequence.length() - 1; i >= 0; i--) {
if (charSequence.charAt(i) == c) {
return i;
}
}
return -1;
}
}