// Copyright 2017 The Bazel Authors. 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 com.google.devtools.build.benchmark.codegenerator;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Joiner;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Random;
import javax.lang.model.element.Modifier;
/** Helper class of {@link JavaCodeGenerator} */
class JavaCodeGeneratorHelper {
private static final Joiner JOINER = Joiner.on("\n");
private static final MethodSpec randomMethod = genRandomMethod("PrintSth");
private static final MethodSpec somethingElseMethod = genRandomMethod("PrintSthElse");
/**
* Writes a class file {@code Deps(index).java} to the directory
* {@code projectPath/com/example/deps(index)}
*
* @param callNext if we should call the method from {@code Deps(index+1).java}
*/
static void targetWithNextHelper(int index, boolean callNext, Path projectPath)
throws IOException {
ClassName nextClass = ClassName.get("com.example.deps" + (index + 1), "Deps" + (index + 1));
MethodSpec callNextMethod =
MethodSpec.methodBuilder("CallNext")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addStatement("$T.PrintSth()", nextClass)
.build();
TypeSpec.Builder klassBuilder =
TypeSpec.classBuilder("Deps" + index)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(randomMethod);
if (callNext) {
klassBuilder.addMethod(callNextMethod);
}
TypeSpec klass = klassBuilder.build();
writeClassToDir(klass, "com.example.deps" + index, projectPath);
}
/**
* Writes a class file {@code Deps(index).java} with extra method {@code printSthElse()}
* to the directory {@code projectPath/com/example/deps(index)}
*
* @param callNext if we should call the method from {@code Deps(index+1).java}
*/
static void targetWithNextExtraHelper(int index, boolean callNext, Path projectPath)
throws IOException {
ClassName nextClass = ClassName.get("com.example.deps" + (index + 1), "Deps" + (index + 1));
MethodSpec callNextMethod =
MethodSpec.methodBuilder("CallNext")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addStatement("$T.PrintSth()", nextClass)
.build();
TypeSpec.Builder klassBuilder =
TypeSpec.classBuilder("Deps" + index)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(randomMethod)
.addMethod(somethingElseMethod);
if (callNext) {
klassBuilder.addMethod(callNextMethod);
}
TypeSpec klass = klassBuilder.build();
writeClassToDir(klass, "com.example.deps" + index, projectPath);
}
/**
* Writes {@code count-1} class files to the directory {@code projectPath/com/example/deps(index)}
* and one main class.
*/
static void parallelDepsMainClassHelper(int count, Path projectPath) throws IOException {
MethodSpec.Builder callDepsBuilder =
MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(String[].class, "args")
.returns(void.class);
for (int i = 1; i < count; ++i) {
ClassName callingClass = ClassName.get("com.example.deps" + i, "Deps" + i);
callDepsBuilder.addStatement("$T.PrintSth()", callingClass);
}
MethodSpec callDeps = callDepsBuilder.build();
TypeSpec klass =
TypeSpec.classBuilder("Main")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(callDeps)
.build();
writeClassToDir(klass, "com.example.generated", projectPath);
}
static void writeRandomClassToDir(
boolean addExtraMethod, String className, String packageName, Path projectPath)
throws IOException {
TypeSpec klass = genRandomClass(addExtraMethod, className);
writeClassToDir(klass, packageName, projectPath);
}
static void writeMainClassToDir(String packageName, Path projectPath) throws IOException {
TypeSpec main = genMainClass();
writeClassToDir(main, packageName, projectPath);
}
static void buildFileWithNextDeps(int index, String deps, Path projectPath) throws IOException {
Path buildFilePath = projectPath.resolve("BUILD");
String buildFileContent =
String.format(
JOINER
.join(
"java_library(",
" name = 'Deps%d',",
" srcs = glob([ 'com/example/deps%d/*.java' ]),",
"%s",
" visibility = [ '//visibility:public' ],",
")",
""),
index,
index,
deps);
createAndAppendFile(buildFilePath, buildFileContent);
}
static void buildFileWithMainClass(String targetName, String deps, Path projectPath)
throws IOException {
Path buildFilePath = projectPath.resolve("BUILD");
String buildFileContent =
String.format(
JOINER
.join(
"java_binary(",
" name = '%s',",
" srcs = glob([ 'com/example/generated/*.java' ]),",
" main_class = 'com.example.generated.Main',",
"%s",
")",
""),
targetName,
deps);
createAndAppendFile(buildFilePath, buildFileContent);
}
private static MethodSpec genRandomMethod(String methodName) {
return MethodSpec.methodBuilder(methodName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addStatement("$T rand = new Random()", Random.class)
.addStatement("int n = rand.nextInt(100)")
.addStatement(
"$T.out.format($S, $S, $L)",
System.class,
"This is method(%s) with random number(%d)\n",
methodName,
"n")
.build();
}
private static TypeSpec genRandomClass(boolean addExtraMethod, String className) {
TypeSpec.Builder klassBuilder =
TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(randomMethod);
if (addExtraMethod) {
klassBuilder.addMethod(somethingElseMethod);
}
return klassBuilder.build();
}
private static TypeSpec genMainClass() {
MethodSpec method =
MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.build();
return TypeSpec.classBuilder("Main").addModifiers(Modifier.PUBLIC).addMethod(method).build();
}
private static void writeClassToDir(TypeSpec klass, String packageName, Path path)
throws IOException {
JavaFile javaFile = JavaFile.builder(packageName, klass).build();
javaFile.writeTo(path);
}
private static void createAndAppendFile(Path path, String content) throws IOException {
if (!Files.exists(path)) {
Files.createFile(path);
}
Files.write(path, content.getBytes(UTF_8), StandardOpenOption.APPEND);
}
}