Merge branch '12-heuristic' into 'master'
Resolve "Структура классов для эвристических методов поиска определенных типов классов проекта" Closes #12 See merge request romanov73/git-extractor!8
This commit is contained in:
commit
b25a1dbab9
@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
versionSpringBoot = '2.3.9.RELEASE'
|
versionSpringBoot = '2.3.9.RELEASE'
|
||||||
@ -53,7 +58,9 @@ dependencies {
|
|||||||
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5'
|
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5'
|
||||||
compile group: 'org.postgresql', name: 'postgresql', version: '9.4.1212'
|
compile group: 'org.postgresql', name: 'postgresql', version: '9.4.1212'
|
||||||
compile group: 'org.liquibase', name: 'liquibase-core', version: '4.3.1'
|
compile group: 'org.liquibase', name: 'liquibase-core', version: '4.3.1'
|
||||||
implementation group: 'commons-io', name: 'commons-io', version: '2.6'
|
compile group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||||
|
compile group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.35.0'
|
||||||
|
compile group: 'com.github.javaparser', name: 'javaparser-core', version: '3.20.2'
|
||||||
|
|
||||||
compile group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0'
|
compile group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0'
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.api;
|
||||||
|
|
||||||
|
import ru.ulstu.extractor.heuristic.component.BuildTool;
|
||||||
|
import ru.ulstu.extractor.heuristic.component.ProgrammingLanguage;
|
||||||
|
import ru.ulstu.extractor.heuristic.model.StructuralUnit;
|
||||||
|
import ru.ulstu.extractor.heuristic.service.DetectorService;
|
||||||
|
import ru.ulstu.extractor.heuristic.service.ProgrammingLanguageService;
|
||||||
|
import ru.ulstu.extractor.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static ru.ulstu.extractor.heuristic.service.DetectorService.LangDetectScrupulousness.HIGH;
|
||||||
|
import static ru.ulstu.extractor.heuristic.service.DetectorService.LangDetectScrupulousness.LOW;
|
||||||
|
|
||||||
|
public abstract class StructuralUnitIdentifier {
|
||||||
|
public List<StructuralUnit> getEntityClasses(String projectPath, List<File> projectFiles, List<File> rootProjectFiles) {
|
||||||
|
String subDirectory = getSourceDirectory(rootProjectFiles);
|
||||||
|
return getEntityClasses(projectPath, subDirectory, projectFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean canAppliedToCode(String sourceCode);
|
||||||
|
|
||||||
|
public abstract boolean canAppliedToFile(File projectFile);
|
||||||
|
|
||||||
|
public abstract boolean canAppliedToProject(String projectPath, List<File> projectFiles, List<File> rootProjectFiles);
|
||||||
|
|
||||||
|
public abstract boolean isEntityClass(File file);
|
||||||
|
|
||||||
|
public abstract boolean isEntityClass(String sourceCode);
|
||||||
|
|
||||||
|
protected abstract boolean isBusinessLogicClass(File file);
|
||||||
|
|
||||||
|
public abstract List<StructuralUnit> getBusinessLogicClasses();
|
||||||
|
|
||||||
|
public abstract boolean isMultiModuleProject();
|
||||||
|
|
||||||
|
public abstract Optional<BuildTool> getBuildTool(List<File> rootDirectoryFiles);
|
||||||
|
|
||||||
|
protected abstract List<StructuralUnit> getEntityClasses(String projectPath, String subDirectory, List<File> projectFiles);
|
||||||
|
|
||||||
|
protected abstract String getSourceDirectory(List<File> rootProjectFiles);
|
||||||
|
|
||||||
|
protected abstract DetectorService getDetectorService();
|
||||||
|
|
||||||
|
protected abstract ProgrammingLanguageService getProgrammingLanguageService();
|
||||||
|
|
||||||
|
protected abstract ProgrammingLanguage getProgrammingLanguage();
|
||||||
|
|
||||||
|
protected Optional<ProgrammingLanguage> getMainProgrammingLanguage(String projectPath, List<File> projectFiles, List<File> rootProjectFiles) {
|
||||||
|
String subDirectory = getSourceDirectory(rootProjectFiles);
|
||||||
|
Map<ProgrammingLanguage, Integer> projectFileLanguageFrequency = new HashMap<>();
|
||||||
|
projectFiles.stream()
|
||||||
|
.filter(file -> StringUtils.fileInSubdirectory(file.getPath(), projectPath, subDirectory))
|
||||||
|
.forEach(projectFile -> {
|
||||||
|
Optional<ProgrammingLanguage> detectedLanguage = getMainProgrammingLanguage(projectFile, LOW);
|
||||||
|
detectedLanguage.ifPresent(programmingLanguage -> projectFileLanguageFrequency.put(programmingLanguage, projectFileLanguageFrequency.getOrDefault(programmingLanguage, 0) + 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
Optional<Map.Entry<ProgrammingLanguage, Integer>> mostFrequentLanguageEntry = projectFileLanguageFrequency
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.max(Map.Entry.comparingByValue());
|
||||||
|
if (mostFrequentLanguageEntry.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(mostFrequentLanguageEntry.get().getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Optional<ProgrammingLanguage> getMainProgrammingLanguage(File projectFile, DetectorService.LangDetectScrupulousness scrupulousness) {
|
||||||
|
String fileContent = "";
|
||||||
|
if (scrupulousness == HIGH) {
|
||||||
|
try {
|
||||||
|
fileContent = new String(Files.readAllBytes(projectFile.toPath()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getProgrammingLanguageService().getByName(getDetectorService().getDetectedLanguage(
|
||||||
|
projectFile.getName(),
|
||||||
|
fileContent,
|
||||||
|
scrupulousness));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class BuildTool {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public BuildTool(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean canAppliedToProject(List<File> rootProjectFiles);
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getSourceDirectoryPath();
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.component;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class GradleBuildTool extends BuildTool {
|
||||||
|
|
||||||
|
public GradleBuildTool() {
|
||||||
|
super("Gradle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAppliedToProject(List<File> rootProjectFiles) {
|
||||||
|
return rootProjectFiles.stream()
|
||||||
|
.anyMatch(file -> file.getName().equals("build.gradle") || file.getName().equals("settings.gradle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSourceDirectoryPath() {
|
||||||
|
return "src";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.component;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JavaProgrammingLanguage extends ProgrammingLanguage {
|
||||||
|
public JavaProgrammingLanguage() {
|
||||||
|
super("java");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.component;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MavenBuildTool extends BuildTool {
|
||||||
|
|
||||||
|
public MavenBuildTool() {
|
||||||
|
super("Maven");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAppliedToProject(List<File> rootProjectFiles) {
|
||||||
|
return rootProjectFiles.stream()
|
||||||
|
.anyMatch(file -> file.getName().equals("pom.xml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSourceDirectoryPath() {
|
||||||
|
return "src";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.component;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public abstract class ProgrammingLanguage {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public ProgrammingLanguage(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canMappedByName(String programmingLanguageName) {
|
||||||
|
if (programmingLanguageName == null || programmingLanguageName.isBlank()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return getName().toLowerCase(Locale.ROOT).equals(programmingLanguageName.toLowerCase(Locale.ROOT))
|
||||||
|
|| programmingLanguageName.toLowerCase(Locale.ROOT).contains(getName().toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.controller;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.ulstu.extractor.heuristic.model.StructuralUnit;
|
||||||
|
import ru.ulstu.extractor.heuristic.service.StructuralUnitService;
|
||||||
|
import ru.ulstu.extractor.service.GitRepositoryService;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("StructuralUnitController")
|
||||||
|
public class StructuralUnitController {
|
||||||
|
private final StructuralUnitService structuralUnitService;
|
||||||
|
private final GitRepositoryService gitRepositoryService;
|
||||||
|
|
||||||
|
public StructuralUnitController(StructuralUnitService structuralUnitService,
|
||||||
|
GitRepositoryService gitRepositoryService) {
|
||||||
|
this.structuralUnitService = structuralUnitService;
|
||||||
|
this.gitRepositoryService = gitRepositoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("get-entities")
|
||||||
|
public List<StructuralUnit> getEntities(String repositoryUrl) throws IOException {
|
||||||
|
File rootPath = gitRepositoryService.getProjectDirectoryFile(repositoryUrl);
|
||||||
|
return structuralUnitService.getEntities(rootPath);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.model;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class EntityUnit extends StructuralUnit {
|
||||||
|
|
||||||
|
public EntityUnit(String projectPath, File file) {
|
||||||
|
super(projectPath, file);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.model;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static ru.ulstu.extractor.util.StringUtils.removePathPrefix;
|
||||||
|
|
||||||
|
public abstract class StructuralUnit {
|
||||||
|
private final String pathToFile;
|
||||||
|
private final String unitName;
|
||||||
|
|
||||||
|
public StructuralUnit(String projectPath, File file) {
|
||||||
|
this.pathToFile = removePathPrefix(file.getPath(), projectPath);
|
||||||
|
this.unitName = file.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPathToFile() {
|
||||||
|
return pathToFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnitName() {
|
||||||
|
return unitName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.extractor.heuristic.component.BuildTool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class BuildToolService {
|
||||||
|
private final List<BuildTool> buildTools;
|
||||||
|
|
||||||
|
public BuildToolService(List<BuildTool> buildTools) {
|
||||||
|
this.buildTools = buildTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<BuildTool> getProjectBuildTool(List<File> rootProjectFiles) {
|
||||||
|
return buildTools.stream().filter(buildTool -> buildTool.canAppliedToProject(rootProjectFiles)).findAny();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.service;
|
||||||
|
|
||||||
|
import com.gargoylesoftware.htmlunit.WebClient;
|
||||||
|
import com.gargoylesoftware.htmlunit.html.DomElement;
|
||||||
|
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static ru.ulstu.extractor.heuristic.service.DetectorService.LangDetectScrupulousness.LOW;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DetectorService {
|
||||||
|
public enum LangDetectScrupulousness {LOW, HIGH}
|
||||||
|
|
||||||
|
private final static String BASE_URL = "http://localhost:8080/lang-detector.html";
|
||||||
|
|
||||||
|
public String getDetectedLanguage(String fileName, String code, LangDetectScrupulousness scrupulousness) {
|
||||||
|
if (scrupulousness == LOW) {
|
||||||
|
return DirectoryService.getFileExtension(fileName).orElse("");
|
||||||
|
}
|
||||||
|
return getDetectedLanguage(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDetectedLanguage(String code) {
|
||||||
|
String selectedLang = null;
|
||||||
|
try (WebClient webClient = new WebClient()) {
|
||||||
|
webClient.setJavaScriptTimeout(60 * 1000);
|
||||||
|
webClient.getOptions().setThrowExceptionOnScriptError(false);
|
||||||
|
final HtmlPage page = webClient.getPage(BASE_URL);
|
||||||
|
DomElement codeElement = page.getElementById("input");
|
||||||
|
codeElement.setTextContent(code);
|
||||||
|
DomElement button = page.getElementById("btn");
|
||||||
|
button.click();
|
||||||
|
DomElement output = page.getElementById("output");
|
||||||
|
String outputString = output.getTextContent();
|
||||||
|
List<String> langsStrings = Arrays.asList(outputString.split("\n"));
|
||||||
|
int max = 0;
|
||||||
|
for (String langString : langsStrings) {
|
||||||
|
int currentVal = Integer.parseInt(langString.split(":")[1].trim());
|
||||||
|
if (currentVal > max) {
|
||||||
|
max = currentVal;
|
||||||
|
selectedLang = langString.split(":")[0].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return selectedLang.toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.service;
|
||||||
|
|
||||||
|
import com.sun.istack.NotNull;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сервис для работы с каталогом файловой системы. Для задач проекта нужно получать список файлов.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class DirectoryService {
|
||||||
|
private static final String FILE_EXTENSION_DELIMITER = ".";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить список файлов, рекурсивно обойдя все дерево каталогов проекта.
|
||||||
|
*
|
||||||
|
* @param directory корневой каталог
|
||||||
|
* @return список всех файлов
|
||||||
|
* @throws IOException при возникновении исключения
|
||||||
|
*/
|
||||||
|
public List<File> getFilesRecursively(@NotNull File directory) throws IOException {
|
||||||
|
return Files.find(directory.toPath(),
|
||||||
|
Integer.MAX_VALUE,
|
||||||
|
(filePath, fileAttr) -> fileAttr.isRegularFile())
|
||||||
|
.map(Path::toFile)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить список файлов только текущего каталога
|
||||||
|
*
|
||||||
|
* @param directory текущий каталог
|
||||||
|
* @return список файлов
|
||||||
|
*/
|
||||||
|
public List<File> getDirectoryFiles(@NotNull Path directory) {
|
||||||
|
if (directory.toFile().listFiles() != null) {
|
||||||
|
return Arrays.asList(Objects.requireNonNull(directory.toFile().listFiles()));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<String> getFileExtension(String fileName) {
|
||||||
|
if (fileName == null || fileName.isEmpty() || fileName.lastIndexOf(FILE_EXTENSION_DELIMITER) < 0) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(fileName.substring(fileName.lastIndexOf(FILE_EXTENSION_DELIMITER) + 1));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.service;
|
||||||
|
|
||||||
|
import com.github.javaparser.JavaParser;
|
||||||
|
import com.github.javaparser.ParseResult;
|
||||||
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.extractor.heuristic.api.StructuralUnitIdentifier;
|
||||||
|
import ru.ulstu.extractor.heuristic.component.BuildTool;
|
||||||
|
import ru.ulstu.extractor.heuristic.component.JavaProgrammingLanguage;
|
||||||
|
import ru.ulstu.extractor.heuristic.component.ProgrammingLanguage;
|
||||||
|
import ru.ulstu.extractor.heuristic.model.EntityUnit;
|
||||||
|
import ru.ulstu.extractor.heuristic.model.StructuralUnit;
|
||||||
|
import ru.ulstu.extractor.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ru.ulstu.extractor.heuristic.service.DetectorService.LangDetectScrupulousness.LOW;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class JavaIdentifier extends StructuralUnitIdentifier {
|
||||||
|
private static final String ENTITY_ANNOTATION = "@Entity";
|
||||||
|
private final DetectorService detectorService;
|
||||||
|
private final BuildToolService buildToolService;
|
||||||
|
private final ProgrammingLanguageService programmingLanguageService;
|
||||||
|
|
||||||
|
public JavaIdentifier(DetectorService detectorService,
|
||||||
|
BuildToolService buildToolService,
|
||||||
|
ProgrammingLanguageService programmingLanguageService) {
|
||||||
|
this.detectorService = detectorService;
|
||||||
|
this.buildToolService = buildToolService;
|
||||||
|
this.programmingLanguageService = programmingLanguageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAppliedToProject(String projectPath, List<File> projectFiles, List<File> rootProjectFiles) {
|
||||||
|
return /*getBuildTool() instanceof GradleBuildTool
|
||||||
|
&&*/ getMainProgrammingLanguage(projectPath, projectFiles, rootProjectFiles).orElse(null) instanceof JavaProgrammingLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canAppliedToFile(File projectFile) {
|
||||||
|
return getMainProgrammingLanguage(projectFile, LOW).orElse(null) instanceof JavaProgrammingLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canAppliedToCode(String sourceCode) {
|
||||||
|
return getMainProgrammingLanguage(sourceCode).orElse(null) instanceof JavaProgrammingLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StructuralUnit> getEntityClasses(String projectPath, String subDirectory, List<File> projectFiles) {
|
||||||
|
return projectFiles.stream()
|
||||||
|
.filter(file -> StringUtils.fileInSubdirectory(file.getPath(), projectPath, subDirectory))
|
||||||
|
.filter(this::isEntityClass)
|
||||||
|
.map(file -> new EntityUnit(projectPath, file))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Optional<ProgrammingLanguage> getMainProgrammingLanguage(String sourceCode) {
|
||||||
|
return sourceCodeContainsClass(sourceCode)
|
||||||
|
? Optional.of(getProgrammingLanguage())
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StructuralUnit> getBusinessLogicClasses() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMultiModuleProject() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<BuildTool> getBuildTool(List<File> rootDirectoryFiles) {
|
||||||
|
return buildToolService.getProjectBuildTool(rootDirectoryFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DetectorService getDetectorService() {
|
||||||
|
return detectorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProgrammingLanguageService getProgrammingLanguageService() {
|
||||||
|
return programmingLanguageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProgrammingLanguage getProgrammingLanguage() {
|
||||||
|
return new JavaProgrammingLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEntityClass(File file) {
|
||||||
|
try {
|
||||||
|
return file.getName().endsWith("java") && classContainsAnnotation(file, ENTITY_ANNOTATION);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEntityClass(String sourceCode) {
|
||||||
|
try {
|
||||||
|
return classContainsAnnotation(sourceCode, ENTITY_ANNOTATION);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean classContainsAnnotation(File file, String annotationDeclaration) throws IOException {
|
||||||
|
return classContainsAnnotation(new String(Files.readAllBytes(file.toPath())), annotationDeclaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean classContainsAnnotation(String sourceCode, String annotationDeclaration) {
|
||||||
|
JavaParser parser = new JavaParser();
|
||||||
|
ParseResult<CompilationUnit> parseResult = parser.parse(sourceCode);
|
||||||
|
if (parseResult.getResult().isPresent() && parseResult.getResult().get().findCompilationUnit().isPresent()) {
|
||||||
|
return parseResult.getResult().get().getTypes().stream()
|
||||||
|
.anyMatch(clazz -> clazz.getAnnotations().stream().anyMatch(annotation -> annotation.toString().startsWith(annotationDeclaration)));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sourceCodeContainsClass(String sourceCode) {
|
||||||
|
JavaParser parser = new JavaParser();
|
||||||
|
ParseResult<CompilationUnit> parseResult = parser.parse(sourceCode);
|
||||||
|
if (parseResult.getResult().isPresent() && parseResult.getResult().get().findCompilationUnit().isPresent()) {
|
||||||
|
return parseResult.getResult().get().getTypes().stream().findAny().isPresent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSourceDirectory(List<File> rootDirectoryFiles) {
|
||||||
|
return buildToolService.getProjectBuildTool(rootDirectoryFiles)
|
||||||
|
.map(BuildTool::getSourceDirectoryPath)
|
||||||
|
.orElse("src");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isBusinessLogicClass(File file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.extractor.heuristic.component.ProgrammingLanguage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ProgrammingLanguageService {
|
||||||
|
private final List<ProgrammingLanguage> programmingLanguages;
|
||||||
|
|
||||||
|
public ProgrammingLanguageService(List<ProgrammingLanguage> programmingLanguages) {
|
||||||
|
this.programmingLanguages = programmingLanguages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ProgrammingLanguage> getByName(String programmingLanguageName) {
|
||||||
|
return programmingLanguages.stream().filter(lang -> lang.canMappedByName(programmingLanguageName)).findAny();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.heuristic.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.extractor.heuristic.api.StructuralUnitIdentifier;
|
||||||
|
import ru.ulstu.extractor.heuristic.model.StructuralUnit;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class StructuralUnitService {
|
||||||
|
private final DirectoryService directoryService;
|
||||||
|
private final List<StructuralUnitIdentifier> structuralUnitIdentifiers;
|
||||||
|
|
||||||
|
public StructuralUnitService(DirectoryService directoryService,
|
||||||
|
List<StructuralUnitIdentifier> structuralUnitIdentifiers) {
|
||||||
|
this.directoryService = directoryService;
|
||||||
|
this.structuralUnitIdentifiers = structuralUnitIdentifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StructuralUnit> getEntities(File rootPath) throws IOException {
|
||||||
|
List<File> projectFiles = directoryService.getFilesRecursively(rootPath);
|
||||||
|
List<File> rootProjectFiles = directoryService.getDirectoryFiles(rootPath.toPath());
|
||||||
|
return getStructuralUnitIdentifier(
|
||||||
|
structuralUnitIdentifier -> structuralUnitIdentifier.canAppliedToProject(
|
||||||
|
rootPath.getPath(),
|
||||||
|
projectFiles,
|
||||||
|
rootProjectFiles))
|
||||||
|
.orElseThrow(() -> new RuntimeException("Identifier not found"))
|
||||||
|
.getEntityClasses(rootPath.getPath(), projectFiles, rootProjectFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsEntity(File projectFile) {
|
||||||
|
return getStructuralUnitIdentifier(
|
||||||
|
structuralUnitIdentifier -> structuralUnitIdentifier.canAppliedToFile(projectFile))
|
||||||
|
.map(identifier -> identifier.isEntityClass(projectFile))
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsEntity(String sourceCode) {
|
||||||
|
return getStructuralUnitIdentifier(
|
||||||
|
structuralUnitIdentifier -> structuralUnitIdentifier.canAppliedToCode(sourceCode))
|
||||||
|
.map(identifier -> identifier.isEntityClass(sourceCode))
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<StructuralUnitIdentifier> getStructuralUnitIdentifier(Predicate<StructuralUnitIdentifier> predicate) {
|
||||||
|
return structuralUnitIdentifiers.stream()
|
||||||
|
.filter(predicate)
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
package ru.ulstu.extractor.model;
|
package ru.ulstu.extractor.model;
|
||||||
|
|
||||||
import org.hibernate.annotations.Fetch;
|
import org.hibernate.annotations.Fetch;
|
||||||
@ -84,4 +89,12 @@ public class Commit extends BaseEntity {
|
|||||||
public void setBranch(Branch branch) {
|
public void setBranch(Branch branch) {
|
||||||
this.branch = branch;
|
this.branch = branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean containsEntity() {
|
||||||
|
return fileChanges != null && fileChanges.stream().anyMatch(
|
||||||
|
fileChange -> fileChange != null
|
||||||
|
&& fileChange.isContainsEntity() != null
|
||||||
|
&& fileChange.isContainsEntity()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
package ru.ulstu.extractor.model;
|
package ru.ulstu.extractor.model;
|
||||||
|
|
||||||
import org.hibernate.annotations.Fetch;
|
import org.hibernate.annotations.Fetch;
|
||||||
@ -20,6 +25,8 @@ public class FileChange extends BaseEntity {
|
|||||||
@Transient
|
@Transient
|
||||||
private boolean added;
|
private boolean added;
|
||||||
|
|
||||||
|
private Boolean containsEntity;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "file_change_id", unique = true)
|
@JoinColumn(name = "file_change_id", unique = true)
|
||||||
@Fetch(FetchMode.SUBSELECT)
|
@Fetch(FetchMode.SUBSELECT)
|
||||||
@ -71,4 +78,12 @@ public class FileChange extends BaseEntity {
|
|||||||
public boolean isAdded() {
|
public boolean isAdded() {
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean isContainsEntity() {
|
||||||
|
return containsEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContainsEntity(boolean containsEntity) {
|
||||||
|
this.containsEntity = containsEntity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,15 @@ import org.eclipse.jgit.api.ListBranchCommand;
|
|||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
import org.eclipse.jgit.diff.DiffFormatter;
|
import org.eclipse.jgit.diff.DiffFormatter;
|
||||||
import org.eclipse.jgit.internal.storage.file.FileRepository;
|
import org.eclipse.jgit.internal.storage.file.FileRepository;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectLoader;
|
||||||
|
import org.eclipse.jgit.lib.ObjectReader;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.extractor.heuristic.service.StructuralUnitService;
|
||||||
import ru.ulstu.extractor.model.Author;
|
import ru.ulstu.extractor.model.Author;
|
||||||
import ru.ulstu.extractor.model.Branch;
|
import ru.ulstu.extractor.model.Branch;
|
||||||
import ru.ulstu.extractor.model.Commit;
|
import ru.ulstu.extractor.model.Commit;
|
||||||
@ -28,6 +33,7 @@ import java.io.IOException;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -44,6 +50,12 @@ public class GitRepositoryService {
|
|||||||
@Value("${extractor.custom-projects-dir}")
|
@Value("${extractor.custom-projects-dir}")
|
||||||
private String customProjectsDir;
|
private String customProjectsDir;
|
||||||
|
|
||||||
|
private final StructuralUnitService structuralUnitService;
|
||||||
|
|
||||||
|
public GitRepositoryService(StructuralUnitService structuralUnitService) {
|
||||||
|
this.structuralUnitService = structuralUnitService;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Branch> getRemoteBranches(String url) throws GitAPIException, IOException {
|
public List<Branch> getRemoteBranches(String url) throws GitAPIException, IOException {
|
||||||
cloneOrUpdateRepo(url);
|
cloneOrUpdateRepo(url);
|
||||||
Repository localRepo = new FileRepository(getProjectGitDirectory(url));
|
Repository localRepo = new FileRepository(getProjectGitDirectory(url));
|
||||||
@ -113,6 +125,12 @@ public class GitRepositoryService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getProjectDirectoryFile(String url) {
|
||||||
|
validateUrl(url);
|
||||||
|
return Path.of(getProjectDirectory(url))
|
||||||
|
.toFile();
|
||||||
|
}
|
||||||
|
|
||||||
public void remove(String repositoryUrl) throws IOException {
|
public void remove(String repositoryUrl) throws IOException {
|
||||||
FileUtils.deleteDirectory(getProjectDirectoryFile(repositoryUrl));
|
FileUtils.deleteDirectory(getProjectDirectoryFile(repositoryUrl));
|
||||||
}
|
}
|
||||||
@ -143,12 +161,6 @@ public class GitRepositoryService {
|
|||||||
return getProjectDirectory(url) + "/.git";
|
return getProjectDirectory(url) + "/.git";
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getProjectDirectoryFile(String url) {
|
|
||||||
validateUrl(url);
|
|
||||||
return Path.of(getProjectDirectory(url))
|
|
||||||
.toFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateUrl(String url) {
|
private void validateUrl(String url) {
|
||||||
if (url == null || url.isEmpty()) {
|
if (url == null || url.isEmpty()) {
|
||||||
throw new RuntimeException("Repository url must not empty");
|
throw new RuntimeException("Repository url must not empty");
|
||||||
@ -177,10 +189,10 @@ public class GitRepositoryService {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Error occurred during diff computation. Message: " + e.getMessage());
|
throw new RuntimeException("Error occurred during diff computation. Message: " + e.getMessage());
|
||||||
}
|
}
|
||||||
return parseOutputDiff(output);
|
return parseOutputDiff(output, localRepo, laterCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FileChange> parseOutputDiff(String output) {
|
private List<FileChange> parseOutputDiff(String output, Repository repository, RevCommit commit) {
|
||||||
List<FileChange> changes = new ArrayList<>();
|
List<FileChange> changes = new ArrayList<>();
|
||||||
String[] strings = output.split("\n");
|
String[] strings = output.split("\n");
|
||||||
FileChange fileChange = new FileChange();
|
FileChange fileChange = new FileChange();
|
||||||
@ -191,6 +203,9 @@ public class GitRepositoryService {
|
|||||||
if (maybeFileName.isPresent()) {
|
if (maybeFileName.isPresent()) {
|
||||||
fileChange = new FileChange();
|
fileChange = new FileChange();
|
||||||
fileChange.setFile(maybeFileName.get());
|
fileChange.setFile(maybeFileName.get());
|
||||||
|
fileChange.setContainsEntity(
|
||||||
|
structuralUnitService.containsEntity(getContent(repository, commit, maybeFileName.get()))
|
||||||
|
);
|
||||||
/// вытащить другие изменения из коммита
|
/// вытащить другие изменения из коммита
|
||||||
changes.add(fileChange);
|
changes.add(fileChange);
|
||||||
}
|
}
|
||||||
@ -226,6 +241,22 @@ public class GitRepositoryService {
|
|||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getContent(Repository repository, RevCommit commit, String path) {
|
||||||
|
try (TreeWalk treeWalk = TreeWalk.forPath(repository, path, commit.getTree())) {
|
||||||
|
if (treeWalk != null) {
|
||||||
|
ObjectId blobId = treeWalk.getObjectId(0);
|
||||||
|
try (ObjectReader objectReader = repository.newObjectReader()) {
|
||||||
|
ObjectLoader objectLoader = objectReader.open(blobId);
|
||||||
|
byte[] bytes = objectLoader.getBytes();
|
||||||
|
return new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<String> getFileName(String commitString) {
|
private Optional<String> getFileName(String commitString) {
|
||||||
String startString = "diff --git a/";
|
String startString = "diff --git a/";
|
||||||
if (commitString.startsWith(startString)) {
|
if (commitString.startsWith(startString)) {
|
||||||
|
28
src/main/java/ru/ulstu/extractor/util/StringUtils.java
Normal file
28
src/main/java/ru/ulstu/extractor/util/StringUtils.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.extractor.util;
|
||||||
|
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
|
||||||
|
public class StringUtils {
|
||||||
|
public final static String EMPTY_STRING = "";
|
||||||
|
private final static String PATH_SEPARATOR = FileSystems.getDefault().getSeparator();
|
||||||
|
|
||||||
|
public static String addPathSeparator(String path) {
|
||||||
|
return path + PATH_SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String removePathPrefix(String path, String prefix) {
|
||||||
|
if (prefix.endsWith(PATH_SEPARATOR)) {
|
||||||
|
return path.replace(prefix, EMPTY_STRING);
|
||||||
|
}
|
||||||
|
return path.replace(addPathSeparator(prefix), EMPTY_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean fileInSubdirectory(String filePath, String projectPath, String subDirectory) {
|
||||||
|
return filePath.startsWith(projectPath + PATH_SEPARATOR + subDirectory);
|
||||||
|
}
|
||||||
|
}
|
15
src/main/resources/db/changelog-20210412_100000-schema.xml
Normal file
15
src/main/resources/db/changelog-20210412_100000-schema.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<changeSet author="orion" id="20210412-100000-1">
|
||||||
|
<addColumn tableName="file_change">
|
||||||
|
<column name="contains_entity" type="boolean"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -1,4 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
-->
|
||||||
|
|
||||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
@ -6,4 +11,5 @@
|
|||||||
<include file="db/changelog-20210317_140000-schema.xml"/>
|
<include file="db/changelog-20210317_140000-schema.xml"/>
|
||||||
<include file="db/changelog-20210326_170000-schema.xml"/>
|
<include file="db/changelog-20210326_170000-schema.xml"/>
|
||||||
<include file="db/changelog-20210329_120000-schema.xml"/>
|
<include file="db/changelog-20210329_120000-schema.xml"/>
|
||||||
|
<include file="db/changelog-20210412_100000-schema.xml"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
.error-popup-container {
|
||||||
|
background: #999999;
|
||||||
|
width: 600px;
|
||||||
|
max-width: 85vw;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 25px;
|
||||||
|
position: fixed;
|
||||||
|
box-shadow: 0px 0px 100px #999999;
|
||||||
|
border-radius: 60px;
|
||||||
|
border: 1px solid black;
|
||||||
|
z-index: 2147483646;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-popup-message {
|
||||||
|
color: #690000;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-games-logo {
|
||||||
|
position: absolute;
|
||||||
|
margin: 8px;
|
||||||
|
z-index: 100000000;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 845 B |
@ -0,0 +1,84 @@
|
|||||||
|
/* globals hljs */
|
||||||
|
|
||||||
|
var allLanguages = hljs.listLanguages();
|
||||||
|
var commonLanguages = ["cpp", "cs", "css", "javascript", "java", "objectivec", "perl", "php", "python", "ruby", "sql", "xml", "autohotkey", "lua", "actionscript", "swift", "vbscript"];
|
||||||
|
var highlightResult;
|
||||||
|
document.getElementById("commonLanguageTitle").title = commonLanguages.toString().replace(/,/g, ", ");
|
||||||
|
|
||||||
|
document.getElementById("highlight").onclick = function() {
|
||||||
|
var code = document.getElementById("pasteCode").value;
|
||||||
|
document.getElementById("languageOutput").hidden = true;
|
||||||
|
document.getElementById("option2").hidden = true;
|
||||||
|
document.getElementById("option2Languages").hidden = true;
|
||||||
|
document.getElementById("option2SelectTd").hidden = true;
|
||||||
|
document.getElementById("option2Select").disabled = false;
|
||||||
|
document.getElementById("option2Select").innerText = "Select";
|
||||||
|
document.getElementById("highlightCode").innerHTML = "";
|
||||||
|
document.getElementById("highlightCode").className = "";
|
||||||
|
document.getElementById("option1Select").onclick = null;
|
||||||
|
document.getElementById("option2Select").onclick = null;
|
||||||
|
document.getElementById("error").hidden = true;
|
||||||
|
|
||||||
|
if (document.getElementById("commonLanguagesOnly").checked) {
|
||||||
|
highlightResult = hljs.highlightAuto(code, commonLanguages);
|
||||||
|
} else {
|
||||||
|
highlightResult = hljs.highlightAuto(code, allLanguages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof highlightResult.language != "undefined") {
|
||||||
|
document.getElementById("highlightCode").innerHTML = highlightResult.value;
|
||||||
|
|
||||||
|
document.getElementById("pasteCode").value = "";
|
||||||
|
var languageObj = hljs.getLanguage(highlightResult.language);
|
||||||
|
var languages = [];
|
||||||
|
if (typeof languageObj.aliases != "undefined") {
|
||||||
|
languages = languageObj.aliases.slice();
|
||||||
|
}
|
||||||
|
languages.unshift(highlightResult.language);
|
||||||
|
|
||||||
|
document.getElementById("languageOutput").hidden = false;
|
||||||
|
document.getElementById("option1Languages").innerText = languages.toString().replace(/,/g, ", ");
|
||||||
|
document.getElementById("option1Select").disabled = true;
|
||||||
|
document.getElementById("option1Select").innerText = "Selected";
|
||||||
|
document.getElementById("option1Select").onclick = function() {
|
||||||
|
document.getElementById("option1Select").disabled = true;
|
||||||
|
document.getElementById("option1Select").innerText = "Selected";
|
||||||
|
document.getElementById("option2Select").disabled = false;
|
||||||
|
document.getElementById("option2Select").innerText = "Select";
|
||||||
|
document.getElementById("highlightCode").innerHTML = highlightResult.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof highlightResult.second_best != "undefined") {
|
||||||
|
var languageObj = hljs.getLanguage(highlightResult.second_best.language);
|
||||||
|
var languages = [];
|
||||||
|
if (typeof languageObj.aliases != "undefined") {
|
||||||
|
languages = languageObj.aliases.slice();
|
||||||
|
}
|
||||||
|
languages.unshift(highlightResult.second_best.language);
|
||||||
|
document.getElementById("option2Languages").innerText = languages.toString().replace(/,/g, ", ");
|
||||||
|
document.getElementById("option2Select").onclick = function() {
|
||||||
|
document.getElementById("option1Select").disabled = false;
|
||||||
|
document.getElementById("option1Select").innerText = "Select";
|
||||||
|
document.getElementById("option2Select").disabled = true;
|
||||||
|
document.getElementById("option2Select").innerText = "Selected";
|
||||||
|
document.getElementById("highlightCode").innerHTML = highlightResult.second_best.value;
|
||||||
|
};
|
||||||
|
document.getElementById("option2").hidden = false;
|
||||||
|
document.getElementById("option2Languages").hidden = false;
|
||||||
|
document.getElementById("option2SelectTd").hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("highlightCode").className = "hljs";
|
||||||
|
} else {
|
||||||
|
document.getElementById("error").hidden = false;
|
||||||
|
if (document.getElementById("pasteCode").value.length == 0) {
|
||||||
|
document.getElementById("error").innerText = "Error: No code entered. Please paste your code above and try again.";
|
||||||
|
} else {
|
||||||
|
if (document.getElementById("commonLanguagesOnly").checked) {
|
||||||
|
document.getElementById("error").innerText = 'Error: Unable to identify the programming language. Please add more code or uncheck the "Common Languages Only" option.';
|
||||||
|
} else {
|
||||||
|
document.getElementById("error").innerText = "Error: Unable to identify the programming language. Please add more code to increase the accuracy of the detection.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Visual Studio 2015 dark style
|
||||||
|
* Author: Nicolas LLOBERA <nllobera@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #1E1E1E;
|
||||||
|
color: #DCDCDC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-name {
|
||||||
|
color: #569CD6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link {
|
||||||
|
color: #569CD6;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-type {
|
||||||
|
color: #4EC9B0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-class {
|
||||||
|
color: #B8D7A3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-meta-string {
|
||||||
|
color: #D69D85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-template-tag {
|
||||||
|
color: #9A5334;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-subst,
|
||||||
|
.hljs-function,
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-params,
|
||||||
|
.hljs-formula {
|
||||||
|
color: #DCDCDC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #57A64A;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-doctag {
|
||||||
|
color: #608B4E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-meta-keyword,
|
||||||
|
.hljs-tag {
|
||||||
|
color: #9B9B9B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable {
|
||||||
|
color: #BD63C5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-builtin-name {
|
||||||
|
color: #9CDCFE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section {
|
||||||
|
color: gold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.hljs-code {
|
||||||
|
font-family:'Monospace';
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo {
|
||||||
|
color: #D7BA7D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-addition {
|
||||||
|
background-color: #144212;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
background-color: #600;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
11167
src/main/resources/public/bundle.js
Normal file
11167
src/main/resources/public/bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
127
src/main/resources/public/detector.html
Normal file
127
src/main/resources/public/detector.html
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- saved from url=(0050)https://creativetechguy.com/utilities/codedetector -->
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
|
||||||
|
<meta name="keywords"
|
||||||
|
content="programming, language, detector, identifier, highlighter, syntax, code, ctg, Creative Tech Guy, Jason O'Neill">
|
||||||
|
<meta name="description" content="Automatically detect a programming language by pasting a snippet of code.">
|
||||||
|
<meta name="author" content="Jason O'Neill">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>Code Detector & Formatter</title>
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="https://creativetechguy.com/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="https://creativetechguy.com/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="https://creativetechguy.com/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="https://creativetechguy.com/manifest.json">
|
||||||
|
<link rel="mask-icon" href="https://creativetechguy.com/safari-pinned-tab.svg" color="#5bbad5">
|
||||||
|
<link rel="shortcut icon" href="https://creativetechguy.com/favicon.ico">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Code Detector">
|
||||||
|
<meta name="application-name" content="Code Detector">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Code Detector & Formatter">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:locale" content="en_US">
|
||||||
|
<meta property="og:url" content="https://creativetechguy.com/utilities/codedetector">
|
||||||
|
<meta property="og:description" content="Automatically detect a programming language by pasting a snippet of code.">
|
||||||
|
<meta property="og:image" content="https://creativetechguy.com/images/logo.png">
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:site" content="@JasonONeillCTG">
|
||||||
|
<meta name="twitter:title" content="Code Detector & Formatter">
|
||||||
|
<meta name="twitter:description"
|
||||||
|
content="Automatically detect a programming language by pasting a snippet of code.">
|
||||||
|
<meta name="twitter:image" content="https://creativetechguy.com/images/logo.png">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="./Code Detector & Formatter_files/games.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="./Code Detector & Formatter_files/vs2015.css">
|
||||||
|
<style>
|
||||||
|
table, tbody, tr, th, td {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-bottom: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center,
|
||||||
|
.center * {
|
||||||
|
text-align: center;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a href="https://creativetechguy.com/utilities" id="homeLogo" title="Back to Utilities">
|
||||||
|
<img src="./Code Detector & Formatter_files/more arrow.png" class="more-games-logo" alt="More Utilities">
|
||||||
|
</a>
|
||||||
|
<div class="center">
|
||||||
|
<h1 style="margin-top: 0px;">Code Detector & Formatter</h1>
|
||||||
|
<p>Paste code below and click "Detect Code" to format and analyze the code snippet.<br>The more code that is
|
||||||
|
included, the more accurate the analysis will be.</p>
|
||||||
|
<textarea id="pasteCode"
|
||||||
|
style="width: 100%; max-width: 600px; height: 350px; text-align: left; resize: vertical; box-sizing: border-box;"></textarea><br>
|
||||||
|
<div id="commonLanguageTitle"
|
||||||
|
title="cpp, cs, css, javascript, java, objectivec, perl, php, python, ruby, sql, xml, autohotkey, lua, actionscript, swift, vbscript">
|
||||||
|
Common Languages Only: <input type="checkbox" id="commonLanguagesOnly" checked=""><br>
|
||||||
|
</div>
|
||||||
|
<button id="highlight">Detect Code</button>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<p id="error" hidden=""></p>
|
||||||
|
<table id="languageOutput" hidden="">
|
||||||
|
<tbody style="text-align: center;">
|
||||||
|
<tr>
|
||||||
|
<th id="option1">Option 1</th>
|
||||||
|
<th id="option2">Option 2</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td id="option1Languages"></td>
|
||||||
|
<td id="option2Languages"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td id="option1SelectTd" style="width: 100px;">
|
||||||
|
<button id="option1Select">Select</button>
|
||||||
|
</td>
|
||||||
|
<td id="option2SelectTd" style="width: 100px;">
|
||||||
|
<button id="option2Select">Select</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<pre><code id="highlightCode" style="max-height: 400px; overflow: auto;"></code></pre>
|
||||||
|
<hr style="border-color:#000000; border-top-width: 5px; border-style: solid; height: 0px; margin-top: 4px; max-width: 600px;">
|
||||||
|
<p style="font-size: 12px; margin: 5px; text-align: center;">Powered by <a href="https://highlightjs.org/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener">highlight.js</a><br>Copyright
|
||||||
|
<span id="copyrightYear">2021</span> Jason O'Neill</p>
|
||||||
|
<script>
|
||||||
|
document.getElementById("copyrightYear").innerText = new Date().getFullYear();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="./Code Detector & Formatter_files/highlight.pack.js.Без названия"></script>
|
||||||
|
<script src="./Code Detector & Formatter_files/script.js.Без названия"></script>
|
||||||
|
<noscript>
|
||||||
|
<div class="error-popup-container">
|
||||||
|
<p class="error-popup-message">This page, along with all of my other web games and utilities, require
|
||||||
|
JavaScript.<br>Please enable JavaScript in your browser and refresh to continue.</p>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<style id="stylish-1" class="stylish" type="text/css">td, div {
|
||||||
|
font-family: arial, sans-serif !important;
|
||||||
|
}</style>
|
||||||
|
</html>
|
153
src/main/resources/public/lang-detector.html
Normal file
153
src/main/resources/public/lang-detector.html
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- saved from url=(0041)https://hosein2398.github.io/lang-detect/ -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
|
||||||
|
<title>lang-detector</title>
|
||||||
|
<script defer="" src="bundle.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<a target="_blank" title="lang-detector on github" href="https://github.com/ts95/lang-detector" class="github-corner">
|
||||||
|
<svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; right: 0;">
|
||||||
|
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||||
|
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||||
|
fill="white" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
||||||
|
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||||
|
fill="white" class="octo-body"></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<h1 id="title">lang-detector <a target="_blank" title="lang-detector on github"
|
||||||
|
href="https://github.com/ts95/lang-detector"><img src="lang-detector.svg"></a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
<textarea id="input" autofocus="" placeholder="Write some code to detect here."></textarea>
|
||||||
|
|
||||||
|
<button id="btn">Detect</button>
|
||||||
|
<pre id="output" disabled="">JavaScript: 0
|
||||||
|
C: 3
|
||||||
|
C++: 3
|
||||||
|
Python: 0
|
||||||
|
Java: 8
|
||||||
|
HTML: 0
|
||||||
|
CSS: 0
|
||||||
|
Ruby: 2
|
||||||
|
Go: 0
|
||||||
|
PHP: 2
|
||||||
|
Unknown: 1</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="copyright">Toni Sučić ©</footer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import url(http://fonts.googleapis.com/css?family=Roboto:700,400,300);
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 20px;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background-color: #f0f8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
color: #555;
|
||||||
|
font-size: 1em;
|
||||||
|
width: 600px;
|
||||||
|
height: 200px;
|
||||||
|
border: 4px solid #eee;
|
||||||
|
padding: 10px;
|
||||||
|
outline: none;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea,
|
||||||
|
textarea[disabled] {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
color: #555;
|
||||||
|
font-size: 1em;
|
||||||
|
width: 600px;
|
||||||
|
height: 150px;
|
||||||
|
border: 4px solid #eee;
|
||||||
|
padding: 10px;
|
||||||
|
outline: none;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: #999;
|
||||||
|
background: none;
|
||||||
|
border: 4px solid #eee;
|
||||||
|
padding: 7px 12px 7px 12px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
outline: none;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.butt {
|
||||||
|
text-align: center;
|
||||||
|
margin: auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
color: #777;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 4px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
color: #555;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border: 4px solid #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 2.5em;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input {
|
||||||
|
resize: none;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#output {
|
||||||
|
resize: none;
|
||||||
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
height: auto;
|
||||||
|
font-size: 22px
|
||||||
|
}
|
||||||
|
|
||||||
|
#btn {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright {
|
||||||
|
color: #bbb;
|
||||||
|
padding: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<style id="stylish-1" class="stylish" type="text/css">td, div {
|
||||||
|
font-family: arial, sans-serif !important;
|
||||||
|
}</style>
|
||||||
|
</html>
|
32
src/main/resources/public/lang-detector.svg
Normal file
32
src/main/resources/public/lang-detector.svg
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="76" height="20">
|
||||||
|
<style>a:hover #llink{fill:url(#b);stroke:#ccc}a:hover #rlink{fill:#4183c4}</style>
|
||||||
|
<linearGradient id="a" x2="0" y2="100%">
|
||||||
|
<stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/>
|
||||||
|
<stop offset="1" stop-opacity=".1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="b" x2="0" y2="100%">
|
||||||
|
<stop offset="0" stop-color="#ccc" stop-opacity=".1"/>
|
||||||
|
<stop offset="1" stop-opacity=".1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<g stroke="#d5d5d5">
|
||||||
|
<rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="48" height="19" rx="2"/>
|
||||||
|
<rect x="54.5" y="0.5" width="21" height="19" rx="2" fill="#fafafa"/>
|
||||||
|
<rect x="54" y="7.5" width="0.5" height="5" stroke="#fafafa"/>
|
||||||
|
<path d="M54.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/>
|
||||||
|
</g>
|
||||||
|
<image x="5" y="3" width="14" height="14"
|
||||||
|
xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMTgxNzE3IiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViIGljb248L3RpdGxlPjxwYXRoIGQ9Ik0xMiAuMjk3Yy02LjYzIDAtMTIgNS4zNzMtMTIgMTIgMCA1LjMwMyAzLjQzOCA5LjggOC4yMDUgMTEuMzg1LjYuMTEzLjgyLS4yNTguODItLjU3NyAwLS4yODUtLjAxLTEuMDQtLjAxNS0yLjA0LTMuMzM4LjcyNC00LjA0Mi0xLjYxLTQuMDQyLTEuNjFDNC40MjIgMTguMDcgMy42MzMgMTcuNyAzLjYzMyAxNy43Yy0xLjA4Ny0uNzQ0LjA4NC0uNzI5LjA4NC0uNzI5IDEuMjA1LjA4NCAxLjgzOCAxLjIzNiAxLjgzOCAxLjIzNiAxLjA3IDEuODM1IDIuODA5IDEuMzA1IDMuNDk1Ljk5OC4xMDgtLjc3Ni40MTctMS4zMDUuNzYtMS42MDUtMi42NjUtLjMtNS40NjYtMS4zMzItNS40NjYtNS45MyAwLTEuMzEuNDY1LTIuMzggMS4yMzUtMy4yMi0uMTM1LS4zMDMtLjU0LTEuNTIzLjEwNS0zLjE3NiAwIDAgMS4wMDUtLjMyMiAzLjMgMS4yMy45Ni0uMjY3IDEuOTgtLjM5OSAzLS40MDUgMS4wMi4wMDYgMi4wNC4xMzggMyAuNDA1IDIuMjgtMS41NTIgMy4yODUtMS4yMyAzLjI4NS0xLjIzLjY0NSAxLjY1My4yNCAyLjg3My4xMiAzLjE3Ni43NjUuODQgMS4yMyAxLjkxIDEuMjMgMy4yMiAwIDQuNjEtMi44MDUgNS42MjUtNS40NzUgNS45Mi40Mi4zNi44MSAxLjA5Ni44MSAyLjIyIDAgMS42MDYtLjAxNSAyLjg5Ni0uMDE1IDMuMjg2IDAgLjMxNS4yMS42OS44MjUuNTdDMjAuNTY1IDIyLjA5MiAyNCAxNy41OTIgMjQgMTIuMjk3YzAtNi42MjctNS4zNzMtMTItMTItMTIiLz48L3N2Zz4="/>
|
||||||
|
<g aria-hidden="false" fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif"
|
||||||
|
text-rendering="geometricPrecision" font-weight="700" font-size="110px" line-height="14px">
|
||||||
|
<a target="_blank" xlink:href="https://github.com/ts95/lang-detector">
|
||||||
|
<text aria-hidden="true" x="325" y="150" fill="#fff" transform="scale(.1)" textLength="210">Star</text>
|
||||||
|
<text x="325" y="140" transform="scale(.1)" textLength="210">Star</text>
|
||||||
|
<rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="48" height="19" rx="2"/>
|
||||||
|
</a>
|
||||||
|
<a target="_blank" xlink:href="https://github.com/ts95/lang-detector/stargazers">
|
||||||
|
<rect width="22" x="54" height="20" fill="rgba(0,0,0,0)"/>
|
||||||
|
<text aria-hidden="true" x="645" y="150" fill="#fff" transform="scale(.1)" textLength="130">28</text>
|
||||||
|
<text id="rlink" x="645" y="140" transform="scale(.1)" textLength="130">28</text>
|
||||||
|
</a>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
@ -86,6 +86,7 @@
|
|||||||
<th scope="col">Автор</th>
|
<th scope="col">Автор</th>
|
||||||
<th scope="col" style="width: 30%">Дата</th>
|
<th scope="col" style="width: 30%">Дата</th>
|
||||||
<th scope="col">Сообщение</th>
|
<th scope="col">Сообщение</th>
|
||||||
|
<th scope="col" align="center">Содержит сущность</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -93,6 +94,12 @@
|
|||||||
<td th:text="${commit.author.name}"></td>
|
<td th:text="${commit.author.name}"></td>
|
||||||
<td th:text="${commit.date}"></td>
|
<td th:text="${commit.date}"></td>
|
||||||
<td th:text="${commit.message}"></td>
|
<td th:text="${commit.message}"></td>
|
||||||
|
<td align="center">
|
||||||
|
<div class="form-group form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" th:checked="${commit.containsEntity()}"
|
||||||
|
disabled>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
Loading…
Reference in New Issue
Block a user