/*
* Copyright 2014 Google Inc. All rights reserved.
*
* 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 org.inferred.freebuilder.processor.util;
import com.google.common.annotations.VisibleForTesting;
import com.google.googlejavaformat.java.Formatter;
import org.inferred.freebuilder.processor.util.feature.Feature;
import org.inferred.freebuilder.processor.util.feature.FeatureSet;
import org.inferred.freebuilder.processor.util.feature.FeatureType;
import java.util.Collection;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
/** {@code SourceBuilder} which also handles package declaration and imports. */
public class CompilationUnitBuilder implements SourceBuilder {
private final ImportManager importManager;
private final SourceBuilder source;
private final QualifiedName classToWrite;
/**
* Returns a {@link CompilationUnitBuilder} for {@code classToWrite} using {@code features}. The
* file preamble (package and imports) will be generated automatically, and {@code env} will be
* inspected for potential import collisions.
*/
public CompilationUnitBuilder(
ProcessingEnvironment env,
QualifiedName classToWrite,
Collection<QualifiedName> nestedClasses,
FeatureSet features) {
this.classToWrite = classToWrite;
// Write the source code into an intermediate SourceStringBuilder, as the imports need to be
// written first, but aren't known yet.
ImportManager.Builder importManagerBuilder = new ImportManager.Builder();
importManagerBuilder.addImplicitImport(classToWrite);
PackageElement pkg = env.getElementUtils().getPackageElement(classToWrite.getPackage());
for (TypeElement sibling : ElementFilter.typesIn(pkg.getEnclosedElements())) {
importManagerBuilder.addImplicitImport(QualifiedName.of(sibling));
}
for (QualifiedName nestedClass : nestedClasses) {
importManagerBuilder.addImplicitImport(nestedClass);
}
importManager = importManagerBuilder.build();
source = new SourceStringBuilder(importManager, features);
}
@Override
public CompilationUnitBuilder add(String fmt, Object... args) {
source.add(fmt, args);
return this;
}
@Override
public SourceBuilder add(Excerpt excerpt) {
source.add(excerpt);
return this;
}
@Override
public CompilationUnitBuilder addLine(String fmt, Object... args) {
source.addLine(fmt, args);
return this;
}
@Override
public SourceStringBuilder subBuilder() {
return source.subBuilder();
}
@Override
public <T extends Feature<T>> T feature(FeatureType<T> feature) {
return source.feature(feature);
}
@Override
public String toString() {
StringBuilder unit = new StringBuilder();
unit.append("// Autogenerated code. Do not modify.\n")
.append("package ").append(classToWrite.getPackage()).append(";\n")
.append("\n");
if (!importManager.getClassImports().isEmpty()) {
for (String classImport : importManager.getClassImports()) {
unit.append("import ").append(classImport).append(";\n");
}
unit.append("\n");
}
unit.append(formatSource(source.toString()));
return unit.toString();
}
@VisibleForTesting
public static String formatSource(String source) {
try {
return new Formatter().formatSource(source);
} catch (UnsupportedClassVersionError e) {
// Formatter requires Java 7+; do no formatting in Java 6.
return source;
} catch (Exception e) {
StringBuilder message = new StringBuilder()
.append("Formatter failed:\n")
.append(e.getMessage())
.append("\nGenerated source:");
int lineNo = 0;
for (String line : source.split("\n")) {
message
.append("\n")
.append(++lineNo)
.append(": ")
.append(line);
}
throw new RuntimeException(message.toString());
}
}
}