Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e2e53f2
Convert 'UTSettings.kt' into '.utbot/settings.properties'
Vassiliy-Kudryashov Nov 17, 2022
0fc356d
Parsing UtSettings.kt line by line to get name, type, possible values…
Vassiliy-Kudryashov Nov 17, 2022
e397cda
Misprint
Vassiliy-Kudryashov Nov 17, 2022
bfda1de
Grade task "generateConfigTemplate"
Vassiliy-Kudryashov Nov 18, 2022
5e567df
Add TODOs for myself
Vassiliy-Kudryashov Nov 18, 2022
e5b879c
Implementation of added TODOs
Vassiliy-Kudryashov Nov 20, 2022
8a39fc2
Compact one-line documentation in output file for enum values
Vassiliy-Kudryashov Nov 21, 2022
02cc183
Add Apache 2.0 license to settings.properties output and tune comment…
Vassiliy-Kudryashov Nov 22, 2022
b2a4581
Add generated template to keep is in repository as well
Vassiliy-Kudryashov Nov 22, 2022
52c60b7
Add template generation to gradle build process
Vassiliy-Kudryashov Nov 23, 2022
bc40dbf
Get rid of unused property timeslotForOneToplevelMethodTraversalMs (W…
Vassiliy-Kudryashov Nov 24, 2022
8ab1578
Add AbstractSettings.areCustomized() ability to tell template from us…
Vassiliy-Kudryashov Nov 24, 2022
7cc19c0
Fix misprints in Apache license text.
Vassiliy-Kudryashov Nov 24, 2022
5c83685
Merge branch 'main' into Vassiliy-Kudryashov/experiments
Vassiliy-Kudryashov Nov 25, 2022
2b9373d
Merge branch 'main' into Vassiliy-Kudryashov/experiments
Vassiliy-Kudryashov Nov 25, 2022
aee42d6
Merge branch 'main' into Vassiliy-Kudryashov/experiments
Vassiliy-Kudryashov Nov 28, 2022
5c1328f
Fix an issue with always-default settings state.
Vassiliy-Kudryashov Dec 2, 2022
270aa3c
Fix a misprint in UtSettings property name "treatAssertAsErrorSuite"
Vassiliy-Kudryashov Dec 2, 2022
bd6dde8
Merge branch 'main' into Vassiliy-Kudryashov/experiments
Vassiliy-Kudryashov Dec 2, 2022
b6e4ea7
Fix a misprint in UtSettings property name "treatAssertAsErrorSuite"
Vassiliy-Kudryashov Dec 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add template generation to gradle build process
Save template to user's local file if absent
Add some documentation comments
  • Loading branch information
Vassiliy-Kudryashov committed Nov 23, 2022
commit 52c60b74f9138a9b49b45d4babc80625bcb93772
197 changes: 197 additions & 0 deletions buildSrc/src/main/java/SettingsTemplateHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import org.gradle.api.*;

import java.io.*;
import java.text.*;
import java.util.*;
import java.util.function.*;

/**
* The purpose of this helper is to convert UtSettings.kt source code
* to template resource file settings.properties (top-level entry in plugin JAR file).
* There are two stages: parsing of input to build models and then rendering models to output file
*/

public class SettingsTemplateHelper {
private static final String[] apacheLines =
("Copyright (c) " + new SimpleDateFormat("yyyy").format(System.currentTimeMillis()) + " The UnitTestBot Authors\n" +
"\n" +
"Licensed under the Apache License, Version 2.0 (the \\License\\);\n" +
"you may not use this file except in compliance with the License.\n" +
"You may obtain a copy of the License at\n" +
"\n" +
" http://www.apache.org/licenses/LICENSE-2.0\n" +
"\n" +
"Unless required by applicable law or agreed to in writing, software\n" +
"distributed under the License is distributed on an \\AS IS\\ BASIS,\n" +
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
"See the License for the specific language governing permissions and\n" +
"limitations under the License.").split("\n");

public static void proceed(Project project) {
File settingsSourceDir = new File(project.getBuildDir().getParentFile().getParentFile(), "utbot-framework-api/src/main/kotlin/org/utbot/framework/");
String sourceFileName = "UtSettings.kt";
File settingsResourceDir = new File(project.getBuildDir().getParentFile().getParentFile(), "utbot-intellij/src/main/resources/");
String settingsFileName = "settings.properties";

Map<String, String> dictionary = new HashMap<>();
dictionary.put("Int.MAX_VALUE", String.valueOf(Integer.MAX_VALUE));

List<PropertyModel> models = new ArrayList<>();
List<EnumInfo> enums = new ArrayList<>();
StringBuilder acc = new StringBuilder();
List<String> docAcc = new ArrayList<>();
// Stage one: parsing sourcecode
try(BufferedReader reader = new BufferedReader(new FileReader(new File(settingsSourceDir, sourceFileName)))) {
for (String s = reader.readLine(); s != null; s = reader.readLine()) {
s = s.trim();
if (s.startsWith("enum class ")) {//Enum class declaration
enums.add(new EnumInfo(s.substring(11, s.length() - 2)));
} else if (s.matches("[A-Z_]+,?") && !enums.isEmpty()) {//Enum value
var enumValue = s.substring(0, s.length());
if (enumValue.endsWith(",")) enumValue = enumValue.substring(0, enumValue.length() - 1);
enums.get(enums.size() - 1).docMap.put(enumValue, new ArrayList<>(docAcc));
} else if (s.startsWith("const val ")) {//Constand value to be substitute later if need
int pos = s.indexOf(" = ");
dictionary.put(s.substring(10, pos), s.substring(pos + 3));
} else if (s.equals("/**")) {//Start of docuemntation block
docAcc.clear();
} else if (s.startsWith("* ")) {
if (!s.contains("href")) {//Links are not supported, skip them
docAcc.add(s.substring(2));
}
} else if (s.startsWith("var") || s.startsWith("val")) {//Restart accumulation
acc.delete(0, acc.length());
acc.append(s);
} else if (s.isEmpty() && acc.length() > 0) {//Build model from accumulated lines
s = acc.toString();
acc.delete(0, acc.length());
if (s.startsWith("var") || s.startsWith("val")) {
var i = s.indexOf(" by ", 3);
if (i > 0) {
var key = s.substring(3, i).trim();
if (key.contains(":")) {
key = key.substring(0, key.lastIndexOf(':'));
}
PropertyModel model = new PropertyModel(key);
models.add(model);
s = s.substring(i + 7);
i = s.indexOf("Property");
if (i > 0) model.type = s.substring(0, i);
if (i == 0) {
i = s.indexOf('<', i);
if (i != -1) {
s = s.substring(i+1);
i = s.indexOf('>');
if (i != -1) {
model.type = s.substring(0, i);
}
}
}

i = s.indexOf('(', i);
if (i > 0) {
s = s.substring(i + 1);
var defaultValue = s.substring(0, s.indexOf(')')).trim();
if (defaultValue.contains(",")) defaultValue = defaultValue.substring(0, defaultValue.indexOf(','));
defaultValue = dictionary.getOrDefault(defaultValue, defaultValue);
if (defaultValue.matches("[\\d_]+L")) {
defaultValue = defaultValue.substring(0, defaultValue.length() - 1).replace("_", "");
}
if (defaultValue.matches("^\".+\"$")) {
defaultValue = defaultValue.substring(1, defaultValue.length() - 1);
}
model.defaultValue = defaultValue;
model.docLines.addAll(docAcc);
}
}
}
} else if (acc.length() > 0) {
acc.append(" " + s);
}
}
} catch (IOException ioe) {
System.err.println("Unexpected error when processing " + sourceFileName);
ioe.printStackTrace();
}

// Stage two: properties file rendering
try (PrintWriter writer = new PrintWriter(new File(settingsResourceDir, settingsFileName))) {
for (String apacheLine : apacheLines) {
writer.println("# " + apacheLine);
}
for (PropertyModel model : models) {
if (model.type.equals("Enum")) {
String[] split = model.defaultValue.split("\\.");
if (split.length > 1) {
model.defaultValue = split[1];
EnumInfo enumInfo = enums.stream().filter(new Predicate<EnumInfo>() {
@Override
public boolean test(EnumInfo enumInfo) {
return enumInfo.className.equals(split[0]);
}
}).findFirst().orElse(null);
if (enumInfo != null) {
model.docLines.add("");
for (Map.Entry<String, List<String>> entry : enumInfo.docMap.entrySet()) {
String enumValue = entry.getKey();
if (entry.getValue().size() == 1) {
model.docLines.add(enumValue + ": " + entry.getValue().get(0));
} else {
model.docLines.add(enumValue);
for (String line : entry.getValue()) {
model.docLines.add(line);
}
}
}
}
}
}
writer.println();
writer.println("#");
for (String docLine : model.docLines) {
if (docLine.isEmpty()) {
writer.println("#");
} else {
writer.println("# " + docLine);
}
}
boolean defaultIsAlreadyMentioned = model.docLines.stream().anyMatch(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.toLowerCase(Locale.ROOT).contains("default");
}
});
if (!defaultIsAlreadyMentioned) {
writer.println("#");
writer.println("# Default value is [" + model.defaultValue + "]");
}
writer.println(model.key + "=" + model.defaultValue);
}
writer.flush();
writer.close();
} catch (IOException ioe) {
System.err.println("Unexpected error when saving " + settingsFileName);
ioe.printStackTrace();
}
}

