Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.spring.initializr.generator.language.java.JavaSourceCodeWriter Maven / Gradle / Ivy
/*
* Copyright 2012-2019 the original author or authors.
*
* 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
*
* https://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 io.spring.initializr.generator.language.java;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.spring.initializr.generator.io.IndentingWriter;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.language.Annotatable;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCode;
import io.spring.initializr.generator.language.SourceCodeWriter;
import io.spring.initializr.generator.language.SourceStructure;
/**
* A {@link SourceCodeWriter} that writes {@link SourceCode} in Java.
*
* @author Andy Wilkinson
* @author Matt Berteaux
*/
public class JavaSourceCodeWriter implements SourceCodeWriter {
private static final Map, String> TYPE_MODIFIERS;
private static final Map, String> FIELD_MODIFIERS;
private static final Map, String> METHOD_MODIFIERS;
static {
Map, String> typeModifiers = new LinkedHashMap<>();
typeModifiers.put(Modifier::isPublic, "public");
typeModifiers.put(Modifier::isProtected, "protected");
typeModifiers.put(Modifier::isPrivate, "private");
typeModifiers.put(Modifier::isAbstract, "abstract");
typeModifiers.put(Modifier::isStatic, "static");
typeModifiers.put(Modifier::isFinal, "final");
typeModifiers.put(Modifier::isStrict, "strictfp");
TYPE_MODIFIERS = typeModifiers;
Map, String> fieldModifiers = new LinkedHashMap<>();
fieldModifiers.put(Modifier::isPublic, "public");
fieldModifiers.put(Modifier::isProtected, "protected");
fieldModifiers.put(Modifier::isPrivate, "private");
fieldModifiers.put(Modifier::isStatic, "static");
fieldModifiers.put(Modifier::isFinal, "final");
fieldModifiers.put(Modifier::isTransient, "transient");
fieldModifiers.put(Modifier::isVolatile, "volatile");
FIELD_MODIFIERS = fieldModifiers;
Map, String> methodModifiers = new LinkedHashMap<>(typeModifiers);
methodModifiers.put(Modifier::isSynchronized, "synchronized");
methodModifiers.put(Modifier::isNative, "native");
METHOD_MODIFIERS = methodModifiers;
}
private final IndentingWriterFactory indentingWriterFactory;
public JavaSourceCodeWriter(IndentingWriterFactory indentingWriterFactory) {
this.indentingWriterFactory = indentingWriterFactory;
}
@Override
public void writeTo(SourceStructure structure, JavaSourceCode sourceCode) throws IOException {
for (JavaCompilationUnit compilationUnit : sourceCode.getCompilationUnits()) {
writeTo(structure, compilationUnit);
}
}
private void writeTo(SourceStructure structure, JavaCompilationUnit compilationUnit) throws IOException {
Path output = structure.createSourceFile(compilationUnit.getPackageName(), compilationUnit.getName());
Files.createDirectories(output.getParent());
try (IndentingWriter writer = this.indentingWriterFactory.createIndentingWriter("java",
Files.newBufferedWriter(output))) {
writer.println("package " + compilationUnit.getPackageName() + ";");
writer.println();
Set imports = determineImports(compilationUnit);
if (!imports.isEmpty()) {
for (String importedType : imports) {
writer.println("import " + importedType + ";");
}
writer.println();
}
for (JavaTypeDeclaration type : compilationUnit.getTypeDeclarations()) {
writeAnnotations(writer, type);
writeModifiers(writer, TYPE_MODIFIERS, type.getModifiers());
writer.print("class " + type.getName());
if (type.getExtends() != null) {
writer.print(" extends " + getUnqualifiedName(type.getExtends()));
}
writer.println(" {");
writer.println();
List fieldDeclarations = type.getFieldDeclarations();
if (!fieldDeclarations.isEmpty()) {
writer.indented(() -> {
for (JavaFieldDeclaration fieldDeclaration : fieldDeclarations) {
writeFieldDeclaration(writer, fieldDeclaration);
}
});
}
List methodDeclarations = type.getMethodDeclarations();
if (!methodDeclarations.isEmpty()) {
writer.indented(() -> {
for (JavaMethodDeclaration methodDeclaration : methodDeclarations) {
writeMethodDeclaration(writer, methodDeclaration);
}
});
}
writer.println("}");
}
}
}
private void writeAnnotations(IndentingWriter writer, Annotatable annotatable) {
annotatable.getAnnotations().forEach((annotation) -> writeAnnotation(writer, annotation));
}
private void writeAnnotation(IndentingWriter writer, Annotation annotation) {
writer.print("@" + getUnqualifiedName(annotation.getName()));
List attributes = annotation.getAttributes();
if (!attributes.isEmpty()) {
writer.print("(");
if (attributes.size() == 1 && attributes.get(0).getName().equals("value")) {
writer.print(formatAnnotationAttribute(attributes.get(0)));
}
else {
writer.print(attributes.stream()
.map((attribute) -> attribute.getName() + " = " + formatAnnotationAttribute(attribute))
.collect(Collectors.joining(", ")));
}
writer.print(")");
}
writer.println();
}
private String formatAnnotationAttribute(Annotation.Attribute attribute) {
List values = attribute.getValues();
if (attribute.getType().equals(Class.class)) {
return formatValues(values, (value) -> String.format("%s.class", getUnqualifiedName(value)));
}
if (Enum.class.isAssignableFrom(attribute.getType())) {
return formatValues(values, (value) -> {
String enumValue = value.substring(value.lastIndexOf(".") + 1);
String enumClass = value.substring(0, value.lastIndexOf("."));
return String.format("%s.%s", getUnqualifiedName(enumClass), enumValue);
});
}
if (attribute.getType().equals(String.class)) {
return formatValues(values, (value) -> String.format("\"%s\"", value));
}
return formatValues(values, (value) -> String.format("%s", value));
}
private String formatValues(List values, Function formatter) {
String result = values.stream().map(formatter).collect(Collectors.joining(", "));
return (values.size() > 1) ? "{ " + result + " }" : result;
}
private void writeFieldDeclaration(IndentingWriter writer, JavaFieldDeclaration fieldDeclaration) {
writeAnnotations(writer, fieldDeclaration);
writeModifiers(writer, FIELD_MODIFIERS, fieldDeclaration.getModifiers());
writer.print(getUnqualifiedName(fieldDeclaration.getReturnType()));
writer.print(" ");
writer.print(fieldDeclaration.getName());
if (fieldDeclaration.isInitialized()) {
writer.print(" = ");
writer.print(String.valueOf(fieldDeclaration.getValue()));
}
writer.println(";");
writer.println();
}
private void writeMethodDeclaration(IndentingWriter writer, JavaMethodDeclaration methodDeclaration) {
writeAnnotations(writer, methodDeclaration);
writeModifiers(writer, METHOD_MODIFIERS, methodDeclaration.getModifiers());
writer.print(getUnqualifiedName(methodDeclaration.getReturnType()) + " " + methodDeclaration.getName() + "(");
List parameters = methodDeclaration.getParameters();
if (!parameters.isEmpty()) {
writer.print(parameters.stream()
.map((parameter) -> getUnqualifiedName(parameter.getType()) + " " + parameter.getName())
.collect(Collectors.joining(", ")));
}
writer.println(") {");
writer.indented(() -> {
List statements = methodDeclaration.getStatements();
for (JavaStatement statement : statements) {
if (statement instanceof JavaExpressionStatement) {
writeExpression(writer, ((JavaExpressionStatement) statement).getExpression());
}
else if (statement instanceof JavaReturnStatement) {
writer.print("return ");
writeExpression(writer, ((JavaReturnStatement) statement).getExpression());
}
writer.println(";");
}
});
writer.println("}");
writer.println();
}
private void writeModifiers(IndentingWriter writer, Map, String> availableModifiers,
int declaredModifiers) {
String modifiers = availableModifiers.entrySet().stream()
.filter((entry) -> entry.getKey().test(declaredModifiers)).map(Entry::getValue)
.collect(Collectors.joining(" "));
if (!modifiers.isEmpty()) {
writer.print(modifiers);
writer.print(" ");
}
}
private void writeExpression(IndentingWriter writer, JavaExpression expression) {
if (expression instanceof JavaMethodInvocation) {
writeMethodInvocation(writer, (JavaMethodInvocation) expression);
}
}
private void writeMethodInvocation(IndentingWriter writer, JavaMethodInvocation methodInvocation) {
writer.print(getUnqualifiedName(methodInvocation.getTarget()) + "." + methodInvocation.getName() + "("
+ String.join(", ", methodInvocation.getArguments()) + ")");
}
private Set determineImports(JavaCompilationUnit compilationUnit) {
List imports = new ArrayList<>();
for (JavaTypeDeclaration typeDeclaration : compilationUnit.getTypeDeclarations()) {
if (requiresImport(typeDeclaration.getExtends())) {
imports.add(typeDeclaration.getExtends());
}
imports.addAll(getRequiredImports(typeDeclaration.getAnnotations(), this::determineImports));
for (JavaFieldDeclaration fieldDeclaration : typeDeclaration.getFieldDeclarations()) {
if (requiresImport(fieldDeclaration.getReturnType())) {
imports.add(fieldDeclaration.getReturnType());
}
imports.addAll(getRequiredImports(fieldDeclaration.getAnnotations(), this::determineImports));
}
for (JavaMethodDeclaration methodDeclaration : typeDeclaration.getMethodDeclarations()) {
if (requiresImport(methodDeclaration.getReturnType())) {
imports.add(methodDeclaration.getReturnType());
}
imports.addAll(getRequiredImports(methodDeclaration.getAnnotations(), this::determineImports));
imports.addAll(getRequiredImports(methodDeclaration.getParameters(),
(parameter) -> Collections.singletonList(parameter.getType())));
imports.addAll(getRequiredImports(
methodDeclaration.getStatements().stream().filter(JavaExpressionStatement.class::isInstance)
.map(JavaExpressionStatement.class::cast).map(JavaExpressionStatement::getExpression)
.filter(JavaMethodInvocation.class::isInstance).map(JavaMethodInvocation.class::cast),
(methodInvocation) -> Collections.singleton(methodInvocation.getTarget())));
}
}
Collections.sort(imports);
return new LinkedHashSet<>(imports);
}
private Collection determineImports(Annotation annotation) {
List imports = new ArrayList<>();
imports.add(annotation.getName());
annotation.getAttributes().forEach((attribute) -> {
if (attribute.getType() == Class.class) {
imports.addAll(attribute.getValues());
}
if (Enum.class.isAssignableFrom(attribute.getType())) {
imports.addAll(attribute.getValues().stream().map((value) -> value.substring(0, value.lastIndexOf(".")))
.collect(Collectors.toList()));
}
});
return imports;
}
private List getRequiredImports(List candidates, Function> mapping) {
return getRequiredImports(candidates.stream(), mapping);
}
private List getRequiredImports(Stream candidates, Function> mapping) {
return candidates.map(mapping).flatMap(Collection::stream).filter(this::requiresImport)
.collect(Collectors.toList());
}
private String getUnqualifiedName(String name) {
if (!name.contains(".")) {
return name;
}
return name.substring(name.lastIndexOf(".") + 1);
}
private boolean requiresImport(String name) {
if (name == null || !name.contains(".")) {
return false;
}
String packageName = name.substring(0, name.lastIndexOf('.'));
return !"java.lang".equals(packageName);
}
}