| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8.apimodel; | |
| import com.android.tools.r8.utils.Action; | |
| import com.android.tools.r8.utils.StringUtils; | |
| import com.android.tools.r8.utils.StringUtils.BraceType; | |
| import java.util.Arrays; | |
| import java.util.Collection; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Set; | |
| import java.util.function.Consumer; | |
| import java.util.stream.Collectors; | |
| public final class JavaSourceCodePrinter { | |
| private final Set<String> imports = new HashSet<>(); | |
| private final String className; | |
| private final String packageName; | |
| private final String classModifiers; | |
| private final String header; | |
| private final StringBuilder bodyPrinter = new StringBuilder(); | |
| public enum KnownType { | |
| Consumer("java.util.function", "Consumer", false), | |
| DexItemFactory("com.android.tools.r8.graph", "DexItemFactory", false), | |
| DexMethod("com.android.tools.r8.graph", "DexMethod", false); | |
| private String packageName; | |
| private String simpleName; | |
| private boolean isPrimitive; | |
| KnownType(String packageName, String simpleName, boolean isPrimitive) { | |
| this.packageName = packageName; | |
| this.simpleName = simpleName; | |
| this.isPrimitive = isPrimitive; | |
| } | |
| boolean isPrimitive() { | |
| return isPrimitive; | |
| } | |
| String getCanonicalName() { | |
| return packageName + "." + simpleName; | |
| } | |
| String getSimpleName() { | |
| return simpleName; | |
| } | |
| } | |
| public JavaSourceCodePrinter( | |
| String className, String packageName, String classModifiers, String header) { | |
| this.className = className; | |
| this.packageName = packageName; | |
| this.classModifiers = classModifiers; | |
| this.header = header; | |
| } | |
| public void addClassImport(KnownType type) { | |
| if (!type.isPrimitive) { | |
| imports.add(type.getCanonicalName()); | |
| } | |
| } | |
| public JavaSourceCodePrinter addMethod( | |
| String modifiers, | |
| Type returnType, | |
| String name, | |
| List<MethodParameter> parameters, | |
| Consumer<JavaSourceCodeMethodPrinter> content) { | |
| bodyPrinter.append(modifiers).append(" "); | |
| if (returnType != null) { | |
| bodyPrinter.append(returnType.toString(this::addClassImport)); | |
| } else { | |
| bodyPrinter.append("void"); | |
| } | |
| JavaSourceCodeMethodPrinter javaSourceCodeMethodPrinter = new JavaSourceCodeMethodPrinter(); | |
| content.accept(javaSourceCodeMethodPrinter); | |
| bodyPrinter | |
| .append(" ") | |
| .append(name) | |
| .append( | |
| StringUtils.join( | |
| ", ", | |
| parameters, | |
| parameter -> parameter.toString(this::addClassImport), | |
| BraceType.PARENS)) | |
| .append(" {") | |
| .append(StringUtils.LINE_SEPARATOR) | |
| .append(javaSourceCodeMethodPrinter) | |
| .append("}") | |
| .append(StringUtils.LINE_SEPARATOR); | |
| return this; | |
| } | |
| @Override | |
| public String toString() { | |
| StringBuilder sb = new StringBuilder(header); | |
| if (packageName != null) { | |
| sb.append("package ").append(packageName).append(";").append(StringUtils.LINE_SEPARATOR); | |
| } | |
| sb.append( | |
| StringUtils.joinLines( | |
| imports.stream() | |
| .sorted() | |
| .map(imp -> "import " + imp + ";") | |
| .collect(Collectors.toList()))) | |
| .append(StringUtils.LINE_SEPARATOR); | |
| return sb.append(StringUtils.LINE_SEPARATOR) | |
| .append(classModifiers) | |
| .append(" ") | |
| .append("class ") | |
| .append(className) | |
| .append(" {") | |
| .append(StringUtils.LINE_SEPARATOR) | |
| .append(bodyPrinter) | |
| .append("}") | |
| .toString(); | |
| } | |
| public static Builder builder() { | |
| return new Builder(); | |
| } | |
| public static class Builder { | |
| private String className; | |
| private String packageName = null; | |
| private final String classModifiers = "public"; | |
| private String header = ""; | |
| public Builder setClassName(String className) { | |
| this.className = className; | |
| return this; | |
| } | |
| public Builder setPackageName(String packageName) { | |
| this.packageName = packageName; | |
| return this; | |
| } | |
| public Builder setHeader(String header) { | |
| this.header = header; | |
| return this; | |
| } | |
| public JavaSourceCodePrinter build() { | |
| assert className != null; | |
| return new JavaSourceCodePrinter(className, packageName, classModifiers, header); | |
| } | |
| } | |
| public static class JavaSourceCodeMethodPrinter { | |
| private final StringBuilder sb = new StringBuilder(); | |
| public JavaSourceCodeMethodPrinter addInstanceMethodCall( | |
| String variable, String method, Action... content) { | |
| return addInstanceMethodCall(variable, method, Arrays.asList(content)); | |
| } | |
| public JavaSourceCodeMethodPrinter addInstanceMethodCall( | |
| String variable, String method, Collection<Action> content) { | |
| sb.append(variable).append(".").append(method).append("("); | |
| boolean insertSeparator = false; | |
| for (Action action : content) { | |
| if (insertSeparator) { | |
| sb.append(", "); | |
| } | |
| action.execute(); | |
| insertSeparator = true; | |
| } | |
| sb.append(")"); | |
| return this; | |
| } | |
| public Action literal(String constant) { | |
| return () -> sb.append(StringUtils.quote(constant)); | |
| } | |
| public JavaSourceCodeMethodPrinter addSemicolon() { | |
| sb.append(";"); | |
| return this; | |
| } | |
| public JavaSourceCodeMethodPrinter newLine() { | |
| sb.append(StringUtils.LINE_SEPARATOR); | |
| return this; | |
| } | |
| @Override | |
| public String toString() { | |
| return sb.toString(); | |
| } | |
| } | |
| public static class Type { | |
| private final KnownType type; | |
| private Type(KnownType type) { | |
| this.type = type; | |
| } | |
| public static Type fromType(KnownType type) { | |
| return new Type(type); | |
| } | |
| public String toString(Consumer<KnownType> classConsumer) { | |
| classConsumer.accept(type); | |
| return type.getSimpleName(); | |
| } | |
| } | |
| public static class ParameterizedType extends Type { | |
| private final Type[] arguments; | |
| private ParameterizedType(KnownType clazz, Type[] arguments) { | |
| super(clazz); | |
| this.arguments = arguments; | |
| } | |
| public static ParameterizedType fromType(KnownType type, Type... arguments) { | |
| return new ParameterizedType(type, arguments); | |
| } | |
| @Override | |
| public String toString(Consumer<KnownType> classConsumer) { | |
| StringBuilder sb = new StringBuilder(); | |
| sb.append(super.toString(classConsumer)); | |
| if (arguments.length == 0) { | |
| return sb.toString(); | |
| } | |
| sb.append("<"); | |
| sb.append( | |
| StringUtils.join(", ", Arrays.asList(arguments), type -> type.toString(classConsumer))); | |
| sb.append(">"); | |
| return sb.toString(); | |
| } | |
| } | |
| public static class MethodParameter { | |
| private final Type type; | |
| private final String name; | |
| private MethodParameter(Type type, String name) { | |
| this.type = type; | |
| this.name = name; | |
| } | |
| public static MethodParameter build(Type type, String name) { | |
| return new MethodParameter(type, name); | |
| } | |
| private String toString(Consumer<KnownType> classConsumer) { | |
| return type.toString(classConsumer) + " " + name; | |
| } | |
| } | |
| } |