diff --git a/libsrc/ffdec_cli/build.properties b/libsrc/ffdec_cli/build.properties index d7f6b6524..7d7db547a 100644 --- a/libsrc/ffdec_cli/build.properties +++ b/libsrc/ffdec_cli/build.properties @@ -36,4 +36,4 @@ app.dir = ../../dist app.lib.dir = ../../dist/lib ffdec_lib.path = lib/ffdec_lib.jar -additional.libs = lib/gnujpdf.jar \ No newline at end of file +app.jar = ffdec.jar \ No newline at end of file diff --git a/libsrc/ffdec_cli/build.xml b/libsrc/ffdec_cli/build.xml index 5f7901bd8..e0c2ccf8c 100644 --- a/libsrc/ffdec_cli/build.xml +++ b/libsrc/ffdec_cli/build.xml @@ -52,6 +52,7 @@ + @@ -137,7 +138,7 @@ - + diff --git a/libsrc/ffdec_cli/nbproject/project.xml b/libsrc/ffdec_cli/nbproject/project.xml index 4acbcc702..bf9215df6 100644 --- a/libsrc/ffdec_cli/nbproject/project.xml +++ b/libsrc/ffdec_cli/nbproject/project.xml @@ -68,7 +68,7 @@ auxiliary.show.customizer.message= src - lib/jansi-2.4.0.jar;../../lib/ffdec_lib.jar;../../lib/gnujpdf.jar + ../../dist/ffdec.jar build/classes 1.8 diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/CommandlineInterface.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/CommandlineInterface.java index dfa1aaf4e..57952ec2c 100644 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/CommandlineInterface.java +++ b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/CommandlineInterface.java @@ -16,40 +16,19 @@ */ package com.jpexs.decompiler.flash.cli; -import com.jpexs.decompiler.flash.cli.commands.Main; -import java.net.URLDecoder; -import org.fusesource.jansi.AnsiConsole; -import picocli.CommandLine; +import com.jpexs.decompiler.flash.gui.Main; +import java.io.IOException; /** * * @author JPEXS */ -public class CommandlineInterface { - - /** - * To bypass wrong encoded unicode characters coming from EXE, it Launch5j - * encodes characters using URLEncoder. - * - */ - private static void decodeLaunch5jArgs(String[] args) { - String encargs = System.getProperty("l5j.encargs"); - if ("true".equals(encargs) || "1".equals(encargs)) { - for (int i = 0; i < args.length; ++i) { - try { - args[i] = URLDecoder.decode(args[i], "UTF-8"); - } catch (Exception e) { - //ignored - } - } - } - } +public class CommandlineInterface { - public static void main(String[] args) { - decodeLaunch5jArgs(args); - AnsiConsole.systemInstall(); - int exitCode = new CommandLine(new Main()).execute(args); - AnsiConsole.systemUninstall(); - System.exit(exitCode); + public static void main(String[] args) throws IOException { + if (args.length == 0) { + args = new String[] {"--help"}; + } + Main.main(args); } } diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/VersionProvider.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/VersionProvider.java deleted file mode 100644 index 53fe60cdf..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/VersionProvider.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jpexs.decompiler.flash.cli; - -import com.jpexs.decompiler.flash.ApplicationInfo; -import picocli.CommandLine; - -/** - * - * @author JPEXS - */ -public class VersionProvider implements CommandLine.IVersionProvider { - @Override - public String[] getVersion() throws Exception { - return new String[]{ApplicationInfo.applicationVerName}; - } -} \ No newline at end of file diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/AbcMerge.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/AbcMerge.java deleted file mode 100644 index 548892038..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/AbcMerge.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "abcmerge", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Merge all ABC tags in SWF file to one.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli abcmerge input.swf output.swf", - } -) -public class AbcMerge implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Compress.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Compress.java deleted file mode 100644 index eacb57a96..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Compress.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "compress", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Compress SWF file.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli compress input.swf out_compressed.swf", - "ffdec-cli compress --kind=lzma input.swf out_compressed.swf", - } -) -public class Compress implements Runnable { - @Option( - names = "--kind", - description = "Compression kind. @|bold Enum values|@: ${COMPLETION-CANDIDATES} default: zlib" - ) - CompressionKind kind = CompressionKind.zlib; - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Config.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Config.java deleted file mode 100644 index 933749bac..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Config.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import java.awt.Point; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "config", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "List available configuration options.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli config", - } -) -public class Config implements Runnable { - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Decompress.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Decompress.java deleted file mode 100644 index fc1a59d38..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Decompress.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "decompress", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Decompress SWF file.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli decompress input.swf out_decompressed.swf", - } -) -public class Decompress implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Decrypt.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Decrypt.java deleted file mode 100644 index d0671863e..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Decrypt.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "decrypt", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Decrypt HARMAN Air encrypted SWF file.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli decrypt input.swf out_decrypted.swf", - } -) -public class Decrypt implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Deobfuscate.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Deobfuscate.java deleted file mode 100644 index fa422323f..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Deobfuscate.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "deobfuscate", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Deobfuscate AS3 P-code.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli deobfuscate input.swf output.swf", - "ffdec-cli deobfuscate --level=deadcode input.swf output.swf", - }, - sortSynopsis = false -) -public class Deobfuscate implements Runnable { - - - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Option( - names = "--level", - description = { - "Deobfuscation level.", - "@|bold Enum values|@: ${COMPLETION-CANDIDATES}. Default: traps" - } - ) - DeobfuscateLevel level = DeobfuscateLevel.traps; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Dump.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Dump.java deleted file mode 100644 index de4812a92..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Dump.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.DumpKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -/** - * - * @author JPEXS - */ -@Command( - name="dump", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Dump specific data of SWF file to console.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli dump swf c:/files/input.swf", - "ffdec-cli dump as3 c:/files/input.swf", - } -) -public class Dump implements Runnable { - @Parameters( - index = "0", - description = "Kind of data to dump. @|bold Enum values|@: ${COMPLETION-CANDIDATES}" - ) - DumpKind kind; - - @Parameters( - index = "1", - paramLabel = "FILE", - description = "Input file" - ) - String inputFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/EnableDebugging.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/EnableDebugging.java deleted file mode 100644 index f589f4ef2..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/EnableDebugging.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "enabledebugging", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Enable debugging for SWF file.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli enabledebugging input.swf output.swf", - "ffdec-cli enabledebugging --inject-as3 input.swf output.swf", - "ffdec-cli enabledebugging --inject-as3 --pcode input.swf output.swf", - "ffdec-cli enabledebugging --generate-swd input.swf output.swf", - }, - sortSynopsis = false -) -public class EnableDebugging implements Runnable { - - - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @ArgGroup(exclusive = true) - InjectAs3OrGenerateSwd injectAs3OrGenerateSwd; - - @Option(names = "--pcode") - boolean pcode = false; - - static class InjectAs3OrGenerateSwd { - @Option( - names = "--inject-as3", - description = "Inject debugfile and debugline instructions into the code to match decompiled/pcode source.", - required = true - ) - boolean injectAs3; - - @Option( - names = "--generate-swd", - description = "Create SWD file needed for AS1/2 debugging. for , is generated", - required = true - ) - boolean generateSwd; - } - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Export.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Export.java deleted file mode 100644 index bf5055af9..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Export.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands; -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.ConfigConverter; -import com.jpexs.decompiler.flash.cli.commands.types.ExportObject; -import com.jpexs.decompiler.flash.cli.commands.types.ExportObjectFormat; -import com.jpexs.decompiler.flash.cli.commands.types.ExportObjectFormatConverter; -import com.jpexs.decompiler.flash.cli.commands.types.OnErrorMode; -import com.jpexs.decompiler.flash.cli.commands.types.Selection; -import com.jpexs.decompiler.flash.cli.commands.types.SelectionConverter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command(name = "export", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Export sources to a directory.", - //descriptionHeading = "%n@|bold,underline Description|@:%n", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli export --zoom=2 --character-id=51,43 shape:png,shape:bmp c:/out/ c:/files/input.swf", - "ffdec-cli export --frame=2-10 frame:svg c:/out/ c:/files/input.swf", - "ffdec-cli export --embed --class=mypkg.Main,other.+ script c:/out/ c:/files/input.swf", - "ffdec-cli export all c:/out/ c:/files/input.swf", - } - ) -public class Export implements Runnable { - - @ParentCommand - private Main parent; - - @Option(names = "--frame", - paramLabel = "", - converter = SelectionConverter.class, - description = { - "Selected frame(s) to export.", - "@|bold Sample values|@: 1-5 or 2,4 or 2-5,7,9-" - } - ) - private Selection frames = new Selection(); - - @Option(names = "--character-id", - paramLabel = "", - converter = SelectionConverter.class, - description = { - "Selected character id(s) to export.", - "@|bold Sample values|@: 27 or 2-5 or 12,24 or 2-5,10,9-" - } - ) - private Selection characterIds = new Selection(); - - @Option(names = "--class", - paramLabel = "", - split = ",", - description = { - "Selected scripts to export by classname (ActionScript 3 ONLY).", - "@|bold Sample values|@:", - "com.example.MyClass", - "com.example.+ (all classes in package \"com.example\")", - "com.++,net.company.MyClass (all classes in package \"com\" and all subpackages, class net.company.MyClass)", - }) - private List classes = new ArrayList<>(); - - @Option(names = "--embed", - description = { - "Enables exporting embedded assets via [Embed tag].", - "For script:as exports." - } - ) - private boolean useEmbed = false; - - @Option( - names = "--on-error", - description = { - "Error handling mode.", - "@|bold Possible values|@:", - " @|bold abort|@ - stops exporting", - " @|bold retry|@ - retries exporting (see --num-retries option)", - " @|bold ignore|@ - ignores current file" - } - ) - private OnErrorMode onError = OnErrorMode.abort; - - @Option( - names = "--num-retries", - description = "Number of retries for option --on-error=retry. Default is 3." - ) - private int numRetries = 3; - - @Option( - names = "--timeout-method", - description = "Decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds" - ) - Integer methodTimeout = null; - - @Option( - names = "--timeout-total", - description = "Total export timeout in seconds" - ) - Integer totalTimeout = null; - - @Option( - names = "--timeout-file", - description = "Export timeout for a single AS3 class in seconds" - ) - Integer fileTimeout = null; - - @Option( - names = "--stats", - description = "Show export performance statistics" - ) - boolean showStatistics = false; - - @Option ( - names = "--zoom", - paramLabel = "", - description = "Apply zoom level" - ) - Double zoom; - - @Parameters(index = "0", - split = ",", - arity = "1", - converter = ExportObjectFormatConverter.class, - paramLabel = "", - description = {"What objects to export.", - "@|bold Available types and formats|@:", - "script:as|pcode|pcodehex|hex", - "shape:svg|png|canvas|bmp|svg", - "morphshape:svg|canvas", - "frame:png|gif|avi|svg|canvas|pdf|bmp", - "sprite:png|gif|avi|svg|canvas|pdf|bmp", - "button:png|svg|bmp", - "image:png_gif_jpeg|png|jpeg|bmp|png_gif_jpeg_alpha", - "text:plain|formatted|svg", - "sound:mp3_wav_flv|mp3_wav|wav|flv", - "font:ttf|woff", - "font4:cff", - "fla:cs5|cs5.5|cs6|cc", - "xfl:cs5|cs5.5|cs6|cc", - "all (=everything except fla and xfl)" - }) - private List objects; - - @Parameters(index = "1", description = "Target directory", paramLabel = "OUT_DIR") - private String outDirectory; - - @Parameters(index = "2", description = "Input file or directory", paramLabel = "IN_DIR_OR_FILE") - private String inFileOrDirectory; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/FlashPaper2Pdf.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/FlashPaper2Pdf.java deleted file mode 100644 index 6bc5275f8..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/FlashPaper2Pdf.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "flashpaper2pdf", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Convert FlashPaper SWF file to PDF.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli flashpaper2pdf input.swf output.pdf", - "ffdec-cli flashpaper2pdf --zoom=3 input.swf output.pdf", - } -) -public class FlashPaper2Pdf implements Runnable { - @Option ( - names = "--zoom", - paramLabel = "", - description = "Specify image quality" - ) - Double zoom; - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/GenerateDoc.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/GenerateDoc.java deleted file mode 100644 index 8ac2ec4cc..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/GenerateDoc.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.DocFormat; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "generatedoc", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Generate documentation.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli generatedoc --type=as3.pcode.instructions --format=html --locale=en --out=docs.en.html", - "ffdec-cli generatedoc --type=as3.pcode.instructions --format=html --locale=cs --out=docs.cs.html", - }, - sortSynopsis = false -) -public class GenerateDoc implements Runnable { - - @Option( - names = "--type", - description = "can be currently only: as3.pcode.instructions for list of ActionScript3 AVM2 instructions", - required = true - ) - String type; - - @Option( - names = "--format", - description = "Selects output format. Currently only html is supported." - ) - DocFormat format = DocFormat.html; - - @Option( - names = "--locale", - description = "Override default locale. Sample value: en" - ) - String locale = null; - - @Option( - names = "--out", - description = "File to write docs into. If ommited, it is written to stdout." - ) - String outputFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Header.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Header.java deleted file mode 100644 index 83f1b2e24..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Header.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import java.awt.Point; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "header", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Display and manipulate SWF header values.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli header input.swf", - "ffdec-cli header --set gfx=true --set width=1200 --out-file=output.swf input.swf", - "ffdec-cli header --set displayrect=[0,0,800px,600px] input.swf", - } -) -public class Header implements Runnable { - - @Option( - names = "--set", - paramLabel = "=", - description = {"Set values of the SWF header.", - "@|bold Available keys|@: version\n" + - " gfx (true/false)\n" + - " displayrect ([x1,y1,x2,y2])\n" + - " width\n" + - " height\n" + - " framecount\n" + - " framerate"}, - arity = "0..*" - ) - Map setValues; - - @Option( - names = "--out-file", - description = "Output file to write modifications. If ommited, file is modified inplace." - ) - String outFile = null; - - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Import.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Import.java deleted file mode 100644 index 1c6f40a74..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Import.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "import", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Bulk import items to the SWF.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli import sound input.swf output.swf c:/sounds/", - "ffdec-cli import --update-bounds shape input.swf output.swf c:/shapes/", - "ffdec-cli import symbolclass input.swf output.swf c:/data/sc.csv", - }, - sortSynopsis = false -) -public class Import implements Runnable { - - @Option( - names = "--air", - description = "Use AIR (airglobal.swc) for AS3 compilation instead of playerglobal.swc" - ) - boolean air = false; - - @Parameters( - index = "0", - paramLabel = "", - description = { - "Item kind to import.", - "@|bold Enum values|@: ${COMPLETION-CANDIDATES}" - } - ) - ImportObject itemKind; - - @Parameters( - index = "1", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "2", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Parameters( - index = "3", - paramLabel = "DATA_DIR", - description = "Data directory containing imported items. For symbolclass item kind, it is a file instead of directory." - ) - String dataDirectory; - - @Option( - names = "--update-bounds", - description = "For shape import: Update shape bounds (no fill)", - order = 1 - ) - boolean updateShapeBounds; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadata.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadata.java deleted file mode 100644 index e3f64eb29..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadata.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "instancemetadata", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Instance metadata operations.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - sortSynopsis = false, - subcommands = { - InstanceMetadataGet.class, - InstanceMetadataSet.class, - InstanceMetadataRemove.class - } -) -public class InstanceMetadata { - - -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataGet.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataGet.java deleted file mode 100644 index 56cab1328..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataGet.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.InstanceMetadataFormat; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "get", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Get instance metadata.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli instancemetadata get --instance=myitem --key=subkey1 input.swf", - "ffdec-cli instancemetadata get --instance=other --data-file=output.json input.swf", - "ffdec-cli instancemetadata get --instance=third --data-file=output.bin --output-format=raw input.swf", - }, - sortSynopsis = false -) -public class InstanceMetadataGet implements Runnable { - - - @Option( - names = "--output-format", - description = "Format of output. @|bold Enum values|@: ${COMPLETION-CANDIDATES} Default is jslike." - ) - InstanceMetadataFormat outputFormat = InstanceMetadataFormat.jslike; - - - @Option( - names = "--key", - paramLabel = "", - description = "Name of subkey to display. When present, only value from subkey is shown, whole object value otherwise." - ) - String key; - - @Option( - names = "--instance", - description = "Name of instance to fetch metadata from.", - required = true - ) - String instance; - - @Option( - names = "--data-file", - description = "File to write the data to. If ommited, stdout is used." - ) - String outFile = null; - - - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataRemove.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataRemove.java deleted file mode 100644 index d91e744fd..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataRemove.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.InstanceMetadataFormat; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "remove", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Remove instance metadata.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli instancemetadata remove --instance=myitem --key=subkey1 input.swf", - "ffdec-cli instancemetadata remove --instance=other --out-file=output.swf input.swf", - }, - sortSynopsis = false -) -public class InstanceMetadataRemove implements Runnable { - - - @Option( - names = "--key", - paramLabel = "", - description = { - "Name of subkey to remove. When present, only the value from subkey of the AMF object is removed.", - "Otherwise all metadata are removed from the instance." - } - ) - String key; - - @Option( - names = "--instance", - description = "Name of instance to remove data from.", - required = true - ) - String instance; - - @Option( - names = "--out-file", - description = "Where to save resulting file. If ommited, original SWF file is overwritten." - ) - String outFile = null; - - - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataSet.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataSet.java deleted file mode 100644 index 15c029308..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/InstanceMetadataSet.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.DeobfuscateLevel; -import com.jpexs.decompiler.flash.cli.commands.types.ImportObject; -import com.jpexs.decompiler.flash.cli.commands.types.InstanceMetadataFormat; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "set", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Set instance metadata.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli instancemetadata set --instance=myitem --key=subkey1 --value=5 --out-file=output.swf input.swf", - "ffdec-cli instancemetadata set --instance=other --data-file=input.bin --input-format=raw input.swf", - }, - sortSynopsis = false -) -public class InstanceMetadataSet implements Runnable { - - - @Option( - names = "--input-format", - description = "Format of input data. @|bold Enum values|@: ${COMPLETION-CANDIDATES} Default is jslike." - ) - InstanceMetadataFormat inputFormat = InstanceMetadataFormat.jslike; - - - @Option( - names = "--key", - paramLabel = "", - description = { - "Name of subkey to use. When present, the value is set as object property with the name.", - "Otherwise the value is set directly to the instance without any subkeys." - } - ) - String key; - - @ArgGroup(exclusive = true, multiplicity = "1") - ValueOrDataFile valueOrDataFile; - - - static class ValueOrDataFile { - @Option( - names = "--value", - description = "Value to set." - ) - String value = null; - - @Option( - names = "--data-file", - description = "Value to set from file." - ) - String dataFile = null; - - } - - @Option( - names = "--instance", - description = "Name of instance to replace metadata in.", - required = true - ) - String instance; - - @Option( - names = "--out-file", - description = "Where to save resulting file. If ommited, original SWF file is overwritten." - ) - String outFile = null; - - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/LinkReport.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/LinkReport.java deleted file mode 100644 index 439e8f640..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/LinkReport.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "linkreport", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Generate linker report for the swffile.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli linkreport input.swf", - "ffdec-cli linkreport --out-file=out.xml input.swf" - }, - sortSynopsis = false -) -public class LinkReport implements Runnable { - - @Option( - names = "--out-file", - paramLabel = "", - description = "Saves XML report to . When ommited, the report is printed to stdout." - ) - String outFile = null; - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Main.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Main.java deleted file mode 100644 index 5ef7c1cae..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Main.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.ConfigConverter; -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.IVersionProvider; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.HelpCommand; -import picocli.CommandLine.ScopeType; - -/** - * - * @author JPEXS - */ -@Command(name="ffdec-cli", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - subcommands = { - HelpCommand.class, - Export.class, - Dump.class, - Compress.class, - Decompress.class, - Decrypt.class, - Swf2Xml.class, - Xml2Swf.class, - FlashPaper2Pdf.class, - Replace.class, - ReplaceAlpha.class, - ReplaceCharacter.class, - ReplaceCharacterId.class, - Remove.class, - RemoveCharacter.class, - Import.class, - Deobfuscate.class, - EnableDebugging.class, - GenerateDoc.class, - InstanceMetadata.class, - LinkReport.class, - Swf2Swc.class, - AbcMerge.class, - Swf2Exe.class, - Header.class, - Config.class, - Old.class - }, - descriptionHeading = "%n@|bold,underline Description|@:%n", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - description = {"JPEXS Free Flash Decompiler commandline interface"}, - commandListHeading = "%n@|bold,underline Commands|@:%n" - ) -public class Main { - - @Option(names = "--config", - paramLabel = "=[,=...]", - converter = ConfigConverter.class, - description = { - "Set configuration values for this session.", - "Use command 'config' to list available configuration settings. " - }, - scope = ScopeType.INHERIT - ) - private Map configs = new HashMap<>(); - - @Option( - names = "--charset", - description = "Set desired character set for reading/writing SWF files with SWF version <= 5", - scope = ScopeType.INHERIT - ) - String charset = null; - /*@Parameters(paramLabel = "FILE", description = "one or more files to open in GUI") - private File[] files; */ - -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Old.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Old.java deleted file mode 100644 index 1cb8758ea..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Old.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.legacy.CommandLineArgumentParser; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - - -/** - * - * @author JPEXS - */ -@CommandLine.Command( - name = "old", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Old legacy commandline interface.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli old", - "ffdec-cli old -export script -format script:pcode outdir/ file.swf" - }, - sortSynopsis = false -) -public class Old implements Runnable { - - @Parameters ( - index = "0", - arity = "0..*" - ) - String args[] = new String[]{}; - - @Override - public void run() { - if (args.length == 0) { - args = new String[] {"--help"}; - } - try { - CommandLineArgumentParser.parseArguments(args); - } catch (IOException ex) { - Logger.getLogger(Old.class.getName()).log(Level.SEVERE, null, ex); - } - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Remove.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Remove.java deleted file mode 100644 index 6cb7ec87b..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Remove.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "remove", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Remove a tag from the SWF.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli remove --tag-index=12 --tag-index=13 input.swf output.swf", - "ffdec-cli remove --tag-index=5 input.swf output.swf" - }, - sortSynopsis = false -) -public class Remove implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Option( - names = "--tag-index", - description = "Index of tag on SWF timeline", - paramLabel = "", - required = true, - arity = "1..*" - ) - List tagIndices; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/RemoveCharacter.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/RemoveCharacter.java deleted file mode 100644 index fefbe7a09..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/RemoveCharacter.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "removecharacter", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Remove a character tag from the SWF.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli removecharacter --character-id=12 --character-id=29 input.swf output.swf", - "ffdec-cli removecharacter --character-id=13,57 input.swf output.swf", - "ffdec-cli removecharacter --with-dependencies --character-id=130 input.swf output.swf", - - }, - sortSynopsis = false -) -public class RemoveCharacter implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Option( - names = "--with-dependencies", - description = "Remove with dependencies" - ) - boolean withDependencies; - - @Option( - names = "--character-id", - split = ",", - description = "Character id", - paramLabel = "", - required = true, - arity = "1..*" - ) - List characterIds; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Replace.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Replace.java deleted file mode 100644 index 62c3a72e5..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Replace.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "replace", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Replace the data of the specified items.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli replace --character-id=27 --update-bounds --data-file=char27.svg --character-id=43 --data-file=char43.jpg --format=lossless2 input.swf output.swf", - "ffdec-cli replace --character-id=12 --data-file=data.bin input.swf output.swf", - }, - sortSynopsis = false -) -public class Replace implements Runnable { - - @Option( - names = "--air", - description = "Use AIR (airglobal.swc) for AS3 compilation instead of playerglobal.swc" - ) - boolean air = false; - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @ArgGroup(exclusive = false, multiplicity = "1..*") - List items; - - static class Item { - - @ArgGroup(exclusive = true, multiplicity = "1", order = 0) - CharacterIdOrScriptName characterIdOrScriptName; - - @Option( - names = "--update-bounds", - description = "Update shape bounds (no fill)", - order = 1 - ) - boolean updateShapeBounds; - - @Option( - names = "--format", - description = { - "Input format for images or shapes.", - "@|bold Enum values|@: ${COMPLETION-CANDIDATES}" - }, - order = 2 - ) - ReplaceFormat format = null; - - @Option( - names = "--body-id", - description = "Method body index if the imported entity is an AS3 P-Code", - order = 3 - ) - Integer bodyIndex = null; - - @Option( - names = {"--data-file"}, - description = "Imported data file", - required = true, - order = 4 - ) - String dataFile; - } - - static class CharacterIdOrScriptName { - @Option( - names = "--character-id", - description = "Character id. -1 for main timeline sound stream.", - required = true - ) - int characterId; - - @Option( - names = "--script-name", - description = "Name of the script", - required = true - ) - String scriptName; - } - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceAlpha.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceAlpha.java deleted file mode 100644 index 8589fcd39..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceAlpha.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "replacealpha", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Replace the alpha channel of the specified JPEG3/4 tag.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli replacealpha --character-id=13 --data-file=alpha13.bin --character-id=32 --data-file=alpha32.bin input.swf output.swf", - "ffdec-cli replacealpha --character-id=14 --data-file=data.bin input.swf output.swf", - }, - sortSynopsis = false -) -public class ReplaceAlpha implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @ArgGroup(exclusive = false, multiplicity = "1..*") - List items; - - static class Item { - - @Option( - names = "--character-id", - description = "Character id", - required = true, - order = 0 - ) - int characterId; - - @Option( - names = {"--data-file"}, - description = "Imported data file", - required = true, - order = 4 - ) - String dataFile; - } - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceCharacter.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceCharacter.java deleted file mode 100644 index a789c78fb..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceCharacter.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "replacecharacter", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Replace a character tag with other character.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli replacecharacter --old-id=12 --new-id=15 --old-id=56 --new-id=49 input.swf output.swf", - "ffdec-cli replacecharacter --old-id=7 --new-id=9 input.swf output.swf" - }, - sortSynopsis = false -) -public class ReplaceCharacter implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @ArgGroup(exclusive = false, multiplicity = "1..*") - List items; - - static class Item { - - @Option( - names = "--old-id", - description = "Old character id", - required = true, - order = 0 - ) - int oldCharacterId; - - @Option( - names = {"--new-id"}, - description = "New character id", - required = true, - order = 1 - ) - int newCharacterId; - } - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceCharacterId.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceCharacterId.java deleted file mode 100644 index 7823c81da..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/ReplaceCharacterId.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ReplaceFormat; -import java.util.List; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.ArgGroup; - -/** - * - * @author JPEXS - */ -@Command( - name = "replacecharacterid", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Replace a character id with another.", - optionListHeading = "%n@|bold,underline Options|@:%n", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Examples|@:%n", - footer = { - "ffdec-cli replacecharacterid --old-id=12 --new-id=15 --old-id=56 --new-id=49 input.swf output.swf", - "ffdec-cli replacecharacterid --old-id=7 --new-id=9 input.swf output.swf", - "ffdec-cli replacecharacterid --pack input.swf output.swf", - "ffdec-cli replacecharacterid --sort input.swf output.swf" - }, - sortSynopsis = false -) -public class ReplaceCharacterId implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @ArgGroup(exclusive = true) - PackOrSort packOrSort; - - - static class PackOrSort { - @Option( - names = "--pack", - description = "Removes the spaces between the character ids (1,4,3 => 1,3,2)", - required = true - ) - boolean pack; - - @Option( - names = "--sort", - description = "Assigns increasing IDs to the character tags + pack (1,4,3 => 1,2,3)", - required = true - ) - boolean sort; - } - - - @ArgGroup(exclusive = false, multiplicity = "0..*") - List items; - - static class Item { - - @Option( - names = "--old-id", - description = "Old character id", - required = true, - order = 0 - ) - int oldCharacterId; - - @Option( - names = {"--new-id"}, - description = "New character id", - required = true, - order = 1 - ) - int newCharacterId; - } - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Exe.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Exe.java deleted file mode 100644 index 97626cf0c..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Exe.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import com.jpexs.decompiler.flash.cli.commands.types.ExeExportMode; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "swf2exe", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Export SWF to executable file.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli swf2exe --mode=wrapper input.swf output.exe", - } -) -public class Swf2Exe implements Runnable { - - - @Option( - names = "--mode", - description = "Export mode. @|bold Enum values|@: ${COMPLETION-CANDIDATES}", - required = true - ) - ExeExportMode mode; - - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Swc.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Swc.java deleted file mode 100644 index 52e982dce..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Swc.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "swf2swc", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Generate SWC file from SWF.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli swf2swc input.swf output.swc", - } -) -public class Swf2Swc implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Xml.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Xml.java deleted file mode 100644 index f5a59c4b9..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Swf2Xml.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "swf2xml", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Convert SWF file to XML.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli swf2xml input.swf out.xml", - } -) -public class Swf2Xml implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Xml2Swf.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Xml2Swf.java deleted file mode 100644 index ef8c09c30..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/Xml2Swf.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands; - -import com.jpexs.decompiler.flash.cli.VersionProvider; -import com.jpexs.decompiler.flash.cli.commands.types.CompressionKind; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.Parameters; -import picocli.CommandLine.ParentCommand; - -/** - * - * @author JPEXS - */ -@Command( - name = "xml2swf", - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class, - header = "Convert XML file to SWF.", - parameterListHeading = "%n@|bold,underline Parameters|@:%n", - synopsisHeading = "@|bold,underline Usage|@:", - footerHeading = "%n@|bold,underline Example|@:%n", - footer = { - "ffdec-cli xml2swf input.swf out.xml", - } -) -public class Xml2Swf implements Runnable { - @Parameters( - index = "0", - paramLabel = "IN_FILE", - description = "Input file" - ) - String inFile; - - @Parameters( - index = "1", - paramLabel = "OUT_FILE", - description = "Output file" - ) - String outFile; - - @Override - public void run() { - - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/CompressionKind.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/CompressionKind.java deleted file mode 100644 index 714bde1c7..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/CompressionKind.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum CompressionKind { - zlib, lzma -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ConfigConverter.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ConfigConverter.java deleted file mode 100644 index 42b08330b..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ConfigConverter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -import com.jpexs.decompiler.flash.configuration.Configuration; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; -import picocli.CommandLine; - -/** - * - * @author JPEXS - */ -public class ConfigConverter implements CommandLine.ITypeConverter>{ - - @Override - public Map convert(String value) throws Exception { - String[] cfgs; - if (value.contains(",")) { - cfgs = value.split(","); - } else { - cfgs = new String[]{value}; - } - - Map ret = new HashMap<>(); - - for (String c : cfgs) { - String[] cp = c.split("="); - if (cp.length == 1) { - cp = new String[]{cp[0], "1"}; - } - ret.put(cp[0], cp[1]); - } - return ret; - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DeobfuscateLevel.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DeobfuscateLevel.java deleted file mode 100644 index 77e62f2da..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DeobfuscateLevel.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum DeobfuscateLevel { - deadcode, traps -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DocFormat.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DocFormat.java deleted file mode 100644 index 7afe8ac16..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DocFormat.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum DocFormat { - html -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DumpKind.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DumpKind.java deleted file mode 100644 index 03bd450da..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/DumpKind.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum DumpKind { - swf, as2, as3 -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExeExportMode.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExeExportMode.java deleted file mode 100644 index c92006aa7..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExeExportMode.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum ExeExportMode { - wrapper, projector_win, projector_mac, projector_linux -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObject.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObject.java deleted file mode 100644 index 7f3e6b216..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObject.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; -import com.jpexs.decompiler.flash.exporters.modes.Font4ExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; -import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; -import com.jpexs.decompiler.flash.types.sound.SoundExportFormat; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - * @author JPEXS - */ -public enum ExportObject { - SCRIPT(ScriptExportMode.class), - IMAGE(ImageExportMode.class), - SHAPE(ShapeExportMode.class), - MORPHSHAPE(MorphShapeExportMode.class), - MOVIE(MovieExportMode.class), - FONT(FontExportMode.class), - FONT4(Font4ExportMode.class), - FRAME(FrameExportMode.class), - SPRITE(SpriteExportMode.class), - BUTTON(ButtonExportMode.class), - SOUND(SoundExportMode.class), - BINARYDATA(BinaryDataExportMode.class), - SYMBOLCLASS(SymbolClassExportMode.class), - TEXT(TextExportMode.class), - ALL(null), - FLA(FLAVersion.class), - XFL(FLAVersion.class); - - private Class formatsEnum; - - ExportObject(Class formatsEnum) { - this.formatsEnum = formatsEnum; - } - - public List getAllowedFormats() { - if (formatsEnum == null) { - return new ArrayList<>(); - } - return Arrays.asList(formatsEnum.getEnumConstants()); - } - public List getAllowedFormatsAsStr() { - if (formatsEnum == null) { - return new ArrayList<>(); - } - List ret = new ArrayList<>(); - for (Object o : formatsEnum.getEnumConstants()) { - ret.add(o.toString().toLowerCase()); - } - return ret; - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObjectFormat.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObjectFormat.java deleted file mode 100644 index b8d0beddb..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObjectFormat.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public class ExportObjectFormat { - public ExportObject object; - public String format; - - public ExportObjectFormat(ExportObject type, String format) { - this.object = type; - this.format = format; - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObjectFormatConverter.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObjectFormatConverter.java deleted file mode 100644 index 4e3f7a522..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ExportObjectFormatConverter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import picocli.CommandLine; - - -/** - * - * @author JPEXS - */ -public class ExportObjectFormatConverter implements CommandLine.ITypeConverter{ - - @Override - public ExportObjectFormat convert(String value) throws Exception { - Pattern pat = Pattern.compile("^(?[a-z0-9]+)(:(?[a-z0-9]+))?$"); - Matcher mat = pat.matcher(value); - if (!mat.matches()) { - throw new CommandLine.TypeConversionException("Invalid value: must be 'type:format' or 'type' but was '" + value + "'"); - } - String typeStr = mat.group("type"); - String formatStr = mat.group("format"); - ExportObject type = null; - try { - type = ExportObject.valueOf(typeStr.toUpperCase()); - } catch (IllegalArgumentException ex) { - List ts = new ArrayList<>(); - for (ExportObject t : ExportObject.values()) { - ts.add("'" + ts.toString().toLowerCase() + "'"); - } - throw new CommandLine.TypeConversionException("Invalid type: must be one of " + String.join(", ", ts) + " but was '" + typeStr+ "'"); - } - List allowedFormats = type.getAllowedFormatsAsStr(); - if (!allowedFormats.isEmpty() && formatStr != null) { - if (!allowedFormats.contains(formatStr)) { - throw new CommandLine.TypeConversionException("Invalid format: for type '" + type + "' must format be one of '" + String.join("', '", allowedFormats) + "' but was " + formatStr); - } - } - - return new ExportObjectFormat(type, formatStr); - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ImportObject.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ImportObject.java deleted file mode 100644 index 092783707..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ImportObject.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum ImportObject { - symbolclass,movie,sound,shape,image,sprite,text,script -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/InstanceMetadataFormat.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/InstanceMetadataFormat.java deleted file mode 100644 index dd794ef30..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/InstanceMetadataFormat.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum InstanceMetadataFormat { - jslike, raw -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/OnErrorMode.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/OnErrorMode.java deleted file mode 100644 index f827069e5..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/OnErrorMode.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum OnErrorMode { - abort, retry, ignore -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/Range.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/Range.java deleted file mode 100644 index b2a1ecf4c..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/Range.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public class Range { - - public Integer min; - - public Integer max; - - public Range(Integer min, Integer max) { - this.min = min; - this.max = max; - } - - public boolean contains(int index) { - int minimum = min == null ? Integer.MIN_VALUE : min; - int maximum = max == null ? Integer.MAX_VALUE : max; - - return index >= minimum && index <= maximum; - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ReplaceFormat.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ReplaceFormat.java deleted file mode 100644 index bbdca8279..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/ReplaceFormat.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jpexs.decompiler.flash.cli.commands.types; - -/** - * - * @author JPEXS - */ -public enum ReplaceFormat { - lossless, lossless2, jpeg2, jpeg3, jpeg4 -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/Selection.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/Selection.java deleted file mode 100644 index 9e339fa98..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/Selection.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class Selection { - - public List ranges; - - public Selection() { - this.ranges = new ArrayList<>(); - this.ranges.add(new Range(null, null)); - } - - public Selection(List ranges) { - this.ranges = ranges; - } - - public boolean contains(int index) { - for (Range r : ranges) { - if (r.contains(index)) { - return true; - } - } - return false; - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/SelectionConverter.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/SelectionConverter.java deleted file mode 100644 index c14058b56..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/commands/types/SelectionConverter.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2024 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.commands.types; - -import java.util.ArrayList; -import java.util.List; -import picocli.CommandLine; - -/** - * - * @author JPEXS - */ -public class SelectionConverter implements CommandLine.ITypeConverter{ - - @Override - public Selection convert(String value) throws Exception { - List ret = new ArrayList<>(); - - String[] ranges; - if (value.contains(",")) { - ranges = value.split(","); - } else { - ranges = new String[]{value}; - } - for (String r : ranges) { - Integer min = null; - Integer max = null; - if (r.contains("-")) { - String[] ps = r.split("\\-"); - if (ps.length != 2) { - throw new CommandLine.TypeConversionException("Invalid range: " + r); - } - try { - if (!"".equals(ps[0])) { - min = Integer.parseInt(ps[0]); - } - if (!"".equals(ps[1])) { - max = Integer.parseInt(ps[1]); - } - } catch (NumberFormatException nfe) { - throw new CommandLine.TypeConversionException("Invalid range: " + r); - } - } else { - try { - min = Integer.parseInt(r); - max = min; - } catch (NumberFormatException nfe) { - throw new CommandLine.TypeConversionException("Invalid range: " + r); - } - } - ret.add(new Range(min, max)); - } - return new Selection(ret); - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/CommandLineArgumentParser.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/CommandLineArgumentParser.java deleted file mode 100644 index 15c05fa88..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/CommandLineArgumentParser.java +++ /dev/null @@ -1,5104 +0,0 @@ -/* - * Copyright (C) 2010-2023 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.legacy; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.Bundle; -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; -import com.jpexs.decompiler.flash.OpenableSourceInfo; -import com.jpexs.decompiler.flash.ReadOnlyTagList; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFCompression; -import com.jpexs.decompiler.flash.SearchMode; -import com.jpexs.decompiler.flash.SwfOpenException; -import com.jpexs.decompiler.flash.ValueTooLargeException; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; -import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser; -import com.jpexs.decompiler.flash.abc.types.Decimal; -import com.jpexs.decompiler.flash.abc.types.Float4; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser; -import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream; -import com.jpexs.decompiler.flash.amf.amf3.Amf3OutputStream; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; -import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; -import com.jpexs.decompiler.flash.amf.amf3.Traits; -import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import com.jpexs.decompiler.flash.docs.As3PCodeDocs; -import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; -import com.jpexs.decompiler.flash.exporters.DualPdfGraphics2D; -import com.jpexs.decompiler.flash.exporters.Font4Exporter; -import com.jpexs.decompiler.flash.exporters.FontExporter; -import com.jpexs.decompiler.flash.exporters.FrameExporter; -import com.jpexs.decompiler.flash.exporters.ImageExporter; -import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.MovieExporter; -import com.jpexs.decompiler.flash.exporters.ShapeExporter; -import com.jpexs.decompiler.flash.exporters.SoundExporter; -import com.jpexs.decompiler.flash.exporters.SymbolClassExporter; -import com.jpexs.decompiler.flash.exporters.TextExporter; -import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.Font4ExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; -import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; -import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter; -import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.Font4ExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FrameExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SymbolClassExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; -import com.jpexs.decompiler.flash.exporters.swf.SwfToSwcExporter; -import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; -import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer; -import com.jpexs.decompiler.flash.helpers.FileTextWriter; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; -import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; -import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException; -import com.jpexs.decompiler.flash.importers.As3ScriptReplaceExceptionItem; -import com.jpexs.decompiler.flash.importers.As3ScriptReplacerFactory; -import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface; -import com.jpexs.decompiler.flash.importers.BinaryDataImporter; -import com.jpexs.decompiler.flash.importers.FFDecAs3ScriptReplacer; -import com.jpexs.decompiler.flash.importers.FontImporter; -import com.jpexs.decompiler.flash.importers.ImageImporter; -import com.jpexs.decompiler.flash.importers.MorphShapeImporter; -import com.jpexs.decompiler.flash.importers.MovieImporter; -import com.jpexs.decompiler.flash.importers.ScriptImporterProgressListener; -import com.jpexs.decompiler.flash.importers.ShapeImporter; -import com.jpexs.decompiler.flash.importers.SoundImporter; -import com.jpexs.decompiler.flash.importers.SpriteImporter; -import com.jpexs.decompiler.flash.importers.SwfXmlImporter; -import com.jpexs.decompiler.flash.importers.SymbolClassImporter; -import com.jpexs.decompiler.flash.importers.TextImporter; -import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; -import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; -import com.jpexs.decompiler.flash.importers.svg.SvgImporter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; -import com.jpexs.decompiler.flash.tags.ScriptLimitsTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RenderContext; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundImportException; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.base.UnsupportedSamplingRateException; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.OpenableList; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.sound.SoundFormat; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.decompiler.flash.xfl.XFLExportSettings; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.DottedChain; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.stat.StatisticData; -import com.jpexs.helpers.stat.Statistics; -import com.jpexs.helpers.streams.SeekableInputStream; -import com.jpexs.helpers.utf8.Utf8Helper; -import com.jpexs.decompiler.flash.exporters.swf.SwfToExeExporter; -/*import com.jpexs.process.Process; -import com.jpexs.process.ProcessTools; -import com.sun.jna.Platform; -import com.sun.jna.platform.win32.Kernel32;*/ -import gnu.jpdf.PDFGraphics; -import gnu.jpdf.PDFJob; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.print.PageFormat; -import java.awt.print.Paper; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Field; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.Stack; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * @author JPEXS - */ -public class CommandLineArgumentParser { - - private static final Logger logger = Logger.getLogger(CommandLineArgumentParser.class.getName()); - - private static boolean commandLineMode = false; - - private static boolean showStat = false; - - private static String stdOut = null; - - private static String stdErr = null; - - private static final String METADATA_FORMAT_JSLIKE = "jslike"; - - private static final String METADATA_FORMAT_RAW = "raw"; - - public static boolean isCommandLineMode() { - return commandLineMode; - } - - public static void printConfigurationSettings() { - Map fields = Configuration.getConfigurationFields(); - String[] keys = new String[fields.size()]; - keys = fields.keySet().toArray(keys); - Arrays.sort(keys); - - System.out.println("Available keys[current setting]-type:"); - for (String name : keys) { - Field field = fields.get(name); - if (ConfigurationItem.isInternal(field)) { - continue; - } - - ConfigurationItem item = ConfigurationItem.getItem(field); - Object value = item.get(); - Class type = ConfigurationItem.getConfigurationFieldType(field); - String valueString = objectToString(value, type); - String typeString = objectTypeToString(type); - - if (typeString != null) { - System.out.println(name + "[" + valueString + "]-" + typeString); - } - } - } - - private static String objectTypeToString(Class type) { - if (type == String.class) { - return "string"; - } else if (type == Calendar.class) { - return "date"; - } else if ((type == Integer.class) || (type == Long.class)) { - return "integer"; - } else if ((type == Double.class) || (type == Float.class)) { - return "float"; - } else if (type == Boolean.class) { - return "bool"; - } else if (type.isEnum()) { - return "enum"; - } - - return null; - } - - private static String objectToString(Object obj, Class type) { - if (obj == null) { - return "null"; - } - - if (type == String.class) { - //return '"' + obj.toString() + '"'; - return obj.toString(); - } else if (type == Calendar.class) { - return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(((Calendar) obj).getTime()); - } else if ((type == Integer.class) || (type == Long.class) || (type == Double.class) || (type == Float.class)) { - return obj.toString(); - } else if (type == Boolean.class) { - return ((boolean) (Boolean) obj) ? "true" : "false"; - } else if (type.isEnum()) { - return obj.toString(); - } - - return null; - } - - public static void printCmdLineUsage(String filter, boolean webHelp) { - printCmdLineUsage(System.out, webHelp, filter); - } - - public static void printCmdLineUsage(PrintStream out, boolean webHelp, String filter) { - int cnt = 1; - out.println("Commandline arguments:"); - if (filter == null) { - out.println(" " + (cnt++) + ") -help | --help | /?"); - out.println(" ...shows commandline arguments (this help)"); - out.println(" " + (cnt++) + ") [ ...]"); - out.println(" ...opens SWF file(s) with the decompiler GUI"); - } - - if (filter == null || filter.equals("proxy")) { - out.println(" " + (cnt++) + ") -proxy [-P]"); - out.println(" ...auto start proxy in the tray. Optional parameter -P specifies port for proxy. Defaults to 55555. "); - } - - if (filter == null || filter.equals("export")) { - out.println(" " + (cnt++) + ") -export "); - out.println(" ...export sources to ."); - out.println(" Exports all files from when it is a folder."); - out.println(" Values for parameter:"); - out.println(" script - Scripts (Default format: ActionScript source)"); - out.println(" image - Images (Default format: PNG/JPEG)"); - out.println(" shape - Shapes (Default format: SVG)"); - out.println(" morphshape - MorphShapes (Default format: SVG)"); - out.println(" movie - Movies (Default format: FLV without sound)"); - out.println(" font - Fonts (Default format: TTF)"); - out.println(" font4 - DefineFont4 (Default format: CFF)"); - out.println(" frame - Frames (Default format: PNG)"); - out.println(" sprite - Sprites (Default format: PNG)"); - out.println(" button - Buttons (Default format: PNG)"); - out.println(" sound - Sounds (Default format: MP3/WAV/FLV only sound)"); - out.println(" binaryData - Binary data (Default format: Raw data)"); - out.println(" symbolClass - Symbol-Class mapping (Default format: CSV)"); - out.println(" text - Texts (Default format: Plain text)"); - out.println(" all - Every resource (but not FLA and XFL)"); - out.println(" fla - Everything to FLA compressed format"); - out.println(" xfl - Everything to uncompressed FLA format (XFL)"); - out.println(" You can export multiple types of items by using colon \",\""); - out.println(" DO NOT PUT space between comma (,) and next value."); - out.println(); - } - - if (filter == null || filter.equals("format")) { - out.println(" " + (cnt++) + ") -format "); - out.println(" ...sets output formats for export"); - out.println(" Values for parameter:"); - out.println(" script:as - ActionScript source"); - out.println(" script:pcode - ActionScript P-code"); - out.println(" script:pcodehex - ActionScript P-code with hex"); - out.println(" script:hex - ActionScript Hex only"); - out.println(" shape:svg - SVG format for Shapes"); - out.println(" shape:png - PNG format for Shapes"); - out.println(" shape:canvas - HTML5 Canvas format for Shapes"); - out.println(" shape:bmp - BMP format for Shapes"); - out.println(" morphshape:svg - SVG format for MorphShapes"); - out.println(" morphshape:canvas - HTML5 Canvas format for MorphShapes"); - out.println(" frame:png - PNG format for Frames"); - out.println(" frame:gif - GIF format for Frames"); - out.println(" frame:avi - AVI format for Frames"); - out.println(" frame:svg - SVG format for Frames"); - out.println(" frame:canvas - HTML5 Canvas format for Frames"); - out.println(" frame:pdf - PDF format for Frames"); - out.println(" frame:bmp - BMP format for Frames"); - out.println(" sprite:png - PNG format for Sprites"); - out.println(" sprite:gif - GIF format for Sprites"); - out.println(" sprite:avi - AVI format for Sprites"); - out.println(" sprite:svg - SVG format for Sprites"); - out.println(" sprite:canvas - HTML5 Canvas format for Sprites"); - out.println(" sprite:pdf - PDF format for Sprites"); - out.println(" sprite:bmp - BMP format for Sprites"); - out.println(" button:png - PNG format for Buttons"); - out.println(" button:svg - SVG format for Buttons"); - out.println(" button:bmp - BMP format for Buttons"); - out.println(" image:png_gif_jpeg - PNG/GIF/JPEG format for Images"); - out.println(" image:png - PNG format for Images"); - out.println(" image:jpeg - JPEG format for Images"); - out.println(" image:bmp - BMP format for Images"); - out.println(" image:png_gif_jpeg_alpha - PNG/GIF/JPEG+ALPHA format for Images"); - out.println(" text:plain - Plain text format for Texts"); - out.println(" text:formatted - Formatted text format for Texts"); - out.println(" text:svg - SVG format for Texts"); - out.println(" sound:mp3_wav_flv - MP3/WAV/FLV format for Sounds"); - out.println(" sound:mp3_wav - MP3/WAV format for Sounds"); - out.println(" sound:wav - WAV format for Sounds"); - out.println(" sound:flv - FLV format for Sounds"); - out.println(" font:ttf - TTF format for Fonts"); - out.println(" font:woff - WOFF format for Fonts"); - out.println(" font4:cff - CFF format for DefineFont4"); - out.println(" fla: or xfl: - Specify FLA format version"); - out.println(" - values for : cs5,cs5.5,cs6,cc"); - out.println(" You can set multiple formats at once using comma (,)"); - out.println(" DO NOT PUT space between comma (,) and next value."); - out.println(" The prefix with colon (:) is neccessary."); - } - - if (filter == null || filter.equals("cli")) { - out.println(" " + (cnt++) + ") -cli"); - out.println(" ...Command line mode. Parses the SWFs without opening the GUI"); - } - - if (filter == null || filter.equals("select")) { - out.println(" " + (cnt++) + ") -select "); - out.println(" ...selects frames/pages for export"); - out.println(" Example formats:"); - out.println(" 1-5"); - out.println(" 2,3"); - out.println(" 2-5,7,9-"); - out.println(" DO NOT PUT space between comma (,) and next ramge."); - out.println(" " + (cnt++) + ") -selectid "); - out.println(" ...selects characters for export by character id"); - out.println(" format is same as in -select"); - } - - if (filter == null || filter.equals("selectclass")) { - out.println(" " + (cnt++) + ") -selectclass "); - out.println(" ...selects scripts to export by class name (ActionScript 3 ONLY)"); - out.println(" format:"); - out.println(" com.example.MyClass"); - out.println(" com.example.+ (all classes in package \"com.example\")"); - out.println(" com.++,net.company.MyClass (all classes in package \"com\" and all subpackages, class net.company.MyClass)"); - out.println(" DO NOT PUT space between comma (,) and next class."); - } - - if (filter == null || filter.equals("exportembed")) { - out.println(" " + (cnt++) + ") -exportembed"); - out.println(" ...Allows exporting embedded assets via [Embed tag]"); - out.println(" Use in combination with -export -format script:as"); - } - - if (filter == null || filter.equals("dumpswf")) { - out.println(" " + (cnt++) + ") -dumpSWF "); - out.println(" ...dumps list of SWF tags to console"); - } - - if (filter == null || filter.equals("dumpas2")) { - out.println(" " + (cnt++) + ") -dumpAS2 "); - out.println(" ...dumps list of AS1/2 scripts to console"); - } - - if (filter == null || filter.equals("dumpas3")) { - out.println(" " + (cnt++) + ") -dumpAS3 "); - out.println(" ...dumps list of AS3 scripts to console"); - } - - if (filter == null || filter.equals("compress")) { - out.println(" " + (cnt++) + ") -compress [(zlib|lzma)]"); - out.println(" ...Compress SWF and save it to . If is already compressed, it will be re-compressed. Default compression method is ZLIB"); - } - - if (filter == null || filter.equals("decompress")) { - out.println(" " + (cnt++) + ") -decompress "); - out.println(" ...Decompress and save it to "); - } - - if (filter == null || filter.equals("encrypt")) { - out.println(" " + (cnt++) + ") -encrypt "); - out.println(" ...Encrypts file with HARMAN Air encryption and saves it to "); - } - - if (filter == null || filter.equals("decrypt")) { - out.println(" " + (cnt++) + ") -decrypt "); - out.println(" ...Decrypts HARMAN Air encrypted file and saves it to "); - } - - if (filter == null || filter.equals("swf2xml")) { - out.println(" " + (cnt++) + ") -swf2xml "); - out.println(" ...Converts the SWF to XML file"); - } - - if (filter == null || filter.equals("xml2swf")) { - out.println(" " + (cnt++) + ") -xml2swf "); - out.println(" ...Converts the XML to SWF file"); - } - - if (filter == null || filter.equals("extract")) { - out.println(" " + (cnt++) + ") -extract [-o |] [nocheck] [(all|biggest|smallest|first|last)]"); - out.println(" ...Extracts SWF files from ZIP or other binary files"); - out.println(" ...-o parameter should contain a file path when \"biggest\" or \"first\" parameter is specified"); - out.println(" ...-o parameter should contain a folder path when no extaction mode or \"all\" parameter is specified"); - } - - if (filter == null || filter.equals("memorysearch")) { - out.println(" " + (cnt++) + ") -memorySearch (|) (|)..."); - out.println(" ...Search SWF files in the memory"); - } - - if (filter == null || filter.equals("renameinvalididentifiers")) { - out.println(" " + (cnt++) + ") -renameInvalidIdentifiers (typeNumber|randomWord) "); - out.println(" ...Renames the invalid identifiers in and save it to "); - } - - if (filter == null || filter.equals("config")) { - out.println(" " + (cnt++) + ") -config key=value[,key2=value2][,key3=value3...] [other parameters]"); - out.print(" ...Sets configuration values. Use -listconfigs command to list the available configuration settings."); - out.println(); - out.println(" Values are boolean, you can use 0/1, true/false, on/off or yes/no."); - out.println(" If no other parameters passed, configuration is saved. Otherwise it is used only once."); - out.println(" DO NOT PUT space between comma (,) and next value."); - } - - if (filter == null || filter.equals("onerror")) { - out.println(" " + (cnt++) + ") -onerror (abort|retryN|ignore)"); - out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file"); - } - - if (filter == null || filter.equals("timeout")) { - out.println(" " + (cnt++) + ") -timeout "); - out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds"); - } - - if (filter == null || filter.equals("exporttimeout")) { - out.println(" " + (cnt++) + ") -exportTimeout "); - out.println(" ...total export timeout in seconds"); - } - - if (filter == null || filter.equals("exportfiletimeout")) { - out.println(" " + (cnt++) + ") -exportFileTimeout "); - out.println(" ...export timeout for a single AS3 class in seconds"); - } - - if (filter == null || filter.equals("stat")) { - out.println(" " + (cnt++) + ") -stat"); - out.println(" ...show export performance statistics"); - } - - if (filter == null || filter.equals("flashpaper2pdf")) { - out.println(" " + (cnt++) + ") -flashpaper2pdf "); - out.println(" ...converts FlashPaper SWF file to PDF . Use -zoom parameter to specify image quality."); - } - - if (filter == null || filter.equals("zoom")) { - out.println(" " + (cnt++) + ") -zoom "); - out.println(" ...apply zoom during export"); - } - - if (filter == null || filter.equals("replace")) { - out.println(" " + (cnt++) + ") -replace (|) [nofill] ([][]) [(|) [nofill] ([][])]..."); - out.println(" ...replaces the data of the specified BinaryData, Image, Shape, Text, Sound tag or Script"); - out.println(" ...nofill parameter can be specified only for shape replace"); - out.println(" ... parameter can be specified for Image and Shape tags"); - out.println(" ...valid formats: lossless, lossless2, jpeg2, jpeg3, jpeg4"); - out.println(" ... parameter should be specified if and only if the imported entity is an AS3 P-Code"); - out.println(" ...use -1 as characterId to replace main timeline SoundStreamHead"); - - out.println(" " + (cnt++) + ") -replace "); - out.println(" ... same as -replace command, but the rest of arguments is read as lines from a text file "); - - } - - if (filter == null || filter.equals("replacealpha")) { - out.println(" " + (cnt++) + ") -replaceAlpha [ ]..."); - out.println(" ...replaces the alpha channel of the specified JPEG3 or JPEG4 tag"); - } - - if (filter == null || filter.equals("replacecharacter")) { - out.println(" " + (cnt++) + ") -replaceCharacter [ ]..."); - out.println(" ...replaces a character tag with another character tag from the same SWF"); - } - - if (filter == null || filter.equals("replacecharacterid")) { - out.println(" " + (cnt++) + ") -replaceCharacterId ,,,... or"); - out.println(" " + (cnt++) + ") -replaceCharacterId (pack|sort)"); - out.println(" ...replaces the character id with "); - out.println(" ...pack: removes the spaces between the character ids (1,4,3 => 1,3,2)"); - out.println(" ...sort: assigns increasing IDs to the character tags + pack (1,4,3 => 1,2,3)"); - out.println(" DO NOT PUT space between comma (,) and next value."); - } - - if (filter == null || filter.equals("remove")) { - out.println(" " + (cnt++) + ") -remove []..."); - out.println(" ...removes a tag from the SWF"); - } - - if (filter == null || filter.equals("removecharacter")) { - out.println(" " + (cnt++) + ") -removeCharacter[WithDependencies] []..."); - out.println(" ...removes a character tag from the SWF"); - } - - if (filter == null || filter.equals("importsymbolclass")) { - out.println(" " + (cnt++) + ") -importSymbolClass "); - out.println(" ...imports Symbol-Class mapping to and saves the result to "); - } - - if (filter == null || filter.equals("importmovies")) { - out.println(" " + (cnt++) + ") -importMovies "); - out.println(" ...imports movies to and saves the result to "); - } - - if (filter == null || filter.equals("importsounds")) { - out.println(" " + (cnt++) + ") -importSounds "); - out.println(" ...imports sounds to and saves the result to "); - } - - if (filter == null || filter.equals("importshapes")) { - out.println(" " + (cnt++) + ") -importShapes [nofill] "); - out.println(" ...imports shapes to and saves the result to "); - } - - if (filter == null || filter.equals("importimages")) { - out.println(" " + (cnt++) + ") -importImages "); - out.println(" ...imports images to and saves the result to "); - } - - if (filter == null || filter.equals("importsprites")) { - out.println(" " + (cnt++) + ") -importSprites "); - out.println(" ...imports sprites to and saves the result to "); - } - - if (filter == null || filter.equals("importtext")) { - out.println(" " + (cnt++) + ") -importText "); - out.println(" ...imports texts to and saves the result to "); - } - - if (filter == null || filter.equals("importscript")) { - out.println(" " + (cnt++) + ") -importScript "); - out.println(" ...imports scripts to and saves the result to "); - } - - if (filter == null || filter.equals("deobfuscate")) { - out.println(" " + (cnt++) + ") -deobfuscate "); - out.println(" ...Deobfuscates AS3 P-code in and saves result to "); - out.println(" ... can be one of: traps/2/max, deadcode/1"); - out.println(" ...WARNING: The deobfuscation result is still probably far enough to be openable by other decompilers."); - } - - if (filter == null || filter.equals("enabledebugging")) { - out.println(" " + (cnt++) + ") -enabledebugging [-injectas3|-generateswd] [-pcode] "); - out.println(" ...Enables debugging for and saves result to "); - out.println(" ...-injectas3 (optional) causes debugfile and debugline instructions to be injected into the code to match decompiled/pcode source."); - out.println(" ...-generateswd (optional) parameter creates SWD file needed for AS1/2 debugging. for , is generated"); - out.println(" ...-pcode (optional) parameter specified after -injectas3 or -generateswd causes lines to be handled as lines in P-code => All P-code lines are injected, etc."); - out.println(" ...WARNING: Injected/SWD script filenames may be different than from standard compiler"); - } - - if (filter == null || filter.equals("custom")) { - out.println(" " + (cnt++) + ") -custom []..."); - out.println(" ...Forwards all parameters after the -custom parameter to the plugins"); - } - - if (filter == null || filter.equals("doc")) { - out.println(" " + (cnt++) + ") -doc -type [-out ] [-format ] [-locale ]"); - out.println(" ...Generate documentation"); - out.println(" ...-type Selects documentation type"); - out.println(" ... can be currently only: as3.pcode.instructions for list of ActionScript3 AVM2 instructions"); - out.println(" ...-out (optional) If specified, output is written to instead of stdout"); - out.println(" ...-format (optional, html is default) Selects output format"); - out.println(" ... is currently only html"); - out.println(" ...-locale (optional) Override default locale"); - out.println(" ... is localization identifier, en for english for example"); - out.println(" ... is currently only html"); - } - - if (filter == null || filter.equals("getinstancemetadata")) { - out.println(" " + (cnt++) + ") -getInstanceMetadata -instance [-outputFormat ] [-key ] [-datafile ] "); - out.println(" ...reads instance metadata"); - out.println(" ...-instance : name of instance to fetch metadata from"); - out.println(" ...-outputFormat (optional): format of output - one of: jslike|raw. Default is jslike."); - out.println(" ...- key (optional): name of subkey to display. When present, only value from subkey is shown, whole object value otherwise."); - out.println(" ...-datafile (optional): File to write the data to. If ommited, stdout is used."); - out.println(" ...: SWF file to read metadata from"); - } - - if (filter == null || filter.equals("setinstancemetadata")) { - out.println(" " + (cnt++) + ") -setInstanceMetadata -instance [-inputFormat ] [-key ] [-value | -datafile ] [-outfile ] "); - out.println(" ...adds metadata to instance"); - out.println(" ...-instance : name of instance to replace data in"); - out.println(" ...-inputFormat : format of input data - one of: jslike|raw. Default is jslike."); - out.println(" ...- key (optional): name of subkey to use. When present, the value is set as object property with the name."); - out.println(" Otherwise the value is set directly to the instance without any subkeys."); - out.println(" ...-value (optional): value to set."); - out.println(" ...-datafile (optional): value to set from file."); - out.println(" ...If no -value or -infile parameter present, the value to set is taken from stdin."); - out.println(" ...-outfile (optional): Where to save resulting file. If ommited, original SWF file is overwritten."); - out.println(" ...: SWF file to search instance in"); - } - - if (filter == null || filter.equals("removeinstancemetadata")) { - out.println(" " + (cnt++) + ") -removeInstanceMetadata -instance [-key ] [-outfile ] "); - out.println(" ...removes metadata from instance"); - out.println(" ...-instance : name of instance to remove data from"); - out.println(" ...- key (optional): name of subkey to remove. When present, only the value from subkey of the AMF object is removed."); - out.println(" Otherwise all metadata are removed from the instance."); - out.println(" ...-outfile (optional): Where to save resulting file. If ommited, original SWF file is overwritten."); - out.println(" ...: SWF file to search instance in"); - } - - if (filter == null || filter.equals("linkreport")) { - out.println(" " + (cnt++) + ") -linkReport [-outfile ] "); - out.println(" ...generates linker report for the swffile"); - out.println(" ...-outfile (optional): Saves XML report to . When ommited, the report is printed to stdout."); - out.println(" ...: SWF file to search instance in"); - } - - if (filter == null || filter.equals("swf2swc")) { - out.println(" " + (cnt++) + ") -swf2swc "); - out.println(" ...generates SWC file from SWF"); - out.println(" ...: Where to save SWC file"); - out.println(" ...: Input SWF file"); - } - - if (filter == null || filter.equals("abcmerge")) { - out.println(" " + (cnt++) + ") -abcmerge "); - out.println(" ...merge all ABC tags in SWF file to one"); - out.println(" ...: Where to save merged file"); - out.println(" ...: Input SWF file"); - } - - if (filter == null || filter.equals("swf2exe")) { - out.println(" " + (cnt++) + ") -swf2exe "); - out.println(" ...export SWF to executable file"); - out.println(" ...: wrapper|projector_win||projector_mac|projector_linux"); - } - - if (filter == null || filter.equals("charset")) { - out.println(" " + (cnt++) + ") -charset "); - out.println(" ...sets desired character set for reading/writing SWF files with SWF version <= 5"); - out.println(" (use in combination with other commands)"); - } - - if (filter == null || filter.equals("air")) { - out.println(" " + (cnt++) + ") -air"); - out.println(" ...use AIR (airglobal.swc) for AS3 compilation instead of playerglobal.swc"); - out.println(" (use in combination with other commands)"); - } - - if (filter == null || filter.equals("header")) { - out.println(" " + (cnt++) + ") -header -set [-set ...] []"); - out.println(" ...prints header or sets SWF header values (with -set arguments) in and saves it to "); - out.println(" Available keys: version"); - out.println(" gfx (true/false)"); - out.println(" displayrect ([x1,y1,x2,y2])"); - out.println(" width"); - out.println(" height"); - out.println(" framecount"); - out.println(" framerate"); - out.println(" For width, height and displayrect subvalues you can use suffix px for pixel values. Otherwise its twips."); - } - - printCmdLineUsageExamples(out, filter); - - System.out.println("You can use special value \"/dev/stdin\" for input files to read data from standard input (even on Windows)"); - } - - private static void printCmdLineUsageExamples(PrintStream out, String filter) { - out.println(); - out.println("Examples:"); - - final String PREFIX = "ffdec-cli old "; - - boolean exampleFound = false; - if (filter == null) { - out.println(PREFIX + "myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("proxy")) { - out.println(PREFIX + "-proxy"); - out.println(PREFIX + "-proxy -P1234"); - exampleFound = true; - } - - if (filter == null || filter.equals("export") || filter.equals("format") || filter.equals("selectclass") || filter.equals("onerror")) { - out.println(PREFIX + "-export script \"C:\\decompiled\" myfile.swf"); - out.println(PREFIX + "-selectclass com.example.MyClass,com.example.SecondClass -export script \"C:\\decompiled\" myfile.swf"); - out.println(PREFIX + "-format script:pcode -export script \"C:\\decompiled\" myfile.swf"); - out.println(PREFIX + "-format script:pcode,text:plain -export script,text,image \"C:\\decompiled\" myfile.swf"); - out.println(PREFIX + "-format fla:cs5.5 -export fla \"C:\\sources\\myfile.fla\" myfile.swf"); - out.println(PREFIX + "-onerror ignore -export script \"C:\\decompiled\" myfile.swf"); - out.println(PREFIX + "-onerror retry 5 -export script \"C:\\decompiled\" myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("cli")) { - out.println(PREFIX + "-cli myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("dumpswf")) { - out.println(PREFIX + "-dumpSWF myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("compress")) { - out.println(PREFIX + "-compress myfile.swf myfilecomp.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("decompress")) { - out.println(PREFIX + "-decompress myfile.swf myfiledec.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("config")) { - out.println(PREFIX + "-config autoDeobfuscate=1,parallelSpeedUp=0 -export script \"C:\\decompiled\" myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("deobfuscate")) { - out.println(PREFIX + "-deobfuscate max myas3file_secure.swf myas3file.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("enabledebugging")) { - out.println(PREFIX + "-enabledebugging -injectas3 myas3file.swf myas3file_debug.swf"); - out.println(PREFIX + "-enabledebugging -generateswd myas2file.swf myas2file_debug.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("doc")) { - out.println(PREFIX + "-doc -type as3.pcode.instructions -format html"); - out.println(PREFIX + "-doc -type as3.pcode.instructions -format html -locale en -out as3_docs_en.html"); - exampleFound = true; - } - - if (filter == null || filter.equals("getinstancemetadata")) { - out.println(PREFIX + "-getInstanceMetadata -instance myobj -key keyone myfile.swf"); - out.println(PREFIX + "-getInstanceMetadata -instance myobj2 -outputFormat raw -outfile out.amf myfile.swf"); - exampleFound = true; - } - if (filter == null || filter.equals("setinstancemetadata")) { - out.println(PREFIX + "-setInstanceMetadata -instance myobj -key mykey -value 1234 myfile.swf"); - out.println(PREFIX + "-setInstanceMetadata -instance myobj -key my -inputFormat raw -datafile value.amf -outfile modified.swf myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("removeinstancemetadata")) { - out.println(PREFIX + "-removeInstanceMetadata -instance myobj -key mykey -outfile result.swf myfile.swf"); - out.println(PREFIX + "-removeInstanceMetadata -instance myobj myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("swf2exe")) { - out.println(PREFIX + "-swf2exe wrapper result.exe myfile.swf"); - exampleFound = true; - } - - if (filter == null || filter.equals("header")) { - out.println(PREFIX + "-header myfile.swf"); - out.println(PREFIX + "-header -set version 10 -set width 800px -set framerate 23.5 myfile.swf outfile.swf"); - out.println(PREFIX + "-header -set displayrect [0,0,800px,600px] myfile.swf outfile.swf"); - out.println(PREFIX + "-header -set gfx true myfile.swf outfile.swf"); - exampleFound = true; - } - - if (!exampleFound) { - out.println("Sorry, no example found for command " + filter + ", Let us know in issue tracker when you need it."); - } - - out.println(); - } - - /** - * Parses the console arguments - * - * @param arguments Arguments - * @return paths to the file which should be opened or null - * @throws java.io.IOException On error - */ - public static String[] parseArguments(String[] arguments) throws IOException { - Level traceLevel = Level.WARNING; - Stack args = new Stack<>(); - for (int i = arguments.length - 1; i >= 0; i--) { - String arg = arguments[i]; - if (arg.length() > 0) { - args.add(arg); - } - } - - AbortRetryIgnoreHandler handler = null; - Map format = new HashMap<>(); - double zoom = 1; - String charset = Charset.defaultCharset().name(); - boolean cliMode = false; - boolean air = false; - boolean exportEmbed = false; - Selection selection = new Selection(); - Selection selectionIds = new Selection(); - List selectionClasses = null; - String nextParam = null; - String nextParamOriginal = null; - OUTER: - while (true) { - nextParamOriginal = args.pop(); - if (nextParamOriginal != null) { - nextParam = nextParamOriginal.toLowerCase(Locale.ENGLISH); - } - if (nextParam == null) { - nextParam = ""; - } - switch (nextParam) { - case "-air": - air = true; - break; - case "-cli": - cliMode = true; - break; - case "-selectid": - selectionIds = parseSelect(args); - break; - case "-select": - selection = parseSelect(args); - break; - case "-selectclass": - selectionClasses = parseSelectClass(args); - break; - case "-exportembed": - exportEmbed = true; - break; - case "-zoom": - zoom = parseZoom(args); - break; - case "-format": - format = parseFormat(args); - break; - case "-charset": - charset = parseCharset(args); - break; - case "-config": - parseConfig(args); - if (args.isEmpty()) { - Configuration.saveConfig(); - System.out.println("Configuration saved"); - return null; - } - break; - case "-onerror": - handler = parseOnError(args); - break; - case "-timeout": - parseTimeout(args); - break; - case "-exporttimeout": - parseExportTimeout(args); - break; - case "-exportfiletimeout": - parseExportFileTimeout(args); - break; - case "-stat": - parseStat(args); - break; - case "-info": - parseInfo(args, charset); - System.exit(0); - break; - case "-stdout": - parseStdOut(args); - break; - case "-stderr": - parseStdErr(args); - break; - /*case "-affinity": - parseAffinity(args); - break; - case "-priority": - parsePriority(args); - break;*/ - case "-verbose": - traceLevel = Level.FINE; - break; - case "-debug": - for (int i = 0; i < arguments.length; i++) { - System.out.println(i + ".:" + arguments[i]); - } - Configuration._debugMode.set(true); - break; - default: - break OUTER; - } - if (args.isEmpty()) { - return null; - } - } - - String command = ""; - if (nextParam == null) { - nextParam = ""; - } - if (nextParam.startsWith("-")) { - command = nextParam.substring(1); - } - - if (command.equals("header")) { - parseHeader(args); - System.exit(0); - } /*else if (command.equals("translator")) { - Translator.main(new String[]{}); - } */ - else if (command.equals("swf2exe")) { - parseSwf2Exe(args, charset); - System.exit(0); - } else if (command.equals("abcmerge")) { - parseAbcMerge(args, charset); - System.exit(0); - } else if (command.equals("swf2swc")) { - parseSwf2Swc(args, charset); - System.exit(0); - } else if (command.equals("linkreport")) { - parseLinkReport(selectionClasses, args, charset); - System.exit(0); - } else if (command.equals("getinstancemetadata")) { - parseGetInstanceMetadata(args, charset); - System.exit(0); - } else if (command.equals("setinstancemetadata")) { - parseSetInstanceMetadata(args, charset); - System.exit(0); - } else if (command.equals("removeinstancemetadata")) { - parseRemoveInstanceMetadata(args, charset); - System.exit(0); - } /*else if (command.equals("removefromcontextmenu")) { - if (!args.isEmpty()) { - badArguments(command); - } - ContextMenuTools.addToContextMenu(false, true); - System.exit(0); - } else if (command.equals("addtocontextmenu")) { - if (!args.isEmpty()) { - badArguments(command); - } - ContextMenuTools.addToContextMenu(true, true); - System.exit(0); - } else if (command.equals("proxy")) { - parseProxy(args); - } */ else if (command.equals("export")) { - parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed); - System.exit(0); - } else if (command.equals("compress")) { - parseCompress(args); - System.exit(0); - } else if (command.equals("decompress")) { - parseDecompress(args); - System.exit(0); - } else if (command.equals("encrypt")) { - parseDecrypt(args); - System.exit(0); - } else if (command.equals("decrypt")) { - parseDecrypt(args); - System.exit(0); - } else if (command.equals("swf2xml")) { - parseSwf2Xml(args, charset); - System.exit(0); - } else if (command.equals("xml2swf")) { - parseXml2Swf(args, charset); - System.exit(0); - } else if (command.equals("extract")) { - parseExtract(args); - System.exit(0); - } /*else if (command.equals("memorysearch")) { - parseMemorySearch(args); - System.exit(0); - } */ else if (command.equals("deobfuscate")) { - parseDeobfuscate(args, charset); - System.exit(0); - } else if (command.equals("renameinvalididentifiers")) { - parseRenameInvalidIdentifiers(args); - System.exit(0); - } /*else if (command.equals("dumpswf")) { - parseDumpSwf(args); - System.exit(0); - } */ else if (command.equals("dumpas2")) { - parseDumpAS2(args, charset); - System.exit(0); - } else if (command.equals("dumpas3")) { - parseDumpAS3(args, charset); - System.exit(0); - } else if (command.equals("enabledebugging")) { - parseEnableDebugging(args, charset); - System.exit(0); - } else if (command.equals("flashpaper2pdf")) { - parseFlashPaperToPdf(selection, zoom, args, charset); - System.exit(0); - } else if (command.equals("replace")) { - parseReplace(args, charset, air); - System.exit(0); - } else if (command.equals("replacealpha")) { - parseReplaceAlpha(args, charset); - System.exit(0); - } else if (command.equals("replacecharacter")) { - parseReplaceCharacter(args, charset); - System.exit(0); - } else if (command.equals("replacecharacterid")) { - parseReplaceCharacterId(args, charset); - System.exit(0); - } else if (command.equals("convert")) { - parseConvert(args, charset); - System.exit(0); - } else if (command.equals("remove")) { - parseRemove(args, charset); - System.exit(0); - } else if (command.equals("removecharacter")) { - parseRemoveCharacter(args, false, charset); - System.exit(0); - } else if (command.equals("removecharacterwithdependencies")) { - parseRemoveCharacter(args, true, charset); - System.exit(0); - } else if (command.equals("doc")) { - parseDoc(args); - } else if (command.equals("importsymbolclass")) { - parseImportSymbolClass(args, charset); - System.exit(0); - } else if (command.equals("importmovies")) { - parseImportMovies(args, charset); - System.exit(0); - } else if (command.equals("importsounds")) { - parseImportSounds(args, charset); - System.exit(0); - } else if (command.equals("importshapes")) { - parseImportShapes(args, charset); - System.exit(0); - } else if (command.equals("importimages")) { - parseImportImages(args, charset); - System.exit(0); - } else if (command.equals("importsprites")) { - parseImportSprites(args, charset); - System.exit(0); - } else if (command.equals("importtext")) { - parseImportText(args, charset); - System.exit(0); - } else if (command.equals("importscript")) { - parseImportScript(args, charset, air, handler); - System.exit(0); - } else if (command.equals("as3compiler")) { - ActionScript3Parser.compile(null /*?*/, args.pop(), args.pop(), 0, 0); - } /*else if (nextParam.equals("--debugtool")) { - parseDebugTool(args, charset); - } else if (nextParam.equals("--compareresources")) { - parseCompareResources(args); - System.exit(0); - } else if (nextParam.equals("--resourcedates")) { - parseResourceDates(args); - System.exit(0); - } */else if (nextParam.equals("-listconfigs")) { - printHeader(); - printConfigurationSettings(); - System.exit(0); - } else if (nextParam.equals("-help") || nextParam.equals("--help") || nextParam.equals("/?") || nextParam.equals("\\_") /* /? translates as this on windows */) { - printHeader(); - printCmdLineUsage(null, false); - System.exit(0); - } else if (nextParam.equals("--webhelp")) { //for generating commandline usage on webpages - ByteArrayOutputStream whbaos = new ByteArrayOutputStream(); - printCmdLineUsage(new PrintStream(whbaos, true), true, null); - String wh = new String(whbaos.toByteArray()); - wh = wh.replace("<", "<").replace(">", ">"); - System.out.println(wh); - System.exit(0); - } else { - args.push(nextParamOriginal); // file names should be the original one - List fileNames = new ArrayList<>(); - boolean allParamIsAFile = true; - while (!args.isEmpty()) { - String arg = args.pop(); - if (arg.equals("-custom")) { - parseCustom(args); - break; - } - - fileNames.add(arg); - File file = new File(arg); - if (!file.exists() || !file.isFile()) { - allParamIsAFile = false; - } - } - - if (allParamIsAFile) { - String[] fileNamesArray = fileNames.toArray(new String[fileNames.size()]); - if (cliMode) { - //loadFiles(fileNamesArray); - return null; - } else { - return fileNamesArray; - } - } else { - badArguments(); - } - } - - return null; - } - - public static void printHeader() { - System.out.println(ApplicationInfo.applicationVerName); - for (int i = 0; i < ApplicationInfo.applicationVerName.length(); i++) { - System.out.print("-"); - } - System.out.println(); - - System.err.println("WARNING: You are using outdated CLI interface. Please use other commands than 'ffde-cli old'."); - } - - public static void badArguments() { - badArguments(null); - } - - public static void badArguments(String command) { - System.err.println("Error: Bad Commandline Arguments!"); - printCmdLineUsage(command, false); - System.exit(1); - } - - @SuppressWarnings("unchecked") - private static void setConfigurations(String cfgStr) { - String[] cfgs; - if (cfgStr.contains(",")) { - cfgs = cfgStr.split(","); - } else { - cfgs = new String[]{cfgStr}; - } - - Map fields = Configuration.getConfigurationFields(true); - for (String c : cfgs) { - String[] cp = c.split("="); - if (cp.length == 1) { - cp = new String[]{cp[0], "1"}; - } - - Field field = fields.get(cp[0].toLowerCase(Locale.ENGLISH)); - ConfigurationItem item = ConfigurationItem.getItem(field); - String stringValue = cp[1]; - Class type = ConfigurationItem.getConfigurationFieldType(field); - - if (type == String.class) { - System.out.println("Config " + item.getName() + " set to " + stringValue); - ((ConfigurationItem) item).set(stringValue); - } else if (type == Calendar.class) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - Date dateValue; - try { - dateValue = dateFormat.parse(stringValue); - GregorianCalendar calendarValue = new GregorianCalendar(); - calendarValue.setTime(dateValue); - ((ConfigurationItem) item).set(calendarValue); - } catch (ParseException ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - } - } else if (type == Integer.class) { - int intValue = Integer.parseInt(stringValue); - ((ConfigurationItem) item).set(intValue); - } else if ((type == Integer.class) || (type == Long.class)) { - long longValue = Long.parseLong(stringValue); - ((ConfigurationItem) item).set(longValue); - } else if (type == Double.class) { - double doubleValue = Double.parseDouble(stringValue); - ((ConfigurationItem) item).set(doubleValue); - } else if (type == Float.class) { - float floatValue = Float.parseFloat(stringValue); - ((ConfigurationItem) item).set(floatValue); - } else if (type == Boolean.class) { - Boolean boolValue = parseBooleanConfigValue(stringValue); - if (boolValue != null) { - System.out.println("Config " + item.getName() + " set to " + boolValue); - ((ConfigurationItem) item).set(boolValue); - } else { - System.out.println("Invalid config value for " + item.getName() + ": " + stringValue); - } - } else if (type.isEnum()) { - Enum enumValue = Enum.valueOf((Class) type, stringValue); - ConfigurationItem uncheckedItem = (ConfigurationItem) item; - uncheckedItem.set(enumValue); - } - } - } - - private static Boolean parseBooleanConfigValue(String value) { - if (value == null) { - return null; - } - - Boolean bValue = null; - value = value.toLowerCase(Locale.ENGLISH); - if (value.equals("0") || value.equals("false") || value.equals("no") || value.equals("off")) { - bValue = false; - } - - if (value.equals("1") || value.equals("true") || value.equals("yes") || value.equals("on")) { - bValue = true; - } - - return bValue; - } - - private static void parseConfig(Stack args) { - if (args.isEmpty()) { - System.err.println("Config values expected"); - badArguments("config"); - } - - setConfigurations(args.pop()); - } - - private static void parseSwf2Exe(Stack args, String charset) { - if (args.size() != 3) { - badArguments("swf2exe"); - } - final String type = args.pop(); - final File outFile = new File(args.pop()); - final File swfFile = new File(args.pop()); - ExeExportMode exportMode = enumFromStr(type, ExeExportMode.class); - processReadSWF(swfFile, null, (SWF swf, OutputStream stdout) -> { - SwfToExeExporter.saveFileToExe(swf, exportMode, outFile); - }, charset); - } - - private static void parseAbcMerge(Stack args, String charset) { - if (args.size() < 2) { - badArguments("abcmerge"); - } - final File outFile = new File(args.pop()); - final File swfFile = new File(args.pop()); - processModifySWF(swfFile, outFile, null, (SWF swf, OutputStream stdout) -> { - List abcList = swf.getAbcList(); - if (!abcList.isEmpty() && abcList.size() > 1) { - ABC firstAbc = abcList.get(0).getABC(); - for (int i = 1; i < abcList.size(); i++) { - firstAbc.mergeABC(abcList.get(i).getABC()); - } - for (int i = 1; i < abcList.size(); i++) { - swf.removeTag((Tag) abcList.get(i)); - } - } - }, charset); - - } - - private static void parseSwf2Swc(Stack args, String charset) { - if (args.size() < 2) { - badArguments("swf2swc"); - } - final File outFile = new File(args.pop()); - final File swfFile = new File(args.pop()); - processReadSWF(swfFile, null, (SWF swf, OutputStream stdout) -> { - SwfToSwcExporter exporter = new SwfToSwcExporter(); - try { - exporter.exportSwf(swf, outFile, false); - } catch (IOException | InterruptedException ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - } - }, charset); - } - - private static void parseLinkReport(List selectionClasses, Stack args, String charset) { - if (args.isEmpty()) { - badArguments("linkreport"); - } - File stdOutFile = null; - File swfFile = null; - while (!args.isEmpty()) { - String paramName = args.pop().toLowerCase(Locale.ENGLISH); - switch (paramName) { - case "-outfile": - if (args.empty()) { - System.err.println("Missing output file"); - badArguments("linkreport"); - } - stdOutFile = new File(args.pop()); - break; - default: - if (!args.isEmpty()) { - badArguments("linkreport"); - } - swfFile = new File(paramName); - } - } - if (swfFile == null) { - System.err.println("No SWF file specified"); - badArguments("getinstancemetadata"); - } - processReadSWF(swfFile, stdOutFile, (SWF swf, OutputStream stdout) -> { - - LinkReportExporter lre = new LinkReportExporter(); - - List reportPacks; - try { - reportPacks = selectionClasses != null ? swf.getScriptPacksByClassNames(selectionClasses) : swf.getAS3Packs(); - } catch (Exception ex) { - System.err.println("Error while getting packs"); - System.exit(1); - return; - } - try { - String reportStr = lre.generateReport(swf, reportPacks, null); - stdout.write(reportStr.getBytes("UTF-8")); - } catch (InterruptedException ex) { - System.err.println("Report generation interrupted"); - } - }, charset); - } - - private static void parseGetInstanceMetadata(Stack args, String charset) { - if (args.size() < 3) { - badArguments("getinstancemetadata"); - } - Set processedParams = new HashSet<>(); - String format = METADATA_FORMAT_JSLIKE; - String key = null; - String instance = null; - File stdOutFile = null; - File swfFile = null; - - while (!args.empty()) { - String paramName = args.pop().toLowerCase(Locale.ENGLISH); - if (processedParams.contains(paramName)) { - System.err.println("Parameter " + paramName + " can appear only once."); - } - switch (paramName) { - case "-instance": - if (args.isEmpty()) { - System.err.println("Missing instance name"); - badArguments("getinstancemetadata"); - } - instance = args.pop(); - break; - case "-outputformat": - if (args.empty()) { - System.err.println("Missing format value"); - badArguments("getinstancemetadata"); - } - format = args.pop(); - if (!Arrays.asList(METADATA_FORMAT_RAW, METADATA_FORMAT_JSLIKE).contains(format)) { - System.err.println("Invalid output format"); - badArguments("getinstancemetadata"); - } - break; - case "-key": - if (args.empty()) { - System.err.println("Missing key value"); - badArguments("getinstancemetadata"); - } - - key = args.pop(); - break; - case "-datafile": - if (args.empty()) { - System.err.println("Missing datafile file"); - badArguments("getinstancemetadata"); - } - stdOutFile = new File(args.pop()); - break; - default: - if (!args.isEmpty()) { - badArguments("getinstancemetadata"); - } - swfFile = new File(paramName); - paramName = null; - } - if (paramName != null) { - processedParams.add(paramName); - } - } - if (instance == null) { - System.err.println("No instance specified"); - badArguments("getinstancemetadata"); - } - if (swfFile == null) { - System.err.println("No SWF file specified"); - badArguments("getinstancemetadata"); - } - - final String fInstance = instance; - final String fKey = key; - final String fFormat = format; - - processReadSWF(swfFile, stdOutFile, new SwfAction() { - @Override - public void swfAction(SWF swf, OutputStream stdout) throws IOException { - if (!processTimelined(swf, stdout)) { - System.err.println("No instance with name " + fInstance + " found"); - System.exit(0); - } - } - - private boolean processTimelined(Timelined tim, OutputStream stdout) throws IOException { - ReadOnlyTagList rtl = tim.getTags(); - for (int i = 0; i < rtl.size(); i++) { - Tag t = rtl.get(i); - if (t instanceof Timelined) { - if (processTimelined((Timelined) t, stdout)) { - return true; - } - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - String instanceName = pt.getInstanceName(); - if (fInstance.equals(instanceName)) { - Amf3Value oldValue = pt.getAmfData(); - if (oldValue == null) { - System.err.println("No metadata for instance " + instanceName + " found"); - System.exit(1); //TODO? Different exit code - } - Object actualValue = oldValue.getValue(); - - Object displayVal = actualValue; - if (fKey != null) { - if (actualValue instanceof ObjectType) { - ObjectType ot = (ObjectType) actualValue; - if (ot.containsDynamicMember(fKey)) { - displayVal = ot.getDynamicMember(fKey); - } else { - System.err.println("No value with key " + fKey + " exists"); - System.err.println("Available keys: " + String.join(",", ot.dynamicMembersKeySet())); - System.exit(1); - } - } else { - System.err.println("Metadata present, but not as Object type, cannot get key " + fKey); - System.exit(1); - } - } - - switch (fFormat) { - case METADATA_FORMAT_JSLIKE: - stdout.write(Utf8Helper.getBytes(Amf3Exporter.amfToString(displayVal, " ", System.lineSeparator()) + System.lineSeparator())); - break; - case METADATA_FORMAT_RAW: - Amf3OutputStream aos = new Amf3OutputStream(stdout); - try { - aos.writeValue(displayVal); - } catch (NoSerializerExistsException ex) { - //should not happen - } - break; - } - return true; - } - } - } - return false; - } - }, charset); - } - - private static void parseSetInstanceMetadata(Stack args, String charset) { - if (args.size() < 3) { - badArguments("setinstancemetadata"); - } - Set processedParams = new HashSet<>(); - String format = METADATA_FORMAT_JSLIKE; - String key = null; - String instance = null; - File outFile = null; - File swfFile = null; - String value = null; - File valueFile = null; - - while (!args.empty()) { - String paramName = args.pop().toLowerCase(Locale.ENGLISH); - if (processedParams.contains(paramName)) { - System.err.println("Parameter " + paramName + " can appear only once."); - } - switch (paramName) { - case "-instance": - if (args.isEmpty()) { - System.err.println("Missing instance name"); - badArguments("setinstancemetadata"); - } - instance = args.pop(); - break; - case "-inputformat": - if (args.empty()) { - System.err.println("Missing format value"); - badArguments("setinstancemetadata"); - } - format = args.pop(); - if (!Arrays.asList(METADATA_FORMAT_RAW, METADATA_FORMAT_JSLIKE).contains(format)) { - System.err.println("Invalid output format"); - badArguments("setinstancemetadata"); - } - break; - case "-key": - if (args.empty()) { - System.err.println("Missing key value"); - badArguments("setinstancemetadata"); - } - - key = args.pop(); - break; - case "-value": - if (args.empty()) { - System.err.println("Missing value"); - badArguments("setinstancemetadata"); - } - - value = args.pop(); - break; - case "-outfile": - if (args.empty()) { - System.err.println("Missing outFile"); - badArguments("setinstancemetadata"); - } - outFile = new File(args.pop()); - break; - - case "-datafile": - if (args.empty()) { - System.err.println("Missing datafile file"); - badArguments("setinstancemetadata"); - } - valueFile = new File(args.pop()); - break; - default: - if (!args.isEmpty()) { - badArguments("setinstancemetadata"); - } - swfFile = new File(paramName); - paramName = null; - } - if (paramName != null) { - processedParams.add(paramName); - } - } - if (instance == null) { - System.err.println("No instance specified"); - badArguments("getinstancemetadata"); - } - if (swfFile == null) { - System.err.println("No SWF file specified"); - badArguments("getinstancemetadata"); - } - if (outFile == null) { - outFile = swfFile; - } - - byte[] valueBytes = new byte[]{}; - if (valueFile != null) { - try { - valueBytes = Helper.readFileEx(valueFile.getAbsolutePath()); - } catch (IOException ex) { - System.err.println("Cannot read value: " + ex.getMessage()); - System.exit(1); - return; - } - } else if (value != null) { - valueBytes = Utf8Helper.getBytes(value); - } - - if (valueBytes.length == 0) { - valueBytes = Helper.readStream(System.in); - } - - if (valueBytes.length < 1) { - System.err.println("No value to set specified"); - System.exit(1); - } - - Object amfValue = null; - try { - switch (format) { - case METADATA_FORMAT_JSLIKE: - Amf3Importer importer = new Amf3Importer(); - amfValue = importer.stringToAmf(value); - break; - case METADATA_FORMAT_RAW: - Amf3InputStream ais = new Amf3InputStream(new MemoryInputStream(valueBytes)); - amfValue = ais.readValue("val"); - break; - } - } catch (IOException | Amf3ParseException | NoSerializerExistsException ex) { - System.err.println("Error parsing input value: " + ex.getMessage()); - System.exit(1); - return; - } - - final String fInstance = instance; - final String fKey = key; - final Object fAmfValue = amfValue; - - processModifySWF(swfFile, outFile, null, new SwfAction() { - @Override - public void swfAction(SWF swf, OutputStream stdout) throws IOException { - if (!processTimelined(swf, stdout)) { - System.err.println("No instance with name " + fInstance + " found"); - System.exit(0); - } - } - - private boolean processTimelined(Timelined tim, OutputStream stdout) throws IOException { - ReadOnlyTagList rtl = tim.getTags(); - for (int i = 0; i < rtl.size(); i++) { - Tag t = rtl.get(i); - if (t instanceof Timelined) { - if (processTimelined((Timelined) t, stdout)) { - return true; - } - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - String instanceName = pt.getInstanceName(); - if (fInstance.equals(instanceName)) { - - Amf3Value oldValue = pt.getAmfData(); - if (oldValue != null && oldValue.getValue() == null) { - oldValue = null; - } - if (oldValue != null && fKey != null) { //it has AMFData and we are going to set key - Object actualValue = oldValue.getValue(); - if (actualValue instanceof ObjectType) { //add it to ObjectType - ObjectType ot = (ObjectType) actualValue; - ot.putDynamicMember(fKey, fAmfValue); - t.setModified(true); - oldValue.setValue(ot); - System.out.println("Key " + fKey + " added"); - System.out.println("New instance data for " + instanceName + ":"); - System.out.println(Amf3Exporter.amfToString(ot, " ", System.lineSeparator())); - return true; - } - } - - PlaceObject4Tag pt4; - if (pt instanceof PlaceObject4Tag) { - pt4 = (PlaceObject4Tag) pt; - } else { - pt4 = new PlaceObject4Tag( - pt.getSwf(), pt.flagMove(), pt.getDepth(), pt.getClassName(), pt.getCharacterId(), pt.getMatrix(), pt.getColorTransform() == null ? null : new CXFORMWITHALPHA(pt.getColorTransform()), pt.getRatio(), - pt.getInstanceName(), pt.getClipDepth(), pt.getFilters(), pt.getBlendMode(), pt.getBitmapCache(), pt.getVisible(), pt.getBackgroundColor(), pt.getClipActions(), pt.getAmfData(), pt.hasImage()); - tim.replaceTag(i, pt4); - } - - Object newValue; - if (fKey != null) { - ObjectType ot = new ObjectType(new Traits("", true, new ArrayList<>())); - ot.put(fKey, fAmfValue); - newValue = ot; - } else { - newValue = fAmfValue; - } - pt4.amfData = new Amf3Value(newValue); - pt4.setModified(true); - - System.out.println("New instance data for " + instanceName + ":"); - System.out.println(Amf3Exporter.amfToString(newValue, " ", System.lineSeparator())); - - return true; - } - } - } - return false; - } - }, charset); - } - - private static void parseRemoveInstanceMetadata(Stack args, String charset) { - if (args.size() < 2) { - badArguments("removeinstancemetadata"); - } - - Set processedParams = new HashSet<>(); - String key = null; - String instance = null; - File swfFile = null; - File outFile = null; - while (!args.empty()) { - String paramName = args.pop().toLowerCase(Locale.ENGLISH); - if (processedParams.contains(paramName)) { - System.err.println("Parameter " + paramName + " can appear only once."); - } - switch (paramName) { - case "-instance": - if (args.isEmpty()) { - System.err.println("Missing instance name"); - badArguments("removeinstancemetadata"); - } - instance = args.pop(); - break; - case "-key": - if (args.empty()) { - System.err.println("Missing key value"); - badArguments("removeinstancemetadata"); - } - - key = args.pop(); - break; - case "-outfile": - if (args.empty()) { - System.err.println("Missing outFile"); - badArguments("removeinstancemetadata"); - } - outFile = new File(args.pop()); - break; - default: - if (!args.isEmpty()) { - badArguments("removeinstancemetadata"); - } - swfFile = new File(paramName); - paramName = null; - } - if (paramName != null) { - processedParams.add(paramName); - } - } - if (instance == null) { - System.err.println("No instance specified"); - badArguments("removeinstancemetadata"); - } - if (swfFile == null) { - System.err.println("No SWF file specified"); - badArguments("removeinstancemetadata"); - } - if (outFile == null) { - outFile = swfFile; - } - - final String fInstance = instance; - final String fKey = key; - - processModifySWF(swfFile, outFile, null, new SwfAction() { - @Override - public void swfAction(SWF swf, OutputStream stdout) throws IOException { - if (!processTimelined(swf, stdout)) { - System.err.println("No instance with name " + fInstance + " found"); - System.exit(0); - } - } - - private boolean processTimelined(Timelined tim, OutputStream stdout) throws IOException { - ReadOnlyTagList rtl = tim.getTags(); - for (int i = 0; i < rtl.size(); i++) { - Tag t = rtl.get(i); - if (t instanceof Timelined) { - if (processTimelined((Timelined) t, stdout)) { - return true; - } - } - if (t instanceof PlaceObject4Tag) { - PlaceObject4Tag pt4 = (PlaceObject4Tag) t; - String instanceName = pt4.getInstanceName(); - if (fInstance.equals(instanceName)) { - Amf3Value oldValue = pt4.getAmfData(); - if (oldValue == null) { - System.err.println("No metadata for instance " + instanceName + " found"); - System.exit(1); //TODO? Different exit code - } - Object actualValue = oldValue.getValue(); - - if (fKey != null) { - if (actualValue instanceof ObjectType) { - ObjectType ot = (ObjectType) actualValue; - if (ot.containsDynamicMember(fKey)) { - ot.remove(fKey); - oldValue.setValue(ot); - System.out.println("Key " + fKey + " removed"); - System.out.println("New instance data for " + instanceName + ":"); - System.out.println(Amf3Exporter.amfToString(ot, " ", System.lineSeparator())); - pt4.setModified(true); - return true; - } else { - System.err.println("No value with key " + fKey + " exists"); - System.err.println("Available keys: " + String.join(",", ot.dynamicMembersKeySet())); - System.exit(1); - } - } else { - System.err.println("Metadata present, but not as Object type, cannot remove key " + fKey); - System.exit(1); - } - } else { - pt4.amfData = null; - pt4.setModified(true); - System.out.println("Whole metadata removed for instance " + instanceName); - } - - return true; - } - } - } - return false; - } - }, charset); - } - - private static class Range { - - public Integer min; - - public Integer max; - - public Range(Integer min, Integer max) { - this.min = min; - this.max = max; - } - - public boolean contains(int index) { - int minimum = min == null ? Integer.MIN_VALUE : min; - int maximum = max == null ? Integer.MAX_VALUE : max; - - return index >= minimum && index <= maximum; - } - } - - private static class Selection { - - public List ranges; - - public Selection() { - this.ranges = new ArrayList<>(); - this.ranges.add(new Range(null, null)); - } - - public Selection(List ranges) { - this.ranges = ranges; - } - - public boolean contains(int index) { - for (Range r : ranges) { - if (r.contains(index)) { - return true; - } - } - return false; - } - } - - private static Selection parseSelect(Stack args) { - List ret = new ArrayList<>(); - if (args.isEmpty()) { - System.err.println("range parameter expected"); - badArguments("select"); - } - String range = args.pop(); - String[] ranges; - if (range.contains(",")) { - ranges = range.split(","); - } else { - ranges = new String[]{range}; - } - for (String r : ranges) { - Integer min = null; - Integer max = null; - if (r.contains("-")) { - String[] ps = r.split("\\-"); - if (ps.length != 2) { - System.err.println("invalid range"); - badArguments("select"); - } - try { - if (!"".equals(ps[0])) { - min = Integer.parseInt(ps[0]); - } - if (!"".equals(ps[1])) { - max = Integer.parseInt(ps[1]); - } - } catch (NumberFormatException nfe) { - System.err.println("invalid range"); - badArguments("select"); - } - } else { - try { - min = Integer.parseInt(r); - max = min; - } catch (NumberFormatException nfe) { - System.err.println("invalid range"); - badArguments("select"); - } - } - ret.add(new Range(min, max)); - } - return new Selection(ret); - } - - private static double parseZoom(Stack args) { - if (args.isEmpty()) { - System.err.println("zoom parameter expected"); - badArguments("zoom"); - } - try { - return Double.parseDouble(args.pop()); - } catch (NumberFormatException nfe) { - System.err.println("invalid zoom"); - badArguments("zoom"); - } - return 1; - } - - private static AbortRetryIgnoreHandler parseOnError(Stack args) { - int errorMode = AbortRetryIgnoreHandler.UNDEFINED; - int retryCount = 0; - - if (args.isEmpty()) { - System.err.println("onerror parameter expected"); - badArguments("onerror"); - } - String errorModeParameter = args.pop(); - switch (errorModeParameter) { - case "abort": - errorMode = AbortRetryIgnoreHandler.ABORT; - break; - case "retry": - errorMode = AbortRetryIgnoreHandler.RETRY; - if (args.isEmpty()) { - System.err.println("onerror retry count parameter expected"); - badArguments("onerror"); - } - - try { - retryCount = Integer.parseInt(args.pop()); - } catch (NumberFormatException nex) { - System.err.println("Bad retry count number"); - } - break; - case "ignore": - errorMode = AbortRetryIgnoreHandler.IGNORE; - break; - } - - return new ConsoleAbortRetryIgnoreHandler(errorMode, retryCount); - } - - private static void parseTimeout(Stack args) { - if (args.isEmpty()) { - System.err.println("timeout parameter expected"); - badArguments("timeout"); - } - try { - int timeout = Integer.parseInt(args.pop()); - Configuration.decompilationTimeoutSingleMethod.set(timeout); - } catch (NumberFormatException nex) { - System.err.println("Bad timeout value"); - } - } - - private static void parseExportTimeout(Stack args) { - if (args.isEmpty()) { - System.err.println("timeout parameter expected"); - badArguments("exporttimeout"); - } - try { - int timeout = Integer.parseInt(args.pop()); - Configuration.exportTimeout.set(timeout); - } catch (NumberFormatException nex) { - System.err.println("Bad timeout value"); - } - } - - private static void parseExportFileTimeout(Stack args) { - if (args.isEmpty()) { - System.err.println("timeout parameter expected"); - badArguments("exportfiletimeout"); - } - try { - int timeout = Integer.parseInt(args.pop()); - Configuration.decompilationTimeoutFile.set(timeout); - } catch (NumberFormatException nex) { - System.err.println("Bad timeout value"); - } - } - - private static void parseStat(Stack args) { - showStat = true; - Configuration.showStat = showStat; - } - - private static void parseStdOut(Stack args) { - if (args.isEmpty()) { - System.err.println("stdOut parameter expected"); - badArguments("stdout"); - } - - stdOut = args.pop(); - } - - private static void parseStdErr(Stack args) { - if (args.isEmpty()) { - System.err.println("stdErr parameter expected"); - badArguments("stderr"); - } - - stdErr = args.pop(); - } - - /*private static void parseAffinity(Stack args) { - if (Platform.isWindows()) { - if (args.isEmpty()) { - System.err.println("affinity parameter expected"); - badArguments("affinity"); - } - try { - int affinityMask = Integer.parseInt(args.pop()); - Kernel32.INSTANCE.SetProcessAffinityMask(Kernel32.INSTANCE.GetCurrentProcess(), affinityMask); - } catch (NumberFormatException nex) { - System.err.println("Bad affinityMask value"); - } - } else { - System.err.println("Process affinity setting is only available on Windows platform."); - } - } - - private static void parsePriority(Stack args) { - if (Platform.isWindows()) { - if (args.isEmpty()) { - System.err.println("priority parameter expected"); - badArguments("priority"); - } - String priority = args.pop(); - int priorityClass = 0; - switch (priority) { - case "low": - priorityClass = Kernel32.IDLE_PRIORITY_CLASS; - break; - case "belownormal": - priorityClass = Kernel32.BELOW_NORMAL_PRIORITY_CLASS; - break; - case "normal": - priorityClass = Kernel32.NORMAL_PRIORITY_CLASS; - break; - case "abovenormal": - priorityClass = Kernel32.ABOVE_NORMAL_PRIORITY_CLASS; - break; - case "high": - priorityClass = Kernel32.HIGH_PRIORITY_CLASS; - break; - case "realtime": - priorityClass = Kernel32.REALTIME_PRIORITY_CLASS; - break; - default: - System.err.println("Bad affinityMask value"); - } - if (priorityClass != 0) { - Kernel32.INSTANCE.SetPriorityClass(Kernel32.INSTANCE.GetCurrentProcess(), priorityClass); - } - } else { - System.err.println("Process priority setting is only available on Windows platform."); - } - } - - private static void parseDebugTool(Stack args, String charset) { - String cmd = args.pop().toLowerCase(Locale.ENGLISH); - switch (cmd) { - case "findtag": { - String folder = args.pop(); - String tagIdOrName = args.pop(); - int tagId; - try { - tagId = Integer.parseInt(tagIdOrName); - } catch (NumberFormatException e) { - tagId = Tag.getKnownClassesByName().get(tagIdOrName).getId(); - } - - PrintStream oldOut = System.out; - PrintStream oldErr = System.err; - PrintStream nullStream = new PrintStream(new OutputStream() { - @Override - public void write(int b) { - } - }); - System.setOut(nullStream); - System.setErr(nullStream); - Main.initLogging(Configuration._debugMode.get()); - - File[] files = new File(folder).listFiles(getSwfFilter()); - for (File file : files) { - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, file.getAbsolutePath(), file.getName()); - try { - SWF swf = new SWF(new FileInputStream(file), sourceInfo.getFile(), sourceInfo.getFileTitle(), Configuration.parallelSpeedUp.get(), charset); - swf.openableList = new OpenableList(); - swf.openableList.sourceInfo = sourceInfo; - boolean found = false; - for (Tag tag : swf.getTags()) { - if (tag.getId() == tagId) { - found = true; - break; - } - } - - if (found) { - oldOut.println("Tag found in file: " + file.getAbsolutePath()); - } - } catch (IOException | InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - System.setOut(oldOut); - System.setErr(oldErr); - Main.initLogging(Configuration._debugMode.get()); - break; - } - case "finderrorheader": { - String folder = args.pop(); - - PrintStream oldOut = System.out; - PrintStream oldErr = System.err; - PrintStream nullStream = new PrintStream(new OutputStream() { - @Override - public void write(int b) { - } - }); - System.setOut(nullStream); - System.setErr(nullStream); - Main.initLogging(Configuration._debugMode.get()); - - File[] files = new File(folder).listFiles(getSwfFilter()); - for (File file : files) { - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, file.getAbsolutePath(), file.getName()); - try { - SWF swf = new SWF(new FileInputStream(file), sourceInfo.getFile(), sourceInfo.getFileTitle(), Configuration.parallelSpeedUp.get(), charset); - swf.openableList = new OpenableList(); - swf.openableList.sourceInfo = sourceInfo; - boolean found = false; - for (Tag tag : swf.getTags()) { - if (tag instanceof JPEGTablesTag) { - JPEGTablesTag jtt = (JPEGTablesTag) tag; - if (ImageTag.hasErrorHeader(jtt.jpegData)) { - found = true; - break; - } - } else if (tag instanceof DefineBitsJPEG2Tag) { - DefineBitsJPEG2Tag jpeg = (DefineBitsJPEG2Tag) tag; - if (ImageTag.hasErrorHeader(jpeg.imageData)) { - found = true; - break; - } - } else if (tag instanceof DefineBitsJPEG3Tag) { - DefineBitsJPEG3Tag jpeg = (DefineBitsJPEG3Tag) tag; - if (ImageTag.hasErrorHeader(jpeg.imageData)) { - found = true; - break; - } - } - } - - if (found) { - oldOut.println("Tag found in file: " + file.getAbsolutePath()); - } - } catch (IOException | InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - System.setOut(oldOut); - System.setErr(oldErr); - Main.initLogging(Configuration._debugMode.get()); - break; - } - } - } - - private static void parseCompareResources(Stack args) { - String revision = args.pop().toLowerCase(Locale.ENGLISH); - String revision2 = null; - if (!args.isEmpty()) { - revision2 = args.pop(); - } - - CheckResources.compareResources(System.out, revision, revision2); - } - - private static void parseResourceDates(Stack args) { - CheckResources.checkTranslationDate(System.out); - } - - private static void parseProxy(Stack args) { - int port = 55555; - String portStr = args.peek(); - if (portStr != null && portStr.startsWith("-P")) { - args.pop(); - try { - port = Integer.parseInt(portStr.substring(2)); - } catch (NumberFormatException nex) { - System.err.println("Bad port number"); - } - } - Main.startProxy(port); - } -*/ - - private static List parseSelectClass(Stack args) { - if (args.size() < 1) { - badArguments("selectclass"); - } - List ret = new ArrayList<>(); - String classesStr = args.pop(); - String[] classes; - if (classesStr.contains(",")) { - classes = classesStr.split(","); - } else { - classes = new String[]{classesStr}; - } - ret.addAll(Arrays.asList(classes)); - return ret; - - } - - private static void parseExport(List selectionClasses, Selection selection, Selection selectionIds, Stack args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats, double zoom, String charset, boolean exportEmbed) { - if (args.size() < 3) { - badArguments("export"); - } - String[] validExportItems = new String[]{ - "script", - "script_as2", - "script_as3", - "image", - "shape", - "morphshape", - "movie", - "font", - "font4", - "frame", - "sprite", - "button", - "sound", - "binarydata", - "symbolclass", - "text", - "all", - "fla", - "xfl" - }; - - String[] removedExportFormats = new String[]{ - "as", "pcode", "hex", "pcodehex", "all_as", "all_pcode", "all_pcodehex", "all_hex", "textplain" - }; - - if (handler == null) { - handler = new ConsoleAbortRetryIgnoreHandler(AbortRetryIgnoreHandler.UNDEFINED, 0); - } - - String exportFormatString = args.pop().toLowerCase(Locale.ENGLISH); - List exportFormats = Arrays.asList(exportFormatString.split(",")); - long startTime = System.currentTimeMillis(); - - File outDirBase = new File(args.pop()); - String inFileOrFolderStr = args.pop(); - File inFileOrFolder = new File(inFileOrFolderStr); - if (!StdInAwareFileInputStream.STDIN_PATH.equals(inFileOrFolderStr) && !inFileOrFolder.exists()) { - System.err.println("Input SWF file does not exist!"); - badArguments("export"); - } - - if (!args.isEmpty() && args.peek().equals("-selectas3class")) { - System.err.println("Error: -selectas3class parameter was REMOVED. Please use -selectclass instead. See --help for usage."); - System.exit(1); - } - - printHeader(); - boolean exportOK = true; - - List as3classes = new ArrayList<>(); - if (selectionClasses != null) { - as3classes.addAll(selectionClasses); - } - - Map stat = new HashMap<>(); - - try { - File[] inFiles; - boolean singleFile = true; - if (inFileOrFolder.isDirectory()) { - singleFile = false; - inFiles = inFileOrFolder.listFiles(getSwfFilter()); - } else { - inFiles = new File[]{inFileOrFolder}; - } - - for (File inFile : inFiles) { - String inFileName = Path.getFileNameWithoutExtension(inFile); - if (stdOut != null) { - String outFilePath = stdOut.replace("{swfFile}", inFileName); - Path.createDirectorySafe(new File(outFilePath).getParentFile()); - System.setOut(new PrintStream(new FileOutputStream(outFilePath, true))); - } - - if (stdErr != null) { - String errFilePath = stdErr.replace("{swfFile}", inFileName); - Path.createDirectorySafe(new File(errFilePath).getParentFile()); - System.setErr(new PrintStream(new FileOutputStream(errFilePath, true))); - //Main.initLogging(Configuration._debugMode.get()); - } - - long startTimeSwf = 0; - if (!singleFile) { - startTimeSwf = System.currentTimeMillis(); - System.out.println("Start exporting " + inFile.getName()); - } - - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, inFile.getAbsolutePath(), inFile.getName()); - SWF swf; - try { - swf = new SWF(new BufferedInputStream(new StdInAwareFileInputStream(inFile)), sourceInfo.getFile(), sourceInfo.getFileTitle(), null, Configuration.parallelSpeedUp.get(), false, true, charset); - } catch (FileNotFoundException | SwfOpenException ex) { - // FileNotFoundException when anti virus software blocks to open the file - logger.log(Level.SEVERE, "Failed to open swf: " + inFile.getName(), ex); - continue; - } - - swf.openableList = new OpenableList(); - swf.openableList.sourceInfo = sourceInfo; - String outDir = outDirBase.getAbsolutePath(); - if (!singleFile) { - outDir = Path.combine(outDir, inFile.getName()); - } - - List extags = new ArrayList<>(); - for (Tag t : swf.getTags()) { - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - if (selectionIds.contains(c.getCharacterId())) { - extags.add(t); - } - } else if (selectionIds.contains(0)) { - extags.add(t); - } - } - - final Level level = traceLevel; - swf.addEventListener(new EventListener() { - @Override - public void handleExportingEvent(String type, int index, int count, Object data) { - if (level.intValue() <= Level.FINE.intValue()) { - String text = "Exporting "; - if (type != null && type.length() > 0) { - text += type + " "; - } - System.out.println(text + index + "/" + count + " " + data); - } - } - - @Override - public void handleExportedEvent(String type, int index, int count, Object data) { - String text = "Exported "; - if (type != null && type.length() > 0) { - text += type + " "; - } - System.out.println(text + index + "/" + count + " " + data); - } - - @Override - public void handleEvent(String event, Object data) { - } - }); - - // First check all the specified export formats - for (String exportFormat : exportFormats) { - if (Arrays.asList(removedExportFormats).contains(exportFormat)) { - System.err.println("Error: Export format : " + exportFormat + " was REMOVED. Run application with --help parameter to see available formats."); - System.exit(1); - } else if (!Arrays.asList(validExportItems).contains(exportFormat)) { - System.err.println("Invalid export item:" + exportFormat); - badArguments("export"); - } - } - - // Here the exportFormats array should contain only validitems - commandLineMode = true; - boolean exportAll = exportFormats.contains("all"); - boolean multipleExportTypes = exportAll || exportFormats.size() > 1; - EventListener evl = swf.getExportEventListener(); - - if (exportAll || exportFormats.contains("image")) { - System.out.println("Exporting images..."); - new ImageExporter().exportImages(handler, outDir + (multipleExportTypes ? File.separator + ImageExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("shape")) { - System.out.println("Exporting shapes..."); - new ShapeExporter().exportShapes(handler, outDir + (multipleExportTypes ? File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME : ""), swf, new ReadOnlyTagList(extags), new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class), zoom), evl, zoom); - } - - if (exportAll || exportFormats.contains("morphshape")) { - System.out.println("Exporting morphshapes..."); - new MorphShapeExporter().exportMorphShapes(handler, outDir + (multipleExportTypes ? File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class), zoom), evl); - } - - if (exportAll || exportFormats.contains("movie")) { - System.out.println("Exporting movies..."); - new MovieExporter().exportMovies(handler, outDir + (multipleExportTypes ? File.separator + MovieExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("font")) { - System.out.println("Exporting fonts..."); - new FontExporter().exportFonts(handler, outDir + (multipleExportTypes ? File.separator + FontExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("font4")) { - System.out.println("Exporting DefineFont4s..."); - new Font4Exporter().exportFonts(handler, outDir + (multipleExportTypes ? File.separator + FontExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new Font4ExportSettings(enumFromStr(formats.get("font4"), Font4ExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("sound")) { - System.out.println("Exporting sounds..."); - new SoundExporter().exportSounds(handler, outDir + (multipleExportTypes ? File.separator + SoundExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("binarydata")) { - System.out.println("Exporting binaryData..."); - new BinaryDataExporter().exportBinaryData(handler, outDir + (multipleExportTypes ? File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("symbolclass")) { - System.out.println("Exporting symbolClass..."); - new SymbolClassExporter().exportNames(handler, outDir + (multipleExportTypes ? File.separator + SymbolClassExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SymbolClassExportSettings(enumFromStr(formats.get("symbolclass"), SymbolClassExportMode.class)), evl); - } - - if (exportAll || exportFormats.contains("text")) { - System.out.println("Exporting texts..."); - Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); - if (singleTextFile == null) { - singleTextFile = Configuration.textExportSingleFile.get(); - } - new TextExporter().exportTexts(handler, outDir + (multipleExportTypes ? File.separator + TextExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile, zoom), evl); - } - - FrameExporter frameExporter = new FrameExporter(); - - if (exportAll || exportFormats.contains("frame")) { - System.out.println("Exporting frames..."); - List frames = new ArrayList<>(); - for (int i = 0; i < swf.frameCount; i++) { - if (selection.contains(i + 1)) { - frames.add(i); - } - } - FrameExportSettings fes = new FrameExportSettings(enumFromStr(formats.get("frame"), FrameExportMode.class), zoom); - frameExporter.exportFrames(handler, outDir + (multipleExportTypes ? File.separator + FrameExportSettings.EXPORT_FOLDER_NAME : ""), swf, 0, frames, fes, evl); - } - - if (exportAll || exportFormats.contains("sprite")) { - System.out.println("Exporting sprite..."); - SpriteExportSettings ses = new SpriteExportSettings(enumFromStr(formats.get("sprite"), SpriteExportMode.class), zoom); - for (Tag t : extags) { - if (t instanceof DefineSpriteTag) { - frameExporter.exportSpriteFrames(handler, outDir + (multipleExportTypes ? File.separator + SpriteExportSettings.EXPORT_FOLDER_NAME : ""), swf, ((DefineSpriteTag) t).getCharacterId(), null, ses, evl); - } - } - } - - if (exportAll || exportFormats.contains("button")) { - System.out.println("Exporting buttons..."); - ButtonExportSettings bes = new ButtonExportSettings(enumFromStr(formats.get("button"), ButtonExportMode.class), zoom); - for (Tag t : extags) { - if (t instanceof ButtonTag) { - frameExporter.exportButtonFrames(handler, outDir + (multipleExportTypes ? File.separator + ButtonExportSettings.EXPORT_FOLDER_NAME : ""), swf, ((ButtonTag) t).getCharacterId(), null, bes, evl); - } - } - } - - boolean parallel = Configuration.parallelSpeedUp.get(); - Boolean singleScriptFile = parseBooleanConfigValue(formats.get("singlescript")); - if (singleScriptFile == null) { - singleScriptFile = Configuration.scriptExportSingleFile.get(); - } - - if (parallel && singleScriptFile) { - logger.log(Level.WARNING, "Single file script export is not supported with enabled parallel speedup"); - singleScriptFile = false; - } - - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(enumFromStr(formats.get("script"), ScriptExportMode.class), singleScriptFile, false, exportEmbed, false); - boolean exportAllScript = exportAll || exportFormats.contains("script"); - boolean exportAs2Script = exportAllScript || exportFormats.contains("script_as2"); - boolean exportAs3Script = exportAllScript || exportFormats.contains("script_as3"); - if (exportAs2Script || exportAs3Script) { - System.out.println("Exporting scripts..."); - - String scriptsFolder = Path.combine(outDir, ScriptExportSettings.EXPORT_FOLDER_NAME); - Path.createDirectorySafe(new File(scriptsFolder)); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - List as3packs = as3classes.isEmpty() ? null : swf.getScriptPacksByClassNames(as3classes); - exportOK = swf.exportActionScript(handler, scriptsFolder, as3classes.isEmpty() ? null : as3packs, scriptExportSettings, parallel, evl, exportAs2Script, exportAs3Script) != null && exportOK; - } - - if (showStat) { - Statistics.print(); - Statistics.addToMap(stat); - Statistics.clear(); - } - } - - if (exportFormats.contains("fla")) { - System.out.println("Exporting FLA..."); - exportFla(true, outDir, inFile, swf, multipleExportTypes, formats, handler); - } - - if (exportFormats.contains("xfl")) { - System.out.println("Exporting XFL..."); - exportFla(false, outDir, inFile, swf, multipleExportTypes, formats, handler); - } - - if (!singleFile) { - long stopTimeSwf = System.currentTimeMillis(); - long time = stopTimeSwf - startTimeSwf; - System.out.println("Export finished: " + inFile.getName() + " Export time: " + Helper.formatTimeSec(time)); - } - - swf.clearAllCache(); - CancellableWorker.cancelBackgroundThreads(); - } - } catch (OutOfMemoryError | Exception ex) { - System.err.print("FAIL: Exporting Failed on Exception - "); - logger.log(Level.SEVERE, null, ex); - System.exit(1); - } - - if (showStat) { - Statistics.print(stat); - } - - long stopTime = System.currentTimeMillis(); - long time = stopTime - startTime; - System.out.println("Export finished. Total export time: " + Helper.formatTimeSec(time)); - System.out.println(exportOK ? "OK" : "FAIL"); - System.exit(exportOK ? 0 : 1); - } - - private static void exportFla(boolean compressed, String outDir, File inFile, SWF swf, boolean multipleExportTypes, Map formats, AbortRetryIgnoreHandler handler) throws IOException, InterruptedException { - String exportFormat = compressed ? "fla" : "xfl"; - String format = formats.get(exportFormat); - boolean exportScript = true; - if (format != null && format.endsWith("_noscript")) { - format = format.substring(0, format.length() - 9); - exportScript = false; - } - - FLAVersion flaVersion = FLAVersion.fromString(format); - if (flaVersion == null) { - flaVersion = FLAVersion.CS6; //Defaults to CS6 - } - - String outFile = outDir; - if (multipleExportTypes) { - outFile = Path.combine(outFile, exportFormat); - } - - String outFileName = inFile.getName().toLowerCase(Locale.ENGLISH).endsWith(".swf") ? inFile.getName().substring(0, inFile.getName().length() - 3) + exportFormat : inFile.getName(); - outFile = Path.combine(outFile, outFileName); - XFLExportSettings settings = new XFLExportSettings(); - settings.compressed = compressed; - settings.exportScript = exportScript; - - try { - if (Configuration.setFFDecVersionInExportedFont.get()) { - swf.exportXfl(handler, outFile, inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), flaVersion, settings, null); - } else { - swf.exportXfl(handler, outFile, inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.APPLICATION_NAME, "1.0.0", Configuration.parallelSpeedUp.get(), flaVersion, settings, null); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error during XFL/FLA export", ex); - } - } - - private static void parseDeobfuscate(Stack args, String charset) { - if (args.size() < 3) { - badArguments("deobfuscate"); - } - String mode = args.pop(); - DeobfuscationLevel lev; - switch (mode) { - case "controlflow": - case "3": - System.err.println("WARNING: Control flow level(3) is not implemented - it is the same as remove traps (2) level."); - lev = DeobfuscationLevel.LEVEL_REMOVE_TRAPS; - break; - case "traps": - case "max": - case "2": - lev = DeobfuscationLevel.LEVEL_REMOVE_TRAPS; - break; - case "deadcode": - case "1": - lev = DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE; - break; - default: - System.err.println("Invalid level, must be one of: controlflow,traps,deadcode or 1,2,3/max"); - System.exit(1); - return; - } - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - File tmpFile = null; - if (inFile.equals(outFile)) { - try { - tmpFile = File.createTempFile("ffdec_deobf_", ".swf"); - outFile = tmpFile; - } catch (IOException ex) { - System.err.println("Unable to create temp file"); - System.exit(1); - } - } - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile); FileOutputStream fos = new FileOutputStream(outFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - if (!swf.isAS3()) { - System.out.println("Warning: The file is not AS3. Only AS3 deobfuscation from commandline is available."); - System.exit(0); - } - swf.deobfuscate(lev); - swf.saveTo(fos); - if (tmpFile != null) { - inFile.delete(); - tmpFile.renameTo(inFile); - tmpFile = null; - System.out.println(inFile + " overwritten."); - } - System.out.println("OK"); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - System.exit(1); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error", ex); - System.exit(1); - } finally { - if (tmpFile != null && tmpFile.exists()) { - tmpFile.delete(); - } - } - } - - private static void parseCompress(Stack args) { - if (args.size() < 2) { - badArguments("compress"); - } - - boolean result = false; - try { - SWFCompression compression = SWFCompression.ZLIB; - String compressionString = !args.isEmpty() ? args.pop() : null; - if (compressionString != null) { - switch (compressionString.toLowerCase(Locale.ENGLISH)) { - case "zlib": - compression = SWFCompression.ZLIB; - break; - case "lzma": - compression = SWFCompression.LZMA; - break; - default: - System.out.println("Unsupported compression method: " + compressionString); - System.exit(0); - break; - } - } - - try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) { - result = SWF.compress(fis, fos, compression); - System.out.println(result ? "OK" : "FAIL"); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - System.exit(result ? 0 : 1); - } - - private static void parseEncrypt(Stack args) { - if (args.size() < 2) { - badArguments("encrypt"); - } - - boolean result = false; - try { - try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) { - result = SWF.encrypt(fis, fos); - System.out.println(result ? "OK" : "FAIL"); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - System.exit(result ? 0 : 1); - } - - private static void parseDecrypt(Stack args) { - if (args.size() < 2) { - badArguments("decrypt"); - } - - boolean result = false; - try { - try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) { - result = SWF.decrypt(fis, fos); - System.out.println(result ? "OK" : "FAIL"); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - System.exit(result ? 0 : 1); - } - - private static void parseDecompress(Stack args) { - if (args.size() < 2) { - badArguments("decompress"); - } - - boolean result = false; - try { - try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) { - result = SWF.decompress(fis, fos); - System.out.println(result ? "OK" : "FAIL"); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - System.exit(result ? 0 : 1); - } - - private static void parseSwf2Xml(Stack args, String charset) { - if (args.size() < 2) { - badArguments("swf2xml"); - } - - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(args.pop())) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - new SwfXmlExporter().exportXml(swf, new File(args.pop())); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private static void parseXml2Swf(Stack args, String charset) { - if (args.size() < 2) { - badArguments("xml2swf"); - } - - try { - SWF swf = new SWF(charset); - try (StdInAwareFileInputStream in = new StdInAwareFileInputStream(args.pop())) { - new SwfXmlImporter().importSwf(swf, in); - } - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(args.pop())))) { - swf.saveTo(fos); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - } - - private static void parseExtract(Stack args) { - if (args.size() < 1) { - badArguments("extract"); - } - - String fileName = args.pop(); - SearchMode mode = SearchMode.ALL; - - boolean noCheck = false; - String output = null; - - if (args.size() > 0 && args.peek().toLowerCase(Locale.ENGLISH).equals("-o")) { - args.pop(); - if (args.size() < 1) { - badArguments("extract"); - } - output = args.pop(); - } - - if (args.size() > 0 && args.peek().toLowerCase(Locale.ENGLISH).equals("nocheck")) { - noCheck = true; - args.pop(); - } - - if (args.size() > 0) { - String modeStr = args.pop().toLowerCase(Locale.ENGLISH); - switch (modeStr) { - case "biggest": - mode = SearchMode.BIGGEST; - break; - case "smallest": - mode = SearchMode.SMALLEST; - break; - case "first": - mode = SearchMode.FIRST; - break; - case "last": - mode = SearchMode.LAST; - break; - } - } - - try { - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, fileName, null); - if (!sourceInfo.isBundle()) { - System.err.println("Error: should be a bundle. (ZIP or non SWF binary file)"); - System.exit(1); - } - Bundle bundle = sourceInfo.getBundle(noCheck, mode); - List> streamsToExtract = new ArrayList<>(); - for (Map.Entry streamEntry : bundle.getAll().entrySet()) { - InputStream stream = streamEntry.getValue(); - stream.reset(); - streamsToExtract.add(streamEntry); - } - - for (Map.Entry streamEntry : streamsToExtract) { - InputStream stream = streamEntry.getValue(); - stream.reset(); - String fileNameOut; - if (mode != SearchMode.ALL) { - if (output == null) { - fileNameOut = Path.getFileNameWithoutExtension(new File(fileName)) + ".swf"; - } else { - fileNameOut = output; - } - } else { - fileNameOut = streamEntry.getKey() + ".swf"; - if (output != null) { - fileNameOut = Path.combine(output, fileNameOut); - } - } - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileNameOut))) { - byte[] swfData = new byte[stream.available()]; - int cnt = stream.read(swfData); - fos.write(swfData, 0, cnt); - } - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - } - - /*private static void parseMemorySearch(Stack args) { - if (args.size() < 1) { - badArguments("memorysearch"); - } - - if (Platform.isWindows()) { - AtomicInteger cnt = new AtomicInteger(); - List procs = new ArrayList<>(); - List processList = ProcessTools.listProcesses(); - while (args.size() > 0) { - String arg = args.pop(); - if (arg.matches("\\d+")) { - int processId = 0; - try { - processId = Integer.parseInt(arg); - } catch (NumberFormatException nfe) { - System.err.println("ProcessId should be integer"); - badArguments("memorysearch"); - } - - boolean found = false; - if (processList != null) { - for (Process process : processList) { - if (process.getPid() == processId) { - if (!procs.contains(process)) { - procs.add(process); - } - - found = true; - break; // only 1 process can have this process id - } - } - } - - if (!found) { - System.out.println("Process id=" + processId + " was not found."); - } - } else { - boolean found = false; - if (processList != null) { - for (Process process : processList) { - if (process.getFileName().equals(arg)) { - if (!procs.contains(process)) { - procs.add(process); - } - - found = true; - } - } - } - - if (!found) { - System.out.println("Process name=" + arg + " was not found."); - } - } - } - - try { - new SearchInMemory(new SearchInMemoryListener() { - @Override - public void publish(Object... chunks) { - for (Object s : chunks) { - if (s instanceof SwfInMemory) { - SwfInMemory swf = (SwfInMemory) s; - String fileName = cnt.getAndIncrement() + ".swf"; - System.out.println("SWF found (" + fileName + "). Version: " + swf.version + ", file size: " + swf.fileSize + ", address: " + swf.address); - Helper.writeFile(fileName, swf.is); - } - } - } - - @Override - public void setProgress(int progress) { - // ignore - } - }).search(procs); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - } - } else { - System.err.println("Memory search is only available on Windows platform."); - } - - }*/ - - private static void parseRenameInvalidIdentifiers(Stack args) { - if (args.size() < 3) { - badArguments("renameinvalididentifiers"); - } - - String renameTypeStr = args.pop(); - RenameType renameType; - switch (renameTypeStr.toLowerCase(Locale.ENGLISH)) { - case "typenumber": - renameType = RenameType.TYPENUMBER; - break; - case "randomword": - renameType = RenameType.RANDOMWORD; - break; - default: - System.err.println("Invalid rename type:" + renameTypeStr); - badArguments("renameinvalididentifiers"); - return; - } - - boolean result = false; - try { - try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) { - result = SWF.renameInvalidIdentifiers(renameType, fis, fos); - System.out.println(result ? "OK" : "FAIL"); - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - System.exit(1); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - System.exit(result ? 0 : 1); - } - - private static String parseCharset(Stack args) { - if (args.size() < 1) { - badArguments("charset"); - } - String charsetName = args.pop(); - boolean charsetValid = false; - try { - if (Charset.isSupported(charsetName)) { - charsetValid = true; - } - } catch (Exception ex) { - charsetValid = false; - } - - if (!charsetValid) { - System.err.println("Specified charset is not valid"); - badArguments("charset"); - } - return charsetName; - } - - private static Map parseFormat(Stack args) { - if (args.size() < 1) { - badArguments("format"); - } - String fmtStr = args.pop(); - String[] fmts; - if (fmtStr.contains(",")) { - fmts = fmtStr.split(","); - } else { - fmts = new String[]{fmtStr}; - } - Map ret = new HashMap<>(); - for (String fmt : fmts) { - if (!fmt.contains(":")) { - badArguments("format"); - } - String[] parts = fmt.split(":"); - ret.put(parts[0].toLowerCase(Locale.ENGLISH), parts[1].toLowerCase(Locale.ENGLISH)); - } - return ret; - } - - private static void parseFlashPaperToPdf(Selection selection, double zoom, Stack args, String charset) { - if (args.size() < 2) { - badArguments("flashpaper2pdf"); - } - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - printHeader(); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - - PDFJob job = null; - - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - int totalPages = 0; - - for (Tag t : swf.getTags()) { - if (t instanceof DefineSpriteTag) { - DefineSpriteTag ds = (DefineSpriteTag) t; - String exportName = ds.getExportName(); - if (exportName != null && exportName.matches("^page[0-9]+$")) { - int pageNum = Integer.parseInt(exportName.substring(4)); - if (pageNum > totalPages) { - totalPages = pageNum; - } - } - } - } - - PageFormat pf = new PageFormat(); - pf.setOrientation(PageFormat.PORTRAIT); - Paper p = new Paper(); - int page = 0; - - for (Tag t : swf.getTags()) { - if (t instanceof DefineSpriteTag) { - DefineSpriteTag ds = (DefineSpriteTag) t; - if ("page1".equals(ds.getExportName())) { - page = 1; - job = new PDFJob(new BufferedOutputStream(new FileOutputStream(outFile))); - } else if (page > 0) { - page++; - } - if (("page" + page).equals(ds.getExportName())) { - if (!selection.contains(page)) { - continue; - } - System.out.print("Page " + page + "/" + totalPages + "..."); - Timeline tim = ds.getTimeline(); - RECT rect = tim.displayRect; - - double w = (rect.getWidth() * zoom / SWF.unitDivisor); - double h = (rect.getHeight() * zoom / SWF.unitDivisor); - p.setSize(w, h); - pf.setPaper(p); - Matrix m = new Matrix(); - m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); - m.scale(zoom); - Matrix transformation = m; - Map existingFonts = new HashMap<>(); - - int fframe = 0; - final Graphics2D g = (Graphics2D) job.getGraphics(pf); - - SerializableImage image = new SerializableImage((int) w + 1, (int) h + 1, SerializableImage.TYPE_INT_ARGB_PRE) { - - private Graphics2D compositeGraphics; - - @Override - public Graphics getGraphics() { - if (compositeGraphics != null) { - return compositeGraphics; - } - final Graphics2D parentGraphics = (Graphics2D) super.getGraphics(); - compositeGraphics = new DualPdfGraphics2D(parentGraphics, (PDFGraphics) g, existingFonts); - return compositeGraphics; - } - - @Override - public void fillTransparent() { - - } - - }; - int imgWidth = (int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1; - int imgHeight = (int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1; - - RenderContext renderContext = new RenderContext(); - renderContext.cursorPosition = new Point(-1, -1); - renderContext.mouseButton = 0; - renderContext.stateUnderCursor = new ArrayList<>(); - - try { - tim.toImage(fframe, fframe, renderContext, image, image, false, m, new Matrix(), m, null, zoom, true, new ExportRectangle(rect), m, true, Timeline.DRAW_MODE_ALL, 0, true); - } catch (Exception ex) { - ex.printStackTrace(); - } - g.dispose(); - - if (Thread.currentThread().isInterrupted()) { - return; - } - - System.out.println("OK"); - - } - } - } - - if (job == null) { - System.err.println("No pages found. Maybe it is not a FlashPaper file"); - System.exit(2); - } - job.end(); - - } catch (FileNotFoundException ex) { - System.err.println("File not found"); - System.exit(1); - } catch (IOException | InterruptedException ex) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseReplace(Stack args, String charset, boolean air) { - if (args.size() < 3) { - badArguments("replace"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - if (args.size() == 1) { - System.out.println("Replacing - only single argument passed, taking it as file to load replacements from"); - try { - List lines = Files.readAllLines(Paths.get(args.pop()), StandardCharsets.UTF_8); - Collections.reverse(lines); - - args.clear(); - args.addAll(lines); - - } catch (IOException e) { - System.err.println("I/O Error during reading replacements file"); - System.exit(1); - } - - if (args.isEmpty()) { - System.err.println("Replacements file is empty."); - System.exit(1); - } - } - - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - while (true) { - String objectToReplace = args.pop(); - - if (objectToReplace.matches("\\d+")) { - // replace character tag - int characterId = 0; - try { - characterId = Integer.parseInt(objectToReplace); - } catch (NumberFormatException nfe) { - System.err.println("CharacterId should be integer"); - badArguments("replace"); - } - - SoundStreamHeadTypeTag soundStreamHead = null; - CharacterTag characterTag = null; - if (characterId == -1) { - //replacing soundstreamhead on main timeline - } else if (swf.getCharacters().containsKey(characterId)) { - characterTag = swf.getCharacter(characterId); - } else { - System.err.println("CharacterId does not exist"); - System.exit(1); - } - - String repFile = args.pop(); - - byte[] data = Helper.readFile(repFile); - String ext = Path.getExtension(repFile); - - int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; - if (".mp3".equals(ext)) { - soundFormat = SoundFormat.FORMAT_MP3; - } - - if ((characterTag == null || (characterTag instanceof DefineSpriteTag)) && (".wav".equals(ext) || ".mp3".equals(ext))) { - ReadOnlyTagList tags = (characterTag == null) ? swf.getTags() : ((DefineSpriteTag) characterTag).getTags(); - for (Tag t : tags) { - if (t instanceof SoundStreamHeadTypeTag) { - soundStreamHead = (SoundStreamHeadTypeTag) t; - break; - } - } - if (soundStreamHead == null) { - System.err.println((characterTag == null ? "Main timeline " : "DefineSprite(" + characterId + ")") + " does not contain any SoundStreamHead"); - System.exit(1); - } - boolean ok = false; - try { - ok = new SoundImporter().importSoundStream(soundStreamHead, new ByteArrayInputStream(data), soundFormat); - } catch (UnsupportedSamplingRateException usre) { - List supportedRatesStr = new ArrayList<>(); - for (int i : usre.getSupportedRates()) { - supportedRatesStr.add("" + i); - } - System.err.println("Import FAILED. Input file has unsupported sampling rate (" + usre.getSoundRate() + "). Supported rates for this sound format: " + String.join(", ", supportedRatesStr) + "."); - System.exit(2); - } - if (!ok) { - System.err.println("Import FAILED. Maybe unsuppoted media type? Only MP3 and uncompressed WAV are available."); - System.exit(1); - } - } else if (characterTag instanceof DefineBinaryDataTag) { - DefineBinaryDataTag defineBinaryData = (DefineBinaryDataTag) characterTag; - new BinaryDataImporter().importData(defineBinaryData, data); - } else if (characterTag instanceof ImageTag) { - int format = parseImageFormat(args); - ImageTag imageTag = (ImageTag) characterTag; - new ImageImporter().importImage(imageTag, data, format); - } else if (characterTag instanceof ShapeTag) { - boolean fill = true; - if (!args.isEmpty() && args.peek().equals("nofill")) { - args.pop(); - fill = false; - } - ShapeTag shapeTag = (ShapeTag) characterTag; - - if (".svg".equals(ext)) { - String svgText = Helper.readTextFile(repFile); - new SvgImporter().importSvg(shapeTag, svgText); - } else { - int format = parseImageFormat(args); - new ShapeImporter().importImage(shapeTag, data, format, fill); - } - } else if (characterTag instanceof TextTag) { - TextTag textTag = (TextTag) characterTag; - new TextImporter(new MissingCharacterHandler(), new TextImportErrorHandler() { - @Override - public boolean handle(TextTag textTag) { - String msg = "Error during text import."; - logger.log(Level.SEVERE, msg); - return false; - } - - @Override - public boolean handle(TextTag textTag, String message, long line) { - String msg = "Error during text import: %text% on line %line%".replace("%text%", message).replace("%line%", Long.toString(line)); - logger.log(Level.SEVERE, msg); - return false; - } - }).importText(textTag, new String(data, Utf8Helper.charset)); - } else if (characterTag instanceof SoundTag) { - SoundTag st = (SoundTag) characterTag; - boolean ok = false; - SoundImporter soundImporter = new SoundImporter(); - try { - ok = soundImporter.importSound(st, new ByteArrayInputStream(data), soundFormat); - } catch (UnsupportedSamplingRateException usre) { - List supportedRatesStr = new ArrayList<>(); - for (int i : usre.getSupportedRates()) { - supportedRatesStr.add("" + i); - } - System.err.println("Import FAILED. Input file has unsupported sampling rate (" + usre.getSoundRate() + "). Supported rates for this sound format: " + String.join(", ", supportedRatesStr) + "."); - System.exit(2); - } catch (SoundImportException sie) { - ok = false; - } - if (!ok) { - System.err.println("Import FAILED. Maybe unsuppoted media type? Only MP3 and uncompressed WAV are available."); - System.exit(1); - } - } else if (characterTag instanceof DefineVideoStreamTag) { - DefineVideoStreamTag movie = (DefineVideoStreamTag) characterTag; - try { - new MovieImporter().importMovie(movie, data); - } catch (IOException iex) { - System.err.println("Import FAILED: " + iex.getMessage()); - System.exit(1); - } - } else if (characterTag instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) characterTag; - try { - new SpriteImporter().importSprite(sprite, new ByteArrayInputStream(data)); - } catch (IOException iex) { - System.err.println("Import FAILED: " + iex.getMessage()); - System.exit(1); - } - } else { - System.err.println("The specified tag type is not supported for import"); - System.exit(1); - } - } else { - Map asms = swf.getASMs(false); - boolean found = false; - if (asms.containsKey(objectToReplace)) { - found = true; - // replace AS1/2 - String repFile = args.pop(); - String repText = Helper.readTextFile(repFile); - ASMSource src = asms.get(objectToReplace); - if (Path.getExtension(repFile).equals(".as")) { - replaceAS2(repText, src); - } else { - replaceAS2PCode(repText, src); - } - } else { - List packs = swf.getAS3Packs(); - - for (ScriptPack entry : packs) { - if (entry.getClassPath().toString().equals(objectToReplace)) { - found = true; - // replace AS3 - String repFile = args.pop(); - String repText = Helper.readTextFile(repFile); - ScriptPack pack = entry; - if (Path.getExtension(repFile).equals(".as")) { - replaceAS3(repFile, repText, pack, air); - } else { - // todo: get traits - if (args.isEmpty()) { - badArguments("replace"); - } - - int bodyIndex = Integer.parseInt(args.pop()); - ABC abc = pack.abc; - List resultTraits = abc.getMethodIndexing().findMethodTraits(pack, bodyIndex); - - //int classIndex = 0; - //int traitId = 0; - Trait trait = null; //abc.findTraitByTraitId(classIndex, traitId); - if (resultTraits.size() == 1) { - trait = resultTraits.get(0); - } - - replaceAS3PCode(repText, abc, bodyIndex, trait); - } - } - } - } - - if (!found) { - System.err.println(objectToReplace + " is not reocginized as a CharacterId or a script name."); - System.exit(1); - } - } - - if (args.isEmpty() || args.peek().startsWith("-")) { - break; - } - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static int parseImageFormat(Stack args) { - if (args.isEmpty()) { - return 0; - } - - int res = ImageImporter.getImageTagType(args.peek().toLowerCase(Locale.ENGLISH)); - if (res != 0) { - args.pop(); - } - - return res; - } - - private static void parseReplaceAlpha(Stack args, String charset) { - if (args.size() < 4) { - badArguments("replacealpha"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - while (true) { - String objectToReplace = args.pop(); - - int imageId = 0; - try { - imageId = Integer.parseInt(objectToReplace); - } catch (NumberFormatException nfe) { - System.err.println("ImageId should be integer"); - badArguments("replacealpha"); - } - if (!swf.getCharacters().containsKey(imageId)) { - System.err.println("ImageId does not exist"); - System.exit(1); - } - - CharacterTag characterTag = swf.getCharacter(imageId); - String repFile = args.pop(); - byte[] data = Helper.readFile(repFile); - if (characterTag instanceof HasSeparateAlphaChannel) { - ImageTag imageTag = (ImageTag) characterTag; - new ImageImporter().importImageAlpha(imageTag, data); - } else { - System.err.println("The specified tag type is not supported for alpha channel import"); - badArguments("replacealpha"); - } - - if (args.isEmpty() || args.peek().startsWith("-")) { - break; - } - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseReplaceCharacter(Stack args, String charset) { - if (args.size() < 4) { - badArguments("replacecharacter"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - while (true) { - String objectToReplace = args.pop(); - - int characterId = 0; - try { - characterId = Integer.parseInt(objectToReplace); - } catch (NumberFormatException nfe) { - System.err.println("CharacterId should be integer"); - badArguments("replacecharacter"); - } - if (!swf.getCharacters().containsKey(characterId)) { - System.err.println("CharacterId does not exist"); - System.exit(1); - } - - CharacterTag characterTag = swf.getCharacter(characterId); - String newCharacterIdStr = args.pop(); - - int newCharacterId = 0; - try { - newCharacterId = Integer.parseInt(newCharacterIdStr); - } catch (NumberFormatException nfe) { - System.err.println("NewCharacterId should be integer"); - badArguments("replacecharacter"); - } - if (!swf.getCharacters().containsKey(newCharacterId)) { - System.err.println("NewCharacterId does not exist"); - System.exit(1); - } - - swf.replaceCharacterTags(characterTag, newCharacterId); - - if (args.isEmpty() || args.peek().startsWith("-")) { - break; - } - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseReplaceCharacterId(Stack args, String charset) { - if (args.size() < 3) { - badArguments("replacecharacterid"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - String arg = args.pop().toLowerCase(Locale.ENGLISH); - if (arg.equals("pack")) { - swf.packCharacterIds(); - } else if (arg.equals("sort")) { - swf.sortCharacterIds(); - } else { - String[] characterIdsStr = arg.split(","); - if (characterIdsStr.length % 2 != 0) { - System.err.println("CharacterId count should be an even number"); - badArguments("replacecharacterid"); - } - - List characterIds = new ArrayList<>(); - for (int i = 0; i < characterIdsStr.length; i++) { - int characterId; - try { - characterId = Integer.parseInt(characterIdsStr[i]); - characterIds.add(characterId); - } catch (NumberFormatException nfe) { - System.err.println("CharacterId should be integer"); - badArguments("replacecharacterid"); - } - } - - for (int i = 0; i < characterIds.size(); i += 2) { - int oldCharacterId = characterIds.get(i); - int newCharacterId = characterIds.get(i + 1); - swf.replaceCharacter(oldCharacterId, newCharacterId); - } - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseConvert(Stack args, String charset) { - if (args.size() < 4) { - badArguments("convert"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - - String objectToConvert = args.pop(); - - int characterId = 0; - try { - characterId = Integer.parseInt(objectToConvert); - } catch (NumberFormatException nfe) { - System.err.println("CharacterId should be integer"); - badArguments("convert"); - } - if (!swf.getCharacters().containsKey(characterId)) { - System.err.println("CharacterId does not exist"); - System.exit(1); - } - - CharacterTag characterTag = swf.getCharacter(characterId); - String targetType = args.pop().toLowerCase(Locale.ENGLISH); - if (characterTag instanceof ImageTag) { - int format = ImageImporter.getImageTagType(targetType); - ImageTag imageTag = (ImageTag) characterTag; - new ImageImporter().convertImage(imageTag, format); - } else if (characterTag instanceof ShapeTag) { - int format = ShapeImporter.getShapeTagType(targetType); - ShapeTag shapeTag = (ShapeTag) characterTag; - System.err.println("Converting shape tag is currently not supported"); - } else if (characterTag instanceof MorphShapeTag) { - int format = MorphShapeImporter.getMorphShapeTagType(targetType); - MorphShapeTag morphShapeTag = (MorphShapeTag) characterTag; - System.err.println("Converting morph shape tag is currently not supported"); - } else if (characterTag instanceof FontTag) { - int format = FontImporter.getFontTagType(targetType); - FontTag fontTag = (FontTag) characterTag; - System.err.println("Converting font tag is currently not supported"); - } else if (characterTag instanceof TextTag) { - int format = TextImporter.getTextTagType(targetType); - TextTag textTag = (TextTag) characterTag; - System.err.println("Converting text tag is currently not supported"); - } else { - System.err.println("The specified tag type is not supported for import"); - System.exit(1); - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseRemove(Stack args, String charset) { - if (args.size() < 3) { - badArguments("remove"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - List tagNumbersToRemove = new ArrayList<>(); - while (true) { - String tagNoToRemoveStr = args.pop(); - - int tagNo; - try { - tagNo = Integer.parseInt(tagNoToRemoveStr); - } catch (NumberFormatException nfe) { - System.err.println("Tag number should be integer"); - System.exit(1); - return; - } - if (tagNo < 0 || tagNo >= swf.getTags().size()) { - System.err.println("Tag number does not exist. Tag number should be between 0 and " + (swf.getTags().size() - 1)); - System.exit(1); - } - - if (!tagNumbersToRemove.contains(tagNo)) { - tagNumbersToRemove.add(tagNo); - } - - if (args.isEmpty() || args.peek().startsWith("-")) { - break; - } - } - - Collections.sort(tagNumbersToRemove); - for (int i = tagNumbersToRemove.size() - 1; i >= 0; i--) { - swf.removeTag((int) tagNumbersToRemove.get(i)); - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseDoc(Stack args) { - String type = null; - String format = null; - String out = null; - String locale = null; - boolean nightMode = false; - while (!args.isEmpty()) { - String arg = args.pop(); - switch (arg) { - case "-night": - nightMode = true; - break; - case "-out": - if (args.isEmpty() || out != null) { - badArguments("doc"); - } - out = args.pop(); - break; - case "-type": - if (args.isEmpty() || type != null) { - badArguments("doc"); - } - type = args.pop(); - break; - case "-format": - if (args.isEmpty() || format != null) { - badArguments("doc"); - } - format = args.pop(); - break; - case "-locale": - if (args.isEmpty() || locale != null) { - badArguments("doc"); - } - locale = args.pop(); - - break; - } - } - - if (format == null) { - format = "html"; - } - if (type == null) { - badArguments("doc"); - } else if (!type.equals("as3.pcode.instructions")) { - badArguments("doc"); - } - - if (!format.equals("html")) { - badArguments("doc"); - } - if (locale != null) { - Locale.setDefault(Locale.forLanguageTag(locale)); - } - - String doc = As3PCodeDocs.getAllInstructionDocs(nightMode); - - PrintStream outStream; - - if (out == null) { - outStream = System.out; - } else { - try { - outStream = new PrintStream(out, "UTF-8"); - } catch (UnsupportedEncodingException | FileNotFoundException ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage()); - System.exit(1); - return; - } - } - - outStream.print(doc); - } - - private static void parseRemoveCharacter(Stack args, boolean removeDependencies, String charset) { - if (args.size() < 3) { - badArguments("removecharacter"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - while (true) { - String objectToRemove = args.pop(); - - int characterId = 0; - try { - characterId = Integer.parseInt(objectToRemove); - } catch (NumberFormatException nfe) { - System.err.println("CharacterId should be integer"); - badArguments("removecharacter"); - } - if (!swf.getCharacters().containsKey(characterId)) { - System.err.println("CharacterId does not exist"); - System.exit(1); - } - - CharacterTag characterTag = swf.getCharacter(characterId); - swf.removeTag(characterTag, removeDependencies, null); - - if (args.isEmpty() || args.peek().startsWith("-")) { - break; - } - } - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseImportSymbolClass(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importsymbolclass"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - - String selFile = args.pop(); - File importFile = new File(Path.combine(selFile, SymbolClassExporter.SYMBOL_CLASS_EXPORT_FILENAME)); - SymbolClassImporter importer = new SymbolClassImporter(); - - if (!importFile.exists()) { - System.err.println("Symbol class file " + importFile + " does not exist."); - System.exit(1); - } - importer.importSymbolClasses(importFile, swf); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - - } - - private static void parseImportShapes(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importshapes"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - boolean noFill = false; - if (args.size() > 1) { - if (args.pop().equals("nofill")) { - noFill = true; - } else { - badArguments("importshapes"); - } - } - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - System.out.println("Source file opened"); - String selFile = args.pop(); - - File shapesDir = new File(Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME)); - if (shapesDir.exists()) { - System.out.println("Using the directory: " + shapesDir.getAbsolutePath()); - } else { - shapesDir = new File(selFile); - } - if (!shapesDir.exists()) { - System.err.println("Shapes directory does not exist: " + shapesDir.getAbsolutePath()); - System.exit(1); - } - ShapeImporter shapeImporter = new ShapeImporter(); - int shapeCount = shapeImporter.bulkImport(shapesDir, swf, noFill, true); - - System.out.println("Writing outfile"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - System.out.println("" + shapeCount + " shapes successfully imported"); - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - - private static void parseImportMovies(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importmovies"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - System.out.println("Source file opened"); - String selFile = args.pop(); - - File moviesDir = new File(Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME)); - if (moviesDir.exists()) { - System.out.println("Using the directory: " + moviesDir.getAbsolutePath()); - } else { - moviesDir = new File(selFile); - } - if (!moviesDir.exists()) { - System.err.println("Movies directory does not exist: " + moviesDir.getAbsolutePath()); - System.exit(1); - } - MovieImporter movieImporter = new MovieImporter(); - int movieCount = movieImporter.bulkImport(moviesDir, swf, true); - System.out.println("Writing outfile"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - System.out.println("" + movieCount + " movies successfully imported"); - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - - private static void parseImportSounds(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importsounds"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - System.out.println("Source file opened"); - String selFile = args.pop(); - - File soundsDir = new File(Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME)); - if (soundsDir.exists()) { - System.out.println("Using the directory: " + soundsDir.getAbsolutePath()); - } else { - soundsDir = new File(selFile); - } - if (!soundsDir.exists()) { - System.err.println("Sounds directory does not exist: " + soundsDir.getAbsolutePath()); - System.exit(1); - } - SoundImporter soundImporter = new SoundImporter(); - int soundCount = soundImporter.bulkImport(soundsDir, swf, true); - System.out.println("Writing outfile"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - System.out.println("" + soundCount + " sounds successfully imported"); - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - - private static void parseImportImages(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importimages"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - System.out.println("Source file opened"); - String selFile = args.pop(); - - File imagesDir = new File(Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME)); - if (imagesDir.exists()) { - System.out.println("Using the directory: " + imagesDir.getAbsolutePath()); - } else { - imagesDir = new File(selFile); - } - if (!imagesDir.exists()) { - System.err.println("Images directory does not exist: " + imagesDir.getAbsolutePath()); - System.exit(1); - } - ImageImporter imageImporter = new ImageImporter(); - int imageCount = imageImporter.bulkImport(imagesDir, swf, true); - System.out.println("Writing outfile"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - System.out.println("" + imageCount + " images successfully imported"); - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - - private static void parseImportSprites(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importsprites"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - System.out.println("Source file opened"); - String selFile = args.pop(); - - File spritesDir = new File(Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME)); - if (spritesDir.exists()) { - System.out.println("Using the directory: " + spritesDir.getAbsolutePath()); - } else { - spritesDir = new File(selFile); - } - if (!spritesDir.exists()) { - System.err.println("Sprites directory does not exist: " + spritesDir.getAbsolutePath()); - System.exit(1); - } - SpriteImporter spriteImporter = new SpriteImporter(); - int spriteCount = spriteImporter.bulkImport(spritesDir, swf, true); - System.out.println("Writing outfile"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - System.out.println("" + spriteCount + " sprites successfully imported"); - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - - private static void parseImportText(Stack args, String charset) { - if (args.size() < 3) { - badArguments("importtext"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - - String selFile = args.pop(); - boolean textsFolderExists = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME)).exists(); - File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); - if (textsFolderExists) { - System.out.println("Using the directory: " + new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME)).getAbsolutePath()); - } else { - textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); - } - TextImporter textImporter = new TextImporter(new MissingCharacterHandler() { - @Override - public boolean getIgnoreMissingCharacters() { - return true; - } - - @Override - public boolean handle(TextTag textTag, final FontTag font, final char character) { - String fontName = font.getSwf().sourceFontNamesMap.get(font.getFontId()); - if (fontName == null) { - fontName = font.getFontName(); - } - final Font f = FontTag.getInstalledFontsByName().get(fontName); - if (f == null || !f.canDisplay(character)) { - String msg = "Selected source font does not contain character \"%char%\".".replace("%char%", "" + character); - logger.log(Level.SEVERE, "{0} FontId: {1} TextId: {2}", new Object[]{msg, font.getCharacterId(), textTag.getCharacterId()}); - return false; - } - - return font.addCharacter(character, f); - } - }, new TextImportErrorHandler() { - private String getTextTagInfo(TextTag textTag) { - StringBuilder ret = new StringBuilder(); - if (textTag != null) { - ret.append(" TextId: ").append(textTag.getCharacterId()).append(" (").append(String.join(", ", textTag.getTexts())).append(")"); - } - - return ret.toString(); - } - - @Override - public boolean handle(TextTag textTag) { - String msg = "Error during text import. Do you want to continue?"; - logger.log(Level.SEVERE, "{0}{1}", new Object[]{msg, getTextTagInfo(textTag)}); - return true; - } - - @Override - public boolean handle(TextTag textTag, String message, long line) { - String msg = "Invalid text: %text% on line %line%. Do you want to continue?".replace("%text%", message).replace("%line%", Long.toString(line)); - logger.log(Level.SEVERE, "{0}{1}", new Object[]{msg, getTextTagInfo(textTag)}); - return true; - } - }); - - // try to import formatted texts - if (textsFile.exists()) { - textImporter.importTextsSingleFileFormatted(textsFile, swf); - } else { - textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); - if (!textsFolderExists) { - textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); - } - // try to import plain texts - if (textsFile.exists()) { - textImporter.importTextsSingleFile(textsFile, swf); - } else { - textImporter.importTextsMultipleFiles(selFile, swf); - } - } - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - - private static void parseImportScript(Stack args, String charset, boolean air, AbortRetryIgnoreHandler errorHandler) { - - String flexLocation = Configuration.flexSdkLocation.get(); - if (Configuration.useFlexAs3Compiler.get() && (flexLocation.isEmpty() || (!new File(flexLocation).exists()))) { - System.err.println("Flex AS3 compiler enabled but Flex SDK path not set"); - System.exit(1); - } - - if (args.size() < 3) { - badArguments("importscript"); - } - - File inFile = new File(args.pop()); - File outFile = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - String baseFolder = args.pop(); - String scriptsFolder = Path.combine(baseFolder, ScriptExportSettings.EXPORT_FOLDER_NAME); - if (new File(scriptsFolder).exists()) { - System.out.println("Using the directory: " + new File(scriptsFolder).getAbsolutePath()); - } else { - scriptsFolder = baseFolder; - } - ScriptImporterProgressListener listener = new ScriptImporterProgressListener() { - @Override - public void scriptImported() { - } - - @Override - public void scriptImportError() { - if (errorHandler == null || ((ConsoleAbortRetryIgnoreHandler) errorHandler).errorMode == AbortRetryIgnoreHandler.ABORT) { - System.exit(1); - } - } - }; - new AS2ScriptImporter().importScripts(scriptsFolder, swf.getASMs(true), listener); - new AS3ScriptImporter().importScripts(As3ScriptReplacerFactory.createByConfig(air), scriptsFolder, swf.getAS3Packs(), listener, new ArrayList<>() /*FIXME DEPENDENCIES!*/);//Main.getDependencies(swf)); - - try { - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { - swf.saveTo(fos); - } - } catch (IOException e) { - System.err.println("I/O error during writing"); - System.exit(2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseCustom(Stack args) { - String[] customParameters = new String[args.size()]; - for (int i = 0; i < customParameters.length; i++) { - customParameters[i] = args.pop(); - } - - SWFDecompilerPlugin.customParameters = customParameters; - } - - /*private static void loadFiles(String[] fileNames) { - boolean result = true; - for (String fileName : fileNames) { - try { - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, fileName, null); - Main.parseOpenable(sourceInfo); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - result = false; - } - } - - System.exit(result ? 0 : 1); - }*/ - - private static void replaceAS2PCode(String text, ASMSource src) throws IOException, InterruptedException { - System.out.println("Replace AS1/2 PCode"); - if (text.trim().startsWith(Helper.hexData)) { - src.setActionBytes(Helper.getBytesFromHexaText(text)); - } else { - try { - src.setActions(ASMParser.parse(0, true, text, src.getSwf().version, false, src.getSwf().getCharset())); - } catch (ActionParseException ex) { - System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line))); - System.exit(1); - } - } - src.setModified(); - } - - private static void replaceAS2(String as, ASMSource src) throws IOException, InterruptedException { - System.out.println("Replace AS1/2"); - System.out.println("Warning: This feature is EXPERIMENTAL"); - ActionScript2Parser par = new ActionScript2Parser(src.getSwf(), src); - try { - src.setActions(par.actionsFromString(as, src.getSwf().getCharset())); - } catch (ValueTooLargeException ex) { - System.err.println("Script or some of its functions are too large"); - System.exit(1); - } catch (ActionParseException ex) { - System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line))); - System.exit(1); - } catch (CompilationException ex) { - System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line))); - System.exit(1); - } - src.setModified(); - } - - private static void replaceAS3PCode(String text, ABC abc, int bodyIndex, Trait trait) throws IOException, InterruptedException { - System.out.println("Replace AS3 PCode"); - if (text.trim().startsWith(Helper.hexData)) { - byte[] data = Helper.getBytesFromHexaText(text); - MethodBody mb = abc.bodies.get(bodyIndex); - mb.setCodeBytes(data); - } else { - try { - AVM2Code acode = ASM3Parser.parse(abc, new StringReader(text), trait, new MissingSymbolHandler() { - //no longer ask for adding new constants - @Override - public boolean missingString(String value) { - return true; - } - - @Override - public boolean missingInt(long value) { - return true; - } - - @Override - public boolean missingUInt(long value) { - return true; - } - - @Override - public boolean missingDouble(double value) { - return true; - } - - @Override - public boolean missingDecimal(Decimal value) { - return true; - } - - @Override - public boolean missingFloat(float value) { - return true; - } - - @Override - public boolean missingFloat4(Float4 value) { - return true; - } - }, abc.bodies.get(bodyIndex), abc.method_info.get(abc.bodies.get(bodyIndex).method_info)); - //acode.getBytes(abc.bodies.get(bodyIndex).getCodeBytes()); - abc.bodies.get(bodyIndex).setCode(acode); - } catch (AVM2ParseException ex) { - System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line))); - System.exit(1); - } - } - ((Tag) abc.parentTag).setModified(true); - } - - private static void replaceAS3(String asp, String as, ScriptPack pack, boolean air) throws IOException, InterruptedException { - System.out.println("Replacing: " + asp); - System.out.println("Warning: This feature is EXPERIMENTAL"); - As3ScriptReplacerInterface scriptReplacer = As3ScriptReplacerFactory.createByConfig(air); - if (!scriptReplacer.isAvailable()) { - System.err.println("Current script replacer is not available."); - if (scriptReplacer instanceof FFDecAs3ScriptReplacer) { - System.err.println("Current replacer: FFDec"); - final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; - System.err.println("For ActionScript 3 direct editation, a library called \"PlayerGlobal.swc\" needs to be downloaded from Adobe homepage:"); - System.err.println(adobePage); - System.err.println("Download the library called PlayerGlobal(.swc), and place it to directory"); - System.err.println(Configuration.getFlashLibPath().getAbsolutePath()); - } else if (scriptReplacer instanceof MxmlcAs3ScriptReplacer) { - System.err.println("Current replacer: Flex SDK"); - final String flexPage = "http://www.adobe.com/devnet/flex/flex-sdk-download.html"; - System.err.println("For ActionScript 3 direct editation, Flex SDK needs to be download"); - System.err.println(flexPage); - System.err.println("Download FLEX Sdk, unzip it to some directory and set its directory path in the configuration"); - - } - System.exit(1); - } - - try { - pack.abc.replaceScriptPack(scriptReplacer, pack, as, new ArrayList<>() /*FIXME dependencies!!!*/);//Main.getDependencies(pack.abc.getSwf())); - } catch (As3ScriptReplaceException asre) { - for (As3ScriptReplaceExceptionItem item : asre.getExceptionItems()) { - String r = "%error% on line %line%, column %col%, file: %file%".replace("%error%", "" + item.getMessage()); - r = r.replace("%line%", Long.toString(item.getLine())); - r = r.replace("%file%", "" + item.getFile()); - r = r.replace("%col%", "" + item.getCol()); - logger.log(Level.SEVERE, r); - } - System.exit(1); - } - } - - private static void parseInfo(Stack args, String charset) throws FileNotFoundException { - File out; - PrintWriter pw = new PrintWriter(System.out); - boolean found = false; - boolean detectBundle = true; - while (!args.isEmpty()) { - String a = args.pop(); - switch (a) { - case "-out": - if (args.isEmpty()) { - badArguments("info"); - } - out = new File(args.pop()); - if (out.isDirectory()) { - logger.log(Level.SEVERE, "File is a directory"); - System.exit(1); - } else { - pw = new PrintWriter(out); - } - break; - case "-nobundle": - detectBundle = false; - break; - default: - Bundle bundle; - String sfile = a; - File file = new File(sfile); - try { - - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, sfile, sfile, detectBundle); - bundle = sourceInfo.getBundle(false, SearchMode.ALL); - logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile()); - - if (bundle != null) { - int subcnt = 0; - for (Entry streamEntry : bundle.getAll().entrySet()) { - InputStream stream = streamEntry.getValue(); - stream.reset(); - CancellableWorker worker = new CancellableWorker() { - @Override - public SWF doInBackground() throws Exception { - final CancellableWorker worker = this; - SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() { - @Override - public void progress(int p) { - //... - } - - @Override - public void status(String status) { - } - }, Configuration.parallelSpeedUp.get(), charset); - return swf; - } - }; - //loadingDialog.setWroker(worker); - worker.execute(); - - subcnt++; - try { - proccessInfoSWF(file, worker.get(), pw); - } catch (CancellationException | ExecutionException | InterruptedException ex) { - logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey()); - } - } - if (subcnt > 0) { - found = true; - } else { - System.err.println("No SWFs found in \"" + file + "\""); - } - } else { - StdInAwareFileInputStream fis = new StdInAwareFileInputStream(file); - BufferedInputStream inputStream = new BufferedInputStream(fis); - - InputStream fInputStream = inputStream; - CancellableWorker worker = new CancellableWorker() { - @Override - public SWF doInBackground() throws Exception { - final CancellableWorker worker = this; - SWF swf = new SWF(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle(), new ProgressListener() { - @Override - public void progress(int p) { - //startWork(AppStrings.translate("work.reading.swf"), p, worker); - } - - @Override - public void status(String status) { - } - }, Configuration.parallelSpeedUp.get(), charset); - return swf; - } - }; - - worker.execute(); - - try { - proccessInfoSWF(null, worker.get(), pw); - } catch (CancellationException | ExecutionException | InterruptedException ex) { - logger.log(Level.WARNING, "Loading SWF was cancelled."); - } - found = true; - } - } catch (IOException ex) { - logger.log(Level.SEVERE, "Cannot read."); - System.exit(1); - } - } - } - if (!found) { - System.exit(1); - } - } - - private static String doubleToString(double d) { - String ds = "" + d; - if (ds.endsWith(".0")) { - ds = ds.substring(0, ds.length() - 2); - } - return ds; - } - - private static void proccessInfoSWF(File bundle, SWF swf, PrintWriter pw) throws IOException { - pw.println("[swf]"); - pw.println("file=" + (bundle == null ? swf.getFile() : bundle + ":" + swf.getFileTitle())); - pw.println("fileSize=" + swf.fileSize); - pw.println("version=" + swf.version); - pw.println("compression=" + swf.compression); - pw.println("encrypted=" + swf.encrypted); - pw.println("gfx=" + swf.gfx); - pw.println("width=" + doubleToString(swf.displayRect.getWidth() / SWF.unitDivisor)); - pw.println("height=" + doubleToString(swf.displayRect.getHeight() / SWF.unitDivisor)); - pw.println("frameCount=" + swf.frameCount); - pw.println("frameRate=" + doubleToString(swf.frameRate)); - for (Tag t : swf.getTags()) { - if (t instanceof SetBackgroundColorTag) { - pw.println("backgroundColor=" + ((SetBackgroundColorTag) t).backgroundColor.toHexRGB()); - } - if (t instanceof ScriptLimitsTag) { - pw.println("maxRecursionDepth=" + ((ScriptLimitsTag) t).maxRecursionDepth); - pw.println("scriptTimeoutSeconds=" + ((ScriptLimitsTag) t).scriptTimeoutSeconds); - } - } - pw.println(); - - pw.println("[tags]"); - pw.println("tagCount=" + swf.getTags().size()); - pw.println("hasEndTag=" + swf.hasEndTag); - pw.println("characterCount=" + (swf.getCharacters().size())); - pw.println("maxCharacterId=" + (swf.getNextCharacterId() - 1)); - pw.println(); - - FileAttributesTag fa = swf.getFileAttributes(); - if (fa != null) { - pw.println("[attributes]"); - pw.println("actionScript3=" + fa.actionScript3); - pw.println("hasMetadata=" + fa.hasMetadata); - pw.println("noCrossDomainCache=" + fa.noCrossDomainCache); - pw.println("swfRelativeUrls=" + fa.swfRelativeUrls); - pw.println("useDirectBlit=" + fa.useDirectBlit); - pw.println("useGPU=" + fa.useGPU); - pw.println("useNetwork=" + fa.useNetwork); - - pw.println(); - } - - pw.println("[as2]"); - pw.println("scriptsCount=" + swf.getASMs(true).size()); - pw.println(); - - pw.println("[as3]"); - pw.println("ABCtagCount=" + swf.getAbcList().size()); - pw.println("packsCount=" + swf.getAS3Packs().size()); - String dcs = swf.getDocumentClass(); - if (dcs != null) { - if (dcs.contains(".")) { - DottedChain dc = DottedChain.parseWithSuffix(dcs); - pw.println("documentClass=" + dc.toPrintableString(true)); - } else { - pw.println("documentClass=" + IdentifiersDeobfuscation.printIdentifier(true, dcs)); - } - } else { - pw.println("documentClass="); - } - - pw.println(); - pw.flush(); - } - - /*private static void parseDumpSwf(Stack args) { - if (args.isEmpty()) { - badArguments("dumpswf"); - } - try { - Configuration.dumpTags.set(true); - Configuration.parallelSpeedUp.set(false); - OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, args.pop(), null); - Main.parseOpenable(sourceInfo); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - System.exit(1); - } - }*/ - - private static void parseDumpAS2(Stack args, String charset) { - if (args.isEmpty()) { - badArguments("dumpas2"); - } - File file = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(file)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - Map asms = swf.getASMs(false); - for (String as2 : asms.keySet()) { - System.out.println(as2); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseEnableDebugging(Stack args, String charset) { - if (args.size() < 2) { - badArguments("enabledebugging"); - } - - boolean injectas3 = false; - boolean doPCode = false; - boolean generateSwd = false; - String file = args.pop(); - if (file.equals("-generateswd")) { - if (args.size() < 2) { - badArguments("enabledebugging"); - } - file = args.pop(); - generateSwd = true; - } - if (file.equals("-injectas3")) { - if (args.size() < 2) { - badArguments("enabledebugging"); - } - file = args.pop(); - injectas3 = true; - } - if (file.equals("-pcode")) { - if (args.size() < 2) { - badArguments("enabledebugging"); - } - doPCode = true; - file = args.pop(); - } - String outfile = args.pop(); - try { - System.out.print("Working..."); - StdInAwareFileInputStream fis = new StdInAwareFileInputStream(file); - SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get(), charset); - fis.close(); - if (swf.isAS3()) { - swf.enableDebugging(injectas3, new File(outfile).getParentFile(), doPCode); - } else { - swf.enableDebugging(); - } - FileOutputStream fos = new FileOutputStream(outfile); - swf.saveTo(fos); - fos.close(); - if (!swf.isAS3()) { - if (generateSwd) { - fis = new StdInAwareFileInputStream(outfile); - swf = new SWF(fis, Configuration.parallelSpeedUp.get(), charset); - fis.close(); - String outSwd = outfile; - if (outSwd.toLowerCase(Locale.ENGLISH).endsWith(".swf")) { - outSwd = outSwd.substring(0, outSwd.length() - 4) + ".swd"; - } else { - outSwd = outSwd + ".swd"; - } - if (doPCode) { - if (!swf.generatePCodeSwdFile(new File(outSwd), new HashMap<>())) { - System.err.println("Generating SWD failed"); - } - } else if (!swf.generateSwdFile(new File(outSwd), new HashMap<>())) { - System.err.println("Generating SWD failed"); - } - } - } else if (generateSwd) { - System.err.println("WARNING: Cannot generate SWD for AS3 file"); - } - System.out.println("OK"); - } catch (FileNotFoundException ex) { - logger.log(Level.SEVERE, "Cannot read {0}", file); - System.exit(1); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Reading error {0}", file); - System.exit(2); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, "Cancelled {0}", file); - System.exit(3); - } - - System.out.println("Finished"); - } - - private static void parseDumpAS3(Stack args, String charset) { - if (args.isEmpty()) { - badArguments("dumpas3"); - } - File file = new File(args.pop()); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(file)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - List packs = swf.getAS3Packs(); - for (ScriptPack entry : packs) { - System.out.println(entry.getClassPath().toString() + " " + entry.scriptIndex); - } - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - } - - private static void parseHeader(Stack args) { - if (args.isEmpty()) { - badArguments("header"); - } - Float frameRate = null; - Integer frameCount = null; - Integer width = null; - Integer height = null; - Integer x = null; - Integer y = null; - Boolean gfx = null; - Integer version = null; - - Pattern displayRectPattern = Pattern.compile("\\[(?-?[0-9.]+)(?px)?,(?-?[0-9.]+)(?px)?,(?-?[0-9.]+)(?px)?,(?-?[0-9.]+)(?px)?\\]"); - - boolean printOnly = args.size() == 1; - - while (!args.isEmpty()) { - String arg = args.pop(); - if (arg.equals("-set")) { - if (args.size() < 3) { - badArguments("header"); - } - String key = args.pop(); - String value = args.pop(); - switch (key.toLowerCase()) { - case "version": - try { - version = Integer.valueOf(value); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - break; - case "gfx": - gfx = parseBooleanConfigValue(value); - if (gfx == null) { - badArguments("header"); - } - break; - case "displayrect": - Matcher displayRectMatcher = displayRectPattern.matcher(value); - if (!displayRectMatcher.matches()) { - badArguments("header"); - } - if (displayRectMatcher.group("x1px") != null) { - x = (int) Math.round(Float.parseFloat(displayRectMatcher.group("x1")) * SWF.unitDivisor); - } else { - try { - x = Integer.valueOf(displayRectMatcher.group("x1")); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } - if (displayRectMatcher.group("y1px") != null) { - y = (int) Math.round(Float.parseFloat(displayRectMatcher.group("y1")) * SWF.unitDivisor); - } else { - try { - y = Integer.valueOf(displayRectMatcher.group("y1")); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } - - int x2 = 0; - int y2 = 0; - - if (displayRectMatcher.group("x2px") != null) { - x2 = (int) Math.round(Float.parseFloat(displayRectMatcher.group("x2")) * SWF.unitDivisor); - } else { - try { - x2 = Integer.parseInt(displayRectMatcher.group("x2")); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } - if (displayRectMatcher.group("y2px") != null) { - y2 = (int) Math.round(Float.parseFloat(displayRectMatcher.group("y2")) * SWF.unitDivisor); - } else { - try { - y2 = Integer.parseInt(displayRectMatcher.group("y2")); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } - - width = x2 - x; - height = y2 - y; - break; - case "width": - if (value.endsWith("px")) { - value = value.substring(0, value.length() - 2).trim(); - try { - width = (int) Math.round(Float.parseFloat(value) * SWF.unitDivisor); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } else { - try { - width = Integer.valueOf(value); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } - break; - case "height": - if (value.endsWith("px")) { - value = value.substring(0, value.length() - 2).trim(); - try { - height = (int) Math.round(Float.parseFloat(value) * SWF.unitDivisor); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } else { - try { - height = Integer.valueOf(value); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - } - break; - case "framecount": - try { - frameCount = Integer.valueOf(value); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - break; - case "framerate": - try { - frameRate = Float.valueOf(value); - } catch (NumberFormatException nfe) { - badArguments("header"); - } - break; - default: - badArguments("header"); - } - } else { - args.push(arg); - break; - } - } - - if (!printOnly && args.size() != 2) { - badArguments("header"); - } - - File file = new File(args.pop()); - File outfile = printOnly ? null : new File(args.pop()); - PrintWriter pw = new PrintWriter(System.out); - try { - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(file)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), true); - - if (printOnly) { - pw.println("[header]"); - pw.println("fileSize=" + swf.fileSize); - pw.println("version=" + swf.version); - pw.println("compression=" + swf.compression); - pw.println("encrypted=" + swf.encrypted); - pw.println("gfx=" + swf.gfx); - pw.println("displayRect=[" + swf.displayRect.Xmin + ", " + swf.displayRect.Ymin + ", " + swf.displayRect.Xmax + ", " + swf.displayRect.Ymax + "]"); - pw.println("width=" + swf.displayRect.getWidth()); - pw.println("widthPx=" + doubleToString(swf.displayRect.getWidth() / SWF.unitDivisor)); - pw.println("height=" + swf.displayRect.getHeight()); - pw.println("heightPx=" + doubleToString(swf.displayRect.getHeight() / SWF.unitDivisor)); - pw.println("frameCount=" + swf.frameCount); - pw.println("frameRate=" + doubleToString(swf.frameRate)); - pw.flush(); - return; - } - if (version != null) { - swf.version = version; - pw.println("version set to " + version); - } - if (gfx != null) { - swf.gfx = gfx; - pw.println("gfx set to " + gfx); - } - if (x != null) { - swf.displayRect.Xmin = x; - pw.println("displayrect.x1 set to " + x); - } - if (y != null) { - swf.displayRect.Ymin = y; - pw.println("displayrect.y1 set to " + y); - } - if (width != null) { - swf.displayRect.Xmax = swf.displayRect.Xmin + width; - pw.println("displayrect.x2 set to " + swf.displayRect.Xmax); - } - if (height != null) { - swf.displayRect.Ymax = swf.displayRect.Ymin + height; - pw.println("displayrect.y2 set to " + swf.displayRect.Ymax); - } - if (frameCount != null) { - swf.frameCount = frameCount; - pw.println("framecount set to " + frameCount); - } - if (frameRate != null) { - swf.frameRate = frameRate; - pw.println("framerate set to " + frameRate); - } - - try (FileOutputStream fos = new FileOutputStream(outfile)) { - swf.saveTo(fos); - } - pw.println("Successfully saved"); - } - } catch (IOException | InterruptedException e) { - System.err.println("I/O error during reading"); - System.exit(2); - } - pw.flush(); - } - - private static FilenameFilter getSwfFilter() { - return (File dir, String name) -> name.toLowerCase(Locale.ENGLISH).endsWith(".swf"); - } - - private static E enumFromStr(String str, Class cls) { - E[] vals = cls.getEnumConstants(); - if (str == null) { - return vals[0]; - } - for (E e : vals) { - if (e.toString().toLowerCase(Locale.ENGLISH).replace("_", "").equals(str.toLowerCase(Locale.ENGLISH).replace("_", ""))) { - return e; - } - } - return vals[0]; - } - - private static interface SwfAction { - - public void swfAction(SWF swf, OutputStream stdout) throws IOException; - } - - private static void processReadSWF(File inFile, File stdOutFile, SwfAction action, String charset) { - OutputStream stdout = null; - - try { - if (stdOutFile != null) { - try { - stdout = new FileOutputStream(stdOutFile); - } catch (FileNotFoundException ex) { - System.err.println("File not found: " + ex.getMessage()); - System.exit(1); - } - } else { - stdout = System.out; - } - - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - action.swfAction(swf, stdout); - } catch (FileNotFoundException ex) { - System.err.println("File not found: " + ex.getMessage()); - System.exit(1); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - System.exit(1); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error", ex); - System.exit(1); - } - } finally { - if (stdOutFile != null) { - if (stdout != null) { - try { - stdout.close(); - } catch (IOException ex) { - //ignore - } - } - } - } - } - - private static void processModifySWF(File inFile, File outFile, File stdOutFile, SwfAction action, String charset) { - - OutputStream stdout = null; - - try { - if (stdOutFile != null) { - try { - stdout = new FileOutputStream(stdOutFile); - } catch (FileNotFoundException ex) { - System.err.println("File not found: " + ex.getMessage()); - System.exit(1); - } - } else { - stdout = System.out; - } - - File tmpFile = null; - if (inFile.equals(outFile)) { - try { - tmpFile = File.createTempFile("ffdec_modify_", ".swf"); - outFile = tmpFile; - } catch (IOException ex) { - System.err.println("Unable to create temp file"); - System.exit(1); - } - } - try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile); FileOutputStream fos = new FileOutputStream(outFile)) { - SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); - action.swfAction(swf, stdout); - swf.saveTo(fos); - } catch (FileNotFoundException ex) { - System.err.println("File not found: " + ex.getMessage()); - System.exit(1); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - System.exit(1); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error", ex); - System.exit(1); - } - - if (tmpFile != null) { - try { - if (!inFile.delete()) { - System.err.println("Cannot overwrite original file"); - System.exit(1); - } - if (!tmpFile.renameTo(inFile)) { - System.err.println("Cannot rename tempfile to original file"); - System.exit(1); - } - tmpFile = null; - System.out.println(inFile + " overwritten."); - } finally { - if (tmpFile != null && tmpFile.exists()) { - tmpFile.delete(); - } - } - } - } finally { - if (stdOutFile != null) { - if (stdout != null) { - try { - stdout.close(); - } catch (IOException ex) { - //ignore - } - } - } - } - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/ConsoleAbortRetryIgnoreHandler.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/ConsoleAbortRetryIgnoreHandler.java deleted file mode 100644 index dcfc04648..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/ConsoleAbortRetryIgnoreHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2010-2023 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.legacy; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import java.util.Locale; -import java.util.Scanner; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class ConsoleAbortRetryIgnoreHandler implements AbortRetryIgnoreHandler { - - int errorCount = 0; - - int errorMode; - - int retryCount; - - public ConsoleAbortRetryIgnoreHandler(int errorMode, int retryCount) { - this.errorMode = errorMode; - this.retryCount = retryCount; - } - - @Override - public int handle(Throwable thrown) { - if (errorMode != AbortRetryIgnoreHandler.UNDEFINED) { - int result = errorMode; - - if (errorMode == AbortRetryIgnoreHandler.RETRY && errorCount < retryCount) { - errorCount++; - } else { - result = AbortRetryIgnoreHandler.IGNORE; - } - - return result; - } - Scanner sc = new Scanner(System.in); - if (thrown != null) { - Logger.getLogger(ConsoleAbortRetryIgnoreHandler.class.getName()).log(Level.SEVERE, "Error occured", thrown); - System.out.println("Error occured: " + thrown.getLocalizedMessage()); - } - do { - System.out.print("Select action: (A)bort, (R)Retry, (I)Ignore:"); - String n = sc.nextLine(); - switch (n.toLowerCase(Locale.ENGLISH)) { - case "a": - return AbortRetryIgnoreHandler.ABORT; - case "r": - return AbortRetryIgnoreHandler.RETRY; - case "i": - return AbortRetryIgnoreHandler.IGNORE; - } - } while (true); - } - - @Override - public AbortRetryIgnoreHandler getNewInstance() { - return new ConsoleAbortRetryIgnoreHandler(errorMode, retryCount); - } -} diff --git a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/StdInAwareFileInputStream.java b/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/StdInAwareFileInputStream.java deleted file mode 100644 index 5ed07e064..000000000 --- a/libsrc/ffdec_cli/src/com/jpexs/decompiler/flash/cli/legacy/StdInAwareFileInputStream.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2022-2023 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.cli.legacy; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -/** - * FileInputStream to which can be passed /dev/stdin as special file for stdin - * on Windows. On linux, standard /dev/stdin is used. - * - * @author JPEXS - */ -public class StdInAwareFileInputStream extends InputStream implements AutoCloseable { - - public static final String STDIN_PATH = "/dev/stdin"; - - private InputStream is; - - public StdInAwareFileInputStream(File file) throws FileNotFoundException { - String absPath = file.getPath().replace("\\", "/"); - if (absPath.equals(STDIN_PATH) && !file.exists()) { - is = System.in; - } else { - is = new FileInputStream(file); - } - } - - public StdInAwareFileInputStream(String file) throws FileNotFoundException { - this(new File(file)); - } - - @Override - public int available() throws IOException { - return is.available(); - } - - @Override - public long skip(long n) throws IOException { - return is.skip(n); - } - - @Override - public synchronized void reset() throws IOException { - is.reset(); - } - - @Override - public void close() throws IOException { - is.close(); - } - - @Override - public int read() throws IOException { - return is.read(); - } - - @Override - public int read(byte[] b) throws IOException { - return is.read(b); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - return is.read(b, off, len); - } -} diff --git a/libsrc/ffdec_cli/src/picocli/CommandLine.java b/libsrc/ffdec_cli/src/picocli/CommandLine.java deleted file mode 100644 index 8c2cd5bdd..000000000 --- a/libsrc/ffdec_cli/src/picocli/CommandLine.java +++ /dev/null @@ -1,19205 +0,0 @@ -/* - Copyright 2017 Remko Popma - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -package picocli; - -import java.io.*; -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.*; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.NetworkInterface; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.ByteOrder; -import java.nio.charset.Charset; -import java.text.BreakIterator; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import picocli.CommandLine.Help.Ansi.IStyle; -import picocli.CommandLine.Help.Ansi.Style; -import picocli.CommandLine.Help.Ansi.Text; -import picocli.CommandLine.Model.*; -import picocli.CommandLine.ParseResult.GroupMatchContainer; - -import static java.util.Locale.ENGLISH; -import static picocli.CommandLine.Help.Column.Overflow.SPAN; -import static picocli.CommandLine.Help.Column.Overflow.TRUNCATE; -import static picocli.CommandLine.Help.Column.Overflow.WRAP; - -/** - *

- * CommandLine interpreter that uses reflection to initialize an annotated user object with values obtained from the - * command line arguments. - *

- * The full user manual is hosted at https://picocli.info. - *

Example

- *

- * An example that implements {@code Callable} and uses the {@link #execute(String...) CommandLine.execute} convenience API to run in a single line of code: - *

- *
- * @Command(name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0",
- *          description = "Prints the checksum (SHA-1 by default) of a file to STDOUT.")
- * class CheckSum implements Callable<Integer> {
- *
- *     @Parameters(index = "0", description = "The file whose checksum to calculate.")
- *     private File file;
- *
- *     @Option(names = {"-a", "--algorithm"}, description = "MD5, SHA-1, SHA-256, ...")
- *     private String algorithm = "SHA-1";
- *
- *     @Override
- *     public Integer call() throws Exception { // your business logic goes here...
- *         byte[] fileContents = Files.readAllBytes(file.toPath());
- *         byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
- *         System.out.printf("%0" + (digest.length*2) + "x%n", new BigInteger(1,digest));
- *         return 0;
- *     }
- *
- *     // CheckSum implements Callable, so parsing, error handling and handling user
- *     // requests for usage help or version help can be done with one line of code.
- *     public static void main(String[] args) {
- *         int exitCode = new CommandLine(new CheckSum()).execute(args);
- *         System.exit(exitCode);
- *     }
- * }
- * 
- *

Another example where the application calls {@code parseArgs} and takes responsibility - * for error handling and checking whether the user requested help:

- *
import static picocli.CommandLine.*;
- *
- * @Command(mixinStandardHelpOptions = true, version = "v3.0.0",
- *         header = "Encrypt FILE(s), or standard input, to standard output or to the output file.")
- * public class Encrypt {
- *
- *     @Parameters(description = "Any number of input files")
- *     private List<File> files = new ArrayList<File>();
- *
- *     @Option(names = { "-o", "--out" }, description = "Output file (default: print to console)")
- *     private File outputFile;
- *
- *     @Option(names = { "-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting. Multiple -v options increase the verbosity.")
- *     private boolean[] verbose;
- * }
- * 
- *

- * Use {@code CommandLine} to initialize a user object as follows: - *

- * public static void main(String... args) {
- *     Encrypt encrypt = new Encrypt();
- *     try {
- *         ParseResult parseResult = new CommandLine(encrypt).parseArgs(args);
- *         if (!CommandLine.printHelpIfRequested(parseResult)) {
- *             runProgram(encrypt);
- *         }
- *     } catch (ParameterException ex) { // command line arguments could not be parsed
- *         System.err.println(ex.getMessage());
- *         ex.getCommandLine().usage(System.err);
- *     }
- * }
- * 

- * Invoke the above program with some command line arguments. The below are all equivalent: - *

- *
- * --verbose --out=outfile in1 in2
- * --verbose --out outfile in1 in2
- * -v --out=outfile in1 in2
- * -v -o outfile in1 in2
- * -v -o=outfile in1 in2
- * -vo outfile in1 in2
- * -vo=outfile in1 in2
- * -v -ooutfile in1 in2
- * -vooutfile in1 in2
- * 
- *

Classes and Interfaces for Defining a CommandSpec Model

- *

- * Classes and Interfaces for Defining a CommandSpec Model - *

- *

Classes Related to Parsing Command Line Arguments

- *

- * Classes Related to Parsing Command Line Arguments - *

- */ -public class CommandLine { - - /** This is picocli version {@value}. */ - public static final String VERSION = "4.7.5"; - private static final Tracer TRACER = new Tracer(); - - private CommandSpec commandSpec; - private final Interpreter interpreter; - private final IFactory factory; - - private Object executionResult; - private PrintWriter out; - private PrintWriter err; - private Help.ColorScheme colorScheme = Help.defaultColorScheme(Help.Ansi.AUTO); - private IExitCodeExceptionMapper exitCodeExceptionMapper; - private IExecutionStrategy executionStrategy = new RunLast(); - private IParameterExceptionHandler parameterExceptionHandler = new IParameterExceptionHandler() { - public int handleParseException(ParameterException ex, String[] args) { - CommandLine cmd = ex.getCommandLine(); - DefaultExceptionHandler.internalHandleParseException(ex, cmd.getErr(), cmd.getColorScheme()); - return mappedExitCode(ex, cmd.getExitCodeExceptionMapper(), cmd.getCommandSpec().exitCodeOnInvalidInput()); - } - }; - private IExecutionExceptionHandler executionExceptionHandler = new IExecutionExceptionHandler() { - public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) throws Exception { - throw ex; - } - }; - - /** - * Constructs a new {@code CommandLine} interpreter with the specified object (which may be an annotated user object or a {@link CommandSpec CommandSpec}) and a default {@linkplain IFactory factory}. - *

The specified object may be a {@link CommandSpec CommandSpec} object, or it may be a {@code @Command}-annotated - * user object with {@code @Option} and {@code @Parameters}-annotated fields and methods, in which case picocli automatically - * constructs a {@code CommandSpec} from this user object. - *

If the specified command object is an interface {@code Class} with {@code @Option} and {@code @Parameters}-annotated methods, - * picocli creates a {@link java.lang.reflect.Proxy Proxy} whose methods return the matched command line values. - * If the specified command object is a concrete {@code Class}, picocli delegates to the default factory to get an instance. - *

- * If the specified object implements {@code Runnable} or {@code Callable}, or if it is a {@code Method} object, - * the command can be run as an application in a single line of code by using the - * {@link #execute(String...) execute} method to omit some boilerplate code for handling help requests and invalid input. - * See {@link #getCommandMethods(Class, String) getCommandMethods} for a convenient way to obtain a command {@code Method}. - *

- * When the {@link #parseArgs(String...)} method is called, the {@link CommandSpec CommandSpec} object will be - * initialized based on command line arguments. If the commandSpec is created from an annotated user object, this - * user object will be initialized based on the command line arguments. - *

- * @param command an annotated user object or a {@code CommandSpec} object to initialize from the command line arguments - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public CommandLine(Object command) { - this(command, new DefaultFactory()); - } - /** - * Constructs a new {@code CommandLine} interpreter with the specified object (which may be an annotated user object or a {@link CommandSpec CommandSpec}) and object factory. - *

The specified object may be a {@link CommandSpec CommandSpec} object, or it may be a {@code @Command}-annotated - * user object with {@code @Option} and {@code @Parameters}-annotated fields and methods, in which case picocli automatically - * constructs a {@code CommandSpec} from this user object. - *

If the specified command object is an interface {@code Class} with {@code @Option} and {@code @Parameters}-annotated methods, - * picocli creates a {@link java.lang.reflect.Proxy Proxy} whose methods return the matched command line values. - * If the specified command object is a concrete {@code Class}, picocli delegates to the {@linkplain IFactory factory} to get an instance. - *

- * If the specified object implements {@code Runnable} or {@code Callable}, or if it is a {@code Method} object, - * the command can be run as an application in a single line of code by using the - * {@link #execute(String...) execute} method to omit some boilerplate code for handling help requests and invalid input. - * See {@link #getCommandMethods(Class, String) getCommandMethods} for a convenient way to obtain a command {@code Method}. - *

- * When the {@link #parseArgs(String...)} method is called, the {@link CommandSpec CommandSpec} object will be - * initialized based on command line arguments. If the commandSpec is created from an annotated user object, this - * user object will be initialized based on the command line arguments. - *

- * @param command an annotated user object or a {@code CommandSpec} object to initialize from the command line arguments - * @param factory the factory used to create instances of {@linkplain Command#subcommands() subcommands}, {@linkplain Option#converter() converters}, etc., that are registered declaratively with annotation attributes - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @since 2.2 */ - public CommandLine(Object command, IFactory factory) { - this(command, factory, true); - } - - private CommandLine(Object command, IFactory factory, boolean userCalled) { - this.factory = Assert.notNull(factory, "factory"); - interpreter = new Interpreter(); - commandSpec = CommandSpec.forAnnotatedObject(command, factory); - commandSpec.commandLine(this); - if (userCalled) { this.applyModelTransformations(); } - commandSpec.validate(); - if (commandSpec.unmatchedArgsBindings().size() > 0) { setUnmatchedArgumentsAllowed(true); } - } - - /** Apply transformers to command spec recursively. */ - private void applyModelTransformations() { - if (commandSpec.modelTransformer != null) { - commandSpec = commandSpec.modelTransformer.transform(commandSpec); - } - for (CommandLine cmd : getSubcommands().values()) { - cmd.applyModelTransformations(); - } - } - - private CommandLine copy() { - CommandLine result = new CommandLine(commandSpec.copy(), factory); // create a new sub-hierarchy - result.err = err; - result.out = out; - result.colorScheme = colorScheme; - result.executionStrategy = executionStrategy; - result.exitCodeExceptionMapper = exitCodeExceptionMapper; - result.executionExceptionHandler = executionExceptionHandler; - result.parameterExceptionHandler = parameterExceptionHandler; - - result.interpreter.converterRegistry.clear(); - result.interpreter.converterRegistry.putAll(interpreter.converterRegistry); - return result; - } - - /** - * Returns the {@code CommandSpec} model that this {@code CommandLine} was constructed with. - * @return the {@code CommandSpec} model - * @since 3.0 */ - public CommandSpec getCommandSpec() { return commandSpec; } - - /** - * Adds the options and positional parameters in the specified mixin to this command. - *

The specified object may be a {@link CommandSpec CommandSpec} object, or it may be a user object with - * {@code @Option} and {@code @Parameters}-annotated fields, in which case picocli automatically - * constructs a {@code CommandSpec} from this user object. - *

- * @param name the name by which the mixin object may later be retrieved - * @param mixin an annotated user object or a {@link CommandSpec CommandSpec} object whose options and positional parameters to add to this command - * @return this CommandLine object, to allow method chaining - * @since 3.0 */ - public CommandLine addMixin(String name, Object mixin) { - getCommandSpec().addMixin(name, CommandSpec.forAnnotatedObject(mixin, factory)); - return this; - } - - /** - * Returns a map of user objects whose options and positional parameters were added to ("mixed in" with) this command. - * @return a new Map containing the user objects mixed in with this command. If {@code CommandSpec} objects without - * user objects were programmatically added, use the {@link CommandSpec#mixins() underlying model} directly. - * @since 3.0 */ - public Map getMixins() { - Map mixins = getCommandSpec().mixins(); - Map result = new LinkedHashMap(); - for (String name : mixins.keySet()) { result.put(name, mixins.get(name).userObject.getInstance()); } - return result; - } - - /** Registers a subcommand with the name obtained from the {@code @Command(name = "...")} {@linkplain Command#name() annotation attribute} of the specified command. - * @param command the object to initialize with command line arguments following the subcommand name. - * This may be a {@code Class} that has a {@code @Command} annotation, or an instance of such a - * class, or a {@code CommandSpec} or {@code CommandLine} instance with its own (nested) subcommands. - * @return this CommandLine object, to allow method chaining - * @since 4.0 - * @throws InitializationException if no name could be found for the specified subcommand, - * or if another subcommand was already registered under the same name, or if one of the aliases - * of the specified subcommand was already used by another subcommand. - * @see #addSubcommand(String, Object) */ - public CommandLine addSubcommand(Object command) { - return addSubcommand(null, command, new String[0]); - } - - /** Registers a subcommand with the specified name. For example: - *
-     * CommandLine commandLine = new CommandLine(new Git())
-     *         .addSubcommand("status",   new GitStatus())
-     *         .addSubcommand("commit",   new GitCommit();
-     *         .addSubcommand("add",      new GitAdd())
-     *         .addSubcommand("branch",   new GitBranch())
-     *         .addSubcommand("checkout", new GitCheckout())
-     *         //...
-     *         ;
-     * 
- * - *

The specified object can be an annotated object or a - * {@code CommandLine} instance with its own nested subcommands. For example:

- *
-     * CommandLine commandLine = new CommandLine(new MainCommand())
-     *         .addSubcommand("cmd1",                 new ChildCommand1()) // subcommand
-     *         .addSubcommand("cmd2",                 new ChildCommand2())
-     *         .addSubcommand("cmd3", new CommandLine(new ChildCommand3()) // subcommand with nested sub-subcommands
-     *                 .addSubcommand("cmd3sub1",                 new GrandChild3Command1())
-     *                 .addSubcommand("cmd3sub2",                 new GrandChild3Command2())
-     *                 .addSubcommand("cmd3sub3", new CommandLine(new GrandChild3Command3()) // deeper nesting
-     *                         .addSubcommand("cmd3sub3sub1", new GreatGrandChild3Command3_1())
-     *                         .addSubcommand("cmd3sub3sub2", new GreatGrandChild3Command3_2())
-     *                 )
-     *         );
-     * 
- *

The default type converters are available on all subcommands and nested sub-subcommands, but custom type - * converters are registered only with the subcommand hierarchy as it existed when the custom type was registered. - * To ensure a custom type converter is available to all subcommands, register the type converter last, after - * adding subcommands.

- *

See also the {@link Command#subcommands()} annotation to register subcommands declaratively.

- * - * @param name the string to recognize on the command line as a subcommand. - * If {@code null}, the {@linkplain CommandSpec#name() name} of the specified subcommand is used; - * if this is also {@code null}, the first {@linkplain CommandSpec#aliases() alias} is used. - * @param command the object to initialize with command line arguments following the subcommand name. - * This may be a {@code Class} that has a {@code @Command} annotation, or an instance of such a - * class, or a {@code CommandSpec} or {@code CommandLine} instance with its own (nested) subcommands. - * @return this CommandLine object, to allow method chaining - * @see #registerConverter(Class, ITypeConverter) - * @since 0.9.7 - * @see Command#subcommands() - * @throws InitializationException if the specified name is {@code null}, and no alternative name could be found, - * or if another subcommand was already registered under the same name, or if one of the aliases - * of the specified subcommand was already used by another subcommand. - */ - public CommandLine addSubcommand(String name, Object command) { - return addSubcommand(name, command, new String[0]); - } - - /** Registers a subcommand with the specified name and all specified aliases. See also {@link #addSubcommand(String, Object)}. - * @param name the string to recognize on the command line as a subcommand. - * If {@code null}, the {@linkplain CommandSpec#name() name} of the specified subcommand is used; - * if this is also {@code null}, the first {@linkplain CommandSpec#aliases() alias} is used. - * @param command the object to initialize with command line arguments following the subcommand name. - * This may be a {@code Class} that has a {@code @Command} annotation, or an instance of such a - * class, or a {@code CommandSpec} or {@code CommandLine} instance with its own (nested) subcommands. - * @param aliases zero or more alias names that are also recognized on the command line as this subcommand - * @return this CommandLine object, to allow method chaining - * @since 3.1 - * @see #addSubcommand(String, Object) - * @throws InitializationException if the specified name is {@code null}, and no alternative name could be found, - * or if another subcommand was already registered under the same name, or if one of the aliases - * of the specified subcommand was already used by another subcommand. - */ - public CommandLine addSubcommand(String name, Object command, String... aliases) { - CommandLine subcommandLine = toCommandLine(command, factory); - subcommandLine.getCommandSpec().aliases.addAll(Arrays.asList(aliases)); - getCommandSpec().addSubcommand(name, subcommandLine); - return this; - } - /** Returns a map with the subcommands {@linkplain #addSubcommand(String, Object) registered} on this instance. - * @return a map with the registered subcommands - * @since 0.9.7 - */ - public Map getSubcommands() { - return new CaseAwareLinkedMap(getCommandSpec().commands); - } - /** - * Returns the command that this is a subcommand of, or {@code null} if this is a top-level command. - * @return the command that this is a subcommand of, or {@code null} if this is a top-level command - * @see #addSubcommand(String, Object) - * @see Command#subcommands() - * @since 0.9.8 - */ - public CommandLine getParent() { - CommandSpec parent = getCommandSpec().parent(); - return parent == null ? null : parent.commandLine(); - } - - /** Returns the annotated user object that this {@code CommandLine} instance was constructed with. - * @param the type of the variable that the return value is being assigned to - * @return the annotated object that this {@code CommandLine} instance was constructed with - * @since 0.9.7 - */ - @SuppressWarnings("unchecked") - public T getCommand() { - return (T) getCommandSpec().userObject(); - } - - /** Returns the factory that this {@code CommandLine} was constructed with. - * @return the factory that this {@code CommandLine} was constructed with, never {@code null} - * @since 4.6 */ - public IFactory getFactory() { return factory; } - - /** Returns {@code true} if an option annotated with {@link Option#usageHelp()} was specified on the command line. - * @return whether the parser encountered an option annotated with {@link Option#usageHelp()}. - * @since 0.9.8 */ - public boolean isUsageHelpRequested() { return interpreter.parseResultBuilder != null && interpreter.parseResultBuilder.usageHelpRequested; } - - /** Returns {@code true} if an option annotated with {@link Option#versionHelp()} was specified on the command line. - * @return whether the parser encountered an option annotated with {@link Option#versionHelp()}. - * @since 0.9.8 */ - public boolean isVersionHelpRequested() { return interpreter.parseResultBuilder != null && interpreter.parseResultBuilder.versionHelpRequested; } - /** Returns a new {@code Help} object created by the {@code IHelpFactory} with the {@code CommandSpec} and {@code ColorScheme} of this command. - * @see Help#Help(CommandSpec, Help.ColorScheme) - * @see #getHelpFactory() - * @see #getCommandSpec() - * @see #getColorScheme() - * @since 4.1 - */ - public Help getHelp() { - return getHelpFactory().create(getCommandSpec(), getColorScheme()); - } - /** Returns the {@code IHelpFactory} that is used to construct the usage help message. - * @see #setHelpFactory(IHelpFactory) - * @since 3.9 - */ - public IHelpFactory getHelpFactory() { - return getCommandSpec().usageMessage().helpFactory(); - } - - /** Sets a new {@code IHelpFactory} to customize the usage help message. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param helpFactory the new help factory. Must be non-{@code null}. - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.9 - */ - public CommandLine setHelpFactory(IHelpFactory helpFactory) { - getCommandSpec().usageMessage().helpFactory(helpFactory); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setHelpFactory(helpFactory); - } - return this; - } - - /** - * Returns the section keys in the order that the usage help message should render the sections. - * This ordering may be modified with {@link #setHelpSectionKeys(List) setSectionKeys}. The default keys are (in order): - *
    - *
  1. {@link UsageMessageSpec#SECTION_KEY_HEADER_HEADING SECTION_KEY_HEADER_HEADING}
  2. - *
  3. {@link UsageMessageSpec#SECTION_KEY_HEADER SECTION_KEY_HEADER}
  4. - *
  5. {@link UsageMessageSpec#SECTION_KEY_SYNOPSIS_HEADING SECTION_KEY_SYNOPSIS_HEADING}
  6. - *
  7. {@link UsageMessageSpec#SECTION_KEY_SYNOPSIS SECTION_KEY_SYNOPSIS}
  8. - *
  9. {@link UsageMessageSpec#SECTION_KEY_DESCRIPTION_HEADING SECTION_KEY_DESCRIPTION_HEADING}
  10. - *
  11. {@link UsageMessageSpec#SECTION_KEY_DESCRIPTION SECTION_KEY_DESCRIPTION}
  12. - *
  13. {@link UsageMessageSpec#SECTION_KEY_PARAMETER_LIST_HEADING SECTION_KEY_PARAMETER_LIST_HEADING}
  14. - *
  15. {@link UsageMessageSpec#SECTION_KEY_AT_FILE_PARAMETER SECTION_KEY_AT_FILE_PARAMETER}
  16. - *
  17. {@link UsageMessageSpec#SECTION_KEY_PARAMETER_LIST SECTION_KEY_PARAMETER_LIST}
  18. - *
  19. {@link UsageMessageSpec#SECTION_KEY_OPTION_LIST_HEADING SECTION_KEY_OPTION_LIST_HEADING}
  20. - *
  21. {@link UsageMessageSpec#SECTION_KEY_OPTION_LIST SECTION_KEY_OPTION_LIST}
  22. - *
  23. {@link UsageMessageSpec#SECTION_KEY_COMMAND_LIST_HEADING SECTION_KEY_COMMAND_LIST_HEADING}
  24. - *
  25. {@link UsageMessageSpec#SECTION_KEY_COMMAND_LIST SECTION_KEY_COMMAND_LIST}
  26. - *
  27. {@link UsageMessageSpec#SECTION_KEY_EXIT_CODE_LIST_HEADING SECTION_KEY_EXIT_CODE_LIST_HEADING}
  28. - *
  29. {@link UsageMessageSpec#SECTION_KEY_EXIT_CODE_LIST SECTION_KEY_EXIT_CODE_LIST}
  30. - *
  31. {@link UsageMessageSpec#SECTION_KEY_FOOTER_HEADING SECTION_KEY_FOOTER_HEADING}
  32. - *
  33. {@link UsageMessageSpec#SECTION_KEY_FOOTER SECTION_KEY_FOOTER}
  34. - *
- * @since 3.9 - */ - public List getHelpSectionKeys() { return getCommandSpec().usageMessage().sectionKeys(); } - - /** - * Sets the section keys in the order that the usage help message should render the sections. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- *

Use {@link UsageMessageSpec#sectionKeys(List)} to customize a command without affecting its subcommands.

- * @see #getHelpSectionKeys - * @since 3.9 - */ - public CommandLine setHelpSectionKeys(List keys) { - getCommandSpec().usageMessage().sectionKeys(keys); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setHelpSectionKeys(keys); - } - return this; - } - - /** - * Returns the map of section keys and renderers used to construct the usage help message. - * The usage help message can be customized by adding, replacing and removing section renderers from this map. - * Sections can be reordered with {@link #setHelpSectionKeys(List) setSectionKeys}. - * Sections that are either not in this map or not in the list returned by {@link #getHelpSectionKeys() getSectionKeys} are omitted. - *

- * NOTE: By modifying the returned {@code Map}, only the usage help message of this command is affected. - * Use {@link #setHelpSectionMap(Map)} to customize the usage help message for this command and all subcommands. - *

- * @since 3.9 - */ - public Map getHelpSectionMap() { return getCommandSpec().usageMessage().sectionMap(); } - - /** - * Sets the map of section keys and renderers used to construct the usage help message. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- *

Use {@link UsageMessageSpec#sectionMap(Map)} to customize a command without affecting its subcommands.

- * @see #getHelpSectionMap - * @since 3.9 - */ - public CommandLine setHelpSectionMap(Map map) { - getCommandSpec().usageMessage().sectionMap(map); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setHelpSectionMap(map); - } - return this; - } - /** - * Returns whether line breaks should take wide Chinese, Japanese and Korean characters into account for line-breaking purposes. The default is {@code true}. - * @return true if wide Chinese, Japanese and Korean characters are counted as double the size of other characters for line-breaking purposes - * @since 4.0 */ - public boolean isAdjustLineBreaksForWideCJKCharacters() { return getCommandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters(); } - /** Sets whether line breaks should take wide Chinese, Japanese and Korean characters into account, and returns this UsageMessageSpec. The default is {@code true}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param adjustForWideChars if true, wide Chinese, Japanese and Korean characters are counted as double the size of other characters for line-breaking purposes - * @since 4.0 */ - public CommandLine setAdjustLineBreaksForWideCJKCharacters(boolean adjustForWideChars) { - getCommandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters(adjustForWideChars); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setAdjustLineBreaksForWideCJKCharacters(adjustForWideChars); - } - return this; - } - - /** Returns whether the value of boolean flag options should be "toggled" when the option is matched. - * From 4.0, this is {@code false} by default, and when a flag option is specified on the command line picocli - * will set its value to the opposite of its default value. - * If this method returns {@code true}, flags are toggled, so if the value is {@code true} it is - * set to {@code false}, and when the value is {@code false} it is set to {@code true}. - * When toggling is enabled, specifying a flag option twice on the command line will have no effect because they cancel each other out. - * @return {@code true} the value of boolean flag options should be "toggled" when the option is matched, {@code false} otherwise - * @since 3.0 - */ - public boolean isToggleBooleanFlags() { - return getCommandSpec().parser().toggleBooleanFlags(); - } - - /** Sets whether the value of boolean flag options should be "toggled" when the option is matched. The default is {@code false}, - * and when a flag option is specified on the command line picocli will set its value to the opposite of its default value. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.0 - */ - public CommandLine setToggleBooleanFlags(boolean newValue) { - getCommandSpec().parser().toggleBooleanFlags(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setToggleBooleanFlags(newValue); - } - return this; - } - - /** Returns whether variables should be interpolated in String values. The default is {@code true}. - * @since 4.0 */ - public boolean isInterpolateVariables() { return getCommandSpec().interpolateVariables(); } - /** Sets whether variables should be interpolated in String values. The default is {@code true}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @since 4.0 */ - public CommandLine setInterpolateVariables(boolean interpolate) { - getCommandSpec().interpolateVariables(interpolate); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setInterpolateVariables(interpolate); - } - return this; - } - - /** Returns whether options for single-value fields can be specified multiple times on the command line. - * The default is {@code false} and a {@link OverwrittenOptionException} is thrown if this happens. - * When {@code true}, the last specified value is retained. - * @return {@code true} if options for single-value fields can be specified multiple times on the command line, {@code false} otherwise - * @since 0.9.7 - */ - public boolean isOverwrittenOptionsAllowed() { - return getCommandSpec().parser().overwrittenOptionsAllowed(); - } - - /** Sets whether options for single-value fields can be specified multiple times on the command line without a {@link OverwrittenOptionException} being thrown. - * The default is {@code false}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 0.9.7 - */ - public CommandLine setOverwrittenOptionsAllowed(boolean newValue) { - getCommandSpec().parser().overwrittenOptionsAllowed(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setOverwrittenOptionsAllowed(newValue); - } - return this; - } - - /** Returns whether the parser accepts clustered short options. The default is {@code true}. - * @return {@code true} if short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}, {@code false} otherwise - * @since 3.0 */ - public boolean isPosixClusteredShortOptionsAllowed() { return getCommandSpec().parser().posixClusteredShortOptionsAllowed(); } - - /** Sets whether short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}. The default is {@code true}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.0 - */ - public CommandLine setPosixClusteredShortOptionsAllowed(boolean newValue) { - getCommandSpec().parser().posixClusteredShortOptionsAllowed(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setPosixClusteredShortOptionsAllowed(newValue); - } - return this; - } - - /** Returns whether the parser should ignore case when converting arguments to {@code enum} values. The default is {@code false}. - * @return {@code true} if enum values can be specified that don't match the {@code toString()} value of the enum constant, {@code false} otherwise; - * e.g., for an option of type java.time.DayOfWeek, - * values {@code MonDaY}, {@code monday} and {@code MONDAY} are all recognized if {@code true}. - * @since 3.4 */ - public boolean isCaseInsensitiveEnumValuesAllowed() { return getCommandSpec().parser().caseInsensitiveEnumValuesAllowed(); } - - /** Sets whether the parser should ignore case when converting arguments to {@code enum} values. The default is {@code false}. - * When set to true, for example, for an option of type java.time.DayOfWeek, - * values {@code MonDaY}, {@code monday} and {@code MONDAY} are all recognized if {@code true}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.4 - */ - public CommandLine setCaseInsensitiveEnumValuesAllowed(boolean newValue) { - getCommandSpec().parser().caseInsensitiveEnumValuesAllowed(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setCaseInsensitiveEnumValuesAllowed(newValue); - } - return this; - } - - /** Returns whether the parser should trim quotes from command line arguments. The default is - * read from the system property "picocli.trimQuotes" and will be {@code true} if the property is present and empty, - * or if its value is "true". - *

If this property is set to {@code true}, the parser will remove quotes from the command line arguments, as follows:

- *
    - *
  • if the command line argument contains just the leading and trailing quote, these quotes are removed
  • - *
  • if the command line argument contains more quotes than just the leading and trailing quote, the parser first - * tries to process the parameter with the quotes intact. For example, the {@code split} regular expression inside - * a quoted region should be ignored, so arguments like {@code "a,b","x,y"} are handled correctly. - * For arguments with nested quotes, quotes are removed later in the processing pipeline, after {@code split} operations are applied.
  • - *
- * @return {@code true} if the parser should trim quotes from command line arguments before processing them, {@code false} otherwise; - * @see ParserSpec#trimQuotes() - * @since 3.7 */ - public boolean isTrimQuotes() { return getCommandSpec().parser().trimQuotes(); } - - /** Sets whether the parser should trim quotes from command line arguments before processing them. The default is - * read from the system property "picocli.trimQuotes" and will be {@code true} if the property is set and empty, or - * if its value is "true". - *

If this property is set to {@code true}, the parser will remove quotes from the command line arguments, as follows:

- *
    - *
  • if the command line argument contains just the leading and trailing quote, these quotes are removed
  • - *
  • if the command line argument contains more quotes than just the leading and trailing quote, the parser first - * tries to process the parameter with the quotes intact. For example, the {@code split} regular expression inside - * a quoted region should be ignored, so arguments like {@code "a,b","x,y"} are handled correctly. - * For arguments with nested quotes, quotes are removed later in the processing pipeline, after {@code split} operations are applied.
  • - *
- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- *

Calling this method will cause the "picocli.trimQuotes" property to have no effect.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @see ParserSpec#trimQuotes(boolean) - * @since 3.7 - */ - public CommandLine setTrimQuotes(boolean newValue) { - getCommandSpec().parser().trimQuotes(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setTrimQuotes(newValue); - } - return this; - } - - /** Returns whether the parser is allowed to split quoted Strings or not. The default is {@code false}, - * so quotes are respected: quoted strings are treated as a single value that should not be broken up. - *

- * For example, take a single command line parameter {@code "a,b","x,y"}. With a comma split regex, the default of {@code splitQuotedStrings = false} - * means that this value will be split into two strings: {@code "a,b"} and {@code "x,y"}. This is usually what you want. - *

- * If {@code splitQuotedStrings} is set to {@code true}, quotes are not respected, and the value is split up into four parts: - * the first is {@code "a}, the second is {@code b"}, the third is {@code "x}, and the last part is {@code y"}. This is generally not what you want. - *

- * @deprecated Most applications should not change the default. The rare application that does need to split parameter values - * without respecting quotes should use {@link ParserSpec#splitQuotedStrings(boolean)}. - * @return {@code true} if the parser is allowed to split quoted Strings, {@code false} otherwise; - * @see ArgSpec#splitRegex() - * @see ParserSpec#splitQuotedStrings() - * @since 3.7 */ - @Deprecated public boolean isSplitQuotedStrings() { return getCommandSpec().parser().splitQuotedStrings(); } - - /** Sets whether the parser is allowed to split quoted Strings. The default is {@code false}, - * so quotes are respected: quoted strings are treated as a single value that should not be broken up. - *

- * For example, take a single command line parameter {@code "a,b","x,y"}. With a comma split regex, the default of {@code splitQuotedStrings = false} - * means that this value will be split into two strings: {@code "a,b"} and {@code "x,y"}. This is usually what you want. - *

- * However, if {@code splitQuotedStrings} is set to {@code true}, quotes are not respected, and the value is split up into four parts: - * the first is {@code "a}, the second is {@code b"}, the third is {@code "x}, and the last part is {@code y"}. This is generally not what you want. - *

- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @deprecated Most applications should not change the default. The rare application that does need to split parameter values - * without respecting quotes should use {@link ParserSpec#splitQuotedStrings(boolean)}. - * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @see ArgSpec#splitRegex() - * @see ParserSpec#splitQuotedStrings(boolean) - * @since 3.7 - */ - @Deprecated public CommandLine setSplitQuotedStrings(boolean newValue) { - getCommandSpec().parser().splitQuotedStrings(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setSplitQuotedStrings(newValue); - } - return this; - } - - /** Returns the end-of-options delimiter that signals that the remaining command line arguments should be treated as positional parameters. - * @return the end-of-options delimiter. The default is {@code "--"}. - * @since 3.5 */ - public String getEndOfOptionsDelimiter() { return getCommandSpec().parser().endOfOptionsDelimiter(); } - - /** Sets the end-of-options delimiter that signals that the remaining command line arguments should be treated as positional parameters. - * @param delimiter the end-of-options delimiter; must not be {@code null}. The default is {@code "--"}. - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.5 */ - public CommandLine setEndOfOptionsDelimiter(String delimiter) { - getCommandSpec().parser().endOfOptionsDelimiter(delimiter); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setEndOfOptionsDelimiter(delimiter); - } - return this; - } - - /** Returns whether upper case and lower case should be ignored when matching subcommands. The default is {@code false}. - * @return {@code true} if subcommands can be matched when they differ only in case from the {@code getCommandName()} value of a registered one, {@code false} otherwise. - * For example, if true, for a subcommand with name {@code help}, inputs like {@code help}, {@code HeLp} and {@code HELP} are all recognized. - * @since 4.3 */ - public boolean isSubcommandsCaseInsensitive() { return getCommandSpec().subcommandsCaseInsensitive(); } - - /** Sets whether upper case and lower case should be ignored when matching subcommands. The default is {@code false}. - * For example, when set to {@code true}, for a subcommand with name {@code help}, inputs like {@code help}, {@code HeLp} and {@code HELP} are all recognized. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.3 - */ - public CommandLine setSubcommandsCaseInsensitive(boolean newValue) { - getCommandSpec().subcommandsCaseInsensitive(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setSubcommandsCaseInsensitive(newValue); - } - return this; - } - - /** Returns whether upper case and lower case should be ignored when matching option names. The default is {@code false}. - * @return {@code true} if options can be matched when they differ only in case from the {@code names()} value of a registered one, {@code false} otherwise; - * For example, if true, for an option with name {@code -h}, inputs like {@code -h}, {@code -H} are both recognized. - * @since 4.3 */ - public boolean isOptionsCaseInsensitive() { return getCommandSpec().optionsCaseInsensitive(); } - - /** Sets whether upper case and lower case should be ignored when matching option names. The default is {@code false}. - * For example, when set to {@code true}, for an option with name {@code -h}, inputs like {@code -h}, {@code -H} are both recognized. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * Note that changing case sensitivity will also change the case sensitivity of {@linkplain Option#negatable() negatable} options: - * any custom {@link INegatableOptionTransformer} that was previously installed will be replaced by the case-insensitive - * version of the default transformer. To ensure your custom transformer is used, install it last, after changing case sensitivity. - * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.3 - */ - public CommandLine setOptionsCaseInsensitive(boolean newValue) { - getCommandSpec().optionsCaseInsensitive(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setOptionsCaseInsensitive(newValue); - } - return this; - } - - /** Returns whether abbreviation of subcommands should be allowed when matching subcommands. The default is {@code false}. - * @return {@code true} if subcommands can be matched when they are abbreviations of the {@code getCommandName()} value of a registered one, {@code false} otherwise. - * For example, if true, for a subcommand with name {@code helpCommand}, inputs like {@code h}, {@code h-c} and {@code hC} are all recognized. - * @since 4.4 */ - public boolean isAbbreviatedSubcommandsAllowed() { - return getCommandSpec().parser().abbreviatedSubcommandsAllowed(); - } - - /** Sets whether abbreviated subcommands should be matched. The default is {@code false}. - * For example, when set to {@code true}, for a subcommand {@code helpCommand}, inputs like {@code h}, {@code h-c} and {@code hC} are all recognized. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.4 - */ - public CommandLine setAbbreviatedSubcommandsAllowed(boolean newValue) { - getCommandSpec().parser().abbreviatedSubcommandsAllowed(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setAbbreviatedSubcommandsAllowed(newValue); - } - return this; - } - - /** Returns whether abbreviation of option names should be allowed when matching options. The default is {@code false}. - * @return {@code true} if options can be matched when they are abbreviations of the {@code names()} value of a registered one, {@code false} otherwise. - * For example, if true, for a subcommand with name {@code --helpMe}, inputs like {@code --h}, {@code --h-m} and {@code --hM} are all recognized. - * @since 4.4 */ - public boolean isAbbreviatedOptionsAllowed() { - return getCommandSpec().parser().abbreviatedOptionsAllowed(); - } - - /** Sets whether abbreviated option names should be matched. The default is {@code false}. - * For example, when set to {@code true}, for an option with name {@code --helpMe}, inputs like {@code --h}, {@code --h-m} and {@code --hM} are all recognized. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.4 - */ - public CommandLine setAbbreviatedOptionsAllowed(boolean newValue) { - getCommandSpec().parser().abbreviatedOptionsAllowed(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setAbbreviatedOptionsAllowed(newValue); - } - return this; - } - - /** Returns the default value provider for the command, or {@code null} if none has been set. - * @return the default value provider for this command, or {@code null} - * @since 3.6 - * @see Command#defaultValueProvider() - * @see CommandSpec#defaultValueProvider() - * @see ArgSpec#defaultValueString() - */ - public IDefaultValueProvider getDefaultValueProvider() { - return getCommandSpec().defaultValueProvider(); - } - - /** Sets a default value provider for the command and sub-commands - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * sub-commands and nested sub-subcommands at the moment this method is called. Sub-commands added - * later will have the default setting. To ensure a setting is applied to all - * sub-commands, call the setter last, after adding sub-commands.

- * @param newValue the default value provider to use - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.6 - */ - public CommandLine setDefaultValueProvider(IDefaultValueProvider newValue) { - getCommandSpec().defaultValueProvider(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setDefaultValueProvider(newValue); - } - return this; - } - - /** Returns whether the parser interprets the first positional parameter as "end of options" so the remaining - * arguments are all treated as positional parameters. The default is {@code false}. - * @return {@code true} if all values following the first positional parameter should be treated as positional parameters, {@code false} otherwise - * @since 2.3 - */ - public boolean isStopAtPositional() { - return getCommandSpec().parser().stopAtPositional(); - } - - /** Sets whether the parser interprets the first positional parameter as "end of options" so the remaining - * arguments are all treated as positional parameters. The default is {@code false}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue {@code true} if all values following the first positional parameter should be treated as positional parameters, {@code false} otherwise - * @return this {@code CommandLine} object, to allow method chaining - * @since 2.3 - */ - public CommandLine setStopAtPositional(boolean newValue) { - getCommandSpec().parser().stopAtPositional(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setStopAtPositional(newValue); - } - return this; - } - - /** Returns whether the parser should stop interpreting options and positional parameters as soon as it encounters an - * unmatched option. Unmatched options are arguments that look like an option but are not one of the known options, or - * positional arguments for which there is no available slots (the command has no positional parameters or their size is limited). - * The default is {@code false}. - *

Setting this flag to {@code true} automatically sets the {@linkplain #isUnmatchedArgumentsAllowed() unmatchedArgumentsAllowed} flag to {@code true} also.

- * @return {@code true} when an unmatched option should result in the remaining command line arguments to be added to the - * {@linkplain #getUnmatchedArguments() unmatchedArguments list} - * @since 2.3 - */ - public boolean isStopAtUnmatched() { - return getCommandSpec().parser().stopAtUnmatched(); - } - - /** Sets whether the parser should stop interpreting options and positional parameters as soon as it encounters an - * unmatched option. Unmatched options are arguments that look like an option but are not one of the known options, or - * positional arguments for which there is no available slots (the command has no positional parameters or their size is limited). - * The default is {@code false}. - *

Setting this flag to {@code true} automatically sets the {@linkplain #setUnmatchedArgumentsAllowed(boolean) unmatchedArgumentsAllowed} flag to {@code true} also.

- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue {@code true} when an unmatched option should result in the remaining command line arguments to be added to the - * {@linkplain #getUnmatchedArguments() unmatchedArguments list} - * @return this {@code CommandLine} object, to allow method chaining - * @since 2.3 - */ - public CommandLine setStopAtUnmatched(boolean newValue) { - getCommandSpec().parser().stopAtUnmatched(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setStopAtUnmatched(newValue); - } - if (newValue) { setUnmatchedArgumentsAllowed(true); } - return this; - } - /** Returns whether options can have parameter values that match subcommand names or aliases, - * or whether such values should be rejected with a missing parameter exception. - * The default is {@code false}, so by default input like {@code -x=subcommand} is rejected if {@code -x} is an option that takes a String parameter, and {@code subcommand} is a subcommand of this command. - * @return {@code true} when options can have parameter values that match subcommand names or aliases, {@code false} when such values should be rejected with a missing parameter exception - * @since 4.7.5 - * @see ParserSpec#allowSubcommandsAsOptionParameters() - */ - public boolean isAllowSubcommandsAsOptionParameters() { - return getCommandSpec().parser().allowSubcommandsAsOptionParameters(); - } - /** Sets whether options can have parameter values that match subcommand names or aliases, or whether such values should be rejected with a missing parameter exception. - * The default is {@code false}, so by default - * input like {@code -x=subcommand} is rejected if {@code -x} is an option that takes a String parameter, and {@code subcommand} is a subcommand of this command. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting. When {@code true}, options can have parameter values that match subcommand names or aliases, when {@code false}, such values are rejected with a missing parameter exception - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.7.5 - * @see ParserSpec#allowSubcommandsAsOptionParameters(boolean) - */ - public CommandLine setAllowSubcommandsAsOptionParameters(boolean newValue) { - getCommandSpec().parser().allowSubcommandsAsOptionParameters(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setAllowSubcommandsAsOptionParameters(newValue); - } - return this; - } - /** Returns whether options can have parameter values that match the name of an option in this command, - * or whether such values should be rejected with a missing parameter exception. - * The default is {@code false}, so by default input like {@code -x=--some-option} is rejected if {@code -x} is an option that takes a String parameter, and {@code --some-option} is an option of this command. - *

This method only considers actual options of this command, as opposed to {@link #isUnmatchedOptionsAllowedAsOptionParameters()}, which considers values that resemble options.

- * @return {@code true} when options can have parameter values that match the name of an option in this command, {@code false} when such values should be rejected with a missing parameter exception - * @since 4.7.5 - * @see #isUnmatchedOptionsAllowedAsOptionParameters() - * @see ParserSpec#allowOptionsAsOptionParameters() - */ - public boolean isAllowOptionsAsOptionParameters() { - return getCommandSpec().parser().allowOptionsAsOptionParameters(); - } - /** Sets whether options can have parameter values that match the name of an option in this command, or whether such values should be rejected with a missing parameter exception. - * The default is {@code false}, so by default - * input like {@code -x=--some-option} is rejected if {@code -x} is an option that takes a String parameter, and {@code --some-option} is an option of this command. - *

This method only considers actual options of this command, as opposed to {@link #setUnmatchedOptionsAllowedAsOptionParameters(boolean)}, which considers values that resemble options.

- *

Use with caution! When set to {@code true}, any option in the command will consume the maximum number of arguments possible for its arity. - * This means that an option with {@code arity = "*"} will consume all command line arguments following that option. - * If this is not what you want, consider custom parameter processing.

- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting. When {@code true}, options can have parameter values that match the name of an option in this command, when {@code false}, such values are rejected with a missing parameter exception - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.7.5 - * @see #setUnmatchedOptionsAllowedAsOptionParameters(boolean) - * @see ParserSpec#allowOptionsAsOptionParameters(boolean) - */ - public CommandLine setAllowOptionsAsOptionParameters(boolean newValue) { - getCommandSpec().parser().allowOptionsAsOptionParameters(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setAllowOptionsAsOptionParameters(newValue); - } - return this; - } - - /** Returns whether options can have parameter values that resemble an option, or whether such values should be rejected as unknown options. - * The default is {@code true}, so by default input like {@code -x=-unknown} is accepted if {@code -x} is an option that takes a String parameter. - *

This method only considers values that resemble options, as opposed to {@link #isAllowOptionsAsOptionParameters()}, which considers actual options of this command.

- * @return {@code true} when options can have parameter values that resemble an option, {@code false} when such values should be rejected as unknown options - * @since 4.4 - * @see #isAllowOptionsAsOptionParameters() - * @see ParserSpec#unmatchedOptionsAllowedAsOptionParameters() - */ - public boolean isUnmatchedOptionsAllowedAsOptionParameters() { - return getCommandSpec().parser().unmatchedOptionsAllowedAsOptionParameters(); - } - - /** Sets whether options can have parameter values that resemble an option, or whether such values should be rejected as unknown options. - * The default is {@code true}, so by default - * input like {@code -x=-unknown} is accepted if {@code -x} is an option that takes a String parameter. - *

This method only considers values that resemble options, as opposed to {@link #setAllowOptionsAsOptionParameters(boolean)}, which considers actual options of this command.

- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting. When {@code true}, options can have parameter values that resemble an option, when {@code false}, such values are rejected as unknown options - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.4 - * @see #setAllowOptionsAsOptionParameters(boolean) - * @see ParserSpec#unmatchedOptionsAllowedAsOptionParameters(boolean) - */ - public CommandLine setUnmatchedOptionsAllowedAsOptionParameters(boolean newValue) { - getCommandSpec().parser().unmatchedOptionsAllowedAsOptionParameters(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUnmatchedOptionsAllowedAsOptionParameters(newValue); - } - return this; - } - /** Returns whether arguments on the command line that resemble an option should be treated as positional parameters. - * The default is {@code false} and the parser behaviour depends on {@link #isUnmatchedArgumentsAllowed()}. - * @return {@code true} arguments on the command line that resemble an option should be treated as positional parameters, {@code false} otherwise - * @see #getUnmatchedArguments() - * @since 3.0 - */ - public boolean isUnmatchedOptionsArePositionalParams() { - return getCommandSpec().parser().unmatchedOptionsArePositionalParams(); - } - - /** Sets whether arguments on the command line that resemble an option should be treated as positional parameters. - * The default is {@code false}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting. When {@code true}, arguments on the command line that resemble an option should be treated as positional parameters. - * @return this {@code CommandLine} object, to allow method chaining - * @since 3.0 - * @see #getUnmatchedArguments() - * @see #isUnmatchedArgumentsAllowed - */ - public CommandLine setUnmatchedOptionsArePositionalParams(boolean newValue) { - getCommandSpec().parser().unmatchedOptionsArePositionalParams(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUnmatchedOptionsArePositionalParams(newValue); - } - return this; - } - - /** Returns whether the end user may specify arguments on the command line that are not matched to any option or parameter fields. - * The default is {@code false} and a {@link UnmatchedArgumentException} is thrown if this happens. - * When {@code true}, the last unmatched arguments are available via the {@link #getUnmatchedArguments()} method. - * @return {@code true} if the end use may specify unmatched arguments on the command line, {@code false} otherwise - * @see #getUnmatchedArguments() - * @since 0.9.7 - */ - public boolean isUnmatchedArgumentsAllowed() { - return getCommandSpec().parser().unmatchedArgumentsAllowed(); - } - - /** Sets whether the end user may specify unmatched arguments on the command line without a {@link UnmatchedArgumentException} being thrown. - * The default is {@code false}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param newValue the new setting. When {@code true}, the last unmatched arguments are available via the {@link #getUnmatchedArguments()} method. - * @return this {@code CommandLine} object, to allow method chaining - * @since 0.9.7 - * @see #getUnmatchedArguments() - */ - public CommandLine setUnmatchedArgumentsAllowed(boolean newValue) { - getCommandSpec().parser().unmatchedArgumentsAllowed(newValue); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUnmatchedArgumentsAllowed(newValue); - } - return this; - } - - /** Returns the list of unmatched command line arguments, if any. - * @return the list of unmatched command line arguments or an empty list - * @see #isUnmatchedArgumentsAllowed() - * @since 0.9.7 - */ - public List getUnmatchedArguments() { - return interpreter.parseResultBuilder == null ? Collections.emptyList() : UnmatchedArgumentException.stripErrorMessage(interpreter.parseResultBuilder.unmatched); - } - - /** - * Defines some exit codes used by picocli as default return values from the {@link #execute(String...) execute} - * and {@link #executeHelpRequest(ParseResult) executeHelpRequest} methods. - *

Commands can override these defaults with annotations (e.g. {@code @Command(exitCodeOnInvalidInput = 64, exitCodeOnExecutionException = 70)} - * or programmatically (e.g. {@link CommandSpec#exitCodeOnInvalidInput(int)}).

- *

Additionally, there are several mechanisms for commands to return custom exit codes. - * See the javadoc of the {@link #execute(String...) execute} method for details.

- *

Standard Exit Codes

- *

There are a few conventions, but there is no - * standard. The specific set of codes returned is unique to the program that sets it. - * Typically an exit code of zero indicates success, any non-zero exit code indicates failure. For reference, here are a few conventions:

- * - *

Valid Ranges

- *

Note that *nix shells may restrict exit codes to the 0-255 range, DOS seems to allow larger numbers. - * See this StackOverflow question.

- * @since 4.0 */ - public static final class ExitCode { - /** Return value from the {@link #execute(String...) execute} and - * {@link #executeHelpRequest(ParseResult) executeHelpRequest} methods signifying successful termination. - *

The value of this constant is {@value}.

*/ - public static final int OK = 0; - /** Return value from the {@link #execute(String...) execute} method signifying internal software error: an exception occurred when invoking the Runnable, Callable or Method user object of a command.

The value of this constant is {@value}.

*/ - public static final int SOFTWARE = 1; - /** Return value from the {@link #execute(String...) execute} method signifying command line usage error: user input for the command was incorrect, e.g., the wrong number of arguments, a bad flag, a bad syntax in a parameter, or whatever.

The value of this constant is {@value}.

*/ - public static final int USAGE = 2; - private ExitCode() {} // don't instantiate - } - - /** {@code @Command}-annotated classes can implement this interface to specify an exit code that will be returned - * from the {@link #execute(String...) execute} method when the command is successfully invoked. - * - *

Example usage:

- *
-     * @Command
-     * class MyCommand implements Runnable, IExitCodeGenerator {
-     *     public void run() { System.out.println("Hello"); }
-     *     public int getExitCode() { return 123; }
-     * }
-     * CommandLine cmd = new CommandLine(new MyCommand());
-     * int exitCode = cmd.execute(args);
-     * assert exitCode == 123;
-     * System.exit(exitCode);
-     * 
- * @since 4.0 - */ - public interface IExitCodeGenerator { - /** Returns the exit code that should be returned from the {@link #execute(String...) execute} method. - * @return the exit code - */ - int getExitCode(); - } - /** Interface that provides the appropriate exit code that will be returned from the {@link #execute(String...) execute} - * method for an exception that occurred during parsing or while invoking the command's Runnable, Callable, or Method. - *

Example usage:

- *
-     * @Command
-     * class FailingCommand implements Callable<Void> {
-     *     public Void call() throws IOException {
-     *         throw new IOException("error");
-     *     }
-     * }
-     * IExitCodeExceptionMapper mapper = new IExitCodeExceptionMapper() {
-     *     public int getExitCode(Throwable t) {
-     *         if (t instanceof IOException && "error".equals(t.getMessage())) {
-     *             return 123;
-     *         }
-     *         return 987;
-     *     }
-     * }
-     *
-     * CommandLine cmd = new CommandLine(new FailingCommand());
-     * cmd.setExitCodeExceptionMapper(mapper);
-     * int exitCode = cmd.execute(args);
-     * assert exitCode == 123;
-     * System.exit(exitCode);
-     * 
- * @see #setExitCodeExceptionMapper(IExitCodeExceptionMapper) - * @since 4.0 - */ - public interface IExitCodeExceptionMapper { - /** Returns the exit code that should be returned from the {@link #execute(String...) execute} method. - * @param exception the exception that occurred during parsing or while invoking the command's Runnable, Callable, or Method. - * @return the exit code - */ - int getExitCode(Throwable exception); - } - private static int mappedExitCode(Throwable t, IExitCodeExceptionMapper mapper, int defaultExitCode) { - try { - return (mapper != null) ? mapper.getExitCode(t) : defaultExitCode; - } catch (Exception ex) { - ex.printStackTrace(); - return defaultExitCode; - } - } - - /** Returns the color scheme to use when printing help. - * The default value is the {@linkplain picocli.CommandLine.Help#defaultColorScheme(CommandLine.Help.Ansi) default color scheme} with {@link Help.Ansi#AUTO Ansi.AUTO}. - * @see #execute(String...) - * @see #usage(PrintStream) - * @see #usage(PrintWriter) - * @see #getUsageMessage() - * @see Help#defaultColorScheme(CommandLine.Help.Ansi) - * @since 4.0 - */ - public Help.ColorScheme getColorScheme() { return colorScheme; } - - /** Sets the color scheme to use when printing help. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param colorScheme the new color scheme - * @see #execute(String...) - * @see #usage(PrintStream) - * @see #usage(PrintWriter) - * @see #getUsageMessage() - * @since 4.0 - */ - public CommandLine setColorScheme(Help.ColorScheme colorScheme) { - this.colorScheme = Assert.notNull(colorScheme, "colorScheme"); - for (CommandLine sub : getSubcommands().values()) { sub.setColorScheme(colorScheme); } - return this; - } - - /** Returns the writer used when printing user-requested usage help or version help during command {@linkplain #execute(String...) execution}. - * Defaults to a PrintWriter wrapper around {@code System.out} unless {@link #setOut(PrintWriter)} was called with a different writer. - *

This method is used by {@link #execute(String...)}. Custom {@link IExecutionStrategy IExecutionStrategy} implementations should also use this writer. - *

- * By convention, when the user requests - * help with a {@code --help} or similar option, the usage help message is printed to the standard output stream so that it can be easily searched and paged.

- * @since 4.0 */ - public PrintWriter getOut() { - if (out == null) { setOut(newPrintWriter(System.out, getStdoutEncoding())); } - return out; - } - - /** Sets the writer to use when printing user-requested usage help or version help during command {@linkplain #execute(String...) execution}. - *

This method is used by {@link #execute(String...)}. Custom {@link IExecutionStrategy IExecutionStrategy} implementations should also use this writer.

- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param out the new PrintWriter to use - * @return this CommandLine for method chaining - * @since 4.0 - */ - public CommandLine setOut(PrintWriter out) { - this.out = Assert.notNull(out, "out"); - for (CommandLine sub : getSubcommands().values()) { sub.setOut(out); } - return this; - } - - /** Returns the writer to use when printing diagnostic (error) messages during command {@linkplain #execute(String...) execution}. - * Defaults to a PrintWriter wrapper around {@code System.err}, unless {@link #setErr(PrintWriter)} was called with a different writer. - *

This method is used by {@link #execute(String...)}. - * {@link IParameterExceptionHandler IParameterExceptionHandler} and {@link IExecutionExceptionHandler IExecutionExceptionHandler} implementations - * should use this writer to print error messages (which may include a usage help message) when an unexpected error occurs.

- * @since 4.0 */ - public PrintWriter getErr() { - if (err == null) { setErr(newPrintWriter(System.err, getStderrEncoding())); } - return err; - } - - /** Sets the writer to use when printing diagnostic (error) messages during command {@linkplain #execute(String...) execution}. - *

This method is used by {@link #execute(String...)}. - * {@link IParameterExceptionHandler IParameterExceptionHandler} and {@link IExecutionExceptionHandler IExecutionExceptionHandler} implementations - * should use this writer to print error messages (which may include a usage help message) when an unexpected error occurs.

- *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param err the new PrintWriter to use - * @return this CommandLine for method chaining - * @since 4.0 */ - public CommandLine setErr(PrintWriter err) { - this.err = Assert.notNull(err, "err"); - for (CommandLine sub : getSubcommands().values()) { sub.setErr(err); } - return this; - } - - /** - * Returns the mapper that was set by the application to map from exceptions to exit codes, for use by the {@link #execute(String...) execute} method. - * @return the mapper that was {@linkplain #setExitCodeExceptionMapper(IExitCodeExceptionMapper) set}, or {@code null} if none was set - * @since 4.0 */ - public IExitCodeExceptionMapper getExitCodeExceptionMapper() { return exitCodeExceptionMapper; } - - /** Sets the mapper used by the {@link #execute(String...) execute} method to map exceptions to exit codes. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param exitCodeExceptionMapper the new value - * @return this CommandLine for method chaining - * @since 4.0 */ - public CommandLine setExitCodeExceptionMapper(IExitCodeExceptionMapper exitCodeExceptionMapper) { - this.exitCodeExceptionMapper = Assert.notNull(exitCodeExceptionMapper, "exitCodeExceptionMapper"); - for (CommandLine sub : getSubcommands().values()) { sub.setExitCodeExceptionMapper(exitCodeExceptionMapper); } - return this; - } - - /** Returns the execution strategy used by the {@link #execute(String...) execute} method to invoke - * the business logic on the user objects of this command and/or the user-specified subcommand(s). - * The default value is {@link RunLast RunLast}. - * @return the execution strategy to run the user-specified command - * @since 4.0 */ - public IExecutionStrategy getExecutionStrategy() { return executionStrategy; } - - /** Sets the execution strategy that the {@link #execute(String...) execute} method should use to invoke - * the business logic on the user objects of this command and/or the user-specified subcommand(s). - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param executionStrategy the new execution strategy to run the user-specified command - * @return this CommandLine for method chaining - * @since 4.0 */ - public CommandLine setExecutionStrategy(IExecutionStrategy executionStrategy) { - this.executionStrategy = Assert.notNull(executionStrategy, "executionStrategy"); - for (CommandLine sub : getSubcommands().values()) { sub.setExecutionStrategy(executionStrategy); } - return this; - } - - /** - * Returns the handler for dealing with invalid user input when the command is {@linkplain #execute(String...) executed}. - *

The default implementation prints an error message describing the problem, followed by either {@linkplain UnmatchedArgumentException#printSuggestions(PrintWriter) suggested alternatives} - * for mistyped options, or the full {@linkplain #usage(PrintWriter, Help.ColorScheme) usage} help message of the {@linkplain ParameterException#getCommandLine() problematic command}; - * it then delegates to the {@linkplain #getExitCodeExceptionMapper() exit code exception mapper} for an exit code, with - * {@link CommandSpec#exitCodeOnInvalidInput() exitCodeOnInvalidInput} as the default exit code.

- *

- * Alternatively, you can install a "short error message handler" like this: - *

- *
-     * static class ShortErrorMessageHandler implements IParameterExceptionHandler {
-     *     public int handleParseException(ParameterException ex, String[] args) {
-     *         CommandLine cmd = ex.getCommandLine();
-     *         PrintWriter writer = cmd.getErr();
-     *
-     *         writer.println(ex.getMessage());
-     *         UnmatchedArgumentException.printSuggestions(ex, writer);
-     *         writer.print(cmd.getHelp().fullSynopsis());
-     *
-     *         CommandSpec spec = cmd.getCommandSpec();
-     *         writer.printf("Try '%s --help' for more information.%n", spec.qualifiedName());
-     *
-     *         return cmd.getExitCodeExceptionMapper() != null
-     *                     ? cmd.getExitCodeExceptionMapper().getExitCode(ex)
-     *                     : spec.exitCodeOnInvalidInput();
-     *     }
-     * }
-     * 
- *

Install this error handler like this:

- *
-     * new CommandLine(new MyApp())
-     *     .setParameterExceptionHandler(new ShortErrorMessageHandler())
-     *     .execute(args);
-     * 
- * @return the handler for dealing with invalid user input - * @since 4.0 */ - public IParameterExceptionHandler getParameterExceptionHandler() { return parameterExceptionHandler; } - - /** - * Sets the handler for dealing with invalid user input when the command is {@linkplain #execute(String...) executed}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param parameterExceptionHandler the new handler for dealing with invalid user input - * @return this CommandLine for method chaining - * @see #getParameterExceptionHandler() an example short exception handler - * @since 4.0 */ - public CommandLine setParameterExceptionHandler(IParameterExceptionHandler parameterExceptionHandler) { - this.parameterExceptionHandler = Assert.notNull(parameterExceptionHandler, "parameterExceptionHandler"); - for (CommandLine sub : getSubcommands().values()) { sub.setParameterExceptionHandler(parameterExceptionHandler); } - return this; - } - - /** Returns the handler for dealing with exceptions that occurred in the {@code Callable}, {@code Runnable} or {@code Method} - * user object of a command when the command was {@linkplain #execute(String...) executed}. - *

The default implementation rethrows the specified exception.

- * @return the handler for dealing with exceptions that occurred in the business logic when the {@link #execute(String...) execute} method was invoked. - * @since 4.0 */ - public IExecutionExceptionHandler getExecutionExceptionHandler() { return executionExceptionHandler; } - - /** - * Sets a custom handler for dealing with exceptions that occurred in the {@code Callable}, {@code Runnable} or {@code Method} - * user object of a command when the command was executed via the {@linkplain #execute(String...) execute} method. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param executionExceptionHandler the handler for dealing with exceptions that occurred in the business logic when the {@link #execute(String...) execute} method was invoked. - * @return this CommandLine for method chaining - * @since 4.0 */ - public CommandLine setExecutionExceptionHandler(IExecutionExceptionHandler executionExceptionHandler) { - this.executionExceptionHandler = Assert.notNull(executionExceptionHandler, "executionExceptionHandler"); - for (CommandLine sub : getSubcommands().values()) { sub.setExecutionExceptionHandler(executionExceptionHandler); } - return this; - } - - /** - *

- * Convenience method that initializes the specified annotated object from the specified command line arguments. - *

- * This is equivalent to - *

-     * new CommandLine(command).parseArgs(args);
-     * return command;
-     * 
- *

All this method does is parse the arguments and populate the annotated fields and methods. - * The caller is responsible for catching any exceptions, handling requests for usage help - * or version information, and invoking the business logic. - * Applications may be interested in using the {@link #execute(String...)} method instead.

- * - * @param command the object to initialize. This object contains fields annotated with - * {@code @Option} or {@code @Parameters}. - * @param args the command line arguments to parse - * @param the type of the annotated object - * @return the specified annotated object - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ParameterException if the specified command line arguments are invalid - * @see #execute(String...) - * @since 0.9.7 - */ - public static T populateCommand(T command, String... args) { - CommandLine cli = toCommandLine(command, new DefaultFactory()); - cli.parse(args); - return command; - } - - /** - *

- * Convenience method that derives the command specification from the specified interface class, and returns an - * instance of the specified interface. The interface is expected to have annotated getter methods. Picocli will - * instantiate the interface and the getter methods will return the option and positional parameter values matched on the command line. - *

- * This is equivalent to - *

-     * CommandLine cli = new CommandLine(spec);
-     * cli.parse(args);
-     * return cli.getCommand();
-     * 
- *

All this method does is parse the arguments and return an instance whose annotated methods return the specified values. - * The caller is responsible for catching any exceptions, handling requests for usage help - * or version information, and invoking the business logic. - * Applications may be interested in using the {@link #execute(String...)} method instead.

- * - * @param spec the interface that defines the command specification. This object contains getter methods annotated with - * {@code @Option} or {@code @Parameters}. - * @param args the command line arguments to parse - * @param the type of the annotated object - * @return an instance of the specified annotated interface - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ParameterException if the specified command line arguments are invalid - * @see #execute(String...) - * @since 3.1 - */ - public static T populateSpec(Class spec, String... args) { - CommandLine cli = toCommandLine(spec, new DefaultFactory()); - cli.parse(args); - return cli.getCommand(); - } - - /** Expands any {@linkplain CommandLine#isExpandAtFiles() @-files} in the specified command line arguments, then - * parses the arguments and returns a list of {@code CommandLine} objects representing the - * top-level command and any subcommands (if any) that were recognized and initialized during the parsing process. - *

- * If parsing succeeds, the first element in the returned list is always {@code this CommandLine} object. The - * returned list may contain more elements if subcommands were {@linkplain #addSubcommand(String, Object) registered} - * and these subcommands were initialized by matching command line arguments. If parsing fails, a - * {@link ParameterException} is thrown. - *

- *

All this method does is parse the arguments and populate the annotated fields and methods. - * The caller is responsible for catching any exceptions, handling requests for usage help - * or version information, and invoking the business logic. - * Applications may be interested in using the {@link #execute(String...)} method instead.

- * - * @param args the command line arguments to parse - * @return a list with the top-level command and any subcommands initialized by this method - * @throws ParameterException if the specified command line arguments are invalid; use - * {@link ParameterException#getCommandLine()} to get the command or subcommand whose user input was invalid - * @deprecated use {@link #parseArgs(String...)} instead - */ - @Deprecated public List parse(String... args) { - return interpreter.parse(args); - } - /** Expands any {@linkplain CommandLine#isExpandAtFiles() @-files} in the specified command line arguments, then - * parses the arguments and returns a {@code ParseResult} with the options, positional - * parameters, and subcommands (if any) that were recognized and initialized during the parsing process. - *

If parsing fails, a {@link ParameterException} is thrown.

- *

All this method does is parse the arguments and populate the annotated fields and methods. - * The caller is responsible for catching any exceptions, handling requests for usage help - * or version information, and invoking the business logic. - * Applications may be interested in using the {@link #execute(String...)} method instead.

- * - * @param args the command line arguments to parse - * @return a list with the top-level command and any subcommands initialized by this method - * @throws ParameterException if the specified command line arguments are invalid; use - * {@link ParameterException#getCommandLine()} to get the command or subcommand whose user input was invalid - * @see #execute(String...) - */ - public ParseResult parseArgs(String... args) { - interpreter.parse(args); - return getParseResult(); - } - public ParseResult getParseResult() { return interpreter.parseResultBuilder == null ? null : interpreter.parseResultBuilder.build(); } - - /** Returns the result of calling the user object {@code Callable} or invoking the user object {@code Method} - * after parsing the user input, or {@code null} if this command has not been {@linkplain #execute(String...) executed} - * or if this {@code CommandLine} is for a subcommand that was not specified by the end user on the command line. - *

Implementation note:

- *

It is the responsibility of the {@link IExecutionStrategy IExecutionStrategy} to set this value.

- * @param type of the result value - * @return the result of the user object {@code Callable} or {@code Method} (may be {@code null}), or {@code null} if this (sub)command was not executed - * @since 4.0 - */ - @SuppressWarnings("unchecked") public T getExecutionResult() { return (T) executionResult; } - - /** Sets the result of calling the business logic on the command's user object. - * @param result the business logic result, may be {@code null} - * @see #execute(String...) - * @see IExecutionStrategy - * @since 4.0 - */ - public void setExecutionResult(Object result) { executionResult = result; } - - /** Clears the {@linkplain #getExecutionResult() execution result} of a previous invocation from this {@code CommandLine} and all subcommands. - * @since 4.0 */ - public void clearExecutionResults() { - executionResult = null; - for (CommandLine sub : getSubcommands().values()) { sub.clearExecutionResults(); } - } - /** - * Represents a function that can process a List of {@code CommandLine} objects resulting from successfully - * {@linkplain #parse(String...) parsing} the command line arguments. This is a - * functional interface - * whose functional method is {@link #handleParseResult(List, PrintStream, CommandLine.Help.Ansi)}. - *

- * Implementations of this functions can be passed to the {@link #parseWithHandlers(IParseResultHandler, PrintStream, Help.Ansi, IExceptionHandler, String...) CommandLine::parseWithHandler} - * methods to take some next step after the command line was successfully parsed. - *

- * @see RunFirst - * @see RunLast - * @see RunAll - * @deprecated Use {@link IExecutionStrategy} instead. - * @since 2.0 */ - @Deprecated public interface IParseResultHandler { - /** Processes a List of {@code CommandLine} objects resulting from successfully - * {@linkplain #parse(String...) parsing} the command line arguments and optionally returns a list of results. - * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments - * @param out the {@code PrintStream} to print help to if requested - * @param ansi for printing help messages using ANSI styles and colors - * @return a list of results, or an empty list if there are no results - * @throws ParameterException if a help command was invoked for an unknown subcommand. Any {@code ParameterExceptions} - * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) throws ExecutionException; - } - - /** - * Represents a function that can process the {@code ParseResult} object resulting from successfully - * {@linkplain #parseArgs(String...) parsing} the command line arguments. This is a - * functional interface - * whose functional method is {@link IParseResultHandler2#handleParseResult(CommandLine.ParseResult)}. - *

- * Implementations of this function can be passed to the {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) CommandLine::parseWithHandlers} - * methods to take some next step after the command line was successfully parsed. - *

- * This interface replaces the {@link IParseResultHandler} interface; it takes the parse result as a {@code ParseResult} - * object instead of a List of {@code CommandLine} objects, and it has the freedom to select the {@link Help.Ansi} style - * to use and what {@code PrintStreams} to print to. - *

- * @param the return type of this handler - * @see RunFirst - * @see RunLast - * @see RunAll - * @deprecated use {@link IExecutionStrategy} instead, see {@link #execute(String...)} - * @since 3.0 */ - @Deprecated public interface IParseResultHandler2 { - /** Processes the {@code ParseResult} object resulting from successfully - * {@linkplain CommandLine#parseArgs(String...) parsing} the command line arguments and returns a return value. - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @throws ParameterException if a help command was invoked for an unknown subcommand. Any {@code ParameterExceptions} - * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler2} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - R handleParseResult(ParseResult parseResult) throws ExecutionException; - } - - /** - * Implementations are responsible for "executing" the user input and returning an exit code. - * The {@link #execute(String...)} method delegates to a {@linkplain #setExecutionStrategy(IExecutionStrategy) configured} execution strategy. - *

Implementation Requirements:

- *

Implementers responsibilities are:

- *
    - *
  • From the {@code ParseResult}, select which {@code CommandSpec} should be executed. This is especially important for commands that have subcommands.
  • - *
  • "Execute" the selected {@code CommandSpec}. Often this means invoking a method on the spec's {@linkplain CommandSpec#userObject() user object}.
  • - *
  • Call {@link CommandLine#setExecutionResult(Object) setExecutionResult} to make the return value of that method invocation available to the application
  • - *
  • Return an exit code. Common sources of exit values are the invoked method's return value, or the user object if it implements {@link IExitCodeGenerator}.
  • - *
- *

Implementors that need to print messages to the console should use the {@linkplain #getOut() output} and {@linkplain #getErr() error} PrintWriters, - * and the {@linkplain #getColorScheme() color scheme} from the CommandLine object obtained from ParseResult's CommandSpec.

- *

API Note:

- *

This interface supersedes {@link IParseResultHandler2}.

- * @since 4.0 */ - public interface IExecutionStrategy { - /** - * "Executes" the user input and returns an exit code. - * Execution often means invoking a method on the selected CommandSpec's {@linkplain CommandSpec#userObject() user object}, - * and making the return value of that invocation available via {@link CommandLine#setExecutionResult(Object) setExecutionResult}. - * @param parseResult the parse result from which to select one or more {@code CommandSpec} instances to execute. - * @return an exit code - * @throws ParameterException if the invoked method on the CommandSpec's user object threw a ParameterException to signify invalid user input. - * @throws ExecutionException if any problem occurred while executing the command. Any exceptions (other than ParameterException) should be wrapped in a ExecutionException and not thrown as is. - */ - int execute(ParseResult parseResult) throws ExecutionException, ParameterException; - } - - /** - * Represents a function that can handle a {@code ParameterException} that occurred while - * {@linkplain #parse(String...) parsing} the command line arguments. This is a - * functional interface - * whose functional method is {@link #handleException(CommandLine.ParameterException, PrintStream, CommandLine.Help.Ansi, String...)}. - *

- * Implementations of this function can be passed to the {@link #parseWithHandlers(IParseResultHandler, PrintStream, Help.Ansi, IExceptionHandler, String...) CommandLine::parseWithHandlers} - * methods to handle situations when the command line could not be parsed. - *

- * @deprecated see {@link #execute(String...)}, {@link IParameterExceptionHandler} and {@link IExecutionExceptionHandler} - * @since 2.0 */ - @Deprecated public interface IExceptionHandler { - /** Handles a {@code ParameterException} that occurred while {@linkplain #parse(String...) parsing} the command - * line arguments and optionally returns a list of results. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param out the {@code PrintStream} to print help to if requested - * @param ansi for printing help messages using ANSI styles and colors - * @param args the command line arguments that could not be parsed - * @return a list of results, or an empty list if there are no results - */ - List handleException(ParameterException ex, PrintStream out, Help.Ansi ansi, String... args); - } - /** - * Classes implementing this interface know how to handle {@code ParameterExceptions} (usually from invalid user input) - * and {@code ExecutionExceptions} that occurred while executing the {@code Runnable} or {@code Callable} command. - *

- * Implementations of this interface can be passed to the - * {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) CommandLine::parseWithHandlers} method. - *

- * This interface replaces the {@link IParseResultHandler} interface. - *

- * @param the return type of this handler - * @see DefaultExceptionHandler - * @deprecated see {@link #execute(String...)}, {@link IParameterExceptionHandler} and {@link IExecutionExceptionHandler} - * @since 3.0 */ - @Deprecated public interface IExceptionHandler2 { - /** Handles a {@code ParameterException} that occurred while {@linkplain #parseArgs(String...) parsing} the command - * line arguments and optionally returns a list of results. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param args the command line arguments that could not be parsed - * @return an object resulting from handling the exception - */ - R handleParseException(ParameterException ex, String[] args); - /** Handles a {@code ExecutionException} that occurred while executing the {@code Runnable} or - * {@code Callable} command and optionally returns a list of results. - * @param ex the ExecutionException describing the problem that occurred while executing the {@code Runnable} or - * {@code Callable} command, and the CommandLine representing the command or subcommand that was being executed - * @param parseResult the result of parsing the command line arguments - * @return an object resulting from handling the exception - */ - R handleExecutionException(ExecutionException ex, ParseResult parseResult); - } - - /** Classes implementing this interface know how to handle {@code ParameterExceptions} (usually from invalid user input). - *

Implementation Requirements:

- *

Implementors that need to print messages to the console should use the {@linkplain #getOut() output} and {@linkplain #getErr() error} PrintWriters, - * and the {@linkplain #getColorScheme() color scheme} from the CommandLine object obtained from the exception.

- *

Implementation Note:

- *

See {@link #getParameterExceptionHandler()} for a description of the default handler.

- *

API Note:

- *

This interface supersedes {@link IExceptionHandler2}.

- * @see CommandLine#setParameterExceptionHandler(IParameterExceptionHandler) - * @since 4.0 - */ - public interface IParameterExceptionHandler { - /** Handles a {@code ParameterException} that occurred while {@linkplain #parseArgs(String...) parsing} the command - * line arguments and returns an exit code suitable for returning from {@link #execute(String...)}. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param args the command line arguments that could not be parsed - * @return an exit code - */ - int handleParseException(ParameterException ex, String[] args) throws Exception; - } - /** - * Classes implementing this interface know how to handle Exceptions that occurred while executing the {@code Runnable}, {@code Callable} or {@code Method} user object of the command. - *

Implementation Requirements:

- *

Implementors that need to print messages to the console should use the {@linkplain #getOut() output} and {@linkplain #getErr() error} PrintWriters, - * and the {@linkplain #getColorScheme() color scheme} from the CommandLine object obtained from the exception.

- *

API Note:

- *

This interface supersedes {@link IExceptionHandler2}.

- *

Example usage:

- *
-     * IExecutionExceptionHandler errorHandler = new IExecutionExceptionHandler() {
-     *     public int handleExecutionException(Exception ex,
-     *                                         CommandLine commandLine,
-     *                                         ParseResult parseResult) {
-     *         //ex.printStackTrace(); // no stack trace
-     *         commandLine.getErr().println(ex.getMessage());
-     *         commandLine.usage(commandLine.getErr());
-     *         return commandLine.getCommandSpec().exitCodeOnExecutionException();
-     *     }
-     * };
-     * int exitCode = new CommandLine(new App())
-     *         .setExecutionExceptionHandler(errorHandler)
-     *         .execute(args);
-     * 
- * @see CommandLine#setExecutionExceptionHandler(IExecutionExceptionHandler) - * @since 4.0 - */ - public interface IExecutionExceptionHandler { - /** Handles an {@code Exception} that occurred while executing the {@code Runnable} or - * {@code Callable} command and returns an exit code suitable for returning from {@link #execute(String...)}. - * @param ex the Exception thrown by the {@code Runnable}, {@code Callable} or {@code Method} user object of the command - * @param commandLine the CommandLine representing the command or subcommand where the exception occurred - * @param parseResult the result of parsing the command line arguments - * @return an exit code - */ - int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) throws Exception; - } - - /** Abstract superclass for {@link IParseResultHandler2} and {@link IExceptionHandler2} implementations. - *

Note that {@code AbstractHandler} is a generic type. This, along with the abstract {@code self} method, - * allows method chaining to work properly in subclasses, without the need for casts. An example subclass can look like this:

- *
{@code
-     * class MyResultHandler extends AbstractHandler implements IParseResultHandler2 {
-     *
-     *     public MyReturnType handleParseResult(ParseResult parseResult) { ... }
-     *
-     *     protected MyResultHandler self() { return this; }
-     * }
-     * }
- * @param the return type of this handler - * @param The type of the handler subclass; for fluent API method chaining - * @deprecated see {@link #execute(String...)} - * @since 3.0 */ - @Deprecated public static abstract class AbstractHandler> { - private Help.ColorScheme colorScheme = Help.defaultColorScheme(Help.Ansi.AUTO); - private Integer exitCode; - private PrintStream out = System.out; - private PrintStream err = System.err; - - /** Returns the stream to print command output to. Defaults to {@code System.out}, unless {@link #useOut(PrintStream)} - * was called with a different stream. - *

{@code IParseResultHandler2} implementations should use this stream. - * By convention, when the user requests - * help with a {@code --help} or similar option, the usage help message is printed to the standard output stream so that it can be easily searched and paged.

*/ - public PrintStream out() { return out; } - /** Returns the stream to print diagnostic messages to. Defaults to {@code System.err}, unless {@link #useErr(PrintStream)} - * was called with a different stream.

{@code IExceptionHandler2} implementations should use this stream to print error - * messages (which may include a usage help message) when an unexpected error occurs.

*/ - public PrintStream err() { return err; } - /** Returns the ANSI style to use. Defaults to {@code Help.Ansi.AUTO}, unless {@link #useAnsi(CommandLine.Help.Ansi)} was called with a different setting. - * @deprecated use {@link #colorScheme()} instead */ - @Deprecated public Help.Ansi ansi() { return colorScheme.ansi(); } - /** Returns the ColorScheme to use. Defaults to {@code Help#defaultColorScheme(Help.Ansi.AUTO)}. - * @since 4.0*/ - public Help.ColorScheme colorScheme() { return colorScheme; } - /** Returns the exit code to use as the termination status, or {@code null} (the default) if the handler should - * not call {@link System#exit(int)} after processing completes. - * @see #andExit(int) */ - public Integer exitCode() { return exitCode; } - /** Returns {@code true} if an exit code was set with {@link #andExit(int)}, or {@code false} (the default) if - * the handler should not call {@link System#exit(int)} after processing completes. */ - public boolean hasExitCode() { return exitCode != null; } - - /** Convenience method for subclasses that returns the specified result object if no exit code was set, - * or otherwise, if an exit code {@linkplain #andExit(int) was set}, calls {@code System.exit} with the configured - * exit code to terminate the currently running Java virtual machine. */ - protected R returnResultOrExit(R result) { - if (hasExitCode()) { exit(exitCode()); } - return result; - } - - /** Convenience method for subclasses that throws the specified ExecutionException if no exit code was set, - * or otherwise, if an exit code {@linkplain #andExit(int) was set}, prints the stacktrace of the specified exception - * to the diagnostic error stream and calls {@code System.exit} with the configured - * exit code to terminate the currently running Java virtual machine. */ - protected R throwOrExit(ExecutionException ex) { - if (hasExitCode()) { - ex.printStackTrace(this.err()); - exit(exitCode()); - return null; - } - throw ex; - } - /** Calls {@code System.exit(int)} with the specified exit code. */ - protected void exit(int exitCode) { System.exit(exitCode); } - - /** Returns {@code this} to allow method chaining when calling the setters for a fluent API. */ - protected abstract T self(); - - /** Sets the stream to print command output to. - * @deprecated use {@link CommandLine#setOut(PrintWriter)} and {@link CommandLine#execute(String...)} instead */ - @Deprecated public T useOut(PrintStream out) { this.out = Assert.notNull(out, "out"); return self(); } - /** Sets the stream to print diagnostic messages to. - * @deprecated use {@link CommandLine#setErr(PrintWriter)} and {@link CommandLine#execute(String...)} instead */ - @Deprecated public T useErr(PrintStream err) { this.err = Assert.notNull(err, "err"); return self(); } - /** Sets the ANSI style to use and resets the color scheme to the default. - * @deprecated use {@link CommandLine#setColorScheme(Help.ColorScheme)} and {@link CommandLine#execute(String...)} instead - * @see #ansi() */ - @Deprecated public T useAnsi(Help.Ansi ansi) { this.colorScheme = Help.defaultColorScheme(Assert.notNull(ansi, "ansi")); return self(); } - /** Indicates that the handler should call {@link System#exit(int)} after processing completes and sets the exit code to use as the termination status. - * @deprecated use {@link CommandLine#execute(String...)} instead, and call {@code System.exit()} in the application. */ - @Deprecated public T andExit(int exitCode) { this.exitCode = exitCode; return self(); } - } - - /** - * Default exception handler that handles invalid user input by printing the exception message, followed by the usage - * message for the command or subcommand whose input was invalid. - *

{@code ParameterExceptions} (invalid user input) is handled like this:

- *
-     *     err().println(paramException.getMessage());
-     *     paramException.getCommandLine().usage(err(), ansi());
-     *     if (hasExitCode()) System.exit(exitCode()); else return returnValue;
-     * 
- *

{@code ExecutionExceptions} that occurred while executing the {@code Runnable} or {@code Callable} command are simply rethrown and not handled.

- * @deprecated see {@link #execute(String...)}, {@link #getParameterExceptionHandler()} and {@link #getExecutionExceptionHandler()} - * @since 2.0 */ - @Deprecated public static class DefaultExceptionHandler extends AbstractHandler> implements IExceptionHandler, IExceptionHandler2 { - public List handleException(ParameterException ex, PrintStream out, Help.Ansi ansi, String... args) { - internalHandleParseException(ex, newPrintWriter(out, getStdoutEncoding()), Help.defaultColorScheme(ansi)); return Collections.emptyList(); } - - /** Prints the message of the specified exception, followed by the usage message for the command or subcommand - * whose input was invalid, to the stream returned by {@link #err()}. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param args the command line arguments that could not be parsed - * @return the empty list - * @since 3.0 */ - public R handleParseException(ParameterException ex, String[] args) { - internalHandleParseException(ex, newPrintWriter(err(), getStderrEncoding()), colorScheme()); return returnResultOrExit(null); } - - static void internalHandleParseException(ParameterException ex, PrintWriter writer, Help.ColorScheme colorScheme) { - writer.println(colorScheme.errorText(ex.getMessage())); - if (!UnmatchedArgumentException.printSuggestions(ex, writer)) { - ex.getCommandLine().usage(writer, colorScheme); - } - Tracer tracer = CommandLine.tracer(); - if (tracer.isDebug()) { // #956 show error details if DEBUG is enabled - ex.printStackTrace(tracer.stream); - } - } - /** This implementation always simply rethrows the specified exception. - * @param ex the ExecutionException describing the problem that occurred while executing the {@code Runnable} or {@code Callable} command - * @param parseResult the result of parsing the command line arguments - * @return nothing: this method always rethrows the specified exception - * @throws ExecutionException always rethrows the specified exception - * @since 3.0 */ - public R handleExecutionException(ExecutionException ex, ParseResult parseResult) { return throwOrExit(ex); } - - @Override protected DefaultExceptionHandler self() { return this; } - } - /** Convenience method that returns {@code new DefaultExceptionHandler>()}. */ - public static DefaultExceptionHandler> defaultExceptionHandler() { return new DefaultExceptionHandler>(); } - - /** @deprecated use {@link #printHelpIfRequested(ParseResult)} instead - * @since 2.0 */ - @Deprecated public static boolean printHelpIfRequested(List parsedCommands, PrintStream out, Help.Ansi ansi) { - return printHelpIfRequested(parsedCommands, out, out, ansi); - } - /** - * Delegates to {@link #executeHelpRequest(ParseResult)}. - * @param parseResult contains the {@code CommandLine} objects found during parsing; check these to see if help was requested - * @return {@code true} if help was printed, {@code false} otherwise - * @since 3.0 */ - public static boolean printHelpIfRequested(ParseResult parseResult) { - return executeHelpRequest(parseResult) != null; - } - /** - * Delegates to the implementation of {@link #executeHelpRequest(ParseResult)}. - * @deprecated use {@link #executeHelpRequest(ParseResult)} instead - * @param parsedCommands the list of {@code CommandLine} objects to check if help was requested - * @param out the {@code PrintStream} to print help to if requested - * @param err the error string to print diagnostic messages to, in addition to the output from the exception handler - * @param ansi for printing help messages using ANSI styles and colors - * @return {@code true} if help was printed, {@code false} otherwise - * @since 3.0 */ - @Deprecated public static boolean printHelpIfRequested(List parsedCommands, PrintStream out, PrintStream err, Help.Ansi ansi) { - return printHelpIfRequested(parsedCommands, out, err, Help.defaultColorScheme(ansi)); - } - /** - * Delegates to the implementation of {@link #executeHelpRequest(ParseResult)}. - * @deprecated use {@link #executeHelpRequest(ParseResult)} instead - * @param parsedCommands the list of {@code CommandLine} objects to check if help was requested - * @param out the {@code PrintStream} to print help to if requested - * @param err the error string to print diagnostic messages to, in addition to the output from the exception handler - * @param colorScheme for printing help messages using ANSI styles and colors - * @return {@code true} if help was printed, {@code false} otherwise - * @since 3.6 */ - @Deprecated public static boolean printHelpIfRequested(List parsedCommands, PrintStream out, PrintStream err, Help.ColorScheme colorScheme) { - // for backwards compatibility - for (CommandLine cmd : parsedCommands) { cmd.setOut(newPrintWriter(out, getStdoutEncoding())).setErr(newPrintWriter(err, getStderrEncoding())).setColorScheme(colorScheme); } - return executeHelpRequest(parsedCommands) != null; - } - - /** - * Helper method that may be useful when processing the {@code ParseResult} that results from successfully - * {@linkplain #parseArgs(String...) parsing} command line arguments. This method prints out - * {@linkplain #usage(PrintWriter, Help.ColorScheme) usage help} to the {@linkplain CommandLine#getOut() configured output writer} - * if {@linkplain #isUsageHelpRequested() requested} or {@linkplain #printVersionHelp(PrintWriter, Help.Ansi, Object...) version help} - * to the {@linkplain CommandLine#getOut() configured output writer} if {@linkplain #isVersionHelpRequested() requested} - * and returns {@link CommandSpec#exitCodeOnUsageHelp()} or {@link CommandSpec#exitCodeOnVersionHelp()}, respectively. - * If the command is a {@link Command#helpCommand()} and {@code runnable} or {@code callable}, - * that command is executed and this method returns {@link CommandSpec#exitCodeOnUsageHelp()}. - * Otherwise, if none of the specified {@code CommandLine} objects have help requested, - * this method returns {@code null}.

- * Note that this method only looks at the {@link Option#usageHelp() usageHelp} and - * {@link Option#versionHelp() versionHelp} attributes. The {@link Option#help() help} attribute is ignored. - *

Implementation note:

- * When an error occurs while processing the help request, it is recommended custom Help commands throw a - * {@link ParameterException} with a reference to the parent command. This will print the error message and the - * usage for the parent command, and will use the exit code of the exception handler if one was set. - *

- * @param parseResult contains the {@code CommandLine} objects found during parsing; check these to see if help was requested - * @return {@link CommandSpec#exitCodeOnUsageHelp()} if usage help was requested, - * {@link CommandSpec#exitCodeOnVersionHelp()} if version help was requested, and {@code null} otherwise - * @see IHelpCommandInitializable2 - * @since 4.0 */ - public static Integer executeHelpRequest(ParseResult parseResult) { - return executeHelpRequest(parseResult.asCommandLineList()); - } - /** @since 4.0 */ - static Integer executeHelpRequest(List parsedCommands) { - Tracer t = CommandLine.tracer(); - for (CommandLine parsed : parsedCommands) { - Help.ColorScheme colorScheme = parsed.getColorScheme(); - PrintWriter out = parsed.getOut(); - if (parsed.isUsageHelpRequested()) { - t.debug("Printing usage help for '%s' as requested.", parsed.commandSpec.qualifiedName()); - parsed.usage(out, colorScheme); - return parsed.getCommandSpec().exitCodeOnUsageHelp(); - } else if (parsed.isVersionHelpRequested()) { - t.debug("Printing version info for '%s' as requested.", parsed.commandSpec.qualifiedName()); - parsed.printVersionHelp(out, colorScheme.ansi); - return parsed.getCommandSpec().exitCodeOnVersionHelp(); - } else if (parsed.getCommandSpec().helpCommand()) { - String fullName = parsed.commandSpec.qualifiedName(); - PrintWriter err = parsed.getErr(); - if (((Object) parsed.getCommand()) instanceof IHelpCommandInitializable2) { - t.debug("Initializing helpCommand '%s' (IHelpCommandInitializable2::init)...", fullName); - ((IHelpCommandInitializable2) parsed.getCommand()).init(parsed, colorScheme, out, err); - } else if (((Object) parsed.getCommand()) instanceof IHelpCommandInitializable) { - t.debug("Initializing helpCommand '%s' (IHelpCommandInitializable::init)...", fullName); - ((IHelpCommandInitializable) parsed.getCommand()).init(parsed, colorScheme.ansi, System.out, System.err); - } else { - t.debug("helpCommand '%s' does not implement IHelpCommandInitializable2 or IHelpCommandInitializable...", fullName); - } - t.debug("Executing helpCommand '%s'...", fullName); - executeUserObject(parsed, new ArrayList()); - return parsed.getCommandSpec().exitCodeOnUsageHelp(); - } - } - t.debug("Help was not requested. Continuing to process ParseResult..."); - return null; - } - private static List executeUserObject(CommandLine parsed, List executionResultList) { - Tracer tracer = CommandLine.tracer(); - - Object command = parsed.getCommand(); - if (command instanceof Runnable) { - try { - tracer.debug("Invoking Runnable::run on user object %s@%s...", command.getClass().getName(), Integer.toHexString(command.hashCode())); - ((Runnable) command).run(); - parsed.setExecutionResult(null); // 4.0 - executionResultList.add(null); // for compatibility with picocli 2.x - return executionResultList; - } catch (ParameterException ex) { - throw ex; - } catch (ExecutionException ex) { - throw ex; - } catch (Exception ex) { - throw new ExecutionException(parsed, "Error while running command (" + command + "): " + ex, ex); - } - } else if (command instanceof Callable) { - try { - tracer.debug("Invoking Callable::call on user object %s@%s...", command.getClass().getName(), Integer.toHexString(command.hashCode())); - @SuppressWarnings("unchecked") Callable callable = (Callable) command; - Object executionResult = callable.call(); - parsed.setExecutionResult(executionResult); - executionResultList.add(executionResult); - return executionResultList; - } catch (ParameterException ex) { - throw ex; - } catch (ExecutionException ex) { - throw ex; - } catch (Exception ex) { - throw new ExecutionException(parsed, "Error while calling command (" + command + "): " + ex, ex); - } - } else if (command instanceof Method) { - try { - Method method = (Method) command; - Object[] parsedArgs = parsed.getCommandSpec().commandMethodParamValues(); - Object executionResult; - if (Modifier.isStatic(method.getModifiers())) { - tracer.debug("Invoking static method %s with parameters %s", method, Arrays.toString(parsedArgs)); - executionResult = method.invoke(null, parsedArgs); // invoke static method - } else { - Object instance = (parsed.getCommandSpec().parent() != null) - ? parsed.getCommandSpec().parent().userObject() - : parsed.factory.create(method.getDeclaringClass()); - tracer.debug("Invoking method %s on %s@%s with parameters %s", - method, instance.getClass().getName(), Integer.toHexString(instance.hashCode()), Arrays.toString(parsedArgs)); - executionResult = method.invoke(instance, parsedArgs); - } - parsed.setExecutionResult(executionResult); - executionResultList.add(executionResult); - return executionResultList; - } catch (InvocationTargetException ex) { - Throwable t = ex.getTargetException(); - if (t instanceof ParameterException) { - throw (ParameterException) t; - } else if (t instanceof ExecutionException) { - throw (ExecutionException) t; - } else { - throw new ExecutionException(parsed, "Error while calling command (" + command + "): " + t, t); - } - } catch (Exception ex) { - throw new ExecutionException(parsed, "Unhandled error while calling command (" + command + "): " + ex, ex); - } - } - if (parsed.getSubcommands().isEmpty()) { - throw new ExecutionException(parsed, "Parsed command (" + command + ") is not a Method, Runnable or Callable"); - } else { - throw new ParameterException(parsed, "Missing required subcommand"); - } - } - - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * To use this method, the annotated object that this {@code CommandLine} is constructed with needs to - * either implement {@link Runnable}, {@link Callable}, or be a {@code Method} object. - * See {@link #getCommandMethods(Class, String) getCommandMethods} for a convenient way to obtain a command {@code Method}. - *

This method replaces the {@link #run(Runnable, String...) run}, {@link #call(Callable, String...) call} - * and {@link #invoke(String, Class, String...) invoke} convenience methods that were available with previous versions of picocli. - *

- * Exit Code - *

- * This method returns an exit code that applications can use to call {@code System.exit}. - * (The return value of the {@code Callable} or {@code Method} can still be obtained via {@link #getExecutionResult() getExecutionResult}.) - * If the user object {@code Callable} or {@code Method} returns an {@code int} or {@code Integer}, - * this will be used as the exit code. Additionally, if the user object implements {@link CommandLine.IExitCodeGenerator IExitCodeGenerator}, - * an exit code is obtained by calling its {@code getExitCode()} method (after invoking the user object). - *

- * In the case of multiple exit codes the highest value will be used (or if all values are negative, the lowest value will be used). - *

- * Exception Handling - *

- * This method never throws an exception. - *

- * If the user specified invalid input, the {@linkplain #getParameterExceptionHandler() parameter exception handler} is invoked. - * By default this prints an error message and the usage help message, and returns an exit code. - *

- * If an exception occurred while the user object {@code Runnable}, {@code Callable}, or {@code Method} - * was invoked, this exception is caught and passed to the {@linkplain #getExecutionExceptionHandler() execution exception handler}. - * The default {@code IExecutionExceptionHandler} will rethrow this Exception. - *

- * Any exception thrown from the {@code IParameterExceptionHandler} or {@code IExecutionExceptionHandler} is caught, - * it stacktrace is printed and is mapped to an exit code, using the following logic: - *

- * If an {@link CommandLine.IExitCodeExceptionMapper IExitCodeExceptionMapper} is {@linkplain #setExitCodeExceptionMapper(IExitCodeExceptionMapper) configured}, - * this mapper is used to determine the exit code based on the exception. - *

- * If an {@code IExitCodeExceptionMapper} is not set, by default this method will return the {@code @Command} annotation's - * {@link Command#exitCodeOnInvalidInput() exitCodeOnInvalidInput} or {@link Command#exitCodeOnExecutionException() exitCodeOnExecutionException} value, respectively. - *

Example Usage:

- *
-     * @Command
-     * class MyCommand implements Callable<Integer> {
-     *     public Integer call() { return 123; }
-     * }
-     * CommandLine cmd = new CommandLine(new MyCommand());
-     * int exitCode = cmd.execute(args);
-     * assert exitCode == 123;
-     * System.exit(exitCode);
-     * 
- *

Since {@code execute} is an instance method, not a static method, applications can do configuration before invoking the command. For example:

- *
{@code
-     * CommandLine cmd = new CommandLine(new MyCallable())
-     *         .setCaseInsensitiveEnumValuesAllowed(true) // configure a non-default parser option
-     *         .setOut(myOutWriter()) // configure an alternative to System.out
-     *         .setErr(myErrWriter()) // configure an alternative to System.err
-     *         .setColorScheme(myColorScheme()); // configure a custom color scheme
-     * int exitCode = cmd.execute(args);
-     * System.exit(exitCode);
-     * }
- *

- * If the specified command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. This can be configured by setting the {@linkplain #setExecutionStrategy(IExecutionStrategy) execution strategy}. - * Built-in alternatives are executing the {@linkplain RunFirst first} subcommand, or executing {@linkplain RunAll all} specified subcommands. - *

- * @param args the command line arguments to parse - * @return the exit code - * @see ExitCode - * @see IExitCodeGenerator - * @see #getExecutionResult() - * @see #getExecutionStrategy() - * @see #getParameterExceptionHandler() - * @see #getExecutionExceptionHandler() - * @see #getExitCodeExceptionMapper() - * @since 4.0 - */ - public int execute(String... args) { - ParseResult[] parseResult = new ParseResult[1]; - clearExecutionResults(); - try { - parseResult[0] = parseArgs(args); - return enrichForBackwardsCompatibility(getExecutionStrategy()).execute(parseResult[0]); - } catch (ParameterException ex) { - try { - return getParameterExceptionHandler().handleParseException(ex, args); - } catch (Exception ex2) { - return handleUnhandled(ex2, ex.getCommandLine(), ex.getCommandLine().getCommandSpec().exitCodeOnInvalidInput()); - } - } catch (ExecutionException ex) { - try { - Exception cause = ex.getCause() instanceof Exception ? (Exception) ex.getCause() : ex; - return getExecutionExceptionHandler().handleExecutionException(cause, ex.getCommandLine(), parseResult[0]); - } catch (Exception ex2) { - return handleUnhandled(ex2, ex.getCommandLine(), ex.getCommandLine().getCommandSpec().exitCodeOnExecutionException()); - } - } catch (Exception ex) { - return handleUnhandled(ex, this, getCommandSpec().exitCodeOnExecutionException()); - } - } - private static int handleUnhandled(Exception ex, CommandLine cmd, int defaultExitCode) { - cmd.getErr().print(throwableToColorString(ex, cmd.getColorScheme())); - cmd.getErr().flush(); - return mappedExitCode(ex, cmd.getExitCodeExceptionMapper(), defaultExitCode); - } - - /** - * Convert a {@code Throwable} to a {@code String} , with message and stack traces extracted and colored - * according to {@code ColorScheme}. - * @param t the {@code Throwable} to be converted - * @param existingColorScheme the {@code ColorScheme} to use - * @return converted and colored {@code String} - */ - private static String throwableToColorString(Throwable t, Help.ColorScheme existingColorScheme) { - Help.ColorScheme colorScheme = new Help.ColorScheme.Builder(existingColorScheme).applySystemProperties().build(); - StringWriter stringWriter = new ColoredStackTraceWriter(colorScheme); - t.printStackTrace(new PrintWriter(stringWriter)); - return stringWriter.toString(); - } - - /** - * Extends StringWriter to use ColorScheme. Allows separating - * exception messages from stack traces by intercepting write method. - */ - static class ColoredStackTraceWriter extends StringWriter { - Help.ColorScheme colorScheme; - - public ColoredStackTraceWriter(Help.ColorScheme colorScheme) { this.colorScheme = colorScheme; } - - @Override - public void write(String str, int off, int len) { - List styles = str.startsWith("\t") ? colorScheme.stackTraceStyles() : colorScheme.errorStyles(); - super.write(colorScheme.apply(str.substring(off, len), styles).toString()); - } - } - - private T enrichForBackwardsCompatibility(T obj) { - // in case the IExecutionStrategy is a built-in like RunLast, - // and the application called #useOut, #useErr or #useAnsi on it - if (obj instanceof AbstractHandler) { - AbstractHandler handler = (AbstractHandler) obj; - if (handler.out() != System.out) { setOut(newPrintWriter(handler.out(), getStdoutEncoding())); } - if (handler.err() != System.err) { setErr(newPrintWriter(handler.err(), getStderrEncoding())); } - if (handler.ansi() != Help.Ansi.AUTO) { setColorScheme(handler.colorScheme()); } - } - return obj; - } - /** Command line parse result handler that returns a value. This handler prints help if requested, and otherwise calls - * {@link #handle(CommandLine.ParseResult)} with the parse result. Facilitates implementation of the {@link IParseResultHandler2} interface. - *

Note that {@code AbstractParseResultHandler} is a generic type. This, along with the abstract {@code self} method, - * allows method chaining to work properly in subclasses, without the need for casts. An example subclass can look like this:

- *
{@code
-     * class MyResultHandler extends AbstractParseResultHandler {
-     *
-     *     protected MyReturnType handle(ParseResult parseResult) throws ExecutionException { ... }
-     *
-     *     protected MyResultHandler self() { return this; }
-     * }
-     * }
- * @deprecated see {@link #execute(String...)}, {@link #getExecutionStrategy()}, {@link #getParameterExceptionHandler()}, {@link #getExecutionExceptionHandler()} - * @since 3.0 */ - @Deprecated public abstract static class AbstractParseResultHandler extends AbstractHandler> implements IParseResultHandler2, IExecutionStrategy { - /** Prints help if requested, and otherwise calls {@link #handle(CommandLine.ParseResult)}. - * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return the result of {@link #handle(CommandLine.ParseResult) processing parse results} - * @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions} - * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler2} - * @throws ExecutionException if a problem occurred while processing the parse results; client code can use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - public R handleParseResult(ParseResult parseResult) throws ExecutionException { - if (printHelpIfRequested(parseResult.asCommandLineList(), out(), err(), colorScheme())) { - return returnResultOrExit(null); - } - return returnResultOrExit(handle(parseResult)); - } - - public int execute(ParseResult parseResult) throws ExecutionException { - Integer helpExitCode = executeHelpRequest(parseResult); - if (helpExitCode != null) { return helpExitCode; } - - Tracer t = CommandLine.tracer(); - t.debug("%s: handling ParseResult...", getClass().getSimpleName()); - R executionResult = handle(parseResult); - List exitCodeGenerators = extractExitCodeGenerators(parseResult); - t.debug("%s: ParseResult has %s exit code generators", getClass().getSimpleName(), exitCodeGenerators.size()); - return resolveExitCode(parseResult.commandSpec().exitCodeOnSuccess(), executionResult, exitCodeGenerators); - } - - // Use the highest value (or if all values are negative, use the lowest value). - private int resolveExitCode(int exitCodeOnSuccess, R executionResult, List exitCodeGenerators) { - int result = 0; - for (IExitCodeGenerator generator : exitCodeGenerators) { - try { - int exitCode = generator.getExitCode(); - if ((exitCode > 0 && exitCode > result) || (exitCode < result && result <= 0)) { - result = exitCode; - } - } catch (Exception ex) { - result = (result == 0) ? 1 : result; - ex.printStackTrace(); - } - } - Tracer t = CommandLine.tracer(); - t.debug("resolveExitCode: exit code generators resulted in exit code=%d", result); - if (executionResult instanceof List) { - List resultList = (List) executionResult; - for (Object obj : resultList) { - if (obj instanceof Integer) { - int exitCode = (Integer) obj; - if ((exitCode > 0 && exitCode > result) || (exitCode < result && result <= 0)) { - result = exitCode; - } - } - } - } - t.debug("resolveExitCode: execution results resulted in exit code=%d", result); - t.debug("resolveExitCode: returning exit code=%d", result == 0 ? exitCodeOnSuccess : result); - return result == 0 ? exitCodeOnSuccess : result; - } - - /** Processes the specified {@code ParseResult} and returns the result as a list of objects. - * Implementations are responsible for catching any exceptions thrown in the {@code handle} method, and - * rethrowing an {@code ExecutionException} that details the problem and captures the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return the result of processing parse results - * @throws ExecutionException if a problem occurred while processing the parse results; client code can use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - protected abstract R handle(ParseResult parseResult) throws ExecutionException; - - protected List extractExitCodeGenerators(ParseResult parseResult) { return Collections.emptyList(); } - } - /** - * Command line {@linkplain IExecutionStrategy execution strategy} that prints help if requested, and otherwise executes the top-level - * {@code Runnable} or {@code Callable} command. - * For use by the {@link #execute(String...) execute} method. - * @since 2.0 */ - public static class RunFirst extends AbstractParseResultHandler> implements IParseResultHandler { - /** {@inheritDoc} */ - public int execute(ParseResult parseResult) throws ExecutionException { return super.execute(parseResult); } - - /** Prints help if requested, and otherwise executes the top-level {@code Runnable} or {@code Callable} command. - * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. - * If the top-level command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments - * @param out the {@code PrintStream} to print help to if requested - * @param ansi for printing help messages using ANSI styles and colors - * @return an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the top-level command was a {@code Runnable} - * @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions} - * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - public List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) { - if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } - return returnResultOrExit(executeUserObject(parsedCommands.get(0), new ArrayList())); - } - /** Executes the top-level {@code Runnable} or {@code Callable} subcommand. - * If the top-level command does not implement either {@code Runnable} or {@code Callable} and is not a {@code Method}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @since 3.0 */ - protected List handle(ParseResult parseResult) throws ExecutionException { - Tracer t = CommandLine.tracer(); - t.debug("RunFirst: executing user object for '%s'...", parseResult.commandSpec().qualifiedName()); - return executeUserObject(parseResult.commandSpec().commandLine(), new ArrayList()); // first - } - - protected List extractExitCodeGenerators(ParseResult parseResult) { - if (parseResult.commandSpec().userObject() instanceof IExitCodeGenerator) { - return Collections.singletonList((IExitCodeGenerator) parseResult.commandSpec().userObject()); - } - return Collections.emptyList(); - } - - @Override protected RunFirst self() { return this; } - } - /** - * Command line {@linkplain IExecutionStrategy execution strategy} that prints help if requested, and otherwise executes the most specific - * {@code Runnable} or {@code Callable} subcommand. - * For use by the {@link #execute(String...) execute} method. - *

- * Something like this:

- *
{@code
-     *     // RunLast implementation: print help if requested, otherwise execute the most specific subcommand
-     *     List parsedCommands = parseResult.asCommandLineList();
-     *     if (CommandLine.printHelpIfRequested(parsedCommands, out(), err(), ansi())) {
-     *         return emptyList();
-     *     }
-     *     CommandLine last = parsedCommands.get(parsedCommands.size() - 1);
-     *     Object command = last.getCommand();
-     *     Object result = null;
-     *     if (command instanceof Runnable) {
-     *         try {
-     *             ((Runnable) command).run();
-     *         } catch (Exception ex) {
-     *             throw new ExecutionException(last, "Error in runnable " + command, ex);
-     *         }
-     *     } else if (command instanceof Callable) {
-     *         try {
-     *             result = ((Callable) command).call();
-     *         } catch (Exception ex) {
-     *             throw new ExecutionException(last, "Error in callable " + command, ex);
-     *         }
-     *     } else {
-     *         throw new ExecutionException(last, "Parsed command (" + command + ") is not Runnable or Callable");
-     *     }
-     *     last.setExecutionResult(result);
-     *     return Arrays.asList(result);
-     * }
- *

- * From picocli v2.0, {@code RunLast} is used to implement the {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run} - * and {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call} convenience methods. - *

- * @since 2.0 */ - public static class RunLast extends AbstractParseResultHandler> implements IParseResultHandler { - /** {@inheritDoc} */ - public int execute(ParseResult parseResult) throws ExecutionException { return super.execute(parseResult); } - - /** Prints help if requested, and otherwise executes the most specific {@code Runnable} or {@code Callable} subcommand. - *

For {@linkplain Command#subcommandsRepeatable() repeatable subcommands}, this method - * may execute multiple subcommands: the most deeply nested subcommands that have the same parent command.

- *

Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}.

- *

If the last (sub)command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object.

- * - * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments - * @param out the {@code PrintStream} to print help to if requested - * @param ansi for printing help messages using ANSI styles and colors - * @return an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} - * @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions} - * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - public List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) { - if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } - return returnResultOrExit(executeUserObjectOfLastSubcommandWithSameParent(parsedCommands)); - } - /** Executes the most specific {@code Runnable} or {@code Callable} subcommand. - *

For {@linkplain Command#subcommandsRepeatable() repeatable subcommands}, this method - * may execute multiple subcommands: the most deeply nested subcommands that have the same parent command.

- *

If the user object of the executed (sub)command does not implement either {@code Runnable} or {@code Callable} and is not a {@code Method}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object.

- * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @since 3.0 */ - protected List handle(ParseResult parseResult) throws ExecutionException { - return executeUserObjectOfLastSubcommandWithSameParent(parseResult.asCommandLineList()); - } - private static List executeUserObjectOfLastSubcommandWithSameParent(List parsedCommands) { - Tracer t = CommandLine.tracer(); - int start = indexOfLastSubcommandWithSameParent(parsedCommands); - List result = new ArrayList(); - for (int i = start; i < parsedCommands.size(); i++) { - t.debug("RunLast: executing user object for '%s'...", parsedCommands.get(i).commandSpec.qualifiedName()); - executeUserObject(parsedCommands.get(i), result); - } - return result; - } - // find list of most deeply nested sub-(sub*)-commands - private static int indexOfLastSubcommandWithSameParent(List parsedCommands) { - int start = parsedCommands.size() - 1; - for (int i = parsedCommands.size() - 2; i >= 0; i--) { - if (parsedCommands.get(i).getParent() != parsedCommands.get(i + 1).getParent()) { break; } - start = i; - } - return start; - } - - protected List extractExitCodeGenerators(ParseResult parseResult) { - List parsedCommands = parseResult.asCommandLineList(); - int start = indexOfLastSubcommandWithSameParent(parsedCommands); - List result = new ArrayList(); - for (int i = start; i < parsedCommands.size(); i++) { - Object userObject = parsedCommands.get(i).getCommandSpec().userObject(); - if (userObject instanceof IExitCodeGenerator) { result.add((IExitCodeGenerator) userObject); } - } - return result; - } - @Override protected RunLast self() { return this; } - } - /** - * Command line {@linkplain IExecutionStrategy execution strategy} that prints help if requested, and otherwise executes the top-level command and - * all subcommands as {@code Runnable}, {@code Callable} or {@code Method}. - * For use by the {@link #execute(String...) execute} method. - * @since 2.0 */ - public static class RunAll extends AbstractParseResultHandler> implements IParseResultHandler { - /** {@inheritDoc} */ - public int execute(ParseResult parseResult) throws ExecutionException { return super.execute(parseResult); } - - /** Prints help if requested, and otherwise executes the top-level command and all subcommands as {@code Runnable}, - * {@code Callable} or {@code Method}. Finally, either a list of result objects is returned, or the JVM is terminated if an exit - * code {@linkplain #andExit(int) was set}. If any of the {@code CommandLine} commands does not implement either - * {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments - * @param out the {@code PrintStream} to print help to if requested - * @param ansi for printing help messages using ANSI styles and colors - * @return an empty list if help was requested, or a list containing the result of executing all commands: - * the return values from calling the {@code Callable} commands, {@code null} elements for commands that implement {@code Runnable} - * @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions} - * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - public List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) { - if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } - List result = new ArrayList(); - for (CommandLine parsed : parsedCommands) { - executeUserObject(parsed, result); - } - return returnResultOrExit(result); - } - /** Executes the top-level command and all subcommands as {@code Runnable} or {@code Callable}. - * If any of the {@code CommandLine} commands does not implement either {@code Runnable} or {@code Callable} and is not a {@code Method}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return an empty list if help was requested, or a list containing the result of executing all commands: - * the return values from calling the {@code Callable} commands, {@code null} elements for commands that implement {@code Runnable} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @since 3.0 */ - protected List handle(ParseResult parseResult) throws ExecutionException { - Tracer t = CommandLine.tracer(); - return returnResultOrExit(recursivelyExecuteUserObject(parseResult, new ArrayList(), t)); - } - private List recursivelyExecuteUserObject(ParseResult parseResult, List result, Tracer t) throws ExecutionException { - t.debug("%s: executing user object for '%s'...", getClass().getSimpleName(), parseResult.commandSpec.qualifiedName()); - executeUserObject(parseResult.commandSpec().commandLine(), result); - for (ParseResult pr : parseResult.subcommands()) { - recursivelyExecuteUserObject(pr, result, t); - } - return result; - } - protected List extractExitCodeGenerators(ParseResult parseResult) { - return recursivelyExtractExitCodeGenerators(parseResult, new ArrayList()); - } - private List recursivelyExtractExitCodeGenerators(ParseResult parseResult, List result) throws ExecutionException { - if (parseResult.commandSpec().userObject() instanceof IExitCodeGenerator) { result.add((IExitCodeGenerator) parseResult.commandSpec().userObject()); } - for (ParseResult pr : parseResult.subcommands()) { - recursivelyExtractExitCodeGenerators(pr, result); - } - return result; - } - @Override protected RunAll self() { return this; } - } - - /** - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 2.0 */ - @Deprecated public List parseWithHandler(IParseResultHandler handler, PrintStream out, String... args) { - return parseWithHandlers(handler, out, Help.Ansi.AUTO, defaultExceptionHandler(), args); - } - /** - * Returns the result of calling {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...)} with - * a new {@link DefaultExceptionHandler} in addition to the specified parse result handler and the specified command line arguments. - *

- * This is a convenience method intended to offer the same ease of use as the {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run} - * and {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call} methods, but with more flexibility and better - * support for nested subcommands. - *

- *

Calling this method roughly expands to:

- *
{@code
-     * try {
-     *     ParseResult parseResult = parseArgs(args);
-     *     return handler.handleParseResult(parseResult);
-     * } catch (ParameterException ex) {
-     *     return new DefaultExceptionHandler().handleParseException(ex, args);
-     * }
-     * }
- *

- * Picocli provides some default handlers that allow you to accomplish some common tasks with very little code. - * The following handlers are available:

- *
    - *
  • {@link RunLast} handler prints help if requested, and otherwise gets the last specified command or subcommand - * and tries to execute it as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunFirst} handler prints help if requested, and otherwise executes the top-level command as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunAll} handler prints help if requested, and otherwise executes all recognized commands and subcommands as {@code Runnable} or {@code Callable} tasks.
  • - *
  • {@link DefaultExceptionHandler} prints the error message followed by usage help
  • - *
- * @param the return type of this handler - * @param handler the function that will handle the result of successfully parsing the command line arguments - * @param args the command line arguments - * @return an object resulting from handling the parse result or the exception that occurred while parsing the input - * @throws ExecutionException if the command line arguments were parsed successfully but a problem occurred while processing the - * parse results; use {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @see RunLast - * @see RunAll - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.0 */ - @Deprecated public R parseWithHandler(IParseResultHandler2 handler, String[] args) { - return parseWithHandlers(handler, new DefaultExceptionHandler(), args); - } - - /** - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 2.0 */ - @Deprecated public List parseWithHandlers(IParseResultHandler handler, PrintStream out, Help.Ansi ansi, IExceptionHandler exceptionHandler, String... args) { - clearExecutionResults(); - try { - List result = parse(args); - return handler.handleParseResult(result, out, ansi); - } catch (ParameterException ex) { - return exceptionHandler.handleException(ex, out, ansi, args); - } - } - /** - * Tries to {@linkplain #parseArgs(String...) parse} the specified command line arguments, and if successful, delegates - * the processing of the resulting {@code ParseResult} object to the specified {@linkplain IParseResultHandler2 handler}. - * If the command line arguments were invalid, the {@code ParameterException} thrown from the {@code parse} method - * is caught and passed to the specified {@link IExceptionHandler2}. - *

- * This is a convenience method intended to offer the same ease of use as the {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run} - * and {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call} methods, but with more flexibility and better - * support for nested subcommands. - *

- *

Calling this method roughly expands to:

- *
-     * ParseResult parseResult = null;
-     * try {
-     *     parseResult = parseArgs(args);
-     *     return handler.handleParseResult(parseResult);
-     * } catch (ParameterException ex) {
-     *     return exceptionHandler.handleParseException(ex, (String[]) args);
-     * } catch (ExecutionException ex) {
-     *     return exceptionHandler.handleExecutionException(ex, parseResult);
-     * }
-     * 
- *

- * Picocli provides some default handlers that allow you to accomplish some common tasks with very little code. - * The following handlers are available:

- *
    - *
  • {@link RunLast} handler prints help if requested, and otherwise gets the last specified command or subcommand - * and tries to execute it as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunFirst} handler prints help if requested, and otherwise executes the top-level command as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunAll} handler prints help if requested, and otherwise executes all recognized commands and subcommands as {@code Runnable} or {@code Callable} tasks.
  • - *
  • {@link DefaultExceptionHandler} prints the error message followed by usage help
  • - *
- * - * @param handler the function that will handle the result of successfully parsing the command line arguments - * @param exceptionHandler the function that can handle the {@code ParameterException} thrown when the command line arguments are invalid - * @param args the command line arguments - * @return an object resulting from handling the parse result or the exception that occurred while parsing the input - * @throws ExecutionException if the command line arguments were parsed successfully but a problem occurred while processing the parse - * result {@code ParseResult} object; use {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @param the return type of the result handler and exception handler - * @see RunLast - * @see RunAll - * @see DefaultExceptionHandler - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.0 */ - @Deprecated public R parseWithHandlers(IParseResultHandler2 handler, IExceptionHandler2 exceptionHandler, String... args) { - clearExecutionResults(); - ParseResult parseResult = null; - try { - parseResult = parseArgs(args); - return handler.handleParseResult(parseResult); - } catch (ParameterException ex) { - return exceptionHandler.handleParseException(ex, args); - } catch (ExecutionException ex) { - return exceptionHandler.handleExecutionException(ex, parseResult); - } - } - static String versionString() { - return String.format("%s, JVM: %s (%s %s %s), OS: %s %s %s", VERSION, - System.getProperty("java.version"), System.getProperty("java.vendor"), System.getProperty("java.vm.name"), System.getProperty("java.vm.version"), - System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")); - } - /** - * Equivalent to {@code new CommandLine(command).usage(out)}. See {@link #usage(PrintStream)} for details. - * @param command the object annotated with {@link Command}, {@link Option} and {@link Parameters} - * @param out the print stream to print the help message to - * @throws IllegalArgumentException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public static void usage(Object command, PrintStream out) { - toCommandLine(command, new DefaultFactory()).usage(out); - } - - /** - * Equivalent to {@code new CommandLine(command).usage(out, ansi)}. - * See {@link #usage(PrintStream, Help.Ansi)} for details. - * @param command the object annotated with {@link Command}, {@link Option} and {@link Parameters} - * @param out the print stream to print the help message to - * @param ansi whether the usage message should contain ANSI escape codes or not - * @throws IllegalArgumentException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public static void usage(Object command, PrintStream out, Help.Ansi ansi) { - toCommandLine(command, new DefaultFactory()).usage(out, ansi); - } - - /** - * Equivalent to {@code new CommandLine(command).usage(out, colorScheme)}. - * See {@link #usage(PrintStream, Help.ColorScheme)} for details. - * @param command the object annotated with {@link Command}, {@link Option} and {@link Parameters} - * @param out the print stream to print the help message to - * @param colorScheme the {@code ColorScheme} defining the styles for options, parameters and commands when ANSI is enabled - * @throws IllegalArgumentException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public static void usage(Object command, PrintStream out, Help.ColorScheme colorScheme) { - toCommandLine(command, new DefaultFactory()).usage(out, colorScheme); - } - - /** - * Delegates to {@link #usage(PrintStream, Help.ColorScheme)} with the {@linkplain #getColorScheme() configured} color scheme. - * @param out the printStream to print to - * @see #usage(PrintStream, Help.ColorScheme) - */ - public void usage(PrintStream out) { usage(out, getColorScheme()); } - /** - * Delegates to {@link #usage(PrintWriter, Help.ColorScheme)} with the {@linkplain #getColorScheme() configured} color scheme. - * @param writer the PrintWriter to print to - * @see #usage(PrintWriter, Help.ColorScheme) - * @since 3.0 */ - public void usage(PrintWriter writer) { usage(writer, getColorScheme()); } - - /** - * Delegates to {@link #usage(PrintStream, Help.ColorScheme)} with the {@linkplain Help#defaultColorScheme(CommandLine.Help.Ansi) default color scheme}. - * @param out the printStream to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @see #usage(PrintStream, Help.ColorScheme) - */ - public void usage(PrintStream out, Help.Ansi ansi) { usage(out, Help.defaultColorScheme(ansi)); } - /** Similar to {@link #usage(PrintStream, Help.Ansi)} but with the specified {@code PrintWriter} instead of a {@code PrintStream}. - * @since 3.0 */ - public void usage(PrintWriter writer, Help.Ansi ansi) { usage(writer, Help.defaultColorScheme(ansi)); } - - /** - * Prints a usage help message for the annotated command class to the specified {@code PrintStream}. - * Delegates construction of the usage help message to the {@link Help} inner class and is equivalent to: - *
-     * Help.ColorScheme colorScheme = Help.defaultColorScheme(Help.Ansi.AUTO);
-     * Help help = getHelpFactory().create(getCommandSpec(), colorScheme)
-     * StringBuilder sb = new StringBuilder();
-     * for (String key : getHelpSectionKeys()) {
-     *     IHelpSectionRenderer renderer = getHelpSectionMap().get(key);
-     *     if (renderer != null) { sb.append(renderer.render(help)); }
-     * }
-     * out.print(sb);
-     * 
- *

Annotate your class with {@link Command} to control many aspects of the usage help message, including - * the program name, text of section headings and section contents, and some aspects of the auto-generated sections - * of the usage help message. - *

To customize the auto-generated sections of the usage help message, like how option details are displayed, - * instantiate a {@link Help} object and use a {@link Help.TextTable} with more of fewer columns, a custom - * {@linkplain Help.Layout layout}, and/or a custom option {@linkplain Help.IOptionRenderer renderer} - * for ultimate control over which aspects of an Option or Field are displayed where.

- * @param out the {@code PrintStream} to print the usage help message to - * @param colorScheme the {@code ColorScheme} defining the styles for options, parameters and commands when ANSI is enabled - * @see UsageMessageSpec - */ - public void usage(PrintStream out, Help.ColorScheme colorScheme) { - out.print(usage(new StringBuilder(), getHelpFactory().create(getCommandSpec(), colorScheme))); - out.flush(); - } - /** Similar to {@link #usage(PrintStream, Help.ColorScheme)}, but with the specified {@code PrintWriter} instead of a {@code PrintStream}. - * @since 3.0 */ - public void usage(PrintWriter writer, Help.ColorScheme colorScheme) { - writer.print(usage(new StringBuilder(), getHelpFactory().create(getCommandSpec(), colorScheme))); - writer.flush(); - } - /** Similar to {@link #usage(PrintStream)}, but returns the usage help message as a String instead of printing it to the {@code PrintStream}. - * @since 3.2 */ - public String getUsageMessage() { - return usage(new StringBuilder(), getHelp()).toString(); - } - /** Similar to {@link #usage(PrintStream, Help.Ansi)}, but returns the usage help message as a String instead of printing it to the {@code PrintStream}. - * @since 3.2 */ - public String getUsageMessage(Help.Ansi ansi) { - return usage(new StringBuilder(), getHelpFactory().create(getCommandSpec(), Help.defaultColorScheme(ansi))).toString(); - } - /** Similar to {@link #usage(PrintStream, Help.ColorScheme)}, but returns the usage help message as a String instead of printing it to the {@code PrintStream}. - * @since 3.2 */ - public String getUsageMessage(Help.ColorScheme colorScheme) { - return usage(new StringBuilder(), getHelpFactory().create(getCommandSpec(), colorScheme)).toString(); - } - - private StringBuilder usage(StringBuilder sb, Help help) { - for (String key : getHelpSectionKeys()) { - IHelpSectionRenderer renderer = getHelpSectionMap().get(key); - if (renderer != null) { sb.append(renderer.render(help)); } - } - return sb; - } - - /** - * Delegates to {@link #printVersionHelp(PrintStream, Help.Ansi)} with the ANSI setting of the {@linkplain #getColorScheme() configured} color scheme. - * @param out the printStream to print to - * @see #printVersionHelp(PrintStream, Help.Ansi) - * @since 0.9.8 - */ - public void printVersionHelp(PrintStream out) { printVersionHelp(out, getColorScheme().ansi()); } - - /** - * Prints version information from the {@link Command#version()} annotation to the specified {@code PrintStream}. - * Each element of the array of version strings is printed on a separate line. Version strings may contain - * markup for colors and style. - * @param out the printStream to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @see Command#version() - * @see Option#versionHelp() - * @see #isVersionHelpRequested() - * @since 0.9.8 - */ - public void printVersionHelp(PrintStream out, Help.Ansi ansi) { - for (String versionInfo : getCommandSpec().version()) { - out.println(ansi.new Text(versionInfo, getColorScheme())); - } - out.flush(); - } - /** - * Prints version information from the {@link Command#version()} annotation to the specified {@code PrintStream}. - * Each element of the array of version strings is {@linkplain String#format(String, Object...) formatted} with the - * specified parameters, and printed on a separate line. Both version strings and parameters may contain - * markup for colors and style. - * @param out the printStream to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param params Arguments referenced by the format specifiers in the version strings - * @see Command#version() - * @see Option#versionHelp() - * @see #isVersionHelpRequested() - * @since 1.0.0 - */ - public void printVersionHelp(PrintStream out, Help.Ansi ansi, Object... params) { - for (String versionInfo : getCommandSpec().version()) { - out.println(ansi.new Text(format(versionInfo, params), getColorScheme())); - } - out.flush(); - } - /** - * Delegates to {@link #printVersionHelp(PrintWriter, Help.Ansi, Object...)} with the ANSI setting of the {@linkplain #getColorScheme() configured} color scheme. - * @param out the PrintWriter to print to - * @since 4.0 */ - public void printVersionHelp(PrintWriter out) { printVersionHelp(out, getColorScheme().ansi()); } - /** - * Prints version information from the {@link Command#version()} annotation to the specified {@code PrintWriter}. - * Each element of the array of version strings is {@linkplain String#format(String, Object...) formatted} with the - * specified parameters, and printed on a separate line. Both version strings and parameters may contain - * markup for colors and style. - * @param out the PrintWriter to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param params Arguments referenced by the format specifiers in the version strings - * @see Command#version() - * @see Option#versionHelp() - * @see #isVersionHelpRequested() - * @since 4.0 */ - public void printVersionHelp(PrintWriter out, Help.Ansi ansi, Object... params) { - for (String versionInfo : getCommandSpec().version()) { - out.println(ansi.new Text(format(versionInfo, params), getColorScheme())); - } - out.flush(); - } - - /** - * Equivalent to {@code new CommandLine(callable).execute(args)}, except for the return value. - * @param callable the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @see #execute(String...) - * @since 3.0 - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - */ - @Deprecated public static , T> T call(C callable, String... args) { - CommandLine cmd = new CommandLine(callable); - List results = cmd.parseWithHandler(new RunLast(), args); - return CommandLine.firstElement(results); - } - - /** - * Delegates to {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for - * diagnostic error messages and {@link Help.Ansi#AUTO}. - * @param callable the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @see RunLast - */ - @Deprecated public static , T> T call(C callable, PrintStream out, String... args) { - return call(callable, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for diagnostic error messages. - * @param callable the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param ansi the ANSI style to use - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @see RunLast - */ - @Deprecated public static , T> T call(C callable, PrintStream out, Help.Ansi ansi, String... args) { - return call(callable, out, System.err, ansi, args); - } - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * The annotated object needs to implement {@link Callable}. - *

Consider using the {@link #execute(String...)} method instead:

- *
{@code
-     * CommandLine cmd = new CommandLine(callable)
-     *         .setOut(myOutWriter()) // System.out by default
-     *         .setErr(myErrWriter()) // System.err by default
-     *         .setColorScheme(myColorScheme()); // default color scheme, Ansi.AUTO by default
-     * int exitCode = cmd.execute(args);
-     * //System.exit(exitCode);
-     * }
- *

- * If the specified Callable command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. - *

- * @param callable the command to call when {@linkplain #parse(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi including whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the specified {@code Callable} - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.0 - */ - @Deprecated public static , T> T call(C callable, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - CommandLine cmd = new CommandLine(callable); - List results = cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - return CommandLine.firstElement(results); - } - /** - * Equivalent to {@code new CommandLine(callableClass, factory).execute(args)}, except for the return value. - * @param callableClass class of the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified callable class and potentially inject other components - * @param args the command line arguments to parse - * @param the annotated class must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @see #execute(String...) - * @since 3.2 - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - */ - @Deprecated public static , T> T call(Class callableClass, IFactory factory, String... args) { - CommandLine cmd = new CommandLine(callableClass, factory); - List results = cmd.parseWithHandler(new RunLast(), args); - return CommandLine.firstElement(results); - } - /** - * Delegates to {@link #call(Class, IFactory, PrintStream, PrintStream, Help.Ansi, String...)} with - * {@code System.err} for diagnostic error messages, and {@link Help.Ansi#AUTO}. - * @param callableClass class of the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified callable class and potentially injecting other components - * @param out the printStream to print the usage help message to when the user requested help - * @param args the command line arguments to parse - * @param the annotated class must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.2 - */ - @Deprecated public static , T> T call(Class callableClass, IFactory factory, PrintStream out, String... args) { - return call(callableClass, factory, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #call(Class, IFactory, PrintStream, PrintStream, Help.Ansi, String...)} with - * {@code System.err} for diagnostic error messages. - * @param callableClass class of the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified callable class and potentially injecting other components - * @param out the printStream to print the usage help message to when the user requested help - * @param ansi the ANSI style to use - * @param args the command line arguments to parse - * @param the annotated class must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.2 - */ - @Deprecated public static , T> T call(Class callableClass, IFactory factory, PrintStream out, Help.Ansi ansi, String... args) { - return call(callableClass, factory, out, System.err, ansi, args); - } - - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * The specified {@linkplain IFactory factory} will create an instance of the specified {@code callableClass}; - * use this method instead of {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call(Callable, ...)} - * if you want to use a factory that performs Dependency Injection. - * The annotated class needs to implement {@link Callable}. - *

Consider using the {@link #execute(String...)} method instead:

- *
{@code
-     * CommandLine cmd = new CommandLine(callableClass, factory)
-     *         .setOut(myOutWriter()) // System.out by default
-     *         .setErr(myErrWriter()) // System.err by default
-     *         .setColorScheme(myColorScheme()); // default color scheme, Ansi.AUTO by default
-     * int exitCode = cmd.execute(args);
-     * //System.exit(exitCode);
-     * }
- *

- * If the specified Callable command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. - *

- * @param callableClass class of the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified callable class and potentially injecting other components - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi the ANSI style to use - * @param args the command line arguments to parse - * @param the annotated class must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.2 - */ - @Deprecated public static , T> T call(Class callableClass, IFactory factory, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - CommandLine cmd = new CommandLine(callableClass, factory); - List results = cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - return CommandLine.firstElement(results); - } - - @SuppressWarnings("unchecked") private static T firstElement(List results) { - return (results == null || results.isEmpty()) ? null : (T) results.get(0); - } - - /** - * Equivalent to {@code new CommandLine(runnable).execute(args)}. - * @param runnable the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #execute(String...) - * @since 3.0 - * @deprecated use {@link #execute(String...)} instead - */ - @Deprecated public static void run(R runnable, String... args) { - run(runnable, System.out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for diagnostic error messages and {@link Help.Ansi#AUTO}. - * @param runnable the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @deprecated use {@link #execute(String...)} instead - * @see RunLast - */ - @Deprecated public static void run(R runnable, PrintStream out, String... args) { - run(runnable, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for diagnostic error messages. - * @param runnable the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @deprecated use {@link #execute(String...)} instead - * @see RunLast - */ - @Deprecated public static void run(R runnable, PrintStream out, Help.Ansi ansi, String... args) { - run(runnable, out, System.err, ansi, args); - } - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * The annotated object needs to implement {@link Runnable}. - *

Consider using the {@link #execute(String...)} method instead:

- *
{@code
-     * CommandLine cmd = new CommandLine(runnable)
-     *         .setOut(myOutWriter()) // System.out by default
-     *         .setErr(myErrWriter()) // System.err by default
-     *         .setColorScheme(myColorScheme()); // default color scheme, Ansi.AUTO by default
-     * int exitCode = cmd.execute(args);
-     * //System.exit(exitCode);
-     * }
- *

- * If the specified Runnable command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. - *

- * From picocli v2.0, this method prints usage help or version help if {@linkplain #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) requested}, - * and any exceptions thrown by the {@code Runnable} are caught and rethrown wrapped in an {@code ExecutionException}. - *

- * @param runnable the command to run when {@linkplain #parse(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @deprecated use {@link #execute(String...)} instead - * @since 3.0 - */ - @Deprecated public static void run(R runnable, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - CommandLine cmd = new CommandLine(runnable); - cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - } - /** - * Equivalent to {@code new CommandLine(runnableClass, factory).execute(args)}. - * @param runnableClass class of the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified Runnable class and potentially injecting other components - * @param args the command line arguments to parse - * @param the annotated class must implement Runnable - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #execute(String...) - * @since 3.2 - * @deprecated use {@link #execute(String...)} instead - */ - @Deprecated public static void run(Class runnableClass, IFactory factory, String... args) { - run(runnableClass, factory, System.out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #run(Class, IFactory, PrintStream, PrintStream, Help.Ansi, String...)} with - * {@code System.err} for diagnostic error messages, and {@link Help.Ansi#AUTO}. - * @param runnableClass class of the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified Runnable class and potentially injecting other components - * @param out the printStream to print the usage help message to when the user requested help - * @param args the command line arguments to parse - * @param the annotated class must implement Runnable - * @see #run(Class, IFactory, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - * @deprecated use {@link #execute(String...)} instead - * @since 3.2 - */ - @Deprecated public static void run(Class runnableClass, IFactory factory, PrintStream out, String... args) { - run(runnableClass, factory, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #run(Class, IFactory, PrintStream, PrintStream, Help.Ansi, String...)} with - * {@code System.err} for diagnostic error messages. - * @param runnableClass class of the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified Runnable class and potentially injecting other components - * @param out the printStream to print the usage help message to when the user requested help - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated class must implement Runnable - * @see #run(Class, IFactory, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified class cannot be instantiated by the factory, or does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - * @deprecated use {@link #execute(String...)} instead - * @since 3.2 - */ - @Deprecated public static void run(Class runnableClass, IFactory factory, PrintStream out, Help.Ansi ansi, String... args) { - run(runnableClass, factory, out, System.err, ansi, args); - } - - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * The specified {@linkplain IFactory factory} will create an instance of the specified {@code runnableClass}; - * use this method instead of {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run(Runnable, ...)} - * if you want to use a factory that performs Dependency Injection. - * The annotated class needs to implement {@link Runnable}. - *

Consider using the {@link #execute(String...)} method instead:

- *
{@code
-     * CommandLine cmd = new CommandLine(runnableClass, factory)
-     *         .setOut(myOutWriter()) // System.out by default
-     *         .setErr(myErrWriter()) // System.err by default
-     *         .setColorScheme(myColorScheme()); // default color scheme, Ansi.AUTO by default
-     * int exitCode = cmd.execute(args);
-     * //System.exit(exitCode);
-     * }
- *

- * If the specified Runnable command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. - *

- * This method prints usage help or version help if {@linkplain #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) requested}, - * and any exceptions thrown by the {@code Runnable} are caught and rethrown wrapped in an {@code ExecutionException}. - *

- * @param runnableClass class of the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param factory the factory responsible for instantiating the specified Runnable class and potentially injecting other components - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated class must implement Runnable - * @deprecated use {@link #execute(String...)} instead - * @since 3.2 - */ - @Deprecated public static void run(Class runnableClass, IFactory factory, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - CommandLine cmd = new CommandLine(runnableClass, factory); - cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - } - - /** - * Delegates to {@link #invoke(String, Class, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.out} for - * requested usage help messages, {@code System.err} for diagnostic error messages, and {@link Help.Ansi#AUTO}. - * @param methodName the {@code @Command}-annotated method to build a {@link CommandSpec} model from, - * and run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param cls the class where the {@code @Command}-annotated method is declared, or a subclass - * @param args the command line arguments to parse - * @see #invoke(String, Class, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified method does not have a {@link Command} annotation, - * or if the specified class contains multiple {@code @Command}-annotated methods with the specified name - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @since 3.6 - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - */ - @Deprecated public static Object invoke(String methodName, Class cls, String... args) { - return invoke(methodName, cls, System.out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #invoke(String, Class, PrintStream, PrintStream, Help.Ansi, String...)} with the specified stream for - * requested usage help messages, {@code System.err} for diagnostic error messages, and {@link Help.Ansi#AUTO}. - * @param methodName the {@code @Command}-annotated method to build a {@link CommandSpec} model from, - * and run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param cls the class where the {@code @Command}-annotated method is declared, or a subclass - * @param out the printstream to print requested help message to - * @param args the command line arguments to parse - * @see #invoke(String, Class, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified method does not have a {@link Command} annotation, - * or if the specified class contains multiple {@code @Command}-annotated methods with the specified name - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.6 - */ - @Deprecated public static Object invoke(String methodName, Class cls, PrintStream out, String... args) { - return invoke(methodName, cls, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #invoke(String, Class, PrintStream, PrintStream, Help.Ansi, String...)} with the specified stream for - * requested usage help messages, {@code System.err} for diagnostic error messages, and the specified Ansi mode. - * @param methodName the {@code @Command}-annotated method to build a {@link CommandSpec} model from, - * and run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param cls the class where the {@code @Command}-annotated method is declared, or a subclass - * @param out the printstream to print requested help message to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @see #invoke(String, Class, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified method does not have a {@link Command} annotation, - * or if the specified class contains multiple {@code @Command}-annotated methods with the specified name - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.6 - */ - @Deprecated public static Object invoke(String methodName, Class cls, PrintStream out, Help.Ansi ansi, String... args) { - return invoke(methodName, cls, out, System.err, ansi, args); - } - - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * Constructs a {@link CommandSpec} model from the {@code @Option} and {@code @Parameters}-annotated method parameters - * of the {@code @Command}-annotated method, parses the specified command line arguments and invokes the specified method. - *

Consider using the {@link #execute(String...)} method instead:

- *
{@code
-     * Method commandMethod = getCommandMethods(cls, methodName).get(0);
-     * CommandLine cmd = new CommandLine(commandMethod)
-     *         .setOut(myOutWriter()) // System.out by default
-     *         .setErr(myErrWriter()) // System.err by default
-     *         .setColorScheme(myColorScheme()); // default color scheme, Ansi.AUTO by default
-     * int exitCode = cmd.execute(args);
-     * //System.exit(exitCode);
-     * }
- * @param methodName the {@code @Command}-annotated method to build a {@link CommandSpec} model from, - * and run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param cls the class where the {@code @Command}-annotated method is declared, or a subclass - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @throws InitializationException if the specified method does not have a {@link Command} annotation, - * or if the specified class contains multiple {@code @Command}-annotated methods with the specified name - * @throws ExecutionException if the method throws an exception - * @deprecated use {@link #execute(String...)} and {@link #getExecutionResult()} instead - * @since 3.6 - */ - @Deprecated public static Object invoke(String methodName, Class cls, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - List candidates = getCommandMethods(cls, methodName); - if (candidates.size() != 1) { throw new InitializationException("Expected exactly one @Command-annotated method for " + cls.getName() + "::" + methodName + "(...), but got: " + candidates); } - Method method = candidates.get(0); - CommandLine cmd = new CommandLine(method); - List list = cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - return list == null ? null : list.get(0); - } - /** - * Helper to get methods of a class annotated with {@link Command @Command} via reflection, optionally filtered by method name (not {@link Command#name() @Command.name}). - * Methods have to be either public (inherited) members or be declared by {@code cls}, that is "inherited" static or protected methods will not be picked up. - * - * @param cls the class to search for methods annotated with {@code @Command} - * @param methodName if not {@code null}, return only methods whose method name (not {@link Command#name() @Command.name}) equals this string. Ignored if {@code null}. - * @return the matching command methods, or an empty list - * @see #invoke(String, Class, String...) - * @since 3.6.0 - */ - public static List getCommandMethods(Class cls, String methodName) { - return getCommandMethods(cls, methodName, true); - } - private static List getCommandMethods(Class cls, String methodName, boolean includeInherited) { - Set candidates = new HashSet(); - if (includeInherited) { - // traverse public member methods (excludes static/non-public, includes inherited) - candidates.addAll(Arrays.asList(Assert.notNull(cls, "class").getMethods())); - } - // traverse directly declared methods (includes static/non-public, excludes inherited) - candidates.addAll(Arrays.asList(Assert.notNull(cls, "class").getDeclaredMethods())); - - List result = new ArrayList(); - for (Method method : candidates) { - if (method.isAnnotationPresent(Command.class)) { - if (methodName == null || methodName.equals(method.getName())) { result.add(method); } - } - } - Collections.sort(result, new Comparator() { - public int compare(Method o1, Method o2) { return o1.getName().compareTo(o2.getName()); } - }); - return result; - } - - /** - * Registers the specified type converter for the specified class. When initializing fields annotated with - * {@link Option}, the field's type is used as a lookup key to find the associated type converter, and this - * type converter converts the original command line argument string value to the correct type. - *

- * Java 8 lambdas make it easy to register custom type converters: - *

- *
-     * commandLine.registerConverter(java.nio.file.Path.class, s -> java.nio.file.Paths.get(s));
-     * commandLine.registerConverter(java.time.Duration.class, s -> java.time.Duration.parse(s));
- *

- * Built-in type converters are pre-registered for the following java 1.5 types: - *

- *
    - *
  • all primitive types
  • - *
  • all primitive wrapper types: Boolean, Byte, Character, Double, Float, Integer, Long, Short
  • - *
  • any enum
  • - *
  • java.io.File
  • - *
  • java.math.BigDecimal
  • - *
  • java.math.BigInteger
  • - *
  • java.net.InetAddress
  • - *
  • java.net.URI
  • - *
  • java.net.URL
  • - *
  • java.nio.charset.Charset
  • - *
  • java.sql.Time
  • - *
  • java.util.Date
  • - *
  • java.util.UUID
  • - *
  • java.util.regex.Pattern
  • - *
  • StringBuilder
  • - *
  • CharSequence
  • - *
  • String
  • - *
- *

The specified converter will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment the converter is registered. Subcommands added - * later will not have this converter added automatically. To ensure a custom type converter is available to all - * subcommands, register the type converter last, after adding subcommands.

- * - * @param cls the target class to convert parameter string values to - * @param converter the class capable of converting string values to the specified target type - * @param the target type - * @return this CommandLine object, to allow method chaining - * @see #addSubcommand(String, Object) - */ - public CommandLine registerConverter(Class cls, ITypeConverter converter) { - interpreter.converterRegistry.put(Assert.notNull(cls, "class"), Assert.notNull(converter, "converter")); - for (CommandLine command : getCommandSpec().commands.values()) { - command.registerConverter(cls, converter); - } - return this; - } - - /** Returns the String that separates option names from option values when parsing command line options. - * @return the String the parser uses to separate option names from option values - * @see ParserSpec#separator() */ - public String getSeparator() { return getCommandSpec().parser().separator(); } - - /** Sets the String the parser uses to separate option names from option values to the specified value. - * The separator may also be set declaratively with the {@link CommandLine.Command#separator()} annotation attribute. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param separator the String that separates option names from option values - * @see ParserSpec#separator(String) - * @return this {@code CommandLine} object, to allow method chaining */ - public CommandLine setSeparator(String separator) { - getCommandSpec().parser().separator(Assert.notNull(separator, "separator")); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setSeparator(separator); - } - return this; - } - - /** Returns the ResourceBundle of this command or {@code null} if no resource bundle is set. - * @see Command#resourceBundle() - * @see CommandSpec#resourceBundle() - * @since 3.6 */ - public ResourceBundle getResourceBundle() { return getCommandSpec().resourceBundle(); } - - /** Sets the ResourceBundle containing usage help message strings. - *

The specified bundle will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will not be impacted. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param bundle the ResourceBundle containing usage help message strings - * @return this {@code CommandLine} object, to allow method chaining - * @see Command#resourceBundle() - * @see CommandSpec#resourceBundle(ResourceBundle) - * @since 3.6 */ - public CommandLine setResourceBundle(ResourceBundle bundle) { - getCommandSpec().resourceBundle(bundle); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setResourceBundle(bundle); - } - return this; - } - - /** Returns the maximum width of the usage help message. The default is 80. - * @see UsageMessageSpec#width() */ - public int getUsageHelpWidth() { return getCommandSpec().usageMessage().width(); } - - /** Sets the maximum width of the usage help message. Longer lines are wrapped. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param width the maximum width of the usage help message - * @see UsageMessageSpec#width(int) - * @return this {@code CommandLine} object, to allow method chaining */ - public CommandLine setUsageHelpWidth(int width) { - getCommandSpec().usageMessage().width(width); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUsageHelpWidth(width); - } - return this; - } - - /** Returns the maximum usage help long options column max width to the specified value. - * This value controls the maximum width of the long options column: any positional parameter - * labels or long options that are longer than the specified value will overflow into - * the description column, and cause the description to be displayed on the next line. - * @see UsageMessageSpec#longOptionsMaxWidth() - * @since 4.2*/ - public int getUsageHelpLongOptionsMaxWidth() { return getCommandSpec().usageMessage().longOptionsMaxWidth(); } - - /** Returns the maximum usage help long options column max width to the specified value. - * This value controls the maximum width of the long options column: any positional parameter - * labels or long options that are longer than the specified value will overflow into - * the description column, and cause the description to be displayed on the next line. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param columnWidth the new maximum usage help long options column max width. Must be 20 or greater. - * @see UsageMessageSpec#longOptionsMaxWidth(int) - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.2 */ - public CommandLine setUsageHelpLongOptionsMaxWidth(int columnWidth) { - getCommandSpec().usageMessage().longOptionsMaxWidth(columnWidth); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUsageHelpLongOptionsMaxWidth(columnWidth); - } - return this; - } - - /** Returns whether picocli should attempt to detect the terminal size and adjust the usage help message width - * to take the full terminal width. End users may enable this by setting system property {@code "picocli.usage.width"} to {@code AUTO}, - * and may disable this by setting this system property to a {@linkplain UsageMessageSpec#width() numeric value}. - * This feature requires Java 7 or greater. The default is {@code false}. - * @see UsageMessageSpec#autoWidth() - * @since 4.0 */ - public boolean isUsageHelpAutoWidth() { return getCommandSpec().usageMessage().autoWidth(); } - - /** Sets whether picocli should attempt to detect the terminal size and adjust the usage help message width - * to take the full terminal width. The default is {@code false}. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * @param detectTerminalSize whether picocli should attempt to detect the terminal size - * @see UsageMessageSpec#autoWidth(boolean) - * @return this {@code CommandLine} object, to allow method chaining - * @since 4.0 */ - public CommandLine setUsageHelpAutoWidth(boolean detectTerminalSize) { - getCommandSpec().usageMessage().autoWidth(detectTerminalSize); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUsageHelpAutoWidth(detectTerminalSize); - } - return this; - } - - /** Returns the command name (also called program name) displayed in the usage help synopsis. - * @return the command name (also called program name) displayed in the usage - * @see CommandSpec#name() - * @since 2.0 */ - public String getCommandName() { return getCommandSpec().name(); } - - /** Sets the command name (also called program name) displayed in the usage help synopsis to the specified value. - * Note that this method only modifies the usage help message, it does not impact parsing behaviour. - * The command name may also be set declaratively with the {@link CommandLine.Command#name()} annotation attribute. - * @param commandName command name (also called program name) displayed in the usage help synopsis - * @return this {@code CommandLine} object, to allow method chaining - * @see CommandSpec#name(String) - * @since 2.0 */ - public CommandLine setCommandName(String commandName) { - getCommandSpec().name(Assert.notNull(commandName, "commandName")); - return this; - } - - /** Returns whether arguments starting with {@code '@'} should be treated as the path to an argument file and its - * contents should be expanded into separate arguments for each line in the specified file. - * This property is {@code true} by default. - * @return whether "argument files" or {@code @files} should be expanded into their content - * @see ParserSpec#expandAtFiles() - * @since 2.1 */ - public boolean isExpandAtFiles() { return getCommandSpec().parser().expandAtFiles(); } - - /** Sets whether arguments starting with {@code '@'} should be treated as the path to an argument file and its - * contents should be expanded into separate arguments for each line in the specified file. ({@code true} by default.) - * @param expandAtFiles whether "argument files" or {@code @files} should be expanded into their content - * @return this {@code CommandLine} object, to allow method chaining - * @see ParserSpec#expandAtFiles(boolean) - * @since 2.1 */ - public CommandLine setExpandAtFiles(boolean expandAtFiles) { - getCommandSpec().parser().expandAtFiles(expandAtFiles); - return this; - } - - /** Returns the character that starts a single-line comment or {@code null} if all content of argument files should - * be interpreted as arguments (without comments). - * If specified, all characters from the comment character to the end of the line are ignored. - * @return the character that starts a single-line comment or {@code null}. The default is {@code '#'}. - * @see ParserSpec#atFileCommentChar() - * @since 3.5 */ - public Character getAtFileCommentChar() { return getCommandSpec().parser().atFileCommentChar(); } - - /** Sets the character that starts a single-line comment or {@code null} if all content of argument files should - * be interpreted as arguments (without comments). - * If specified, all characters from the comment character to the end of the line are ignored. - * @param atFileCommentChar the character that starts a single-line comment or {@code null}. The default is {@code '#'}. - * @return this {@code CommandLine} object, to allow method chaining - * @see ParserSpec#atFileCommentChar(Character) - * @since 3.5 */ - public CommandLine setAtFileCommentChar(Character atFileCommentChar) { - getCommandSpec().parser().atFileCommentChar(atFileCommentChar); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setAtFileCommentChar(atFileCommentChar); - } - return this; - } - - /** Returns whether to use a simplified argument file format that is compatible with JCommander. - * In this format, every line (except empty lines and comment lines) - * is interpreted as a single argument. Arguments containing whitespace do not need to be quoted. - * When system property {@code "picocli.useSimplifiedAtFiles"} is defined, the system property value overrides the programmatically set value. - * @return whether to use a simplified argument file format. The default is {@code false}. - * @see ParserSpec#useSimplifiedAtFiles() - * @since 3.9 */ - public boolean isUseSimplifiedAtFiles() { return getCommandSpec().parser().useSimplifiedAtFiles(); } - - /** Sets whether to use a simplified argument file format that is compatible with JCommander. - * In this format, every line (except empty lines and comment lines) - * is interpreted as a single argument. Arguments containing whitespace do not need to be quoted. - * When system property {@code "picocli.useSimplifiedAtFiles"} is defined, the system property value overrides the programmatically set value. - * @param simplifiedAtFiles whether to use a simplified argument file format. The default is {@code false}. - * @return this {@code CommandLine} object, to allow method chaining - * @see ParserSpec#useSimplifiedAtFiles(boolean) - * @since 3.9 */ - public CommandLine setUseSimplifiedAtFiles(boolean simplifiedAtFiles) { - getCommandSpec().parser().useSimplifiedAtFiles(simplifiedAtFiles); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUseSimplifiedAtFiles(simplifiedAtFiles); - } - return this; - } - /** Returns the {@code INegatableOptionTransformer} used to create the negative form of {@linkplain Option#negatable() negatable} options. - * By default this returns the result of {@link RegexTransformer#createDefault()}. - * @return the {@code INegatableOptionTransformer} used to create negative option names. - * @see Option#negatable() - * @see CommandSpec#negatableOptionTransformer() - * @since 4.0 */ - public INegatableOptionTransformer getNegatableOptionTransformer() { return getCommandSpec().negatableOptionTransformer(); } - - /** Sets the {@code INegatableOptionTransformer} used to create the negative form of {@linkplain Option#negatable() negatable} options. - *

The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added - * later will have the default setting. To ensure a setting is applied to all - * subcommands, call the setter last, after adding subcommands.

- * Note that {@link CommandLine#setOptionsCaseInsensitive} will also change the case sensitivity of {@linkplain Option#negatable() negatable} options: - * any custom {@link INegatableOptionTransformer} that was previously installed will be replaced by the case-insensitive - * version of the default transformer. To ensure your custom transformer is used, install it last, after changing case sensitivity. - * @param transformer the {@code INegatableOptionTransformer} used to create negative option names. - * @return this {@code CommandLine} object, to allow method chaining - * @see Option#negatable() - * @see CommandSpec#negatableOptionTransformer(CommandLine.INegatableOptionTransformer) - * @since 4.0 */ - public CommandLine setNegatableOptionTransformer(INegatableOptionTransformer transformer) { - getCommandSpec().negatableOptionTransformer(transformer); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setNegatableOptionTransformer(transformer); - } - return this; - } - private static boolean empty(String str) { return str == null || str.trim().length() == 0; } - private static boolean empty(Object[] array) { return array == null || array.length == 0; } - private static String str(String[] arr, int i) { return (arr == null || arr.length <= i) ? "" : arr[i]; } - private static boolean isBoolean(Class[] types) { return isBoolean(types[0]) || (isOptional(types[0]) && isBoolean(types[1])); } - private static boolean isBoolean(Class type) { return type == Boolean.class || type == Boolean.TYPE; } - private static CommandLine toCommandLine(Object obj, IFactory factory) { return obj instanceof CommandLine ? (CommandLine) obj : new CommandLine(obj, factory, false);} - private static boolean isMultiValue(Class cls) { return (cls.isArray() && cls != char[].class) || Collection.class.isAssignableFrom(cls) || Map.class.isAssignableFrom(cls); } - private static boolean isOptional(Class cls) { return cls != null && "java.util.Optional".equals(cls.getName()); } // #1108 - private static Object getOptionalEmpty() throws Exception { - return Class.forName("java.util.Optional").getMethod("empty").invoke(null); - } - private static Object getOptionalOfNullable(Object newValue) throws Exception { - return Class.forName("java.util.Optional").getMethod("ofNullable", Object.class).invoke(null, newValue); - } - private static String format(String formatString, Object... params) { - try { - return formatString == null ? "" : String.format(formatString, params); - } catch (IllegalFormatException ex) { - CommandLine.tracer().warn("Could not format '%s' (Underlying error: %s). " + - "Using raw String: '%%n' format strings have not been replaced with newlines. " + - "Please ensure to escape '%%' characters with another '%%'.", formatString, ex.getMessage()); - return formatString; - } - } - private static Map mapOf(String key, Object value, Object... other) { - LinkedHashMap result = new LinkedHashMap(); - result.put(key, value); - for (int i = 0; i < other.length - 1; i += 2) { - result.put(String.valueOf(other[i]), other[i + 1]); - } - return result; - } - - private static class NoCompletionCandidates implements Iterable { - public Iterator iterator() { throw new UnsupportedOperationException(); } - } - /** Specifies the scope of the element. - * @since 4.3 */ - public enum ScopeType { - /** The element only exists in the current command. */ - LOCAL, - /** The element exists in the command where the element is defined and all descendents (subcommands, sub-subcommands, etc.). */ - INHERIT, - } - /** - *

- * Annotate fields in your class with {@code @Option} and picocli will initialize these fields when matching - * arguments are specified on the command line. In the case of command methods (annotated with {@code @Command}), - * command options can be defined by annotating method parameters with {@code @Option}. - *

- * Command class example: - *

- *
-     * import static picocli.CommandLine.*;
-     *
-     * public class MyClass {
-     *     @Parameters(description = "Any number of input files")
-     *     private List<File> files = new ArrayList<File>();
-     *
-     *     @Option(names = { "-o", "--out" }, description = "Output file (default: print to console)")
-     *     private File outputFile;
-     *
-     *     @Option(names = { "-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting. Multiple -v options increase the verbosity.")
-     *     private boolean[] verbose;
-     *
-     *     @Option(names = { "-h", "--help", "-?", "-help"}, usageHelp = true, description = "Display this help and exit")
-     *     private boolean help;
-     * }
-     * 
- *

- * A field cannot be annotated with both {@code @Parameters} and {@code @Option} or a - * {@code ParameterException} is thrown. - *

- */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - public @interface Option { - /** Special value that can be used in some annotation attributes to designate {@code null}. - * @see Option#defaultValue() - * @see Option#fallbackValue() - * @see Option#mapFallbackValue() - * @since 4.6 */ - public static final String NULL_VALUE = ArgSpec.NULL_VALUE; - /** - * One or more option names. At least one option name is required. - *

- * Different environments have different conventions for naming options, but usually options have a prefix - * that sets them apart from parameters. - * Picocli supports all of the below styles. The default separator is {@code '='}, but this can be configured. - *

- * *nix - *

- * In Unix and Linux, options have a short (single-character) name, a long name or both. - * Short options - * (POSIX - * style are single-character and are preceded by the {@code '-'} character, e.g., {@code `-v'}. - * GNU-style long - * (or mnemonic) options start with two dashes in a row, e.g., {@code `--file'}. - *

Picocli supports the POSIX convention that short options can be grouped, with the last option - * optionally taking a parameter, which may be attached to the option name or separated by a space or - * a {@code '='} character. The below examples are all equivalent: - *

-         * -xvfFILE
-         * -xvf FILE
-         * -xvf=FILE
-         * -xv --file FILE
-         * -xv --file=FILE
-         * -x -v --file FILE
-         * -x -v --file=FILE
-         * 

- * DOS - *

- * DOS options mostly have upper case single-character names and start with a single slash {@code '/'} character. - * Option parameters are separated by a {@code ':'} character. Options cannot be grouped together but - * must be specified separately. For example: - *

-         * DIR /S /A:D /T:C
-         * 

- * PowerShell - *

- * Windows PowerShell options generally are a word preceded by a single {@code '-'} character, e.g., {@code `-Help'}. - * Option parameters are separated by a space or by a {@code ':'} character. - *

- * @return one or more option names - */ - String[] names(); - - /** - * Indicates whether this option is required. By default this is false. - *

If an option is required, but a user invokes the program without specifying the required option, - * a {@link MissingParameterException} is thrown from the {@link #parse(String...)} method.

- *

Required options that are part of a {@linkplain ArgGroup group} are required within the group, not required within the command: - * the group's {@linkplain ArgGroup#multiplicity() multiplicity} determines whether the group itself is required or optional.

- * @return whether this option is required - */ - boolean required() default false; - - /** - *

This should rarely be used: the recommended attributes are {@link #usageHelp() usageHelp} and {@link #versionHelp() versionHelp}. - *

- * Only set {@code help=true} when this option should disable validation of the remaining - * arguments, and no error message should be generated for missing required options. - *

- * This is useful for custom help options that are in addition to the standard help and - * version options. For example if your application has many hidden options or - * subcommands, and there is a custom help option like {@code --detailed-help} that prints - * the usage help message for these hidden options and subcommands. - *

- *

Note:

- *

- * Use the {@link #usageHelp() usageHelp} for "normal" help options (like {@code -h} and {@code --help} on unix, - * {@code -?} and {@code -Help} on Windows) - * and use {@link #versionHelp() versionHelp} for "normal" version help ({@code -V} and {@code --version} on unix, - * {@code -Version} on Windows): - * picocli has built-in logic so that options with {@code usageHelp=true} or {@code versionHelp=true} - * will automatically cause the requested help message to be printed in applications - * that use the {@link #execute(String...)} method, without any code in the application. - *

- * Note that there is no such automatic help printing for options with {@code help=true}; - * applications need to check whether the end user specified this option and take appropriate action - * in the business logic of the application. - *

- * @return whether this option disables validation of the other arguments - */ - boolean help() default false; - - /** - * Set {@code usageHelp=true} for the {@code --help} option that triggers display of the usage help message. - * The convenience methods {@code Commandline.call}, - * {@code Commandline.run}, and {@code Commandline.parseWithHandler(s)} will automatically print usage help - * when an option with {@code usageHelp=true} was specified on the command line. - *

- * By default, all options and positional parameters are included in the usage help message - * except when explicitly marked {@linkplain #hidden() hidden}. - *

- * If this option is specified on the command line, picocli will not validate the remaining arguments (so no "missing required - * option" errors) and the {@link CommandLine#isUsageHelpRequested()} method will return {@code true}. - *

- * Alternatively, consider annotating your command with {@linkplain Command#mixinStandardHelpOptions() @Command(mixinStandardHelpOptions = true)}. - *

- * @return whether this option allows the user to request usage help - * @since 0.9.8 - * @see #hidden() - * @see #run(Runnable, String...) - * @see #call(Callable, String...) - * @see #parseWithHandler(IParseResultHandler2, String[]) - * @see #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) - */ - boolean usageHelp() default false; - - /** - * Set {@code versionHelp=true} for the {@code --version} option that triggers display of the version information. - * The convenience methods {@code Commandline.call}, - * {@code Commandline.run}, and {@code Commandline.parseWithHandler(s)} will automatically print version information - * when an option with {@code versionHelp=true} was specified on the command line. - *

- * The version information string is obtained from the command's {@linkplain Command#version() version} annotation - * or from the {@linkplain Command#versionProvider() version provider}. - *

- * If this option is specified on the command line, picocli will not validate the remaining arguments (so no "missing required - * option" errors) and the {@link CommandLine#isUsageHelpRequested()} method will return {@code true}. - *

- * Alternatively, consider annotating your command with {@linkplain Command#mixinStandardHelpOptions() @Command(mixinStandardHelpOptions = true)}. - *

- * @return whether this option allows the user to request version information - * @since 0.9.8 - * @see #hidden() - * @see #run(Runnable, String...) - * @see #call(Callable, String...) - * @see #parseWithHandler(IParseResultHandler2, String[]) - * @see #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) - */ - boolean versionHelp() default false; - - /** - * Description of this option, used when generating the usage documentation. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}. - *

- * The description may contain variables that are rendered when help is requested. - * The string {@code ${DEFAULT-VALUE}} is replaced with the default value of the option. This is regardless of - * the command's {@link Command#showDefaultValues() showDefaultValues} setting or the option's {@link #showDefaultValue() showDefaultValue} setting. - * The string {@code ${COMPLETION-CANDIDATES}} is replaced with the completion candidates generated by - * {@link #completionCandidates()} in the description for this option. - * Also, embedded {@code %n} newline markers are converted to actual newlines. - *

- * @return the description of this option - * @see Variable Interpolation section of the user manual - */ - String[] description() default {}; - - /** - * Specifies the minimum number of required parameters and the maximum number of accepted parameters. - * If an option declares a positive arity, and the user specifies an insufficient number of parameters on the - * command line, a {@link MissingParameterException} is thrown by the {@link #parse(String...)} method. - *

- * In many cases picocli can deduce the number of required parameters from the field's type. - * By default, flags (boolean options) have arity "0..1", - * and single-valued type fields (String, int, Integer, double, Double, File, Date, etc) have arity one. - * Generally, fields with types that cannot hold multiple values can omit the {@code arity} attribute. - *

- * Fields used to capture options with arity two or higher should have a type that can hold multiple values, - * like arrays or Collections. See {@link #type()} for strongly-typed Collection fields. - *

- * For example, if an option has 2 required parameters and any number of optional parameters, - * specify {@code @Option(names = "-example", arity = "2..*")}. - *

- * A note on boolean options - *

- * By default picocli allows boolean options (also called "flags" or "switches") to have an optional parameter, - * which must be either "true" or "false" (lowercase, other values are rejected). - * You can make a boolean option take a required parameter by annotating your field with {@code arity="1"}. - * For example:

- *
@Option(names = "-v", arity = "1") boolean verbose;
- *

- * Because this boolean field is defined with arity 1, the user must specify either {@code -v false} - * or {@code -v true} - * on the command line, or a {@link MissingParameterException} is thrown by the {@link #parse(String...)} - * method. - *

- * To remove the optional parameter, define the field with {@code arity = "0"}. - * For example:

- *
@Option(names="-v", arity="0") boolean verbose;
- *

This will reject any of the below:

- *
-         * -v true
-         * -v false
-         * 
- * @return how many arguments this option requires - */ - String arity() default ""; - - /** - * Specify a {@code paramLabel} for the option parameter to be used in the usage help message. If omitted, - * picocli uses the field name in fish brackets ({@code '<'} and {@code '>'}) by default. Example: - *
class Example {
-         *     @Option(names = {"-o", "--output"}, paramLabel="FILE", description="path of the output file")
-         *     private File out;
-         *     @Option(names = {"-j", "--jobs"}, arity="0..1", description="Allow N jobs at once; infinite jobs with no arg.")
-         *     private int maxJobs = -1;
-         * }
- *

By default, the above gives a usage help message like the following:

-         * Usage: <main class> [OPTIONS]
-         * -o, --output FILE       path of the output file
-         * -j, --jobs [<maxJobs>]  Allow N jobs at once; infinite jobs with no arg.
-         * 
- * @return name of the option parameter used in the usage help message - */ - String paramLabel() default ""; - - /** Returns whether usage syntax decorations around the {@linkplain #paramLabel() paramLabel} should be suppressed. - * The default is {@code false}: by default, the paramLabel is surrounded with {@code '['} and {@code ']'} characters - * if the value is optional and followed by ellipses ("...") when multiple values can be specified. - * @since 3.6.0 */ - boolean hideParamSyntax() default false; - - /**

- * Optionally specify a {@code type} to control exactly what Class the option parameter should be converted - * to. This may be useful when the field type is an interface or an abstract class. For example, a field can - * be declared to have type {@code java.lang.Number}, and annotating {@code @Option(type=Short.class)} - * ensures that the option parameter value is converted to a {@code Short} before setting the field value. - *

- * For array fields whose component type is an interface or abstract class, specify the concrete component type. - * For example, a field with type {@code Number[]} may be annotated with {@code @Option(type=Short.class)} - * to ensure that option parameter values are converted to {@code Short} before adding an element to the array. - *

- * Picocli will use the {@link ITypeConverter} that is - * {@linkplain #registerConverter(Class, ITypeConverter) registered} for the specified type to convert - * the raw String values before modifying the field value. - *

- * Prior to 2.0, the {@code type} attribute was necessary for {@code Collection} and {@code Map} fields, - * but starting from 2.0 picocli will infer the component type from the generic type's type arguments. - * For example, for a field of type {@code Map} picocli will know the option parameter - * should be split up in key=value pairs, where the key should be converted to a {@code java.util.concurrent.TimeUnit} - * enum value, and the value should be converted to a {@code Long}. No {@code @Option(type=...)} type attribute - * is required for this. For generic types with wildcards, picocli will take the specified upper or lower bound - * as the Class to convert to, unless the {@code @Option} annotation specifies an explicit {@code type} attribute. - *

- * If the field type is a raw collection or a raw map, and you want it to contain other values than Strings, - * or if the generic type's type arguments are interfaces or abstract classes, you may - * specify a {@code type} attribute to control the Class that the option parameter should be converted to. - * @return the type(s) to convert the raw String values - */ - Class[] type() default {}; - - /** - * Optionally specify one or more {@link ITypeConverter} classes to use to convert the command line argument into - * a strongly typed value (or key-value pair for map fields). This is useful when a particular field should - * use a custom conversion that is different from the normal conversion for the field's type. - *

For example, for a specific field you may want to use a converter that maps the constant names defined - * in {@link java.sql.Types java.sql.Types} to the {@code int} value of these constants, but any other {@code int} fields should - * not be affected by this and should continue to use the standard int converter that parses numeric values.

- * @return the type converter(s) to use to convert String values to strongly typed values for this field - * @see CommandLine#registerConverter(Class, ITypeConverter) - */ - Class>[] converter() default {}; - - /** - * Specify a regular expression to use to split option parameter values before applying them to the field. - * All elements resulting from the split are added to the array or Collection. Previously ignored for single-value fields, - * from picocli 4.0 a {@code split} regex can only be specified on multi-value options and positional parameters. - * @return a regular expression to split option parameter values or {@code ""} if the value should not be split - * @see String#split(String) - */ - String split() default ""; - - /** - * Specify the string to display for the {@link #split split} regular expression in the usage help synopsis. - * @since 4.3 */ - String splitSynopsisLabel() default ""; - - /** - * Set {@code hidden=true} if this option should not be included in the usage help message. - * @return whether this option should be excluded from the usage documentation - */ - boolean hidden() default false; - - /** Returns the default value of this option, before splitting and type conversion. - *

To get a {@code null} default value, omit specifying a default value or use the special value {@link Option#NULL_VALUE} - - * for options of type {@code Optional} that will result in the {@code Optional.empty()} - * value being assigned when the option is not specified on the command line.

- * @return a String that (after type conversion) will be used as the value for this option if the option was not specified on the command line - * @see #fallbackValue() - * @since 3.2 */ - String defaultValue() default "__no_default_value__"; - - /** Use this attribute to control for a specific option whether its default value should be shown in the usage - * help message. If not specified, the default value is only shown when the {@link Command#showDefaultValues()} - * is set {@code true} on the command. Use this attribute to specify whether the default value - * for this specific option should always be shown or never be shown, regardless of the command setting. - *

Note that picocli 3.2 allows {@linkplain #description() embedding default values} by specifying the variable - * {@code ${DEFAULT-VALUE}} anywhere in the description that ignores this setting.

- * @return whether this option's default value should be shown in the usage help message - */ - Help.Visibility showDefaultValue() default Help.Visibility.ON_DEMAND; - - /** Use this attribute to specify an {@code Iterable} class that generates completion candidates for this option. - * For map fields, completion candidates should be in {@code key=value} form. - *

- * Completion candidates are used in bash completion scripts generated by the {@code picocli.AutoComplete} class. - * Bash has special completion options to generate file names and host names, and the bash completion scripts - * generated by {@code AutoComplete} delegate to these bash built-ins for {@code @Options} whose {@code type} is - * {@code java.io.File}, {@code java.nio.file.Path} or {@code java.net.InetAddress}. - *

- * For {@code @Options} whose {@code type} is a Java {@code enum}, {@code AutoComplete} can generate completion - * candidates from the type. For other types, use this attribute to specify completion candidates. - *

- * - * @return a class whose instances can iterate over the completion candidates for this option - * @see picocli.CommandLine.IFactory - * @since 3.2 */ - Class> completionCandidates() default NoCompletionCandidates.class; - - /** - * Set {@code interactive=true} to make this option prompt the end user for a value (like a password). - * Only supported for single-value options and {@code char[]} arrays (no collections, maps or other array types). - * When running on Java 6 or greater and {@link Option#echo() echo = false} (the default), - * this will use the {@link Console#readPassword()} API to get a value without echoing input to the console, - * otherwise it will simply read a value from {@code System.in}. - *

- * For passwords, best security practice is to use type {@code char[]} instead of {@code String}, and to to null out the array after use. - *

- * When defined with {@code arity = "0..1"}, the option can also take a value from the command line. - * (The user will still be prompted if no option parameter was specified on the command line.) - * This is useful for commands that need to be run interactively as well as in batch mode. - *

- * @return whether this option prompts the end user for a value to be entered on the command line - * @since 3.5 - */ - boolean interactive() default false; - - /** Use this attribute to control whether user input for an interactive option is echoed to the console or not. - * If {@code echo = true}, the user input is echoed to the console. - * This attribute is ignored when {@code interactive = false} (the default). - * @return whether the user input for an interactive option should be echoed to the console or not - * @see OptionSpec#echo() - * @since 4.6 */ - boolean echo() default false; - - /** Use this attribute to customize the text displayed to the end user for an interactive option when asking for user input. - * When omitted, the displayed text is derived from the option name and the first description line. - * This attribute is ignored when {@code interactive = false} (the default). - * @return the text to display to the end user for an interactive option when asking for user input - * @see OptionSpec#prompt() - * @since 4.6 */ - String prompt() default ""; - - /** ResourceBundle key for this option. If not specified, (and a ResourceBundle {@linkplain Command#resourceBundle() exists for this command}) an attempt - * is made to find the option description using any of the option names (without leading hyphens) as key. - * @see OptionSpec#description() - * @since 3.6 - */ - String descriptionKey() default ""; - - /** - * When {@link Command#sortOptions() @Command(sortOptions = false)} is specified, this attribute can be used to control the order in which options are listed in the usage help message. - * When {@link Command#sortSynopsis() @Command(sortSynopsis = false)} is specified, this attribute controls the order in which options appear in the synopsis of the usage help message. - * @return the position in the options list at which this option should be shown. Options with a lower number are shown before options with a higher number. Gaps are allowed. - * @since 3.9 - */ - int order() default -1; - - /** (Only for boolean options): set this to automatically add a negative version for this boolean option. - * For example, for a {@code --force} option the negative version would be {@code --no-force}, - * and for a {@code -XX:+PrintGCDetails} option, the negative version would be {@code -XX:-PrintGCDetails}. - * The synopsis would show {@code --[no-]force} and {@code -XX:(+|-)PrintGCDetails}, respectively. - *

The form of the negative name can be customized by modifying the regular expressions - * used by {@linkplain RegexTransformer#createDefault() default}, or by replacing the default - * {@link INegatableOptionTransformer} with a custom implementation entirely.

- *

Negative option names used to parse the command line are collected when the command is constructed - * (so any variables in the option names will be resolved at that time). - * Documentation strings for negatable options are generated on demand when the usage help message is shown.

- * @see CommandLine#getNegatableOptionTransformer() - * @see CommandLine#setNegatableOptionTransformer(INegatableOptionTransformer) - * @since 4.0 */ - boolean negatable() default false; - - /** Determines on which command(s) this option exists: on this command only (the default), or - * whether this is a "global" option that is applied to this command and all subcommands, sub-subcommands, etc. - * @since 4.3 - */ - ScopeType scope() default ScopeType.LOCAL; - /** - * For options with an optional parameter (for example, {@code arity = "0..1"}), this value is assigned to the annotated element - * if the option is specified on the command line without an option parameter. - *

- * This is different from the {@link #defaultValue()}, which is assigned if the option is not specified at all on the command line. - *

- * Using a {@code fallbackValue} allows applications to distinguish between

- *
    - *
  • option was not specified on the command line (default value assigned)
  • - *
  • option was specified without parameter on the command line (fallback value assigned)
  • - *
  • option was specified with parameter on the command line (command line argument value assigned)
  • - *
- *

This is useful to define options that can function as a boolean "switch" - * and optionally allow users to provide a (strongly typed) extra parameter value. - *

- *

Use the special value {@link Option#NULL_VALUE} to specify {@code null} - - * for options of type {@code Optional} that will result in the {@code Optional.empty()} - * value being assigned when the option name is specified without a parameter on the command line.

- * @see OptionSpec#fallbackValue() - * @since 4.0 */ - String fallbackValue() default ""; - - /** For options of type Map, setting the {@code mapFallbackValue} to any value allows end user - * to specify key-only parameters for this option. For example, {@code -Dkey} instead of {@code -Dkey=value}. - *

The value specified in this annotation is the value that is put into the Map for the user-specified key. - * Use the special value {@link Option#NULL_VALUE} to specify {@code null} - - * for maps of type {@code Map>} that will result in {@code Optional.empty()} - * values in the map when only the key is specified.

- *

If no {@code mapFallbackValue} is set, key-only Map parameters like {@code -Dkey} - * are considered invalid user input and cause a {@link ParameterException} to be thrown.

- * @see ArgSpec#mapFallbackValue() - * @since 4.6 */ - String mapFallbackValue() default ArgSpec.UNSPECIFIED; - - /** - * Optionally specify a custom {@code IParameterConsumer} to temporarily suspend picocli's parsing logic - * and process one or more command line arguments in a custom manner. - * This may be useful when passing arguments through to another program. - * @since 4.0 */ - Class parameterConsumer() default NullParameterConsumer.class; - - /** Returns the preprocessor for this option. - * @see IParameterPreprocessor - * @since 4.6 */ - Class preprocessor() default NoOpParameterPreprocessor.class; - } - /** - *

- * Fields annotated with {@code @Parameters} will be initialized with positional parameters. By specifying the - * {@link #index()} attribute you can pick the exact position or a range of positional parameters to apply. If no - * index is specified, the field will get all positional parameters (and so it should be an array or a collection). - *

- * In the case of command methods (annotated with {@code @Command}), method parameters may be annotated with {@code @Parameters}, - * but are are considered positional parameters by default, unless they are annotated with {@code @Option}. - *

- * Command class example: - *

- *
-     * import static picocli.CommandLine.*;
-     *
-     * public class MyCalcParameters {
-     *     @Parameters(description = "Any number of input numbers")
-     *     private List<BigDecimal> files = new ArrayList<BigDecimal>();
-     *
-     *     @Option(names = { "-h", "--help" }, usageHelp = true, description = "Display this help and exit")
-     *     private boolean help;
-     * }
-     * 

- * A field cannot be annotated with both {@code @Parameters} and {@code @Option} or a {@code ParameterException} - * is thrown.

- */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - public @interface Parameters { - /** Special value that can be used in some annotation attributes to designate {@code null}. - * @see Parameters#defaultValue() - * @see Parameters#mapFallbackValue() - * @since 4.6 */ - public static final String NULL_VALUE = ArgSpec.NULL_VALUE; - /** Specify an index ("0", or "1", etc.) to pick which of the command line arguments should be assigned to this - * field. For array or Collection fields, you can also specify an index range ("0..3", or "2..*", etc.) to assign - * a subset of the command line arguments to this field. The default is "*", meaning all command line arguments. - * @return an index or range specifying which of the command line arguments should be assigned to this field - */ - String index() default ""; - - /** Description of the parameter(s), used when generating the usage documentation. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}. - *

- * The description may contain variables that are rendered when help is requested. - * The string {@code ${DEFAULT-VALUE}} is replaced with the default value of the positional parameter. This is regardless of - * the command's {@link Command#showDefaultValues() showDefaultValues} setting or the positional parameter's {@link #showDefaultValue() showDefaultValue} setting. - * The string {@code ${COMPLETION-CANDIDATES}} is replaced with the completion candidates generated by - * {@link #completionCandidates()} in the description for this positional parameter. - * Also, embedded {@code %n} newline markers are converted to actual newlines. - *

- * @return the description of the parameter(s) - * @see Variable Interpolation section of the user manual - */ - String[] description() default {}; - - /** - * Specifies the minimum number of required parameters and the maximum number of accepted parameters. If a - * positive arity is declared, and the user specifies an insufficient number of parameters on the command line, - * {@link MissingParameterException} is thrown by the {@link #parse(String...)} method. - *

The default depends on the type of the parameter: booleans require no parameters, arrays and Collections - * accept zero to any number of parameters, and any other type accepts one parameter.

- *

For single-value parameters, setting {@code arity = "0..1"} makes a positional parameter optional, while setting {@code arity = "1"} makes it required.

- *

Required parameters that are part of a {@linkplain ArgGroup group} are required within the group, not required within the command: - * the group's {@linkplain ArgGroup#multiplicity() multiplicity} determines whether the group itself is required or optional.

- * @return the range of minimum and maximum parameters accepted by this command - */ - String arity() default ""; - - /** - * Specify a {@code paramLabel} for the parameter to be used in the usage help message. If omitted, - * picocli uses the field name in fish brackets ({@code '<'} and {@code '>'}) by default. Example: - *
class Example {
-         *     @Parameters(paramLabel="FILE", description="path of the input FILE(s)")
-         *     private File[] inputFiles;
-         * }
- *

By default, the above gives a usage help message like the following:

-         * Usage: <main class> [FILE...]
-         * [FILE...]       path of the input FILE(s)
-         * 
- * @return name of the positional parameter used in the usage help message - */ - String paramLabel() default ""; - - /** Returns whether usage syntax decorations around the {@linkplain #paramLabel() paramLabel} should be suppressed. - * The default is {@code false}: by default, the paramLabel is surrounded with {@code '['} and {@code ']'} characters - * if the value is optional and followed by ellipses ("...") when multiple values can be specified. - * @since 3.6.0 */ - boolean hideParamSyntax() default false; - - /** - *

- * Optionally specify a {@code type} to control exactly what Class the positional parameter should be converted - * to. This may be useful when the field type is an interface or an abstract class. For example, a field can - * be declared to have type {@code java.lang.Number}, and annotating {@code @Parameters(type=Short.class)} - * ensures that the positional parameter value is converted to a {@code Short} before setting the field value. - *

- * For array fields whose component type is an interface or abstract class, specify the concrete component type. - * For example, a field with type {@code Number[]} may be annotated with {@code @Parameters(type=Short.class)} - * to ensure that positional parameter values are converted to {@code Short} before adding an element to the array. - *

- * Picocli will use the {@link ITypeConverter} that is - * {@linkplain #registerConverter(Class, ITypeConverter) registered} for the specified type to convert - * the raw String values before modifying the field value. - *

- * Prior to 2.0, the {@code type} attribute was necessary for {@code Collection} and {@code Map} fields, - * but starting from 2.0 picocli will infer the component type from the generic type's type arguments. - * For example, for a field of type {@code Map} picocli will know the positional parameter - * should be split up in key=value pairs, where the key should be converted to a {@code java.util.concurrent.TimeUnit} - * enum value, and the value should be converted to a {@code Long}. No {@code @Parameters(type=...)} type attribute - * is required for this. For generic types with wildcards, picocli will take the specified upper or lower bound - * as the Class to convert to, unless the {@code @Parameters} annotation specifies an explicit {@code type} attribute. - *

- * If the field type is a raw collection or a raw map, and you want it to contain other values than Strings, - * or if the generic type's type arguments are interfaces or abstract classes, you may - * specify a {@code type} attribute to control the Class that the positional parameter should be converted to. - * @return the type(s) to convert the raw String values - */ - Class[] type() default {}; - - /** - * Optionally specify one or more {@link ITypeConverter} classes to use to convert the command line argument into - * a strongly typed value (or key-value pair for map fields). This is useful when a particular field should - * use a custom conversion that is different from the normal conversion for the field's type. - *

For example, for a specific field you may want to use a converter that maps the constant names defined - * in {@link java.sql.Types java.sql.Types} to the {@code int} value of these constants, but any other {@code int} fields should - * not be affected by this and should continue to use the standard int converter that parses numeric values.

- * @return the type converter(s) to use to convert String values to strongly typed values for this field - * @see CommandLine#registerConverter(Class, ITypeConverter) - */ - Class>[] converter() default {}; - - /** - * Specify a regular expression to use to split positional parameter values before applying them to the field. - * All elements resulting from the split are added to the array or Collection. Previously ignored for single-value fields, - * from picocli 4.0 a {@code split} regex can only be specified on multi-value options and positional parameters. - * @return a regular expression to split operand values or {@code ""} if the value should not be split - * @see String#split(String) - */ - String split() default ""; - - /** - * Specify a string to show the split option parameter values in usage - * @since 4.3 - */ - String splitSynopsisLabel() default ""; - - /** - * Set {@code hidden=true} if this parameter should not be included in the usage message. - * @return whether this parameter should be excluded from the usage message - */ - boolean hidden() default false; - - /** Returns the default value of this positional parameter, before splitting and type conversion. - *

To get a {@code null} default value, omit specifying a default value or use the special value {@link Parameters#NULL_VALUE} - - * for positional parameters of type {@code Optional} that will result in the {@code Optional.empty()} - * value being assigned when the positional parameters is not specified on the command line.

- * @return a String that (after type conversion) will be used as the value for this positional parameter if no value was specified on the command line - * @since 3.2 */ - String defaultValue() default "__no_default_value__"; - - /** Use this attribute to control for a specific positional parameter whether its default value should be shown in the usage - * help message. If not specified, the default value is only shown when the {@link Command#showDefaultValues()} - * is set {@code true} on the command. Use this attribute to specify whether the default value - * for this specific positional parameter should always be shown or never be shown, regardless of the command setting. - *

Note that picocli 3.2 allows {@linkplain #description() embedding default values} by specifying the variable - * {@code ${DEFAULT-VALUE}} anywhere in the description that ignores this setting.

- * @return whether this positional parameter's default value should be shown in the usage help message - */ - Help.Visibility showDefaultValue() default Help.Visibility.ON_DEMAND; - - /** Use this attribute to specify an {@code Iterable} class that generates completion candidates for - * this positional parameter. For map fields, completion candidates should be in {@code key=value} form. - *

- * Completion candidates are used in bash completion scripts generated by the {@code picocli.AutoComplete} class. - * Unfortunately, {@code picocli.AutoComplete} is not very good yet at generating completions for positional parameters. - *

- * - * @return a class whose instances can iterate over the completion candidates for this positional parameter - * @see picocli.CommandLine.IFactory - * @since 3.2 */ - Class> completionCandidates() default NoCompletionCandidates.class; - - /** - * Set {@code interactive=true} if this positional parameter will prompt the end user for a value (like a password). - * Only supported for single-value positional parameters (not arrays, collections or maps). - * When running on Java 6 or greater and {@link Option#echo() echo = false} (the default), - * this will use the {@link Console#readPassword()} API to get a value without echoing input to the console, - * otherwise it will simply read a value from {@code System.in}. - * @return whether this positional parameter prompts the end user for a value to be entered on the command line - * @since 3.5 - */ - boolean interactive() default false; - - /** Use this attribute to control whether user input for an interactive positional parameter is echoed to the console or not. - * If {@code echo = true}, the user input is echoed to the console. - * This attribute is ignored when {@code interactive = false} (the default). - * @return whether the user input for an interactive positional parameter should be echoed to the console or not - * @see PositionalParamSpec#echo() - * @since 4.6 */ - boolean echo() default false; - - /** Use this attribute to customize the text displayed to the end user for an interactive positional parameter when asking for user input. - * When omitted, the displayed text is derived from the positional parameter's - * position (index) and the first description line. - * This attribute is ignored when {@code interactive = false} (the default). - * @return the text to display to the end user for an interactive positional parameter when asking for user input - * @see PositionalParamSpec#prompt() - * @since 4.6 */ - String prompt() default ""; - - /** ResourceBundle key for this option. If not specified, (and a ResourceBundle {@linkplain Command#resourceBundle() exists for this command}) an attempt - * is made to find the positional parameter description using {@code paramLabel() + "[" + index() + "]"} as key. - * - * @see PositionalParamSpec#description() - * @since 3.6 - */ - String descriptionKey() default ""; - - /** Determines on which command(s) this positional parameter exists: on this command only (the default), or - * whether this is a "global" parameter that is applied to this command and all subcommands, sub-subcommands, etc. - * @since 4.3 - */ - ScopeType scope() default ScopeType.LOCAL; - /** - * Optionally specify a custom {@code IParameterConsumer} to temporarily suspend picocli's parsing logic - * and process one or more command line arguments in a custom manner. - * @since 4.0 */ - Class parameterConsumer() default NullParameterConsumer.class; - - /** For positional parameters of type Map, setting the {@code mapFallbackValue} to any value allows end user - * to specify key-only parameters for this parameter. For example, {@code key} instead of {@code key=value}. - *

The value specified in this annotation is the value that is put into the Map for the user-specified key. - * Use the special value {@link Parameters#NULL_VALUE} to specify {@code null} - - * for maps of type {@code Map>} that will result in {@code Optional.empty()} - * values in the map when only the key is specified.

- *

If no {@code mapFallbackValue} is set, key-only Map parameters like {@code -Dkey} - * are considered invalid user input and cause a {@link ParameterException} to be thrown.

- * @see ArgSpec#mapFallbackValue() - * @since 4.6 */ - String mapFallbackValue() default ArgSpec.UNSPECIFIED; - - /** Returns the preprocessor for this positional parameter. - * @see IParameterPreprocessor - * @since 4.6 */ - Class preprocessor() default NoOpParameterPreprocessor.class; - } - - /** - *

- * Fields annotated with {@code @ParentCommand} will be initialized with the parent command of the current subcommand. - * If the current command does not have a parent command, this annotation has no effect. - *

- * Parent commands often define options that apply to all the subcommands. - * This annotation offers a convenient way to inject a reference to the parent command into a subcommand, so the - * subcommand can access its parent options. For example: - *

-     * @Command(name = "top", subcommands = Sub.class)
-     * class Top implements Runnable {
-     *
-     *     @Option(names = {"-d", "--directory"}, description = "this option applies to all subcommands")
-     *     File baseDirectory;
-     *
-     *     public void run() { System.out.println("Hello from top"); }
-     * }
-     *
-     * @Command(name = "sub")
-     * class Sub implements Runnable {
-     *
-     *     @ParentCommand
-     *     private Top parent;
-     *
-     *     public void run() {
-     *         System.out.println("Subcommand: parent command 'directory' is " + parent.baseDirectory);
-     *     }
-     * }
-     * 
- * @since 2.2 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface ParentCommand { } - - /** - * Fields annotated with {@code @Unmatched} will be initialized with the list of unmatched command line arguments, if any. - * If this annotation is found, picocli automatically sets {@linkplain CommandLine#setUnmatchedArgumentsAllowed(boolean) unmatchedArgumentsAllowed} to {@code true}. - * @see CommandLine#isUnmatchedArgumentsAllowed() - * @since 3.0 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Unmatched { } - - /** - *

- * Fields annotated with {@code @Mixin} are "expanded" into the current command: {@link Option @Option} and - * {@link Parameters @Parameters} in the mixin class are added to the options and positional parameters of this command. - * A {@link DuplicateOptionAnnotationsException} is thrown if any of the options in the mixin has the same name as - * an option in this command. - *

- * The {@code Mixin} annotation provides a way to reuse common options and parameters without subclassing. For example: - *

-     * @Command(name="HelloWorld")
-     * class HelloWorld implements Runnable {
-     *
-     *     // adds the --help and --version options to this command
-     *     @Mixin
-     *     private HelpOptions options = new HelpOptions();
-     *
-     *     @Option(names = {"-u", "--userName"}, required = true, description = "The user name")
-     *     String userName;
-     *
-     *     public void run() { System.out.println("Hello, " + userName); }
-     * }
-     *
-     * // Common reusable help options.
-     * class HelpOptions {
-     *
-     *     @Option(names = { "-h", "--help"}, usageHelp = true, description = "Display this help and exit")
-     *     private boolean help;
-     *
-     *     @Option(names = { "-V", "--version"}, versionHelp = true, description = "Display version info and exit")
-     *     private boolean versionHelp;
-     * }
-     * 
- * @since 3.0 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.PARAMETER}) - public @interface Mixin { - /** Optionally specify a name that the mixin object can be retrieved with from the {@code CommandSpec}. - * If not specified the name of the annotated field is used. - * @return a String to register the mixin object with, or an empty String if the name of the annotated field should be used */ - String name() default ""; - } - /** - * Fields annotated with {@code @Spec} will be initialized with the {@code CommandSpec} for the command the field is part of. Example usage: - *
-     * class InjectSpecExample implements Runnable {
-     *     @Spec CommandSpec commandSpec;
-     *     //...
-     *     public void run() {
-     *         // do something with the injected objects
-     *     }
-     * }
-     * 
- * @since 3.2 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.METHOD}) - public @interface Spec { - /** Identifies what kind of {@code CommandSpec} should be injected. - * @since 4.3.0 */ - enum Target { - /** Injects the {@code CommandSpec} of the command where this {@code @Spec}-annotated program element is declared. */ - SELF, - /** Injects the {@code CommandSpec} of the "mixee" command that receives the options and other command elements defined here, - * or {@code null} if this commands is not {@linkplain Mixin mixed into} another command. - * The "mixee" command has a {@code @Mixin}-annotated program element with the type of the class where this {@code @Spec}-annotated program element is declared. */ - MIXEE} - - /** Whether to inject the {@code CommandSpec} of this command (the default) or the {@code CommandSpec} - * of the "mixee" command that receives the options and other command elements defined here. - * @see Mixin - * @since 4.3.0 */ - Target value() default Target.SELF; - } - - /** - *

Annotate your class with {@code @Command} when you want more control over the format of the generated help - * message. From 3.6, methods can also be annotated with {@code @Command}, where the method parameters define the - * command options and positional parameters. - *

-     * @Command(name              = "Encrypt", mixinStandardHelpOptions = true,
-     *        description         = "Encrypt FILE(s), or standard input, to standard output or to the output file.",
-     *        version             = "Encrypt version 1.0",
-     *        footer              = "Copyright (c) 2017",
-     *        exitCodeListHeading = "Exit Codes:%n",
-     *        exitCodeList        = { " 0:Successful program execution.",
-     *                                "64:Invalid input: an unknown option or invalid parameter was specified.",
-     *                                "70:Execution exception: an exception occurred while executing the business logic."}
-     *        )
-     * public class Encrypt {
-     *     @Parameters(paramLabel = "FILE", description = "Any number of input files")
-     *     private List<File> files = new ArrayList<File>();
-     *
-     *     @Option(names = { "-o", "--out" }, description = "Output file (default: print to console)")
-     *     private File outputFile;
-     *
-     *     @Option(names = { "-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting. Multiple -v options increase the verbosity.")
-     *     private boolean[] verbose;
-     * }
- *

- * The structure of a help message looks like this: - *

    - *
  • [header]
  • - *
  • [synopsis]: {@code Usage: [OPTIONS] [FILE...]}
  • - *
  • [description]
  • - *
  • [parameter list]: {@code [FILE...] Any number of input files}
  • - *
  • [option list]: {@code -h, --help prints this help message and exits}
  • - *
  • [exit code list]
  • - *
  • [footer]
  • - *
*/ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.TYPE, ElementType.LOCAL_VARIABLE, ElementType.FIELD, ElementType.PACKAGE, ElementType.METHOD}) - public @interface Command { - /** Program name to show in the synopsis. If omitted, {@code "
"} is used. - * For {@linkplain #subcommands() declaratively added} subcommands, this attribute is also used - * by the parser to recognize subcommands in the command line arguments. - * @return the program name to show in the synopsis - * @see CommandSpec#name() - * @see Help#commandName() */ - String name() default CommandSpec.DEFAULT_COMMAND_NAME; - - /** Alternative command names by which this subcommand is recognized on the command line. - * @return one or more alternative command names - * @since 3.1 */ - String[] aliases() default {}; - - /** A list of classes to instantiate and register as subcommands. When registering subcommands declaratively - * like this, you don't need to call the {@link CommandLine#addSubcommand(String, Object)} method. For example, this: - *
-         * @Command(subcommands = {
-         *         GitStatus.class,
-         *         GitCommit.class,
-         *         GitBranch.class })
-         * public class Git { ... }
-         *
-         * CommandLine commandLine = new CommandLine(new Git());
-         * 
is equivalent to this: - *
-         * // alternative: programmatically add subcommands.
-         * // NOTE: in this case there should be no `subcommands` attribute on the @Command annotation.
-         * @Command public class Git { ... }
-         *
-         * CommandLine commandLine = new CommandLine(new Git())
-         *         .addSubcommand("status",   new GitStatus())
-         *         .addSubcommand("commit",   new GitCommit())
-         *         .addSubcommand("branch",   new GitBranch());
-         * 
- * Applications may be interested in the following built-in commands in picocli - * that can be used as subcommands: - *
    - *
  • {@link HelpCommand} - a {@code help} subcommand that prints help on the following or preceding command
  • - *
  • {@link AutoComplete.GenerateCompletion} - a {@code generate-completion} subcommand that prints a Bash/ZSH completion script for its parent command, so that clients can install autocompletion in one line by running {@code source <(parent-command generate-completion)} in the shell
  • - *
- * @return the declaratively registered subcommands of this command, or an empty array if none - * @see CommandLine#addSubcommand(String, Object) - * @see HelpCommand - * @since 0.9.8 - */ - Class[] subcommands() default {}; - - /** Returns whether the subcommands of this command are repeatable, that is, whether such subcommands can - * occur multiple times and may be followed by sibling commands instead of only by child commands of the subcommand. - * @since 4.2 */ - boolean subcommandsRepeatable() default false; - - /** Specify whether methods annotated with {@code @Command} should be registered as subcommands of their - * enclosing {@code @Command} class. - * The default is {@code true}. For example: - *
-         * @Command
-         * public class Git {
-         *     @Command
-         *     void status() { ... }
-         * }
-         *
-         * CommandLine git = new CommandLine(new Git());
-         * 
is equivalent to this: - *
-         * // don't add command methods as subcommands automatically
-         * @Command(addMethodSubcommands = false)
-         * public class Git {
-         *     @Command
-         *     void status() { ... }
-         * }
-         *
-         * // add command methods as subcommands programmatically
-         * CommandLine git = new CommandLine(new Git());
-         * CommandLine status = new CommandLine(CommandLine.getCommandMethods(Git.class, "status").get(0));
-         * git.addSubcommand("status", status);
-         * 
- * @return whether methods annotated with {@code @Command} should be registered as subcommands - * @see CommandLine#addSubcommand(String, Object) - * @see CommandLine#getCommandMethods(Class, String) - * @see CommandSpec#addMethodSubcommands() - * @since 3.6.0 */ - boolean addMethodSubcommands() default true; - - /** String that separates options from option parameters. Default is {@code "="}. Spaces are also accepted. - * @return the string that separates options from option parameters, used both when parsing and when generating usage help - * @see CommandLine#setSeparator(String) */ - String separator() default "="; - - /** Version information for this command, to print to the console when the user specifies an - * {@linkplain Option#versionHelp() option} to request version help. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- *

This is not part of the usage help message.

- * - * @return a string or an array of strings with version information about this command (each string in the array is displayed on a separate line). - * @since 0.9.8 - * @see CommandLine#printVersionHelp(PrintStream) - */ - String[] version() default {}; - - /** Class that can provide version information dynamically at runtime. An implementation may return version - * information obtained from the JAR manifest, a properties file or some other source. - * @return a Class that can provide version information dynamically at runtime - * @since 2.2 */ - Class versionProvider() default NoVersionProvider.class; - - /** - * Adds the standard {@code -h} and {@code --help} {@linkplain Option#usageHelp() usageHelp} options and {@code -V} - * and {@code --version} {@linkplain Option#versionHelp() versionHelp} options to the options of this command. - *

- * Note that if no {@link #version()} or {@link #versionProvider()} is specified, the {@code --version} option will not print anything. - *

- * For {@linkplain #resourceBundle() internationalization}: the help option has {@code descriptionKey = "mixinStandardHelpOptions.help"}, - * and the version option has {@code descriptionKey = "mixinStandardHelpOptions.version"}. - *

- * @return whether the auto-help mixin should be added to this command - * @since 3.0 */ - boolean mixinStandardHelpOptions() default false; - - /** Set this attribute to {@code true} if this subcommand is a help command, and required options and positional - * parameters of the parent command should not be validated. If a subcommand marked as {@code helpCommand} is - * specified on the command line, picocli will not validate the parent arguments (so no "missing required - * option" errors) and the {@link CommandLine#printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi)} method will return {@code true}. - * @return {@code true} if this subcommand is a help command and picocli should not check for missing required - * options and positional parameters on the parent command - * @since 3.0 */ - boolean helpCommand() default false; - - /** Set the heading preceding the header section. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the header section - * @see UsageMessageSpec#headerHeading() - * @see Help#headerHeading(Object...) */ - String headerHeading() default ""; - - /** Optional summary description of the command, shown before the synopsis. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return summary description of the command - * @see UsageMessageSpec#header() - * @see Help#header(Object...) */ - String[] header() default {}; - - /** Set the heading preceding the synopsis text. The default heading is {@code "Usage: "} (without a line break between the heading and the synopsis text). - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the synopsis text - * @see Help#synopsisHeading(Object...) */ - String synopsisHeading() default "Usage: "; - - /** Specify {@code true} to generate an abbreviated synopsis like {@code "
[OPTIONS] [PARAMETERS...] [COMMAND]"}. - * By default, a detailed synopsis with individual option names and parameters is generated. - * @return whether the synopsis should be abbreviated - * @see Help#abbreviatedSynopsis() - * @see Help#detailedSynopsis(int, Comparator, boolean) */ - boolean abbreviateSynopsis() default false; - - /** Specify one or more custom synopsis lines to display instead of an auto-generated synopsis. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return custom synopsis text to replace the auto-generated synopsis - * @see Help#customSynopsis(Object...) */ - String[] customSynopsis() default {}; - - /** - * Specify the String to show in the synopsis for the subcommands of this command. The default is - * {@code "[COMMAND]"}. Ignored if this command has no {@linkplain #subcommands() subcommands}. - * @since 4.0 - */ - String synopsisSubcommandLabel() default "[COMMAND]"; - - /** Set the heading preceding the description section. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the description section - * @see Help#descriptionHeading(Object...) */ - String descriptionHeading() default ""; - - /** Optional text to display between the synopsis line(s) and the list of options. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return description of this command - * @see Help#description(Object...) */ - String[] description() default {}; - - /** Set the heading preceding the parameters list. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the parameters list - * @see Help#parameterListHeading(Object...) */ - String parameterListHeading() default ""; - - /** Set the heading preceding the options list. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the options list - * @see Help#optionListHeading(Object...) */ - String optionListHeading() default ""; - - /** Specify {@code false} to show Options in declaration order in the option list of the usage help message (or to sort options by their {@linkplain Option#order() order index} if set). - * Note that picocli cannot reliably detect declaration order in commands that have both {@code @Option}-annotated methods and {@code @Option}-annotated fields. - * The default ({@code true}) is to sort alphabetically. - * @return whether options should be shown in alphabetic order. */ - boolean sortOptions() default true; - - /** Specify {@code false} to show options in declaration order in the synopsis of the usage help message (or to sort options by their {@linkplain Option#order() order index} if set). - * Note that picocli cannot reliably detect declaration order in commands that have both {@code @Option}-annotated methods and {@code @Option}-annotated fields. - * The default ({@code true}) is to sort alphabetically. - * @return whether options in the synopsis should be shown in alphabetic order. - * @since 4.7.5 */ - boolean sortSynopsis() default true; - - /** Prefix required options with this character in the options list. The default is no marker: the synopsis - * indicates which options and parameters are required. - * @return the character to show in the options list to mark required options */ - char requiredOptionMarker() default ' '; - - /** Class that can provide default values dynamically at runtime. An implementation may return default - * value obtained from a configuration file like a properties file or some other source. - *

- * Applications may be interested in the {@link PropertiesDefaultProvider} built-in default provider - * that allows end users to maintain their own default values for options and positional parameters, - * which may override the defaults that are hard-coded in the application. - *

- * @return a Class that can provide default values dynamically at runtime - * @since 3.6 */ - Class defaultValueProvider() default NoDefaultProvider.class; - - /** Specify {@code true} to show default values in the description column of the options list (except for - * boolean options). False by default. - *

Note that picocli 3.2 allows {@linkplain Option#description() embedding default values} anywhere in the - * option or positional parameter description that ignores this setting.

- * @return whether the default values for options and parameters should be shown in the description column */ - boolean showDefaultValues() default false; - - /** Specify {@code true} to show a {@code [@...]} entry - * in the synopsis and parameter list of the usage help message. - * (The entry is not shown if {@linkplain CommandLine#isExpandAtFiles() expanding parameter files} is disabled.) - * @since 4.2 */ - boolean showAtFileInUsageHelp() default false; - - /** Specify {@code true} to show a {@code [--]} "End of options" entry - * in the synopsis and option list of the usage help message. - * @since 4.3 */ - boolean showEndOfOptionsDelimiterInUsageHelp() default false; - - /** Set the heading preceding the subcommands list. The default heading is {@code "Commands:%n"} (with a line break at the end). - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the subcommands list - * @see Help#commandListHeading(Object...) */ - String commandListHeading() default "Commands:%n"; - - /** Set the heading preceding the footer section. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return the heading preceding the footer section - * @see Help#footerHeading(Object...) */ - String footerHeading() default ""; - - /** Optional text to display after the list of options. Each element of the array is rendered on a separate line. - *

May contain embedded {@linkplain java.util.Formatter format specifiers} like {@code %n} line separators. Literal percent {@code '%'} characters must be escaped with another {@code %}.

- * @return text to display after the list of options - * @see Help#footer(Object...) */ - String[] footer() default {}; - - /** - * Set {@code hidden=true} if this command should not be included in the list of commands in the usage help of the parent command. - * @return whether this command should be excluded from the usage message - * @since 3.0 - */ - boolean hidden() default false; - - /** Set the base name of the ResourceBundle to find option and positional parameters descriptions, as well as - * usage help message sections and section headings.

See {@link Messages} for more details and an example.

- * @return the base name of the ResourceBundle for usage help strings - * @see ArgSpec#messages() - * @see UsageMessageSpec#messages() - * @see CommandSpec#resourceBundle() - * @see CommandLine#setResourceBundle(ResourceBundle) - * @since 3.6 - */ - String resourceBundle() default ""; - - /** Set the {@link UsageMessageSpec#width(int) usage help message width}. The default is 80. - * @see UsageMessageSpec#width() - * @since 3.7 - */ - int usageHelpWidth() default 80; - - /** If {@code true}, picocli will attempt to detect the terminal width and adjust the usage help message accordingly. - * End users may enable this by setting system property {@code "picocli.usage.width"} to {@code AUTO}, - * and may disable this by setting this system property to a {@linkplain UsageMessageSpec#width() numeric value}. - * This feature requires Java 7 or greater. The default is {@code false} - * @see UsageMessageSpec#autoWidth() - * @since 4.0 */ - boolean usageHelpAutoWidth() default false; - - /** Exit code for successful termination. {@value picocli.CommandLine.ExitCode#OK} by default. - * @see #execute(String...) - * @since 4.0 */ - int exitCodeOnSuccess() default ExitCode.OK; - - /** Exit code for successful termination after printing usage help on user request. {@value picocli.CommandLine.ExitCode#OK} by default. - * @see #execute(String...) - * @since 4.0 */ - int exitCodeOnUsageHelp() default ExitCode.OK; - - /** Exit code for successful termination after printing version help on user request. {@value picocli.CommandLine.ExitCode#OK} by default. - * @see #execute(String...) - * @since 4.0 */ - int exitCodeOnVersionHelp() default ExitCode.OK; - - /** Exit code for command line usage error. {@value picocli.CommandLine.ExitCode#USAGE} by default. - * @see #execute(String...) - * @since 4.0 */ - int exitCodeOnInvalidInput() default ExitCode.USAGE; - - /** Exit code signifying that an exception occurred when invoking the Runnable, Callable or Method user object of a command. - * {@value picocli.CommandLine.ExitCode#SOFTWARE} by default. - * @see #execute(String...) - * @since 4.0 */ - int exitCodeOnExecutionException() default ExitCode.SOFTWARE; - - /** Set the heading preceding the exit codes section, may contain {@code "%n"} line separators. {@code ""} (empty string) by default. - * @see Help#exitCodeListHeading(Object...) - * @since 4.0 */ - String exitCodeListHeading() default ""; - - /** Set the values to be displayed in the exit codes section as a list of {@code "key:value"} pairs: - * keys are exit codes, values are descriptions. Descriptions may contain {@code "%n"} line separators. - *

For example:

- *
-         * @Command(exitCodeListHeading = "Exit Codes:%n",
-         *          exitCodeList = { " 0:Successful program execution.",
-         *                           "64:Invalid input: an unknown option or invalid parameter was specified.",
-         *                           "70:Execution exception: an exception occurred while executing the business logic."})
-         * 
- * @since 4.0 */ - String[] exitCodeList() default {}; - - /** Returns whether subcommands inherit their attributes from this parent command. - * @since 4.6 */ - ScopeType scope() default ScopeType.LOCAL; - - /** Returns the model transformer for this command. - * @since 4.6 */ - Class modelTransformer() default NoOpModelTransformer.class; - - /** Returns the preprocessor for this command. - * @see IParameterPreprocessor - * @since 4.6 */ - Class preprocessor() default NoOpParameterPreprocessor.class; - } - /** A {@code Command} may define one or more {@code ArgGroups}: a group of options, positional parameters or a mixture of the two. - * Groups can be used to: - *
    - *
  • define mutually exclusive arguments. By default, options and positional parameters - * in a group are mutually exclusive. This can be controlled with the {@link #exclusive() exclusive} attribute. - * Picocli will throw a {@link MutuallyExclusiveArgsException} if the command line contains multiple arguments that are mutually exclusive.
  • - *
  • define a set of arguments that must co-occur. Set {@link #exclusive() exclusive = false} - * to define a group of options and positional parameters that must always be specified together. - * Picocli will throw a {@link MissingParameterException MissingParameterException} if not all the options and positional parameters in a co-occurring group are specified together.
  • - *
  • create an option section in the usage help message. - * To be shown in the usage help message, a group needs to have a {@link #heading() heading} (which may come from a {@linkplain #headingKey() resource bundle}). - * Groups without a heading are only used for validation. - * Set {@link #validate() validate = false} for groups whose purpose is only to customize the usage help message.
  • - *
  • define composite repeating argument groups. Groups may contain other groups to create composite groups.
  • - *
- *

Groups may be optional ({@code multiplicity = "0..1"}), required ({@code multiplicity = "1"}), or repeating groups ({@code multiplicity = "0..*"} or {@code multiplicity = "1..*"}). - * For a group of mutually exclusive arguments, making the group required means that one of the arguments in the group must appear on the command line, or a {@link MissingParameterException MissingParameterException} is thrown. - * For a group of co-occurring arguments, all arguments in the group must appear on the command line. - *

- *

Groups can be composed for validation purposes:

- *
    - *
  • When the parent group is mutually exclusive, only one of the subgroups may be present.
  • - *
  • When the parent group is a co-occurring group, all subgroups must be present.
  • - *
  • When the parent group is required, at least one subgroup must be present.
  • - *
- *

- * Below is an example of an {@code ArgGroup} defining a set of dependent options that must occur together. - * All options are required within the group, while the group itself is optional:

- *
-     * public class DependentOptions {
-     *     @ArgGroup(exclusive = false, multiplicity = "0..1")
-     *     Dependent group;
-     *
-     *     static class Dependent {
-     *         @Option(names = "-a", required = true) int a;
-     *         @Option(names = "-b", required = true) int b;
-     *         @Option(names = "-c", required = true) int c;
-     *     }
-     * }
- * @see ArgGroupSpec - * @since 4.0 */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) - public @interface ArgGroup { - /** The heading of this group, used when generating the usage documentation. - * When neither a {@link #heading() heading} nor a {@link #headingKey() headingKey} are specified, - * this group is used for validation only and does not change the usage help message. */ - String heading() default "__no_heading__"; - - /** ResourceBundle key for this group's usage help message section heading. - * When neither a {@link #heading() heading} nor a {@link #headingKey() headingKey} are specified, - * this group is used for validation only and does not change the usage help message. */ - String headingKey() default "__no_heading_key__"; - /** Determines whether this is a mutually exclusive group; {@code true} by default. - * If {@code false}, this is a co-occurring group. Ignored if {@link #validate()} is {@code false}. */ - boolean exclusive() default true; - /** Determines how often this group can be specified on the command line; {@code "0..1"} (optional) by default. - * For a group of mutually exclusive arguments, making the group required {@code multiplicity = "1"} means that - * one of the arguments in the group must appear on the command line, or a MissingParameterException is thrown. - * For a group of co-occurring arguments, making the group required means that all arguments in the group must appear on the command line. - * Ignored if {@link #validate()} is {@code false}. */ - String multiplicity() default "0..1"; - /** Determines whether picocli should validate the rules of this group ({@code true} by default). - * For a mutually exclusive group validation means verifying that no more than one elements of the group is specified on the command line; - * for a co-ocurring group validation means verifying that all elements of the group are specified on the command line. - * Set {@link #validate() validate = false} for groups whose purpose is only to customize the usage help message. - * @see #multiplicity() - * @see #heading() */ - boolean validate() default true; - /** Determines the position in the options list in the usage help message at which this group should be shown. - * Groups with a lower number are shown before groups with a higher number. - * This attribute is only honored for groups that have a {@link #heading() heading} (or a {@link #headingKey() headingKey} with a non-{@code null} resource bundle value).*/ - int order() default -1; - } - /** - *

- * When parsing command line arguments and initializing - * fields annotated with {@link Option @Option} or {@link Parameters @Parameters}, - * String values can be converted to any type for which a {@code ITypeConverter} is registered. - *

- * This interface defines the contract for classes that know how to convert a String into some domain object. - * Custom converters can be registered with the {@link #registerConverter(Class, ITypeConverter)} method. - *

- * Java 8 lambdas make it easy to register custom type converters: - *

- *
-     * commandLine.registerConverter(java.nio.file.Path.class, s -> java.nio.file.Paths.get(s));
-     * commandLine.registerConverter(java.time.Duration.class, s -> java.time.Duration.parse(s));
- *

- * Built-in type converters are pre-registered for the following java 1.5 types: - *

- *
    - *
  • all primitive types
  • - *
  • all primitive wrapper types: Boolean, Byte, Character, Double, Float, Integer, Long, Short
  • - *
  • any enum
  • - *
  • java.io.File
  • - *
  • java.math.BigDecimal
  • - *
  • java.math.BigInteger
  • - *
  • java.net.InetAddress
  • - *
  • java.net.URI
  • - *
  • java.net.URL
  • - *
  • java.nio.charset.Charset
  • - *
  • java.sql.Time
  • - *
  • java.util.Date
  • - *
  • java.util.UUID
  • - *
  • java.util.regex.Pattern
  • - *
  • StringBuilder
  • - *
  • CharSequence
  • - *
  • String
  • - *
- * @param the type of the object that is the result of the conversion - */ - public interface ITypeConverter { - /** - * Converts the specified command line argument value to some domain object. - * @param value the command line argument String value - * @return the resulting domain object - * @throws Exception an exception detailing what went wrong during the conversion. - * Any exception thrown from this method will be caught and shown to the end user. - * An example error message shown to the end user could look something like this: - * {@code Invalid value for option '--some-option': cannot convert 'xxxinvalidinput' to SomeType (java.lang.IllegalArgumentException: Invalid format: must be 'x:y:z' but was 'xxxinvalidinput')} - * @throws TypeConversionException throw this exception to have more control over the error - * message that is shown to the end user when type conversion fails. - * An example message shown to the user could look like this: - * {@code Invalid value for option '--some-option': Invalid format: must be 'x:y:z' but was 'xxxinvalidinput'} - */ - K convert(String value) throws Exception; - } - - /** - * Provides version information for a command. Commands may configure a provider with the - * {@link Command#versionProvider()} annotation attribute. - * @since 2.2 */ - public interface IVersionProvider { - /** - * Returns version information for a command. - * @return version information (each string in the array is displayed on a separate line) - * @throws Exception an exception detailing what went wrong when obtaining version information - */ - String[] getVersion() throws Exception; - } - - /** - * Converter that can be used to signal to picocli that it should use the default converter. - * This can be useful with maps: - *
-     *   class App {
-     *       @Option(names = "-D", converter = {UseDefaultConverter.class, GenericValueConverter.class})
-     *       Map<String, GenericValue<?>> values;
-     *  }
-     * 
- * - * The {@link #convert(String)} method of this class always throws an UnsupportedOperationException. - * @since 4.7.5 - */ - public static final class UseDefaultConverter implements ITypeConverter { - /** Always throws UnsupportedOperationException. - * @throws UnsupportedOperationException always */ - public Object convert(String value) throws Exception { - throw new UnsupportedOperationException("This method should never be called."); - } - } - - private static class NoVersionProvider implements IVersionProvider { - public String[] getVersion() throws Exception { throw new UnsupportedOperationException(); } - } - - /** - * Provides a way to modify how the command model is built. - * This is useful for applications that need to modify the model dynamically depending on the runtime environment. - *

- * Commands may configure a model transformer using the - * {@link Command#modelTransformer()} annotation attribute, or via the - * {@link CommandSpec#modelTransformer(IModelTransformer)} programmatic API. - *

- * Model transformers are invoked only once, after the full command hierarchy is constructed. - * @since 4.6 - */ - public interface IModelTransformer { - /** - * Given an original CommandSpec, return the object that should be used - * instead. Implementors may modify the specified CommandSpec and return it, - * or create a full or partial copy of the specified CommandSpec, and return - * that, or even return a completely new CommandSpec. - *

- * Implementors are free to add or remove options, positional parameters, - * subcommands or modify the command in any other way. - *

- * This method is called once, after the full command hierarchy is - * constructed, and before any command line arguments are parsed. - *

- * @return the CommandSpec to use instead of the specified one - */ - CommandSpec transform(CommandSpec commandSpec); - } - - private static class NoOpModelTransformer implements IModelTransformer { - public CommandSpec transform(CommandSpec commandSpec) { return commandSpec; } - } - - /** - * Provides default value for a command. Commands may configure a provider with the - * {@link Command#defaultValueProvider()} annotation attribute. - * @since 3.6 */ - public interface IDefaultValueProvider { - - /** Returns the default value for an option or positional parameter or {@code null}. - * The returned value is converted to the type of the option/positional parameter - * via the same type converter used when populating this option/positional - * parameter from a command line argument. - * @param argSpec the option or positional parameter, never {@code null} - * @return the default value for the option or positional parameter, or {@code null} if - * this provider has no default value for the specified option or positional parameter - * @throws Exception when there was a problem obtaining the default value - */ - String defaultValue(ArgSpec argSpec) throws Exception; - } - private static class NoDefaultProvider implements IDefaultValueProvider { - public String defaultValue(ArgSpec argSpec) { throw new UnsupportedOperationException(); } - } - - /** - * Options or positional parameters can be assigned a {@code IParameterConsumer} that implements - * custom logic to process the parameters for this option or this position. - * When an option or positional parameter with a custom {@code IParameterConsumer} is matched on the - * command line, picocli's internal parser is temporarily suspended, and this object becomes - * responsible for consuming and processing as many command line arguments as needed. - *

This may be useful when passing through parameters to another command.

- *

Example usage:

- *
-     * @Command(name = "find")
-     * class Find {
-     *     @Option(names = "-exec", parameterConsumer = Find.ExecParameterConsumer.class)
-     *     List<String> list = new ArrayList<String>();
-     *
-     *     static class ExecParameterConsumer implements IParameterConsumer {
-     *         public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
-     *             List<String> list = argSpec.getValue();
-     *             while (!args.isEmpty()) {
-     *                 String arg = args.pop();
-     *                 list.add(arg);
-     *
-     *                 // `find -exec` semantics: stop processing after a ';' or '+' argument
-     *                 if (";".equals(arg) || "+".equals(arg)) {
-     *                     break;
-     *                 }
-     *             }
-     *         }
-     *     }
-     * }
- *

If this interface does not meet your requirements, you may have a look at the more powerful - * and flexible {@link IParameterPreprocessor} interface introduced with picocli 4.6.

- * @see Option#parameterConsumer() - * @see Parameters#parameterConsumer() - * @since 4.0 */ - public interface IParameterConsumer { - /** - * Consumes as many of the specified command line arguments as needed by popping them off - * the specified Stack. Implementors are free to ignore the {@linkplain ArgSpec#arity() arity} - * of the option or positional parameter, they are free to consume arguments that would - * normally be matched as other options of the command, and they are free to consume - * arguments that would normally be matched as an end-of-options delimiter. - *

Implementors are responsible for saving the consumed values; - * if the user object of the option or positional parameter is a Collection - * or a Map, a common approach would be to obtain the current instance via the - * {@link ArgSpec#getValue()}, and add to this instance. If the user object is an - * array, the implementation would need to create a new array that contains the - * old values as well as the newly consumed values, and store this array in the - * user object via the {@link ArgSpec#setValue(Object)}. - *

- * If the user input is invalid, implementations should throw a {@link ParameterException} - * with a message to display to the user. - *

- * When this method returns, the picocli parser will process the remaining arguments on the Stack. - *

- * @param args the command line arguments - * @param argSpec the option or positional parameter for which to consume command line arguments - * @param commandSpec the command that the option or positional parameter belongs to - * @throws ParameterException if the user input is invalid - */ - void consumeParameters(Stack args, ArgSpec argSpec, CommandSpec commandSpec); - } - private static class NullParameterConsumer implements IParameterConsumer { - public void consumeParameters(Stack args, ArgSpec argSpec, CommandSpec commandSpec) { throw new UnsupportedOperationException(); } - } - /** - * Options, positional parameters and commands can be assigned a {@code IParameterPreprocessor} that - * implements custom logic to preprocess the parameters for this option, position or command. - * When an option, positional parameter or command with a custom {@code IParameterPreprocessor} is matched - * on the command line, picocli's internal parser is temporarily suspended, and this custom logic is invoked. - *

- * This custom logic may completely replace picocli's internal parsing for this option, positional parameter - * or command, or it may do some preprocessing before picocli's internal parsing is resumed for this option, - * positional parameter or command. - *

- * The "preprocessing" can include modifying the stack of command line parameters, or modifying the model. - *

- *

This may be useful when disambiguating input for commands that have both a positional parameter and an - * option with an optional parameter.

- *

Example usage:

- *
-     * @Command(name = "edit")
-     * class Edit {
-     *     @Parameters(index = "0", description = "The file to edit.")
-     *     File file;
-     *
-     *     enum Editor { defaultEditor, eclipse, idea, netbeans }
-     *
-     *     @Option(names = "--open", arity = "0..1", preprocessor = Edit.MyPreprocessor.class,
-     *         description = {
-     *             "Optionally specify the editor to use; if omitted the default editor is used. ",
-     *             "Example: edit --open=idea FILE opens IntelliJ IDEA (notice the '=' separator)",
-     *             "         edit --open FILE opens the specified file in the default editor"
-     *         })
-     *     Editor editor = Editor.defaultEditor;
-     *
-     *     static class MyPreprocessor implements IParameterPreprocessor {
-     *         public boolean preprocess(Stack<String> args, CommandSpec commandSpec, ArgSpec argSpec, Map<String, Object> info) {
-     *             // we need to decide whether the next arg is the file to edit or the name of the editor to use...
-     *             if (" ".equals(info.get("separator"))) { // parameter was not attached to option
-     *                 args.push(Editor.defaultEditor.name()); // act as if the user specified --open=defaultEditor
-     *             }
-     *             return false; // picocli's internal parsing is resumed for this option
-     *         }
-     *     }
-     * }
- * @see Option#preprocessor() - * @see Parameters#preprocessor() - * @since 4.6 */ - public interface IParameterPreprocessor { - /** - * Called when either the command, option or positional parameter that has this preprocessor configured was - * recognized by the picocli parser. - *

Implementors are free to modify one or more of the specified command line arguments before they are - * processed by the picocli parser (or by the option's {@link IParameterConsumer parameter consumer}, if one is specified). - *

- * Implementors may optionally consume one or more of the specified command line arguments: - * a return value of {@code true} signals that the preprocessor consumed the parameter: - * picocli should skip further processing of this option or positional parameter, - * and the preprocessor implementation takes responsibility for assigning the - * option or positional parameter a value. A return value of {@code false} means that picocli should - * process the stack as usual for this option or positional parameter, and picocli is responsible for - * assigning this option or positional parameter a value. - *

- * For a command, returning {@code true} signals that the preprocessor takes responsibility for parsing all - * options and positional parameters for this command, and takes responsibility for validating constraints like - * whether all required options and positional parameters were specified. - * A return value of {@code false} means that picocli should - * process the stack as usual for this command, and picocli is responsible for validation. - * Command preprocessors can signal back to the picocli parser when they detect that the user requested version - * information or usage help by putting a value of {@code true} in the specified - * {@code info} map for keys {@code versionHelpRequested} or {@code usageHelpRequested}, respectively. - *

- * If the user input is invalid, implementations should throw a {@link ParameterException} - * with a message to display to the user. - *

- * @param args the remaining command line arguments that follow the matched argument - * (the matched argument is not on the stack anymore) - * @param commandSpec the command or subcommand that was matched (if the specified {@code argSpec} is - * {@code null}), or the command that the matched option or positional parameter belongs to - * @param argSpec the option or positional parameter for which to pre-process command line arguments - * (may be {@code null} when this method is called for a subcommand that was matched) - * @param info a map containing additional information on the current parser state, - * including whether the option parameter was attached to the option name with - * a `=` separator, whether quotes have already been stripped off the option, etc. - * Implementations may modify this map to communicate back to the picocli parser. - * Supported values: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Supported values in the info Map
keyvalid valuestype
separator'' (empty string): attached without separator, ' ' (space): not attached, (any other string): option name was attached to option param with specified separatorjava.lang.String
negated{@code true}: the option or positional parameter is a {@linkplain Option#negatable() negated} option/parameter, {@code false}: the option or positional parameter is not a negated option/parameterjava.lang.Boolean
unquoted{@code true}: quotes surrounding the value have already been stripped off, {@code false}: quotes surrounding the value have not yet been stripped offjava.lang.Boolean
versionHelpRequested{@code true}: version help was requested, {@code false}: version help was not requestedjava.lang.Boolean
usageHelpRequested{@code true}: usage help was requested, {@code false}: usage help was not requestedjava.lang.Boolean
- * @return true if the preprocessor consumed the parameter - * and picocli should skip further processing of the stack for this option or positional parameter; - * false if picocli should continue processing the stack for this option or positional parameter - * @throws ParameterException if the user input is invalid - */ - boolean preprocess(Stack args, CommandSpec commandSpec, ArgSpec argSpec, Map info); - } - private static class NoOpParameterPreprocessor implements IParameterPreprocessor { - public boolean preprocess(Stack args, CommandSpec commandSpec, ArgSpec argSpec, Map info) { return false; } - public boolean equals(Object obj) { return obj instanceof NoOpParameterPreprocessor; } - public int hashCode() { return NoOpParameterPreprocessor.class.hashCode() + 7; } - } - - /** Determines the option name transformation of {@linkplain Option#negatable() negatable} boolean options. - * Making an option negatable has two aspects: - *
    - *
  • the negative form recognized by the parser while parsing the command line
  • - *
  • the documentation string showing both the positive and the negative form in the usage help message
  • - *

- * Additionally, this transformer controls which names of a negatable option are actually negatable: - * for example, by default short options like {@code -v} do not have a negative form, even if the same option's - * long form, {@code --verbose}, may have a negative form, {@code --no-verbose}. - *

- * @see RegexTransformer - * @since 4.0 - */ - public interface INegatableOptionTransformer { - /** Returns the negative form of the specified option name for the parser to recognize when parsing command line arguments. - * @param optionName the option name to create a negative form for, for example {@code --force} - * @param cmd the command that the option is part of - * @return the negative form of the specified option name, for example {@code --no-force} - */ - String makeNegative(String optionName, CommandSpec cmd); - /** Returns the documentation string to show in the synopsis and usage help message for the specified option. - * The returned value should be concise and clearly suggest that both the positive and the negative form are valid option names - * @param optionName the option name to create a documentation string for, for example {@code --force}, or {@code -XX:+