Compare commits

...

2 Commits

Author SHA1 Message Date
julian
656eaec1f5 add README 2025-12-02 11:37:01 +01:00
julian
4ed31be23c working state 2025-12-02 11:25:09 +01:00
26 changed files with 479 additions and 139 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
gen/
sample#/
target/
*.dot
*.svg
*.gefx

6
.idea/copilot.data.migration.agent.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

2
.idea/misc.xml generated
View File

@@ -34,5 +34,5 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="homebrew-23" project-jdk-type="JavaSDK" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="homebrew-23" project-jdk-type="JavaSDK" />
</project>

41
README.md Normal file
View File

@@ -0,0 +1,41 @@
This is a first working state of the plugin. It has not been tested thoroughly. It has not been tested on windows at all.
This allows you to mix .java and .jav files in a single project. However, circular dependencies between .java and .jav files are not allowed as both compilers can read .class files but neither is aware of the other source files.
In theory, this should not be a problem as JavaTX is supposed to be a superset of Java. In practice however, there are features missing in JavaTX that Java supports (e.g. Arrays)
# Installation
To install run
```mvn install```
in the root of this directory. This will install the plugin to your local maven repository.
Then you can use it as a plugin in your maven project by adding the plugin to your project's pom.xml. Make sure to change the javaTXCompilerLocation configuration variable to the correct location.
It needs to point to a jar file.
```
<build>
<plugins>
<plugin>
<groupId>de.dhbwstuttgart</groupId>
<artifactId>javatx-compiler-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<javaTXCompilerLocation>path-to-your-java-tx-compiler-location</javaTXCompilerLocation>
</configuration>
<!-- Currently this is the way to override the mvn:compile lifecycle step with this plugin instead of the standard one -->
<executions>
<execution>
<id>custom-compile</id>
<phase>compile</phase>
<goals>
<goal>compile-javatx-project</goal>
</goals>
</execution>
</executions>
</plugin>
... additional plugins
</plugins>
</build>
```
Now you can use the typical `mvn:compile` to compile the project.

View File

@@ -3,6 +3,8 @@
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" dumb="true">
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/antlr4" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/gen" />
<excludeFolder url="file://$MODULE_DIR$/sample#" />
</content>
</component>
</module>

View File

@@ -9,8 +9,7 @@
<packaging>maven-plugin</packaging>
<name>javatx-compiler-plugin</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<url>https://dhbw-stuttgart.de/horb</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -122,7 +121,7 @@
<artifactId>maven-plugin-plugin</artifactId>
<version>3.13.1</version>
<configuration>
<goalPrefix>javatx-compiler</goalPrefix>
<goalPrefix>javatx-compiler-plugin</goalPrefix>
</configuration>
<executions>
<execution>

View File

@@ -1,35 +0,0 @@
package de.dhbwstuttgart;
import de.dhbwstuttgart.parser.JavaTXDependencyObtainer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.io.IOException;
/**
* Hello world!
*/
/*
public class App {
public static void main(String[] args) throws IOException {
CharStream input = CharStreams.fromStream(System.in);
JavaTXLexer lexer = new JavaTXLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
JavaTXParser parser = new JavaTXParser(tokens);
ParseTree tree = parser.sourceFile();
JavaTXDependencyObtainer dependencyObtainer = new JavaTXDependencyObtainer();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(dependencyObtainer, tree);
System.out.println("Package: " + dependencyObtainer.getPackageDecl());
System.out.println("Imports: " + dependencyObtainer.getImports());
System.out.println("Classes: " + dependencyObtainer.getClasses());
}
}
*/

View File

@@ -46,10 +46,6 @@ public class JavaDependencyResolver {
.setSymbolResolver(new JavaSymbolSolver(solver))
.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21);
if (file.getName().equals("StatementToTargetExpression.java")){
System.out.println();
}
// Parse a source file
JavaParser parser = new JavaParser(config);
@@ -152,7 +148,7 @@ public class JavaDependencyResolver {
ResolvedType type = param.getType().resolve();
dependencies.addAll(getAllInnerTypes(type));
} catch (Exception e) {
System.out.println("Unresolved Parameter: " + param);
//System.out.println("Unresolved Parameter: " + param);
}
});
return dependencies;
@@ -289,7 +285,6 @@ public class JavaDependencyResolver {
else{
return null; // Unsupported type declaration
}
System.out.println("generating dependency for type: " + typeDecl.getName() + " at path: " + path.toAbsolutePath());
if(isJavaTXFile) return new Dependency(packagePath, new JavaTXSourceFile(path.toAbsolutePath().toFile()), true);
else return new Dependency(packagePath, new JavaSourceFile(path.toAbsolutePath().toFile()), false);
}

