Skip to content

Commit ab33327

Browse files
committed
Initial commit
0 parents  commit ab33327

File tree

8 files changed

+554
-0
lines changed

8 files changed

+554
-0
lines changed

.gitignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
target/
2+
!.mvn/wrapper/maven-wrapper.jar
3+
!**/src/main/**/target/
4+
!**/src/test/**/target/
5+
6+
### IntelliJ IDEA ###
7+
.idea/modules.xml
8+
.idea/jarRepositories.xml
9+
.idea/compiler.xml
10+
.idea/libraries/
11+
*.iws
12+
*.iml
13+
*.ipr
14+
15+
### Eclipse ###
16+
.apt_generated
17+
.classpath
18+
.factorypath
19+
.project
20+
.settings
21+
.springBeans
22+
.sts4-cache
23+
24+
### NetBeans ###
25+
/nbproject/private/
26+
/nbbuild/
27+
/dist/
28+
/nbdist/
29+
/.nb-gradle/
30+
build/
31+
!**/src/main/**/build/
32+
!**/src/test/**/build/
33+
34+
### VS Code ###
35+
.vscode/
36+
37+
### Mac OS ###
38+
.DS_Store

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# ModInfGen
2+
ModInfGen (Module Info Generator) is a tool written in java that can generate a `module-info.java` file from a *.json config file `module-info.json5`.
3+
4+
But why would you want to use this tool over manually writing your `module-info.java`?
5+
Well, ModInfGen has support for several utilities that make your life easier.
6+
7+
## Unique Features
8+
- module root expansion (e.g. `.` $\rightarrow$ com.example.project)
9+
- automatically injected comments
10+
- better structure than the linear structure of `module-info.java`
11+
- variables
12+
13+
### Module root expansion
14+
Why would you want to type your root module over and over when it could be easier?
15+
Here's an example of the feature in use:
16+
17+
````json5
18+
{
19+
"module": "com.example.project",
20+
"exports": [
21+
".", // expanded to "com.example.project"
22+
".core" // "com.example.project.core"
23+
]
24+
}
25+
````
26+
27+
### Automatically injected comments
28+
You can declare comments which will be automatically injected into the generated `module-info.java`,
29+
the placement of these comments is determined by the section the comments are assigned to. Below is an example:
30+
31+
````json5
32+
{
33+
// ...
34+
35+
"comments": {
36+
"header": "This is a test header comment",
37+
"footer": "This is the closing comment",
38+
"requires": "Dependencies are below",
39+
"exports": "These modules are exported",
40+
"opens": "These modules are opened to reflection",
41+
"legacy": "These are the legacy lines"
42+
}
43+
44+
// ...
45+
}
46+
````
47+
48+
Details about the sections can be found in this documentation.
49+
50+
### Variables
51+
Variables can be declared in the `variables` section and can be used anywhere in `module-info.json5` except for the `variables` section.
52+
53+
Variable names must follow these rules to be considered valid:
54+
- must start with $
55+
- following the $ must be a lowercase letter (a-z)
56+
- following that can be any of the following characters:
57+
- a-z and A-Z
58+
- 0-9
59+
- underscores _ and hyphens -
60+
61+
Example:
62+
````json5
63+
{
64+
"variables": {
65+
"$gson": "com.google.gson"
66+
},
67+
68+
"requires": [
69+
"$gson"
70+
]
71+
}
72+
````
73+
74+
## The file `module-info.json5`
75+
`module-info.json5` is the file that essentially replaces `module-info.java`, as you can generate that file from your config in `module-info.json5`.
76+
If you are unfamiliar with *.json5-files, it's essentially JSON but with comments, which is the reason why it's used over plain JSON.
77+
The following fields can/must be declared in the config:
78+
- ``module``: The root module (e.g. `com.example.project`)
79+
- ``source-root``: The path to your source directory (e.g. `src/main/java`, optional)
80+
- ``variables {}``: Contains the key-value mappings for variable names to their value (optional)
81+
- ``exports []``: Contains a list of modules that are required by your project (optional)
82+
- ``opens []``: A list of declarations that exposes your packages to java's reflection for specified dependencies (optional)
83+
- ``legacy []``: A list of lines that follow the syntax of `module-info.java`, these lines are not validated by ModInfGen and are appended to the generated `module-info.java`
84+
- ``comments {}``: The comment mappings, supported keys are: header, footer, requires, exports, opens and legacy.
85+
86+
87+
...WIP (further documentation coming in the future)...

