55
66import java .io .*;
77import java .nio .file .Files ;
8+ import java .nio .file .Path ;
89import java .nio .file .Paths ;
910import java .util .*;
1011import java .util .jar .Manifest ;
1112import java .util .zip .ZipEntry ;
1213import java .util .zip .ZipFile ;
14+ import java .util .stream .Stream ;
1315
1416public class DecompileUtil {
1517
@@ -32,42 +34,7 @@ private static void decompileJar(String jarPath, String outputDir, boolean isTar
3234 outputJarDir .mkdirs ();
3335
3436 IResultSaver saver = new ConsoleResultSaver (outputJarDir .getAbsolutePath ());
35-
36- Map <String , Object > options = new HashMap <>();
37- options .put (IFernflowerPreferences .REMOVE_BRIDGE , "0" );
38- options .put (IFernflowerPreferences .DUMP_CODE_LINES , "1" );
39- options .put (IFernflowerPreferences .IGNORE_INVALID_BYTECODE , "1" );
40- options .put (IFernflowerPreferences .VERIFY_ANONYMOUS_CLASSES , "0" );
41- options .put (IFernflowerPreferences .INCLUDE_ENTIRE_CLASSPATH , "0" );
42- options .put (IFernflowerPreferences .TERNARY_CONDITIONS , "1" );
43- options .put (IFernflowerPreferences .FORCE_JSR_INLINE , "1" );
44- options .put (IFernflowerPreferences .DUMP_BYTECODE_ON_ERROR , "1" );
45- options .put (IFernflowerPreferences .DUMP_EXCEPTION_ON_ERROR , "1" );
46- options .put (IFernflowerPreferences .DECOMPILER_COMMENTS , "0" );
47- options .put (IFernflowerPreferences .DECOMPILE_INNER , "1" );
48- options .put (IFernflowerPreferences .DECOMPILE_ASSERTIONS , "1" );
49- options .put (IFernflowerPreferences .HIDE_EMPTY_SUPER , "1" );
50- options .put (IFernflowerPreferences .DECOMPILE_ENUM , "1" );
51- options .put (IFernflowerPreferences .DECOMPILE_PREVIEW , "1" );
52- options .put (IFernflowerPreferences .REMOVE_GET_CLASS_NEW , "1" );
53- options .put (IFernflowerPreferences .DECOMPILE_GENERIC_SIGNATURES , "1" );
54- options .put (IFernflowerPreferences .SKIP_EXTRA_FILES , "1" );
55-
56- Fernflower engine = new Fernflower (saver , options , new IFernflowerLogger () {
57- @ Override
58- public void writeMessage (String message , Severity severity ) {
59- if (severity == Severity .ERROR ) {
60- System .err .println ("[Vineflower] " + message );
61- }
62- }
63-
64- @ Override
65- public void writeMessage (String message , Severity severity , Throwable t ) {
66- if (severity == Severity .ERROR ) {
67- System .err .println ("[Vineflower] " + message + " - " + t .getMessage ());
68- }
69- }
70- });
37+ Fernflower engine = createFernflower (saver );
7138
7239 engine .addSource (jarFile );
7340 engine .decompileContext ();
@@ -160,42 +127,7 @@ private static void decompileJarToSpecificDir(String jarPath, String outputDir,
160127 outputJarDir .mkdirs ();
161128
162129 IResultSaver saver = new ConsoleResultSaver (outputJarDir .getAbsolutePath ());
163-
164- Map <String , Object > options = new HashMap <>();
165- options .put (IFernflowerPreferences .REMOVE_BRIDGE , "0" );
166- options .put (IFernflowerPreferences .DUMP_CODE_LINES , "1" );
167- options .put (IFernflowerPreferences .IGNORE_INVALID_BYTECODE , "1" );
168- options .put (IFernflowerPreferences .VERIFY_ANONYMOUS_CLASSES , "0" );
169- options .put (IFernflowerPreferences .INCLUDE_ENTIRE_CLASSPATH , "0" );
170- options .put (IFernflowerPreferences .TERNARY_CONDITIONS , "1" );
171- options .put (IFernflowerPreferences .FORCE_JSR_INLINE , "1" );
172- options .put (IFernflowerPreferences .DUMP_BYTECODE_ON_ERROR , "1" );
173- options .put (IFernflowerPreferences .DUMP_EXCEPTION_ON_ERROR , "1" );
174- options .put (IFernflowerPreferences .DECOMPILER_COMMENTS , "0" );
175- options .put (IFernflowerPreferences .DECOMPILE_INNER , "1" );
176- options .put (IFernflowerPreferences .DECOMPILE_ASSERTIONS , "1" );
177- options .put (IFernflowerPreferences .HIDE_EMPTY_SUPER , "1" );
178- options .put (IFernflowerPreferences .DECOMPILE_ENUM , "1" );
179- options .put (IFernflowerPreferences .DECOMPILE_PREVIEW , "1" );
180- options .put (IFernflowerPreferences .REMOVE_GET_CLASS_NEW , "1" );
181- options .put (IFernflowerPreferences .DECOMPILE_GENERIC_SIGNATURES , "1" );
182- options .put (IFernflowerPreferences .SKIP_EXTRA_FILES , "1" );
183-
184- Fernflower engine = new Fernflower (saver , options , new IFernflowerLogger () {
185- @ Override
186- public void writeMessage (String message , Severity severity ) {
187- if (severity == Severity .ERROR ) {
188- System .err .println ("[Vineflower] " + message );
189- }
190- }
191-
192- @ Override
193- public void writeMessage (String message , Severity severity , Throwable t ) {
194- if (severity == Severity .ERROR ) {
195- System .err .println ("[Vineflower] " + message + " - " + t .getMessage ());
196- }
197- }
198- });
130+ Fernflower engine = createFernflower (saver );
199131
200132 engine .addSource (jarFile );
201133 engine .decompileContext ();
@@ -221,6 +153,58 @@ private static boolean containsTargetPackage(String jarPath, String targetPackag
221153 return false ;
222154 }
223155 }
156+
157+ public static void decompileClassFileInPlace (String classPath ) throws IOException {
158+ File classFile = new File (classPath );
159+ if (!classFile .exists () || !classFile .isFile ()) {
160+ throw new FileNotFoundException ("Class file not found: " + classPath );
161+ }
162+
163+ File outputDir = classFile .getParentFile ();
164+ if (outputDir == null ) {
165+ outputDir = new File ("." );
166+ }
167+
168+ decompileSourcesInPlace (Collections .singletonList (classFile ), outputDir );
169+ }
170+
171+ public static void decompileClassDirectoryInPlace (String directoryPath ) throws IOException {
172+ File dir = new File (directoryPath );
173+ if (!dir .exists () || !dir .isDirectory ()) {
174+ throw new IOException ("Directory not found: " + directoryPath );
175+ }
176+
177+ List <File > classFiles = new ArrayList <>();
178+ try (Stream <Path > stream = Files .walk (dir .toPath ())) {
179+ stream .filter (Files ::isRegularFile )
180+ .filter (path -> path .toString ().endsWith (".class" ))
181+ .forEach (path -> classFiles .add (path .toFile ()));
182+ }
183+
184+ if (classFiles .isEmpty ()) {
185+ throw new IOException ("No .class files found under directory: " + directoryPath );
186+ }
187+
188+ decompileSourcesInPlace (classFiles , dir );
189+ }
190+
191+ private static void decompileSourcesInPlace (List <File > sources , File outputDir ) throws IOException {
192+ if (sources .isEmpty ()) {
193+ return ;
194+ }
195+
196+ try {
197+ IResultSaver saver = new ConsoleResultSaver (outputDir .getAbsolutePath ());
198+ Fernflower engine = createFernflower (saver );
199+ for (File source : sources ) {
200+ engine .addSource (source );
201+ }
202+ engine .decompileContext ();
203+ engine .clearContext ();
204+ } catch (Exception | OutOfMemoryError e ) {
205+ throw new IOException ("Decompilation failed: " + e .getMessage (), e );
206+ }
207+ }
224208
225209 private static void copyNonClassFiles (String jarPath , String outputDir ) throws IOException {
226210 try (ZipFile zipFile = new ZipFile (jarPath )) {
@@ -255,6 +239,44 @@ private static void copyNonClassFiles(String jarPath, String outputDir) throws I
255239 }
256240 }
257241 }
242+
243+ private static Fernflower createFernflower (IResultSaver saver ) {
244+ Map <String , Object > options = new HashMap <>();
245+ options .put (IFernflowerPreferences .REMOVE_BRIDGE , "0" );
246+ options .put (IFernflowerPreferences .DUMP_CODE_LINES , "1" );
247+ options .put (IFernflowerPreferences .IGNORE_INVALID_BYTECODE , "1" );
248+ options .put (IFernflowerPreferences .VERIFY_ANONYMOUS_CLASSES , "0" );
249+ options .put (IFernflowerPreferences .INCLUDE_ENTIRE_CLASSPATH , "0" );
250+ options .put (IFernflowerPreferences .TERNARY_CONDITIONS , "1" );
251+ options .put (IFernflowerPreferences .FORCE_JSR_INLINE , "1" );
252+ options .put (IFernflowerPreferences .DUMP_BYTECODE_ON_ERROR , "1" );
253+ options .put (IFernflowerPreferences .DUMP_EXCEPTION_ON_ERROR , "1" );
254+ options .put (IFernflowerPreferences .DECOMPILER_COMMENTS , "0" );
255+ options .put (IFernflowerPreferences .DECOMPILE_INNER , "1" );
256+ options .put (IFernflowerPreferences .DECOMPILE_ASSERTIONS , "1" );
257+ options .put (IFernflowerPreferences .HIDE_EMPTY_SUPER , "1" );
258+ options .put (IFernflowerPreferences .DECOMPILE_ENUM , "1" );
259+ options .put (IFernflowerPreferences .DECOMPILE_PREVIEW , "1" );
260+ options .put (IFernflowerPreferences .REMOVE_GET_CLASS_NEW , "1" );
261+ options .put (IFernflowerPreferences .DECOMPILE_GENERIC_SIGNATURES , "1" );
262+ options .put (IFernflowerPreferences .SKIP_EXTRA_FILES , "1" );
263+
264+ return new Fernflower (saver , options , new IFernflowerLogger () {
265+ @ Override
266+ public void writeMessage (String message , Severity severity ) {
267+ if (severity == Severity .ERROR ) {
268+ System .err .println ("[Vineflower] " + message );
269+ }
270+ }
271+
272+ @ Override
273+ public void writeMessage (String message , Severity severity , Throwable t ) {
274+ if (severity == Severity .ERROR ) {
275+ System .err .println ("[Vineflower] " + message + " - " + t .getMessage ());
276+ }
277+ }
278+ });
279+ }
258280
259281 private static class ConsoleResultSaver implements IResultSaver {
260282 private final String outputPath ;
0 commit comments