View File

@@ -1,7 +1,5 @@
package de.dhbwstuttgart.javatxanalyzer;
import com.github.javaparser.ast.type.ReferenceType;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
@@ -11,6 +9,8 @@ import de.dhbwstuttgart.javaanalyzer.JavaTXTypeSolver;
import de.dhbwstuttgart.parser.JavaTXDependencyObtainer;
import de.dhbwstuttgart.parser.antlr.JavaTXLexer;
import de.dhbwstuttgart.parser.antlr.JavaTXParser;
import de.dhbwstuttgart.structure.JavaSourceFile;
import de.dhbwstuttgart.structure.JavaTXSourceFile;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
@@ -20,15 +20,13 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Array;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class JavaTXDependencyResolver {
public static Set<Dependency> getDependenciesForJavaTXFile(File sourceFile, List<File> javaFilesPaths, List<File> javFilesPaths) throws IOException {
public static Set<Dependency> getDependenciesForJavaTXFile(File sourceFile, List<JavaSourceFile> javaFilesPaths, List<JavaTXSourceFile> javFilesPaths, List<File> srcDirs) throws IOException {
CharStream input = CharStreams.fromStream(new FileInputStream(sourceFile));
JavaTXLexer lexer = new JavaTXLexer(input);
@@ -45,11 +43,11 @@ public class JavaTXDependencyResolver {
CombinedTypeSolver solver = new CombinedTypeSolver();
solver.add(new ReflectionTypeSolver()); // JDK classes
for (var file : javaFilesPaths) {
solver.add(new JavaParserTypeSolver(file)); // Java Files
for (var dir : srcDirs) {
solver.add(new JavaParserTypeSolver(dir)); // Java Files
}
for (var file : javFilesPaths) {
solver.add(new JavaTXTypeSolver(file)); // Jav Files
for (var dir : srcDirs) {
solver.add(new JavaTXTypeSolver(dir)); // Jav Files
}
for(var dep : dependencyObtainer.getImports()){

View File

@@ -1,13 +1,13 @@
package de.dhbwstuttgart.mojos;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JarTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import de.dhbwstuttgart.javaanalyzer.*;
import de.dhbwstuttgart.javatxanalyzer.JavaTXDependencyResolver;
import de.dhbwstuttgart.structure.*;
import de.dhbwstuttgart.utils.CompilationHelper;
import de.dhbwstuttgart.utils.Utils;
import de.dhbwstuttgart.utils.*;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
@@ -15,13 +15,15 @@ import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
@Mojo(name="compile javatx project", defaultPhase= LifecyclePhase.COMPILE)
@Mojo(name="compile-javatx", defaultPhase= LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class CompilerMojo extends AbstractMojo {
@Parameter(property = "JavaTXCompilerLocation", required = true)
@@ -30,36 +32,83 @@ public class CompilerMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
// public static void main(String[] args) throws IOException {
// // List<File> compileSourceRoots = List.of(new File("/Users/julianschmidt/dhbw/javatx/JavaCompilerCoreInJavaTXMavenPlugin/src/main/java"), new File("/Users/julianschmidt/dhbw/javatx/JavaCompilerCoreInJavaTXMavenPlugin/target/generated-sources/antlr4"));
//// List<File> dependencies = List.of(
//// new File("/Users/julianschmidt/.m2/repository/org/antlr/antlr4/4.13.2/antlr4-4.13.2.jar"),
//// new File("/Users/julianschmidt/.m2/repository/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.jar"),
//// new File("/Users/julianschmidt/.m2/repository/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar"),
//// new File("/Users/julianschmidt/.m2/repository/org/antlr/ST4/4.3.4/ST4-4.3.4.jar"),
//// new File("/Users/julianschmidt/.m2/repository/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar"),
//// new File("/Users/julianschmidt/.m2/repository/com/ibm/icu/icu4j/72.1/icu4j-72.1.jar"),
//// new File("/Users/julianschmidt/.m2/repository/commons-io/commons-io/2.19.0/commons-io-2.19.0.jar"),
//// new File("/Users/julianschmidt/.m2/repository/io/github/classgraph/classgraph/4.8.180/classgraph-4.8.180.jar"),
//// new File("/Users/julianschmidt/.m2/repository/com/google/guava/guava/33.4.8-jre/guava-33.4.8-jre.jar"),
//// new File("/Users/julianschmidt/.m2/repository/com/google/guava/failureaccess/1.0.3/failureaccess-1.0.3.jar"),
//// new File("/Users/julianschmidt/.m2/repository/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar"),
//// new File("/Users/julianschmidt/.m2/repository/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar"),
//// new File("/Users/julianschmidt/.m2/repository/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar"),
//// new File("/Users/julianschmidt/.m2/repository/com/google/j2objc/j2objc-annotations/3.0.0/j2objc-annotations-3.0.0.jar"),
//// new File("/Users/julianschmidt/.m2/repository/org/ow2/asm/asm/9.8/asm-9.8.jar"));
//
//// File outputDir = new File("/Users/julianschmidt/dhbw/javatx/JavaCompilerCoreInJavaTXMavenPlugin/target/classes");
//// File compilerLocation = new File("/Users/julianschmidt/dhbw/javatx/JavaCompilerCore/target/JavaTXcompiler-0.1-jar-with-dependencies.jar");
// //compile(compileSourceRoots, dependencies, outputDir, compilerLocation);
// List<File> compileSourceRoots = List.of(new File("/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/sample#"));
// File outputDir = new File("/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/target/classes");
// File compilerLocation = new File("/Users/julianschmidt/dhbw/javatx/JavaCompilerCore/target/JavaTXcompiler-0.1-jar-with-dependencies.jar");
// compile(compileSourceRoots, List.of(), outputDir, compilerLocation);
// }
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
List<?> dir = project.getCompileSourceRoots();
List<File> compileSourceRoots = new ArrayList<>();
for(var d : dir){
for (var d : dir) {
if (d instanceof String path) {
compileSourceRoots.add(new File(path));
}
getLog().info("Compile Source Root: " + d);
}
var files = CompilerMojo.getAllJavaFiles(compileSourceRoots.getFirst());
var javFiles = CompilerMojo.getAllJavaTXFiles(compileSourceRoots.getFirst());
Set<?> artifacts = project.getArtifacts();
List<File> mavenDependencies = new ArrayList<>();
for (var artifact : project.getArtifacts()) {
if (artifact instanceof Artifact a){
if (artifact instanceof Artifact a) {
File file = a.getFile();
if (file != null) {
getLog().info("Dependency: " + a.getArtifactId() + " at " + file.getAbsolutePath());
mavenDependencies.add(file);
}
}
}
File outputDir = new File(project.getBuild().getOutputDirectory());
if (!outputDir.exists()) {
outputDir.mkdirs();
}
File javaTXCompilerPath = new File(javaTXCompilerLocation);
if(!javaTXCompilerPath.isFile()){
throw new MojoExecutionException("The Java-TX Compiler Path is not correct");
}
getLog().info("Project Path:" + dir);
try {
compile(compileSourceRoots, mavenDependencies, outputDir, javaTXCompilerPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void compile(List<File> compileSourceRoots, List<File> dependencies, File outputDir, File javaTXCompilerLocation) throws IOException {
try {
var javaFiles = CompilerMojo.getAllJavaFiles(compileSourceRoots);
var javFiles = CompilerMojo.getAllJavaTXFiles(compileSourceRoots);
System.out.println(dependencies);
Map<File, Set<Dependency>> javDependenciesMap = new HashMap<>();
for (File file : javFiles) {
var a = JavaTXDependencyResolver.getDependenciesForJavaTXFile(file, compileSourceRoots, compileSourceRoots);
var a = JavaTXDependencyResolver.getDependenciesForJavaTXFile(file, javaFiles, javFiles, compileSourceRoots);
javDependenciesMap.put(new JavaTXSourceFile(file.getAbsoluteFile()), a);
}
@@ -67,11 +116,13 @@ public class CompilerMojo extends AbstractMojo {
solver.add(new ReflectionTypeSolver()); // JDK classes
solver.add(new JavaParserTypeSolver(compileSourceRoots.getFirst())); //Java Files
solver.add(new JavaTXTypeSolver(compileSourceRoots.getFirst())); //Jav Files
for (var dep : dependencies) {
solver.add(new JarTypeSolver(dep)); //add maven dependencies
}
Map<File, Set<Dependency>> dependenciesMap = new HashMap<>();
for (File file: files){
for (File file : javaFiles) {
dependenciesMap.put(new JavaSourceFile(file.getAbsoluteFile()), JavaDependencyResolver.getAllDependenciesForJavaFile(file, solver));
}
dependenciesMap.putAll(javDependenciesMap);
@@ -79,51 +130,104 @@ public class CompilerMojo extends AbstractMojo {
var graph = Utils.buildDependencyGraph(dependenciesMap);
var compilationSteps = Utils.generateCompilationStepsFromDependencyGraph(graph);
System.out.println(Utils.exportGraphToDot(graph));
for(CompilationStep step : compilationSteps) {
if (step instanceof JavacCompilationStep(Set<File> toCompile, List<File> classpath)) {
getLog().info("compiling: " + toCompile + " ...");
List<File> cp = List.of(
new File(project.getBuild().getOutputDirectory()));
CompilationHelper.compileJavaSources(toCompile.stream().toList(),
new File(project.getBuild().getOutputDirectory()),
cp);
} else if (step instanceof JavaTXCompilationStep(Set<File> toCompile, List<File> classpath)) {
getLog().info("compiling: " + toCompile + " ...");
List<File> cp = List.of(
new File(project.getBuild().getOutputDirectory()));
CompilationHelper.compileJavaTXSources(new File(javaTXCompilerLocation),
toCompile.stream().toList(),
new File(project.getBuild().getOutputDirectory()),
cp, getLog());
TarjanSCC tarjan = new TarjanSCC(graph);
var sccs = tarjan.run();
for (Set<File> scc : sccs) {
System.out.println("SCC: " + scc);
}
if(!Utils.checkForCycles(sccs)){
System.exit(1);
}
Map<File, Integer> sccIndex = new HashMap<>();
for (int i = 0; i < sccs.size(); i++) {
for (File f : sccs.get(i)) {
sccIndex.put(f, i);
}
}
getLog().info(compilationSteps.toString());
Map<Integer, Set<Integer>> sccGraph = new HashMap<>();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
for (int i = 0; i < sccs.size(); i++) {
sccGraph.put(i, new HashSet<>());
}
for (File u : graph.vertexSet()) {
int uScc = sccIndex.get(u);
for (LabeledEdge e : graph.outgoingEdgesOf(u)) {
File v = graph.getEdgeTarget(e);
int vScc = sccIndex.get(v);
if (uScc != vScc) {
// Reverse the edge for compilation DAG:
// dependency -> dependent
sccGraph.get(vScc).add(uScc);
}
}
}
var a = Utils.convertSccGraphToJGraphT(sccGraph);
System.out.println(Utils.exportSccGraphWithFileLabels(a, sccs));
List<Integer> topoOrder = Utils.topoSort(sccGraph);
List<List<File>> compilationOrder = new ArrayList<>();
for (int sccId : topoOrder) {
compilationOrder.add(new ArrayList<>(sccs.get(sccId)));
}
System.out.println(compilationOrder);
List<? extends CompilationStep> compilationSteps = compilationOrder.stream().map(x -> x.getFirst() instanceof JavaTXSourceFile ? new JavaTXCompilationStep(new HashSet<>(x), null) : new JavacCompilationStep(new HashSet<>(x), null)).toList();
for(CompilationStep step : compilationSteps) {
if (step instanceof JavacCompilationStep(Set<File> toCompile, List<File> _)) {
CompilationHelper.compileJavaSources(toCompile.stream().toList(),
outputDir,
dependencies);
} else if (step instanceof JavaTXCompilationStep(Set<File> toCompile, List<File> _)) {
CompilationHelper.compileJavaTXSources(javaTXCompilerLocation,
toCompile.stream().toList(),
outputDir,
dependencies);
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
public static List<JavaTXSourceFile> getAllJavaTXFiles(File rootDir){
String[] javaFilePaths = Utils.getFilePathsInDirectory(rootDir, new String[] { "**/*.jav" }, new String[] { });
return Arrays.stream(javaFilePaths)
.map(f -> new JavaTXSourceFile(rootDir, f))
.toList();
public static List<JavaTXSourceFile> getAllJavaTXFiles(List<File> rootDirs){
List<JavaTXSourceFile> javatxSourceFiles = new ArrayList<>();
for(var dir : rootDirs){
String[] javaFilePaths = Utils.getFilePathsInDirectory(dir, new String[] { "**/*.jav" }, new String[] { });
javatxSourceFiles.addAll(Arrays.stream(javaFilePaths)
.map(f -> new JavaTXSourceFile(dir, f))
.toList());
}
return javatxSourceFiles;
}
public static List<JavaSourceFile> getAllJavaFiles(File rootDir){
String[] javaFilePaths = Utils.getFilePathsInDirectory(rootDir, new String[] { "**/*.java" }, new String[] { });
return Arrays.stream(javaFilePaths)
.map(f -> new JavaSourceFile(rootDir, f))
.toList();
public static List<JavaSourceFile> getAllJavaFiles(List<File> rootDirs){
List<JavaSourceFile> javaSourceFiles = new ArrayList<>();
for(var dir : rootDirs){
String[] javaFilePaths = Utils.getFilePathsInDirectory(dir, new String[] { "**/*.java" }, new String[] { });
javaSourceFiles.addAll(Arrays.stream(javaFilePaths)
.map(f -> new JavaSourceFile(dir, f))
.toList());
}
return javaSourceFiles;
}
// public void buildDependencyGraph(List<File> baseDirs) throws IOException {
//
// //currently only supports one base dir

View File

@@ -10,6 +10,7 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class CompilationHelper {
public static void compileJavaSources(List<File> sourceFiles, File outputDir, List<File> classpathFiles) throws IOException {
@@ -29,15 +30,13 @@ public class CompilationHelper {
// Build classpath string from File objects
if (classpathFiles != null && !classpathFiles.isEmpty()) {
StringBuilder cp = new StringBuilder();
for (int i = 0; i < classpathFiles.size(); i++) {
cp.append(classpathFiles.get(i).getAbsolutePath());
if (i < classpathFiles.size() - 1) {
cp.append(File.pathSeparator);
}
}
String classpath = classpathFiles.stream()
.map(File::getAbsolutePath)
.collect(Collectors.joining(File.pathSeparator));
options.add("-classpath");
options.add(cp.toString());
options.add(classpath);
}
JavaCompiler.CompilationTask task = compiler.getTask(
@@ -55,7 +54,7 @@ public class CompilationHelper {
}
}
}
public static void compileJavaTXSources(File compilerLocation, List<File> sourceFiles, File outputDir, List<File> classpathFiles, Log log) throws IOException, InterruptedException {
public static void compileJavaTXSources(File compilerLocation, List<File> sourceFiles, File outputDir, List<File> classpathFiles) throws IOException, InterruptedException {
// JavaTX compilation logic can be similar to Java compilation
List<String> command = new java.util.ArrayList<>();
command.add("java");
@@ -64,14 +63,17 @@ public static void compileJavaTXSources(File compilerLocation, List<File> source
for(File sourceFile : sourceFiles) {
command.add(sourceFile.getAbsolutePath());
}
command.add("-cp");
for (var file : classpathFiles) {
command.add(file.getAbsolutePath());
if(!classpathFiles.isEmpty()) {
command.add("-cp");
String classpath = classpathFiles.stream()
.map(File::getAbsolutePath)
.collect(Collectors.joining(File.pathSeparator));
command.add(classpath);
}
command.add("-d");
command.add(outputDir.getAbsolutePath());
log.info("Compiling JavaTX sources with command: " + String.join(" ", command));
System.out.println("Compiling JavaTX sources with command: " + String.join(" ", command));
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.inheritIO();

View File

@@ -0,0 +1,7 @@
package de.dhbwstuttgart.utils;
public class CyclicDependencyException extends RuntimeException {
public CyclicDependencyException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,73 @@
package de.dhbwstuttgart.utils;
import org.jgrapht.Graph;
import java.io.File;
import java.util.*;
public class TarjanSCC {
private final Graph<File, LabeledEdge> graph;
private Map<File, Integer> indexMap = new HashMap<>();
private Map<File, Integer> lowlinkMap = new HashMap<>();
private Deque<File> stack = new ArrayDeque<>();
private Set<File> onStack = new HashSet<>();
private int index = 0;
private final List<Set<File>> stronglyConnectedComponents = new ArrayList<>();
public TarjanSCC(Graph<File, LabeledEdge> graph) {
this.graph = graph;
}
/** Run the algorithm */
public List<Set<File>> run() {
for (File v : graph.vertexSet()) {
if (!indexMap.containsKey(v)) {
strongConnect(v);
}
}
//
return stronglyConnectedComponents;
}
/** Recursive Tarjan DFS step */
private void strongConnect(File v) {
indexMap.put(v, index);
lowlinkMap.put(v, index);
index++;
stack.push(v);
onStack.add(v);
// Consider successors
for (LabeledEdge e : graph.outgoingEdgesOf(v)) {
File w = graph.getEdgeTarget(e);
if (!indexMap.containsKey(w)) {
// Successor w not yet visited
strongConnect(w);
lowlinkMap.put(v, Math.min(lowlinkMap.get(v), lowlinkMap.get(w)));
}
else if (onStack.contains(w)) {
// Successor w is in the stack -> part of current SCC
lowlinkMap.put(v, Math.min(lowlinkMap.get(v), indexMap.get(w)));
}
}
// If v is the root of an SCC
if (Objects.equals(lowlinkMap.get(v), indexMap.get(v))) {
Set<File> scc = new HashSet<>();
File w;
do {
w = stack.pop();
onStack.remove(w);
scc.add(w);
} while (!w.equals(v));
stronglyConnectedComponents.add(scc);
}
}
}

View File

@@ -10,14 +10,13 @@ import org.codehaus.plexus.util.DirectoryScanner;
import org.jgrapht.Graph;
import org.jgrapht.alg.util.Pair;
import org.jgrapht.graph.AbstractBaseGraph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedMultigraph;
import org.jgrapht.nio.DefaultAttribute;
import org.jgrapht.nio.dot.DOTExporter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
@@ -45,34 +44,74 @@ public class Utils {
if (!graph.containsVertex(dependency.location())) {
graph.addVertex(dependency.location());
}
System.out.println(dependency.clazzPath().getPackagePathAsString());
System.out.println(dependency.clazzPath().getAbsoluteName());
try{
graph.addEdge(file, dependency.location(), new LabeledEdge(dependency.clazzPath().getAbsoluteName()));
}
catch(Exception e){
System.out.println();
System.err.println("Error adding edge from " + file.getAbsolutePath() + " to " + dependency.location().getAbsolutePath() + ": " + e.getMessage());
}
}
}
return graph;
}
public static void exportGraphToDotFile(Graph<File, LabeledEdge> graph, File outputFile) throws IOException {
DOTExporter<File, LabeledEdge> exporter = new DOTExporter<>();
exporter.setVertexIdProvider(file -> "\"" + file.getName() + "\"");
exporter.setEdgeAttributeProvider(e -> Map.of("label", DefaultAttribute.createAttribute(e.getLabel())));
public static <E> String exportGraphToDot(Graph<File, E> graph) {
DOTExporter<File, E> exporter = new DOTExporter<>(
file -> file.getName().replaceAll("[^A-Za-z0-9_]", "_") // DOT-safe ID
);
exporter.setVertexAttributeProvider(file -> Map.of(
"label", DefaultAttribute.createAttribute(file.getName())
));
try (Writer writer = new FileWriter(outputFile)) {
exporter.exportGraph(graph, writer);
}
StringWriter writer = new StringWriter();
exporter.exportGraph(graph, writer);
return writer.toString();
}
public static List<CompilationStep> simplify(List<CompilationStep> steps){
List<CompilationStep> simplifiedSteps = new ArrayList<>();
CompilationStep previousStep = null;
boolean addedTwo = false;
for(int idx = 0; idx < steps.size(); idx++){
//first step
if (previousStep == null){
previousStep = steps.get(idx);
continue;
}
public static List<CompilationStep> generateCompilationStepsFromDependencyGraph(Graph<File, LabeledEdge> originalGraph) throws IOException {
CompilationStep thisStep = steps.get(idx);
if (previousStep instanceof JavaTXCompilationStep prev && thisStep instanceof JavaTXCompilationStep thiz){
Set<File> combined = new HashSet<>(prev.toCompile());
combined.addAll(thiz.toCompile());
simplifiedSteps.removeLast(); //remove previous step and combine the two
thisStep = new JavaTXCompilationStep(combined, null);
simplifiedSteps.add(thisStep);
}
else if(previousStep instanceof JavacCompilationStep prev && thisStep instanceof JavacCompilationStep thiz){
Set<File> combined = new HashSet<>(prev.toCompile());
combined.addAll(thiz.toCompile());
simplifiedSteps.removeLast(); //remove previous step and combine the two
thisStep = new JavacCompilationStep(combined, null);
simplifiedSteps.add(thisStep);
}
else{
simplifiedSteps.add(previousStep);
if(idx == steps.size() - 1){
simplifiedSteps.add(thisStep);
}
}
previousStep = thisStep;
}
return simplifiedSteps;
}
public static List<CompilationStep> generateCompilationStepsFromDependencyGraph(Graph<File, LabeledEdge> originalGraph) throws IOException, CyclicDependencyException {
Graph<File, LabeledEdge> graph = (Graph<File, LabeledEdge>) ((AbstractBaseGraph<File, LabeledEdge>) originalGraph).clone();
List<CompilationStep> steps = new ArrayList<>();
while (!graph.vertexSet().isEmpty()) {
@@ -93,14 +132,62 @@ public class Utils {
if (amoutOfVertices == graph.vertexSet().size()) {
// cannot remove any more vertices, so we probably have a cyclic dependency
System.out.println("Cyclic dependency detected, stopping compilation steps generation.");
Utils.exportGraphToDotFile(graph, new File("cyclic_dependency_graph.dot"));
break;
throw new CyclicDependencyException("Cyclic dependency between Java and Java-TX files detected, stopping compilation steps generation.");
}
}
return steps;
return simplify(steps);
}
public static Graph<Integer,DefaultEdge> convertSccGraphToJGraphT(
Map<Integer, Set<Integer>> sccGraph
) {
// Create a directed graph with default edges
Graph<Integer,DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);
// Add vertices
for (Integer sccId : sccGraph.keySet()) {
g.addVertex(sccId);
}
// Add edges
for (Map.Entry<Integer, Set<Integer>> entry : sccGraph.entrySet()) {
Integer src = entry.getKey();
for (Integer dst : entry.getValue()) {
g.addEdge(src, dst);
}
}
return g;
}
public static String exportSccGraphWithFileLabels(
Graph<Integer, DefaultEdge> sccGraph,
List<Set<File>> sccs
) {
DOTExporter<Integer, DefaultEdge> exporter = new DOTExporter<>(Object::toString);
// Label each vertex with the files in the SCC
exporter.setVertexAttributeProvider(sccId -> {
Set<File> files = sccs.get(sccId);
String label = files.stream()
.map(File::getName)
.collect(Collectors.joining("\\n")); // newline in DOT
return Map.of("label", DefaultAttribute.createAttribute(label));
});
// Optional: label edges
exporter.setEdgeAttributeProvider(e -> Map.of(
"label", DefaultAttribute.createAttribute(e.toString())
));
StringWriter writer = new StringWriter();
exporter.exportGraph(sccGraph, writer);
return writer.toString();
}
public static <T extends File> Set<File> getTransitiveDependentFilesOfType(Class<T> type, Graph<File, LabeledEdge> graph, Set<File> potentialVerticesInThisStep, Set<File> potentialFilesInCycle) {
//initial call
@@ -171,4 +258,45 @@ public class Utils {
.map(graph::getEdgeTarget)
.allMatch(type::isInstance);
}
public static boolean checkForCycles(List<Set<File>> sccs) {
for(var scc : sccs){
boolean allSameType = scc.stream().map(Object::getClass).distinct().count() <= 1;
if (!allSameType) {
return false;
}
}
return true;
}
public static List<Integer> topoSort(Map<Integer, Set<Integer>> graph) {
Set<Integer> visited = new HashSet<>();
Set<Integer> temp = new HashSet<>();
List<Integer> order = new ArrayList<>();
for (int node : graph.keySet()) {
if (!visited.contains(node)) {
dfs(node, graph, visited, temp, order);
}
}
Collections.reverse(order);
return order;
}
static void dfs(int u, Map<Integer, Set<Integer>> graph,
Set<Integer> visited, Set<Integer> temp, List<Integer> order) {
if (temp.contains(u))
throw new IllegalStateException("Graph is not a DAG (should not happen after SCCs)");
if (!visited.contains(u)) {
temp.add(u);
for (int v : graph.get(u)) {
dfs(v, graph, visited, temp, order);
}
temp.remove(u);
visited.add(u);
order.add(u);
}
}
}

View File

@@ -0,0 +1,13 @@
<lifecycles>
<lifecycle>
<id>default</id>
<phases>
<phase>
<id>compile</id>
<goal>
<plugin>de.dhbwstuttgart:javatx-compiler-plugin:compile-javatx</plugin>
</goal>
</phase>
</phases>
</lifecycle>
</lifecycles>

View File

@@ -8,10 +8,11 @@
<groupId>de.dhbwstuttgart</groupId>
<artifactId>javatx-compiler-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<goalPrefix>javatx-compiler</goalPrefix>
<goalPrefix>javatx-compiler-plugin</goalPrefix>
<mojos>
<mojo>
<goal>compile javatx project</goal>
<goal>compile-javatx</goal>
<requiresDependencyResolution>compile+runtime</requiresDependencyResolution>
<requiresDirectInvocation>false</requiresDirectInvocation>
<requiresProject>true</requiresProject>
<requiresReports>false</requiresReports>

View File

@@ -8,14 +8,15 @@
<groupId>de.dhbwstuttgart</groupId>
<artifactId>javatx-compiler-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<goalPrefix>javatx-compiler</goalPrefix>
<goalPrefix>javatx-compiler-plugin</goalPrefix>
<isolatedRealm>false</isolatedRealm>
<inheritedByDefault>true</inheritedByDefault>
<requiredJavaVersion>23</requiredJavaVersion>
<requiredMavenVersion>3.9.9</requiredMavenVersion>
<mojos>
<mojo>
<goal>compile javatx project</goal>
<goal>compile-javatx</goal>
<requiresDependencyResolution>compile+runtime</requiresDependencyResolution>
<requiresDirectInvocation>false</requiresDirectInvocation>
<requiresProject>true</requiresProject>
<requiresReports>false</requiresReports>

View File

@@ -306,6 +306,7 @@ de/dhbwstuttgart/parser/antlr/JavaTXParser$BitwiseorexpressionContext.class
de/dhbwstuttgart/parser/antlr/JavaParser$NonWildcardTypeArgumentsContext.class
de/dhbwstuttgart/parser/antlr/JavaParser$MethodBodyContext.class
de/dhbwstuttgart/parser/antlr/JavaParser$ExplicitGenericInvocationSuffixContext.class
de/dhbwstuttgart/utils/CyclicDependencyException.class
de/dhbwstuttgart/parser/antlr/JavaParser$ElementValuePairsContext.class
de/dhbwstuttgart/parser/antlr/JavaTXParser$SwitchExpression2Context.class
de/dhbwstuttgart/parser/antlr/JavaTXParser$BoolLiteralContext.class
@@ -348,6 +349,7 @@ de/dhbwstuttgart/parser/antlr/JavaTXParser$ConditionalstmtContext.class
de/dhbwstuttgart/parser/JavaTXDependencyObtainer.class
de/dhbwstuttgart/parser/antlr/JavaParser$ConstantDeclaratorContext.class
de/dhbwstuttgart/parser/antlr/JavaParser$InterfaceBodyDeclarationContext.class
de/dhbwstuttgart/utils/TarjanSCC.class
de/dhbwstuttgart/parser/antlr/JavaTXParserBaseVisitor.class
de/dhbwstuttgart/parser/antlr/JavaTXParser$AnnotationTypeBodyContext.class
de/dhbwstuttgart/parser/antlr/JavaTXParser$RefTypeContext.class

View File

@@ -1,4 +1,3 @@
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/App.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/javaanalyzer/CompilationStep.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/javaanalyzer/Dependency.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/javaanalyzer/JavaDependencyResolver.java
@@ -16,7 +15,9 @@
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/structure/JavaSourceFile.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/structure/JavaTXSourceFile.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/utils/CompilationHelper.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/utils/CyclicDependencyException.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/utils/LabeledEdge.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/utils/TarjanSCC.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/src/main/java/de/dhbwstuttgart/utils/Utils.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/target/generated-sources/antlr4/de/dhbwstuttgart/parser/antlr/JavaLexer.java
/Users/julianschmidt/dhbw/javatx/JavaTX-Maven-Plugin/javatx-compiler-plugin/target/generated-sources/antlr4/de/dhbwstuttgart/parser/antlr/JavaParser.java

View File

@@ -8,14 +8,15 @@
<groupId>de.dhbwstuttgart</groupId>
<artifactId>javatx-compiler-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<goalPrefix>javatx-compiler</goalPrefix>
<goalPrefix>javatx-compiler-plugin</goalPrefix>
<isolatedRealm>false</isolatedRealm>
<inheritedByDefault>true</inheritedByDefault>
<requiredJavaVersion>23</requiredJavaVersion>
<requiredMavenVersion>3.9.9</requiredMavenVersion>
<mojos>
<mojo>
<goal>compile javatx project</goal>
<goal>compile-javatx</goal>
<requiresDependencyResolution>compile+runtime</requiresDependencyResolution>
<requiresDirectInvocation>false</requiresDirectInvocation>
<requiresProject>true</requiresProject>
<requiresReports>false</requiresReports>