private static class PropertyModel {
final String key;
String type = "";
String defaultValue = "";
List<String> docLines = new ArrayList<>();

PropertyModel(String key) {
this.key = key;
}
}

private static class EnumInfo {
final String className;
Map<String, List<String>> docMap = new LinkedHashMap<>();

public EnumInfo(String className) {
this.className = className;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const val DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_CHILD_PROCESS_MS = 1000L

object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultSettingsPath) {

fun defaultSettingsPath() = defaultSettingsPath

/**
* Setting to disable coroutines debug explicitly.
*
Expand Down
173 changes: 1 addition & 172 deletions utbot-intellij/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import java.io.ByteArrayOutputStream
import java.io.PrintWriter
import java.text.SimpleDateFormat

val intellijPluginVersion: String? by rootProject
val kotlinLoggingVersion: String? by rootProject
val apacheCommonsTextVersion: String? by rootProject
Expand Down Expand Up @@ -65,175 +60,9 @@ intellij {

version.set(ideVersion)
type.set(ideTypeOrAndroidStudio)
SettingsTemplateHelper.proceed(project)
}

abstract class SettingsToConfigTask : DefaultTask() {
@get:Internal
val apacheLines =
"""Copyright (c) ${SimpleDateFormat("yyyy").format(System.currentTimeMillis())} The UnitTestBot 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.""".split("\n")
@get:Internal
val settingsSourceDir = File(project.buildDir.parentFile.parentFile, "utbot-framework-api/src/main/kotlin/org/utbot/framework/")
@get:Internal
val sourceFileName = "UtSettings.kt"
@get:Internal
val settingsResourceDir = File(project.buildDir.parentFile.parentFile, "utbot-intellij/src/main/resources/")
@get:Internal
val settingsFileName = "settings.properties"

data class PropertyModel(
val key: String,
var type: String = "",
var defaultValue: String = "",
var docLines: MutableList<String> = mutableListOf()
)

data class EnumInfo(var className: String, var docMap: MutableMap<String, MutableList<String>> = linkedMapOf())

@TaskAction
fun proceed() {
try {
val dictionary = mutableMapOf<String, String>().also {
it["Int.MAX_VALUE"] = Int.MAX_VALUE.toString()
}
val models = mutableListOf<PropertyModel>()
val enums = mutableListOf<EnumInfo>()

val acc = StringBuilder()
val docLines = mutableListOf<String>()
File(settingsSourceDir, sourceFileName).useLines { it ->
it.iterator().forEach { line ->
var s = line.trim()
if (s.startsWith("enum class ")) {
enums.add(EnumInfo(s.substring(11, s.length - 2)))
} else if (s.matches(Regex("[A-Z_]+,?")) && enums.isNotEmpty()) {
var enumValue = s.substring(0, s.length)
if (enumValue.endsWith(",")) enumValue = enumValue.substring(0, enumValue.length - 1)
enums.last().docMap[enumValue] = docLines.toMutableList()
} else if (s.startsWith("const val ")) {
val pos = s.indexOf(" = ")
dictionary[s.substring(10, pos)] = s.substring(pos + 3)
} else if (s == "/**") {
docLines.clear()
} else if (s.startsWith("* ")) {
if (!s.contains("href")) {//Links are not supported
docLines.add(s.substring(2))
}
} else if (s.startsWith("var") || s.startsWith("val")) {
acc.clear()
acc.append(s)
} else if (s.isEmpty() && acc.isNotEmpty()) {
s = acc.toString()
acc.clear()
if (s.startsWith("var") || s.startsWith("val")) {
var i = s.indexOf(" by ", 3)
if (i > 0) {
var key = s.substring(3, i).trim()
println(key)
if (key.contains(':')) {
key = key.substring(0, key.lastIndexOf(':'))
}
val model = PropertyModel(key)
models.add(model)
s = s.substring(i + 7)
i = s.indexOf("Property")
if (i > 0) model.type = s.substring(0, i)
if (i == 0) {
i = s.indexOf('<', i)
if (i != -1) {
s = s.substring(i+1)
i = s.indexOf('>')
if (i != -1) {
model.type = s.substring(0, i)
}
}
}

i = s.indexOf('(', i)
if (i > 0) {
s = s.substring(i + 1)
var defaultValue = s.substring(0, s.indexOf(')')).trim()
if (defaultValue.contains(',')) defaultValue = defaultValue.substring(0, defaultValue.indexOf(','))
defaultValue = dictionary[defaultValue] ?:defaultValue
if (defaultValue.matches(Regex("[\\d_]+L"))) {
defaultValue = defaultValue.substring(0, defaultValue.length - 1).replace("_", "")
}
if (defaultValue.matches(Regex("^\".+\"$"))) {
defaultValue = defaultValue.substring(1, defaultValue.length - 1)
}
model.defaultValue = defaultValue
model.docLines.addAll(docLines)
}
} else {
System.err.println(s)
}
}
} else if (acc.isNotEmpty()) {
acc.append(" $s")
}
}
val byteArrayOutputStream = ByteArrayOutputStream()
val writer = PrintWriter(byteArrayOutputStream)
apacheLines.forEach { writer.println("# $it") }

for (model in models) {
if (model.type == "Enum") {
val split = model.defaultValue.split('.')
if (split.size > 1) {
model.defaultValue = split[1]
val enumInfo = enums.find { info -> info.className == split[0] }
if (enumInfo!= null) {
model.docLines.add("")
}
enumInfo?.docMap?.forEach {
if (it.value.size == 1) {
model.docLines.add("${it.key}: ${it.value.first()}")
} else {
model.docLines.add(it.key)
it.value.forEach { line -> model.docLines.add(line) }
}
}
}
}
writer.println()
writer.println("#")
for (docLine in model.docLines) {
if (docLine.isEmpty()) {
writer.println("#")
} else {
writer.println("# $docLine")
}
}
if (!model.docLines.any({ s -> s.toLowerCaseAsciiOnly().contains("default") })) {
writer.println("#")
writer.println("# Default value is [${model.defaultValue}]")
}
writer.println("${model.key}=${model.defaultValue}")
writer.flush()
}
writer.flush()
writer.close()
File(settingsResourceDir, settingsFileName).writeBytes(byteArrayOutputStream.toByteArray())
}
} catch (e : java.io.IOException) {
logger.error("Unexpected error when processing $sourceFileName", e)
}
}
}

tasks.register<SettingsToConfigTask>("generateConfigTemplate")

tasks {
compileKotlin {
kotlinOptions {
Expand Down
Loading