6
6
import net .neoforged .jst .api .FileSink ;
7
7
import net .neoforged .jst .api .FileSource ;
8
8
import net .neoforged .jst .api .Logger ;
9
+ import net .neoforged .jst .api .Replacement ;
9
10
import net .neoforged .jst .api .Replacements ;
10
11
import net .neoforged .jst .api .SourceTransformer ;
11
12
import net .neoforged .jst .api .TransformContext ;
19
20
import java .nio .file .attribute .FileTime ;
20
21
import java .time .Instant ;
21
22
import java .util .ArrayList ;
23
+ import java .util .Collections ;
22
24
import java .util .List ;
25
+ import java .util .concurrent .atomic .AtomicBoolean ;
23
26
24
27
/**
25
28
* Reference for out-of-IDE usage of the IntelliJ Java parser is from the Kotlin compiler
@@ -63,16 +66,22 @@ public boolean process(FileSource source, FileSink sink, List<SourceTransformer>
63
66
});
64
67
}
65
68
} else {
69
+ var success = new AtomicBoolean (true );
66
70
try (var asyncOut = new OrderedParallelWorkQueue (sink , maxQueueDepth );
67
71
var stream = source .streamEntries ()) {
68
72
stream .forEach (entry -> asyncOut .submitAsync (parallelSink -> {
69
73
try {
70
- processEntry (entry , sourceRoot , transformers , parallelSink );
74
+ if (!processEntry (entry , sourceRoot , transformers , parallelSink )) {
75
+ success .set (false );
76
+ }
71
77
} catch (IOException e ) {
72
78
throw new UncheckedIOException (e );
73
79
}
74
80
}));
75
81
}
82
+ if (!success .get ()) {
83
+ return false ;
84
+ }
76
85
}
77
86
78
87
boolean isOk = true ;
@@ -83,25 +92,31 @@ public boolean process(FileSource source, FileSink sink, List<SourceTransformer>
83
92
return isOk ;
84
93
}
85
94
86
- private void processEntry (FileEntry entry , VirtualFile sourceRoot , List <SourceTransformer > transformers , FileSink sink ) throws IOException {
95
+ private boolean processEntry (FileEntry entry , VirtualFile sourceRoot , List <SourceTransformer > transformers , FileSink sink ) throws IOException {
87
96
if (entry .directory ()) {
88
97
sink .putDirectory (entry .relativePath ());
89
- return ;
98
+ return true ;
90
99
}
100
+
101
+ boolean [] success = {true };
91
102
92
103
try (var in = entry .openInputStream ()) {
93
104
byte [] content = in .readAllBytes ();
94
105
var lastModified = entry .lastModified ();
95
106
96
107
if (!isIgnored (entry .relativePath ()) && !transformers .isEmpty () && entry .hasExtension ("java" )) {
97
108
var orgContent = content ;
98
- content = transformSource (sourceRoot , entry .relativePath (), transformers , content );
109
+ content = transformSource (sourceRoot , entry , transformers , content , success );
110
+ if (!success [0 ]) {
111
+ return false ;
112
+ }
99
113
if (orgContent != content ) {
100
114
lastModified = FileTime .from (Instant .now ());
101
115
}
102
116
}
103
117
sink .putFile (entry .relativePath (), lastModified , content );
104
118
}
119
+ return true ;
105
120
}
106
121
107
122
private boolean isIgnored (String relativePath ) {
@@ -113,11 +128,12 @@ private boolean isIgnored(String relativePath) {
113
128
return false ;
114
129
}
115
130
116
- byte [] transformSource (VirtualFile contentRoot , String path , List <SourceTransformer > transformers , byte [] originalContentBytes ) {
131
+ private byte [] transformSource (VirtualFile contentRoot , FileEntry entry , List <SourceTransformer > transformers , byte [] originalContentBytes , boolean [] successOut ) {
117
132
// Instead of parsing the content we actually read from the file, we read the virtual file that is
118
133
// visible to IntelliJ from adding the source jar. The reasoning is that IntelliJ will cache this internally
119
134
// and reuse it when cross-referencing type-references. If we parsed from a String instead, it would parse
120
135
// the same file twice.
136
+ var path = entry .relativePath ();
121
137
var sourceFile = contentRoot .findFileByRelativePath (path );
122
138
if (sourceFile == null ) {
123
139
System .err .println ("Can't transform " + path + " since IntelliJ doesn't see it in the source jar." );
@@ -130,14 +146,23 @@ byte[] transformSource(VirtualFile contentRoot, String path, List<SourceTransfor
130
146
}
131
147
132
148
// Gather replaced ranges in the source-file with their replacement
133
- var replacements = new Replacements ();
149
+ List <Replacement > replacementsList = new ArrayList <>();
150
+ var replacements = new Replacements (replacementsList );
134
151
135
152
for (var transformer : transformers ) {
136
153
transformer .visitFile (psiFile , replacements );
137
154
}
138
155
156
+ var readOnlyReplacements = Collections .unmodifiableList (replacementsList );
157
+ boolean success = true ;
158
+ for (var transformer : transformers ) {
159
+ success = success && transformer .beforeReplacement (entry , readOnlyReplacements );
160
+ }
161
+
162
+ successOut [0 ] = success ;
163
+
139
164
// If no replacements were made, just stream the original content into the destination file
140
- if (replacements .isEmpty ()) {
165
+ if (! success || replacements .isEmpty ()) {
141
166
return originalContentBytes ;
142
167
}
143
168
0 commit comments