mirror of
https://gitee.com/dcren/initializr.git
synced 2025-09-19 18:22:26 +08:00
Add Kotlin language support
This commit provides a Kotlin language implementation with a writer that can generate a `.kt` source file based on a configurable model. See gh-813
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.language.CompilationUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Kotlin-specific {@link CompilationUnit}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinCompilationUnit extends CompilationUnit<KotlinTypeDeclaration> {
|
||||||
|
|
||||||
|
private final List<KotlinFunctionDeclaration> topLevelFunctions = new ArrayList<>();
|
||||||
|
|
||||||
|
KotlinCompilationUnit(String packageName, String name) {
|
||||||
|
super(packageName, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KotlinTypeDeclaration doCreateTypeDeclaration(String name) {
|
||||||
|
return new KotlinTypeDeclaration(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTopLevelFunction(KotlinFunctionDeclaration function) {
|
||||||
|
this.topLevelFunctions.add(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KotlinFunctionDeclaration> getTopLevelFunctions() {
|
||||||
|
return this.topLevelFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Kotlin expression.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinExpression {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A statement that contains a single expression.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinExpressionStatement extends KotlinStatement {
|
||||||
|
|
||||||
|
private final KotlinExpression expression;
|
||||||
|
|
||||||
|
public KotlinExpressionStatement(KotlinExpression expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KotlinExpression getExpression() {
|
||||||
|
return this.expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.language.Annotatable;
|
||||||
|
import io.spring.initializr.generator.language.Annotation;
|
||||||
|
import io.spring.initializr.generator.language.Parameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declaration of a function written in Kotlin.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public final class KotlinFunctionDeclaration implements Annotatable {
|
||||||
|
|
||||||
|
private final List<Annotation> annotations = new ArrayList<>();
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String returnType;
|
||||||
|
|
||||||
|
private final List<KotlinModifier> modifiers;
|
||||||
|
|
||||||
|
private final List<Parameter> parameters;
|
||||||
|
|
||||||
|
private final List<KotlinStatement> statements;
|
||||||
|
|
||||||
|
private KotlinFunctionDeclaration(String name, String returnType,
|
||||||
|
List<KotlinModifier> modifiers, List<Parameter> parameters,
|
||||||
|
List<KotlinStatement> statements) {
|
||||||
|
this.name = name;
|
||||||
|
this.returnType = returnType;
|
||||||
|
this.modifiers = modifiers;
|
||||||
|
this.parameters = parameters;
|
||||||
|
this.statements = statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder function(String name) {
|
||||||
|
return new Builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getReturnType() {
|
||||||
|
return this.returnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Parameter> getParameters() {
|
||||||
|
return this.parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<KotlinModifier> getModifiers() {
|
||||||
|
return this.modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KotlinStatement> getStatements() {
|
||||||
|
return this.statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void annotate(Annotation annotation) {
|
||||||
|
this.annotations.add(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Annotation> getAnnotations() {
|
||||||
|
return Collections.unmodifiableList(this.annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for creating a {@link KotlinFunctionDeclaration}.
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private List<Parameter> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<KotlinModifier> modifiers = new ArrayList<>();
|
||||||
|
|
||||||
|
private String returnType;
|
||||||
|
|
||||||
|
private Builder(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder modifiers(KotlinModifier... modifiers) {
|
||||||
|
this.modifiers = Arrays.asList(modifiers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder returning(String returnType) {
|
||||||
|
this.returnType = returnType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder parameters(Parameter... parameters) {
|
||||||
|
this.parameters = Arrays.asList(parameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KotlinFunctionDeclaration body(KotlinStatement... statements) {
|
||||||
|
return new KotlinFunctionDeclaration(this.name, this.returnType,
|
||||||
|
this.modifiers, this.parameters, Arrays.asList(statements));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An invocation of a function.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinFunctionInvocation extends KotlinExpression {
|
||||||
|
|
||||||
|
private final String target;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final List<String> arguments;
|
||||||
|
|
||||||
|
public KotlinFunctionInvocation(String target, String name, String... arguments) {
|
||||||
|
this.target = target;
|
||||||
|
this.name = name;
|
||||||
|
this.arguments = Arrays.asList(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getArguments() {
|
||||||
|
return this.arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.language.AbstractLanguage;
|
||||||
|
import io.spring.initializr.generator.language.Language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kotlin {@link Language}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public final class KotlinLanguage extends AbstractLanguage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kotlin {@link Language} identifier.
|
||||||
|
*/
|
||||||
|
public static final String ID = "kotlin";
|
||||||
|
|
||||||
|
public KotlinLanguage() {
|
||||||
|
this(DEFAULT_JVM_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KotlinLanguage(String jvmVersion) {
|
||||||
|
super(ID, jvmVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.language.Language;
|
||||||
|
import io.spring.initializr.generator.language.LanguageFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LanguageFactory} for Kotlin.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class KotlinLanguageFactory implements LanguageFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Language createLanguage(String id, String jvmVersion) {
|
||||||
|
if (KotlinLanguage.ID.equals(id)) {
|
||||||
|
return new KotlinLanguage(jvmVersion);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic modifiers for Kotlin.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public enum KotlinModifier {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible to anyone.
|
||||||
|
*/
|
||||||
|
PUBLIC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible inside that class and any subclass.
|
||||||
|
*/
|
||||||
|
PROTECTED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible inside this class only.
|
||||||
|
*/
|
||||||
|
PRIVATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final modifier.
|
||||||
|
*/
|
||||||
|
FINAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow to override a member.
|
||||||
|
*/
|
||||||
|
OPEN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare a member without an implementation (infers open).
|
||||||
|
*/
|
||||||
|
ABSTRACT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override a member.
|
||||||
|
*/
|
||||||
|
OVERRIDE
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invocation of a function with a reified type parameter.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinReifiedFunctionInvocation extends KotlinExpression {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String targetClass;
|
||||||
|
|
||||||
|
private final List<String> arguments;
|
||||||
|
|
||||||
|
public KotlinReifiedFunctionInvocation(String name, String targetClass,
|
||||||
|
String... arguments) {
|
||||||
|
this.name = name;
|
||||||
|
this.targetClass = targetClass;
|
||||||
|
this.arguments = Arrays.asList(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetClass() {
|
||||||
|
return this.targetClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getArguments() {
|
||||||
|
return this.arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A return statement.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class KotlinReturnStatement extends KotlinStatement {
|
||||||
|
|
||||||
|
private final KotlinExpression expression;
|
||||||
|
|
||||||
|
public KotlinReturnStatement(KotlinExpression expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KotlinExpression getExpression() {
|
||||||
|
return this.expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.language.SourceCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kotlin {@link SourceCode}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinSourceCode
|
||||||
|
extends SourceCode<KotlinTypeDeclaration, KotlinCompilationUnit> {
|
||||||
|
|
||||||
|
public KotlinSourceCode() {
|
||||||
|
super(KotlinCompilationUnit::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,337 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
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.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SourceCodeWriter} that writes {@link SourceCode} in Kotlin.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinSourceCodeWriter implements SourceCodeWriter<KotlinSourceCode> {
|
||||||
|
|
||||||
|
private final IndentingWriterFactory indentingWriterFactory;
|
||||||
|
|
||||||
|
public KotlinSourceCodeWriter(IndentingWriterFactory indentingWriterFactory) {
|
||||||
|
this.indentingWriterFactory = indentingWriterFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(Path directory, KotlinSourceCode sourceCode) throws IOException {
|
||||||
|
if (!Files.exists(directory)) {
|
||||||
|
Files.createDirectories(directory);
|
||||||
|
}
|
||||||
|
for (KotlinCompilationUnit compilationUnit : sourceCode.getCompilationUnits()) {
|
||||||
|
writeTo(directory, compilationUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTo(Path directory, KotlinCompilationUnit compilationUnit)
|
||||||
|
throws IOException {
|
||||||
|
Path output = fileForCompilationUnit(directory, compilationUnit);
|
||||||
|
Files.createDirectories(output.getParent());
|
||||||
|
try (IndentingWriter writer = this.indentingWriterFactory
|
||||||
|
.createIndentingWriter("kotlin", Files.newBufferedWriter(output))) {
|
||||||
|
writer.println("package " + compilationUnit.getPackageName());
|
||||||
|
writer.println();
|
||||||
|
Set<String> imports = determineImports(compilationUnit);
|
||||||
|
if (!imports.isEmpty()) {
|
||||||
|
for (String importedType : imports) {
|
||||||
|
writer.println("import " + importedType);
|
||||||
|
}
|
||||||
|
writer.println();
|
||||||
|
}
|
||||||
|
for (KotlinTypeDeclaration type : compilationUnit.getTypeDeclarations()) {
|
||||||
|
writeAnnotations(writer, type);
|
||||||
|
writer.print("class " + type.getName());
|
||||||
|
if (type.getExtends() != null) {
|
||||||
|
writer.print(" : " + getUnqualifiedName(type.getExtends()) + "()");
|
||||||
|
}
|
||||||
|
List<KotlinFunctionDeclaration> functionDeclarations = type
|
||||||
|
.getFunctionDeclarations();
|
||||||
|
if (!functionDeclarations.isEmpty()) {
|
||||||
|
writer.println(" {");
|
||||||
|
writer.println();
|
||||||
|
writer.indented(() -> {
|
||||||
|
for (KotlinFunctionDeclaration functionDeclaration : functionDeclarations) {
|
||||||
|
writeFunction(writer, functionDeclaration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
writer.println("}");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
writer.println("");
|
||||||
|
}
|
||||||
|
writer.println("");
|
||||||
|
}
|
||||||
|
List<KotlinFunctionDeclaration> topLevelFunctions = compilationUnit
|
||||||
|
.getTopLevelFunctions();
|
||||||
|
if (!topLevelFunctions.isEmpty()) {
|
||||||
|
for (KotlinFunctionDeclaration topLevelFunction : topLevelFunctions) {
|
||||||
|
writeFunction(writer, topLevelFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFunction(IndentingWriter writer,
|
||||||
|
KotlinFunctionDeclaration functionDeclaration) {
|
||||||
|
writeAnnotations(writer, functionDeclaration);
|
||||||
|
writeMethodModifiers(writer, functionDeclaration);
|
||||||
|
writer.print("fun ");
|
||||||
|
writer.print(functionDeclaration.getName() + "(");
|
||||||
|
List<Parameter> parameters = functionDeclaration.getParameters();
|
||||||
|
if (!parameters.isEmpty()) {
|
||||||
|
writer.print(parameters.stream()
|
||||||
|
.map((parameter) -> parameter.getName() + ": "
|
||||||
|
+ getUnqualifiedName(parameter.getType()))
|
||||||
|
.collect(Collectors.joining(", ")));
|
||||||
|
}
|
||||||
|
writer.print(")");
|
||||||
|
if (functionDeclaration.getReturnType() != null) {
|
||||||
|
writer.print(": " + getUnqualifiedName(functionDeclaration.getReturnType()));
|
||||||
|
}
|
||||||
|
writer.println(" {");
|
||||||
|
List<KotlinStatement> statements = functionDeclaration.getStatements();
|
||||||
|
writer.indented(() -> {
|
||||||
|
for (KotlinStatement statement : statements) {
|
||||||
|
if (statement instanceof KotlinExpressionStatement) {
|
||||||
|
writeExpression(writer,
|
||||||
|
((KotlinExpressionStatement) statement).getExpression());
|
||||||
|
}
|
||||||
|
else if (statement instanceof KotlinReturnStatement) {
|
||||||
|
writer.print("return ");
|
||||||
|
writeExpression(writer,
|
||||||
|
((KotlinReturnStatement) statement).getExpression());
|
||||||
|
}
|
||||||
|
writer.println("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
writer.println("}");
|
||||||
|
writer.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeAnnotations(IndentingWriter writer, Annotatable annotatable) {
|
||||||
|
for (Annotation annotation : annotatable.getAnnotations()) {
|
||||||
|
writeAnnotation(writer, annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeAnnotation(IndentingWriter writer, Annotation annotation) {
|
||||||
|
writer.print("@" + getUnqualifiedName(annotation.getName()));
|
||||||
|
List<Annotation.Attribute> 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<String> 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<String> values, Function<String, String> formatter) {
|
||||||
|
String result = values.stream().map(formatter).collect(Collectors.joining(", "));
|
||||||
|
return (values.size() > 1) ? "[" + result + "]" : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeMethodModifiers(IndentingWriter writer,
|
||||||
|
KotlinFunctionDeclaration functionDeclaration) {
|
||||||
|
String modifiers = functionDeclaration.getModifiers().stream()
|
||||||
|
.filter((entry) -> !entry.equals(KotlinModifier.PUBLIC)).sorted()
|
||||||
|
.map((entry) -> entry.toString().toLowerCase(Locale.ENGLISH))
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
if (!modifiers.isEmpty()) {
|
||||||
|
writer.print(modifiers);
|
||||||
|
writer.print(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeExpression(IndentingWriter writer, KotlinExpression expression) {
|
||||||
|
if (expression instanceof KotlinFunctionInvocation) {
|
||||||
|
writeFunctionInvocation(writer, (KotlinFunctionInvocation) expression);
|
||||||
|
}
|
||||||
|
else if (expression instanceof KotlinReifiedFunctionInvocation) {
|
||||||
|
writeReifiedFunctionInvocation(writer,
|
||||||
|
(KotlinReifiedFunctionInvocation) expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFunctionInvocation(IndentingWriter writer,
|
||||||
|
KotlinFunctionInvocation functionInvocation) {
|
||||||
|
writer.print(getUnqualifiedName(functionInvocation.getTarget()) + "."
|
||||||
|
+ functionInvocation.getName() + "("
|
||||||
|
+ String.join(", ", functionInvocation.getArguments()) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeReifiedFunctionInvocation(IndentingWriter writer,
|
||||||
|
KotlinReifiedFunctionInvocation functionInvocation) {
|
||||||
|
writer.print(getUnqualifiedName(functionInvocation.getName()) + "<"
|
||||||
|
+ getUnqualifiedName(functionInvocation.getTargetClass()) + ">("
|
||||||
|
+ String.join(", ", functionInvocation.getArguments()) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path fileForCompilationUnit(Path directory,
|
||||||
|
KotlinCompilationUnit compilationUnit) {
|
||||||
|
return directoryForPackage(directory, compilationUnit.getPackageName())
|
||||||
|
.resolve(compilationUnit.getName() + ".kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path directoryForPackage(Path directory, String packageName) {
|
||||||
|
return directory.resolve(packageName.replace('.', '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> determineImports(KotlinCompilationUnit compilationUnit) {
|
||||||
|
List<String> imports = new ArrayList<>();
|
||||||
|
for (KotlinTypeDeclaration typeDeclaration : compilationUnit
|
||||||
|
.getTypeDeclarations()) {
|
||||||
|
if (requiresImport(typeDeclaration.getExtends())) {
|
||||||
|
imports.add(typeDeclaration.getExtends());
|
||||||
|
}
|
||||||
|
imports.addAll(getRequiredImports(typeDeclaration.getAnnotations(),
|
||||||
|
this::determineImports));
|
||||||
|
typeDeclaration.getFunctionDeclarations()
|
||||||
|
.forEach((functionDeclaration) -> imports
|
||||||
|
.addAll(determineFunctionImports(functionDeclaration)));
|
||||||
|
}
|
||||||
|
compilationUnit.getTopLevelFunctions().forEach((functionDeclaration) -> imports
|
||||||
|
.addAll(determineFunctionImports(functionDeclaration)));
|
||||||
|
Collections.sort(imports);
|
||||||
|
return new LinkedHashSet<>(imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> determineFunctionImports(
|
||||||
|
KotlinFunctionDeclaration functionDeclaration) {
|
||||||
|
Set<String> imports = new LinkedHashSet<>();
|
||||||
|
if (requiresImport(functionDeclaration.getReturnType())) {
|
||||||
|
imports.add(functionDeclaration.getReturnType());
|
||||||
|
}
|
||||||
|
imports.addAll(getRequiredImports(functionDeclaration.getAnnotations(),
|
||||||
|
this::determineImports));
|
||||||
|
imports.addAll(getRequiredImports(functionDeclaration.getParameters(),
|
||||||
|
(parameter) -> Collections.singleton(parameter.getType())));
|
||||||
|
imports.addAll(getRequiredImports(
|
||||||
|
getKotlinExpressions(functionDeclaration)
|
||||||
|
.filter(KotlinFunctionInvocation.class::isInstance)
|
||||||
|
.map(KotlinFunctionInvocation.class::cast),
|
||||||
|
(invocation) -> Collections.singleton(invocation.getTarget())));
|
||||||
|
imports.addAll(getRequiredImports(
|
||||||
|
getKotlinExpressions(functionDeclaration)
|
||||||
|
.filter(KotlinReifiedFunctionInvocation.class::isInstance)
|
||||||
|
.map(KotlinReifiedFunctionInvocation.class::cast),
|
||||||
|
(invocation) -> Collections.singleton(invocation.getName())));
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<String> determineImports(Annotation annotation) {
|
||||||
|
List<String> 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 Stream<KotlinExpression> getKotlinExpressions(
|
||||||
|
KotlinFunctionDeclaration functionDeclaration) {
|
||||||
|
return functionDeclaration.getStatements().stream()
|
||||||
|
.filter(KotlinExpressionStatement.class::isInstance)
|
||||||
|
.map(KotlinExpressionStatement.class::cast)
|
||||||
|
.map(KotlinExpressionStatement::getExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> List<String> getRequiredImports(List<T> candidates,
|
||||||
|
Function<T, Collection<String>> mapping) {
|
||||||
|
return getRequiredImports(candidates.stream(), mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> List<String> getRequiredImports(Stream<T> candidates,
|
||||||
|
Function<T, Collection<String>> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A statement in Kotlin.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinStatement {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.language.TypeDeclaration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TypeDeclaration declaration } of a type written in Kotlin.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class KotlinTypeDeclaration extends TypeDeclaration {
|
||||||
|
|
||||||
|
private final List<KotlinFunctionDeclaration> functionDeclarations = new ArrayList<>();
|
||||||
|
|
||||||
|
KotlinTypeDeclaration(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFunctionDeclaration(KotlinFunctionDeclaration methodDeclaration) {
|
||||||
|
this.functionDeclarations.add(methodDeclaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KotlinFunctionDeclaration> getFunctionDeclarations() {
|
||||||
|
return this.functionDeclarations;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kotlin language. Provides a
|
||||||
|
* {@link io.spring.initializr.generator.language.kotlin.KotlinCompilationUnit compilation
|
||||||
|
* unit} implementation and a write for Kotlin
|
||||||
|
* {@link io.spring.initializr.generator.language.kotlin.KotlinSourceCode source code}.
|
||||||
|
*/
|
||||||
|
package io.spring.initializr.generator.language.kotlin;
|
@@ -1,2 +1,3 @@
|
|||||||
io.spring.initializr.generator.language.LanguageFactory=\
|
io.spring.initializr.generator.language.LanguageFactory=\
|
||||||
io.spring.initializr.generator.language.java.JavaLanguageFactory
|
io.spring.initializr.generator.language.java.JavaLanguageFactory,\
|
||||||
|
io.spring.initializr.generator.language.kotlin.KotlinLanguageFactory
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package io.spring.initializr.generator.language;
|
package io.spring.initializr.generator.language;
|
||||||
|
|
||||||
import io.spring.initializr.generator.language.java.JavaLanguage;
|
import io.spring.initializr.generator.language.java.JavaLanguage;
|
||||||
|
import io.spring.initializr.generator.language.kotlin.KotlinLanguage;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -38,6 +39,15 @@ class LanguageTests {
|
|||||||
assertThat(java.jvmVersion()).isEqualTo("11");
|
assertThat(java.jvmVersion()).isEqualTo("11");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void kotlinLanguage() {
|
||||||
|
Language kotlin = Language.forId("kotlin", null);
|
||||||
|
assertThat(kotlin).isInstanceOf(KotlinLanguage.class);
|
||||||
|
assertThat(kotlin.id()).isEqualTo("kotlin");
|
||||||
|
assertThat(kotlin.toString()).isEqualTo("kotlin");
|
||||||
|
assertThat(kotlin.jvmVersion()).isEqualTo("1.8");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unknownLanguage() {
|
void unknownLanguage() {
|
||||||
assertThatIllegalStateException()
|
assertThatIllegalStateException()
|
||||||
|
@@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 io.spring.initializr.generator.language.kotlin;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.io.IndentingWriterFactory;
|
||||||
|
import io.spring.initializr.generator.language.Annotation;
|
||||||
|
import io.spring.initializr.generator.language.Parameter;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link KotlinSourceCodeWriter}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class KotlinSourceCodeWriterTests {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
Path directory;
|
||||||
|
|
||||||
|
private final KotlinSourceCodeWriter writer = new KotlinSourceCodeWriter(
|
||||||
|
IndentingWriterFactory.withDefaultSettings());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyCompilationUnit() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
sourceCode.createCompilationUnit("com.example", "Test");
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyTypeDeclaration() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
compilationUnit.createTypeDeclaration("Test");
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "", "class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyTypeDeclarationWithSuperClass() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
KotlinTypeDeclaration test = compilationUnit.createTypeDeclaration("Test");
|
||||||
|
test.extend("com.example.build.TestParent");
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import com.example.build.TestParent", "", "class Test : TestParent()",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void function() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
KotlinTypeDeclaration test = compilationUnit.createTypeDeclaration("Test");
|
||||||
|
test.addFunctionDeclaration(KotlinFunctionDeclaration.function("reverse")
|
||||||
|
.returning("java.lang.String")
|
||||||
|
.parameters(new Parameter("java.lang.String", "echo"))
|
||||||
|
.body(new KotlinReturnStatement(
|
||||||
|
new KotlinFunctionInvocation("echo", "reversed"))));
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "", "class Test {", "",
|
||||||
|
" fun reverse(echo: String): String {",
|
||||||
|
" return echo.reversed()", " }", "", "}", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void functionModifiers() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
KotlinTypeDeclaration test = compilationUnit.createTypeDeclaration("Test");
|
||||||
|
test.addFunctionDeclaration(KotlinFunctionDeclaration.function("toString")
|
||||||
|
.modifiers(KotlinModifier.OVERRIDE, KotlinModifier.PUBLIC,
|
||||||
|
KotlinModifier.OPEN)
|
||||||
|
.returning("java.lang.String").body(new KotlinReturnStatement(
|
||||||
|
new KotlinFunctionInvocation("super", "toString"))));
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "", "class Test {", "",
|
||||||
|
" open override fun toString(): String {",
|
||||||
|
" return super.toString()", " }", "", "}", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void springBootApplication() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
KotlinTypeDeclaration test = compilationUnit.createTypeDeclaration("Test");
|
||||||
|
test.annotate(Annotation
|
||||||
|
.name("org.springframework.boot.autoconfigure.SpringBootApplication"));
|
||||||
|
compilationUnit.addTopLevelFunction(KotlinFunctionDeclaration.function("main")
|
||||||
|
.parameters(new Parameter("Array<String>", "args"))
|
||||||
|
.body(new KotlinExpressionStatement(new KotlinReifiedFunctionInvocation(
|
||||||
|
"org.springframework.boot.runApplication", "Test", "*args"))));
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import org.springframework.boot.autoconfigure.SpringBootApplication",
|
||||||
|
"import org.springframework.boot.runApplication", "",
|
||||||
|
"@SpringBootApplication", "class Test", "",
|
||||||
|
"fun main(args: Array<String>) {", " runApplication<Test>(*args)", "}",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void annotationWithSimpleAttribute() throws IOException {
|
||||||
|
List<String> lines = writeClassAnnotation(
|
||||||
|
Annotation.name("org.springframework.test.TestApplication",
|
||||||
|
(builder) -> builder.attribute("counter", Integer.class, "42")));
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import org.springframework.test.TestApplication", "",
|
||||||
|
"@TestApplication(counter = 42)", "class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void annotationWithSimpleStringAttribute() throws IOException {
|
||||||
|
List<String> lines = writeClassAnnotation(
|
||||||
|
Annotation.name("org.springframework.test.TestApplication",
|
||||||
|
(builder) -> builder.attribute("name", String.class, "test")));
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import org.springframework.test.TestApplication", "",
|
||||||
|
"@TestApplication(name = \"test\")", "class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void annotationWithOnlyValueAttribute() throws IOException {
|
||||||
|
List<String> lines = writeClassAnnotation(
|
||||||
|
Annotation.name("org.springframework.test.TestApplication",
|
||||||
|
(builder) -> builder.attribute("value", String.class, "test")));
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import org.springframework.test.TestApplication", "",
|
||||||
|
"@TestApplication(\"test\")", "class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void annotationWithSimpleEnumAttribute() throws IOException {
|
||||||
|
List<String> lines = writeClassAnnotation(
|
||||||
|
Annotation.name("org.springframework.test.TestApplication",
|
||||||
|
(builder) -> builder.attribute("unit", Enum.class,
|
||||||
|
"java.time.temporal.ChronoUnit.SECONDS")));
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import java.time.temporal.ChronoUnit",
|
||||||
|
"import org.springframework.test.TestApplication", "",
|
||||||
|
"@TestApplication(unit = ChronoUnit.SECONDS)", "class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void annotationWithClassArrayAttribute() throws IOException {
|
||||||
|
List<String> lines = writeClassAnnotation(
|
||||||
|
Annotation.name("org.springframework.test.TestApplication",
|
||||||
|
(builder) -> builder.attribute("target", Class.class,
|
||||||
|
"com.example.One", "com.example.Two")));
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import com.example.One", "import com.example.Two",
|
||||||
|
"import org.springframework.test.TestApplication", "",
|
||||||
|
"@TestApplication(target = [One::class, Two::class])", "class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void annotationWithSeveralAttributes() throws IOException {
|
||||||
|
List<String> lines = writeClassAnnotation(Annotation.name(
|
||||||
|
"org.springframework.test.TestApplication",
|
||||||
|
(builder) -> builder.attribute("target", Class.class, "com.example.One")
|
||||||
|
.attribute("unit", ChronoUnit.class,
|
||||||
|
"java.time.temporal.ChronoUnit.NANOS")));
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import com.example.One", "import java.time.temporal.ChronoUnit",
|
||||||
|
"import org.springframework.test.TestApplication", "",
|
||||||
|
"@TestApplication(target = One::class, unit = ChronoUnit.NANOS)",
|
||||||
|
"class Test", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> writeClassAnnotation(Annotation annotation) throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
KotlinTypeDeclaration test = compilationUnit.createTypeDeclaration("Test");
|
||||||
|
test.annotate(annotation);
|
||||||
|
return writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void functionWithSimpleAnnotation() throws IOException {
|
||||||
|
KotlinSourceCode sourceCode = new KotlinSourceCode();
|
||||||
|
KotlinCompilationUnit compilationUnit = sourceCode
|
||||||
|
.createCompilationUnit("com.example", "Test");
|
||||||
|
KotlinTypeDeclaration test = compilationUnit.createTypeDeclaration("Test");
|
||||||
|
KotlinFunctionDeclaration function = KotlinFunctionDeclaration
|
||||||
|
.function("something").body();
|
||||||
|
function.annotate(Annotation.name("com.example.test.TestAnnotation"));
|
||||||
|
test.addFunctionDeclaration(function);
|
||||||
|
List<String> lines = writeSingleType(sourceCode, "com/example/Test.kt");
|
||||||
|
assertThat(lines).containsExactly("package com.example", "",
|
||||||
|
"import com.example.test.TestAnnotation", "", "class Test {", "",
|
||||||
|
" @TestAnnotation", " fun something() {", " }", "", "}", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> writeSingleType(KotlinSourceCode sourceCode, String location)
|
||||||
|
throws IOException {
|
||||||
|
Path source = writeSourceCode(sourceCode).resolve(location);
|
||||||
|
assertThat(source).isRegularFile();
|
||||||
|
return Files.readAllLines(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path writeSourceCode(KotlinSourceCode sourceCode) throws IOException {
|
||||||
|
Path projectDirectory = Files.createTempDirectory(this.directory, "project-");
|
||||||
|
this.writer.writeTo(projectDirectory, sourceCode);
|
||||||
|
return projectDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user