pom.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.chaoticsomeone</groupId>
8+
<artifactId>ModInfGen</artifactId>
9+
<version>1.0</version>
10+
11+
<properties>
12+
<maven.compiler.source>21</maven.compiler.source>
13+
<maven.compiler.target>21</maven.compiler.target>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
</properties>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>com.google.code.gson</groupId>
20+
<artifactId>gson</artifactId>
21+
<version>2.11.0</version>
22+
</dependency>
23+
</dependencies>
24+
25+
</project>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.chaoticsomeone.ModInfGen;
2+
3+
import com.chaoticsomeone.ModInfGen.model.ModuleInfo;
4+
import com.chaoticsomeone.ModInfGen.model.OpensDeclaration;
5+
import com.google.gson.Gson;
6+
7+
import java.io.BufferedReader;
8+
import java.io.FileReader;
9+
import java.io.IOException;
10+
import java.util.regex.Matcher;
11+
import java.util.regex.Pattern;
12+
13+
public class ModuleInfoGenerator {
14+
private static final Pattern SL_COMMENT = Pattern.compile("^\\s*//.*");
15+
private static final Pattern ML_COMMENT = Pattern.compile("/\\*.*\\*/");
16+
private static final Pattern MULTI_WHITESPACE = Pattern.compile("\\s+");
17+
18+
private final Gson gson = new Gson();
19+
private final String configFilePath = "module-info.json5";
20+
private final ModuleInfo moduleInfo;
21+
22+
private final boolean collapseWhitespaces;
23+
24+
public ModuleInfoGenerator(boolean collapseWhitespaces) {
25+
this.collapseWhitespaces = collapseWhitespaces;
26+
27+
moduleInfo = gson.fromJson(readJsonString(configFilePath), ModuleInfo.class);
28+
29+
try {
30+
moduleInfo.validateVariables();
31+
moduleInfo.expandVariables();
32+
moduleInfo.expandRoot();
33+
34+
System.out.println(generateModuleInfoContent());
35+
} catch (IllegalArgumentException e) {
36+
e.printStackTrace();
37+
}
38+
}
39+
40+
public ModuleInfoGenerator() {
41+
this(false);
42+
}
43+
44+
private String generateModuleInfoContent() {
45+
SmartStringBuilder outputBuilder = new SmartStringBuilder();
46+
47+
outputBuilder.appendComment(moduleInfo.getComment("header"), 0, 2);
48+
49+
outputBuilder.appendFmtLn("module %s {", moduleInfo.getModuleName());
50+
outputBuilder.alterIndent(1);
51+
52+
outputBuilder.appendSectionLn(() -> {
53+
outputBuilder.appendComment(moduleInfo.getComment("requires"));
54+
for (String requirement : moduleInfo.getRequirements()) {
55+
outputBuilder.appendFmtLn("requires %s;", requirement);
56+
}
57+
});
58+
59+
outputBuilder.appendSectionLn(() -> {
60+
outputBuilder.appendComment(moduleInfo.getComment("exports"));
61+
for (String export : moduleInfo.getExports()) {
62+
outputBuilder.appendFmtLn("exports %s;", export);
63+
}
64+
});
65+
66+
outputBuilder.appendSectionLn(() -> {
67+
outputBuilder.appendComment(moduleInfo.getComment("opens"));
68+
for (OpensDeclaration opens : moduleInfo.getOpens()) {
69+
String baseLine = String.format("opens %s to %s;", "%s", opens.getTargetModule());
70+
71+
for (String sourceModule : opens.getSourceModules()) {
72+
outputBuilder.appendFmtLn(baseLine, sourceModule);
73+
}
74+
}
75+
});
76+
77+
outputBuilder.appendSection(() -> {
78+
outputBuilder.appendComment(moduleInfo.getComment("legacy"));
79+
for (String legacyLine : moduleInfo.getLegacy()) {
80+
String end = legacyLine.endsWith(";") ? "" : ";";
81+
outputBuilder.appendLn(legacyLine + end);
82+
}
83+
});
84+
85+
outputBuilder.alterIndent(-1);
86+
outputBuilder.append("}");
87+
88+
outputBuilder.appendComment(moduleInfo.getComment("footer"), 2);
89+
90+
return outputBuilder.toString();
91+
}
92+
93+
private String readJsonString(String path) {
94+
StringBuilder jsonBuilder = new StringBuilder();
95+
96+
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
97+
String line;
98+
99+
while ((line = br.readLine()) != null) {
100+
// Filter out single line comments for compat between json5 and json
101+
if (SL_COMMENT.matcher(line).matches()) {
102+
continue;
103+
}
104+
105+
if (collapseWhitespaces) {
106+
Matcher matcher = MULTI_WHITESPACE.matcher(line);
107+
String reducedLine = matcher.replaceAll(" ");
108+
jsonBuilder.append(reducedLine);
109+
} else {
110+
jsonBuilder.append(line);
111+
}
112+
}
113+
} catch (IOException e) {
114+
e.printStackTrace();
115+
}
116+
117+
// filter out all multi-line comments for compat
118+
Matcher matcher = ML_COMMENT.matcher(jsonBuilder.toString());
119+
return matcher.replaceAll("");
120+
}
121+
122+
123+
public static void main(String[] args) {
124+
boolean collapseWhitespaces = false;
125+
126+
for (String arg : args) {
127+
if (arg.equalsIgnoreCase("-w")) {
128+
collapseWhitespaces = true;
129+
}
130+
}
131+
132+
ModuleInfoGenerator generator = new ModuleInfoGenerator(collapseWhitespaces);
133+
134+
}
135+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.chaoticsomeone.ModInfGen;
2+
3+
import java.util.regex.Matcher;
4+
import java.util.regex.Pattern;
5+
6+
public class SmartStringBuilder {
7+
private final StringBuilder sb = new StringBuilder();
8+
private int indent = 0;
9+
10+
public SmartStringBuilder() {
11+
12+
}
13+
14+
public void append(Object o) {
15+
sb.append("\t".repeat(indent));
16+
sb.append(o);
17+
}
18+
19+
public void appendLn(Object o) {
20+
append(o + "\n");
21+
}
22+
23+
public void appendLn() {
24+
append("\n");
25+
}
26+
27+
public void appendFmt(String format, Object... args) {
28+
append(String.format(format, args));
29+
}
30+
31+
public void appendFmtLn(String format, Object... args) {
32+
appendFmt(String.format(format + "\n", args));
33+
}
34+
35+
public void appendSectionLn(Runnable section) {
36+
appendSection(section);
37+
appendLn();
38+
}
39+
40+
public void appendSection(Runnable section) {
41+
section.run();
42+
}
43+
44+
public void appendComment(String comment) {
45+
appendComment(comment, 0, 1);
46+
}
47+
48+
public void appendComment(String comment, int newLines) {
49+
appendComment(comment, newLines, 1);
50+
}
51+
52+
public void appendComment(String comment, int newLinesBefore, int newLinesAfter) {
53+
if (comment != null && !comment.isBlank()) {
54+
append("\n".repeat(newLinesBefore));
55+
appendFmt("// %s", comment);
56+
append("\n".repeat(newLinesAfter));
57+
}
58+
}
59+
60+
public void alterIndent(int offset) {
61+
indent = Math.max(0, indent + offset);
62+
}
63+
64+
@Override
65+
public String toString() {
66+
Matcher matcher = Pattern.compile("\t+").matcher(sb.toString());
67+
return matcher.replaceAll("\t");
68+
}
69+
}

0 commit comments

Comments
 (0)