29 import java.io.PrintWriter;
30 import java.lang.module.Configuration;
31 import java.lang.module.ModuleFinder;
32 import java.lang.reflect.Layer;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.text.MessageFormat;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Locale;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.MissingResourceException;
47 import java.util.ResourceBundle;
48 import java.util.Set;
49 import java.util.stream.Stream;
50
51 import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin;
52 import jdk.tools.jlink.plugin.Plugin;
53 import jdk.tools.jlink.plugin.Plugin.Category;
54 import jdk.tools.jlink.builder.DefaultImageBuilder;
55 import jdk.tools.jlink.builder.ImageBuilder;
56 import jdk.tools.jlink.plugin.PluginException;
57 import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
58 import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
59 import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
60 import jdk.tools.jlink.internal.plugins.StripDebugPlugin;
61 import jdk.internal.module.ModulePath;
62
63 /**
64 *
65 * JLink and JImage tools shared helper.
66 */
67 public final class TaskHelper {
68
84 public BadArgs showUsage(boolean b) {
85 showUsage = b;
86 return this;
87 }
88 public final String key;
89 public final Object[] args;
90 public boolean showUsage;
91 }
92
93 public static class Option<T> implements Comparable<T> {
94 public interface Processing<T> {
95
96 void process(T task, String opt, String arg) throws BadArgs;
97 }
98
99 final boolean hasArg;
100 final Processing<T> processing;
101 final boolean hidden;
102 final String name;
103 final String shortname;
104
105 public Option(boolean hasArg, Processing<T> processing, boolean hidden, String name, String shortname) {
106 if (!name.startsWith("--")) {
107 throw new RuntimeException("option name missing --, " + name);
108 }
109 if (!shortname.isEmpty() && !shortname.startsWith("-")) {
110 throw new RuntimeException("short name missing -, " + shortname);
111 }
112
113 this.hasArg = hasArg;
114 this.processing = processing;
115 this.hidden = hidden;
116 this.name = name;
117 this.shortname = shortname;
118 }
119
120 public Option(boolean hasArg, Processing<T> processing, String name, String shortname) {
121 this(hasArg, processing, false, name, shortname);
122 }
123
124 public Option(boolean hasArg, Processing<T> processing, boolean hidden, String name) {
125 this(hasArg, processing, hidden, name, "");
126 }
127
128 public Option(boolean hasArg, Processing<T> processing, String name) {
129 this(hasArg, processing, false, name, "");
130 }
131
132 public boolean isHidden() {
133 return hidden;
134 }
135
136 public boolean matches(String opt) {
137 return opt.equals(name) ||
138 opt.equals(shortname) ||
139 hasArg && opt.startsWith("--") && opt.startsWith(name + "=");
140 }
141
142 public boolean ignoreRest() {
143 return false;
144 }
145
146 void process(T task, String opt, String arg) throws BadArgs {
147 processing.process(task, opt, arg);
148 }
149
150 public String getName() {
151 return name;
152 }
153
154 public String resourceName() {
155 return resourcePrefix() + name.substring(2);
162 public String resourcePrefix() {
163 return "main.opt.";
164 }
165
166 @Override
167 public int compareTo(Object object) {
168 if (!(object instanceof Option<?>)) {
169 throw new RuntimeException("comparing non-Option");
170 }
171
172 Option<?> option = (Option<?>)object;
173
174 return name.compareTo(option.name);
175 }
176
177 }
178
179 private static class PluginOption extends Option<PluginsHelper> {
180 public PluginOption(boolean hasArg,
181 Processing<PluginsHelper> processing, boolean hidden, String name, String shortname) {
182 super(hasArg, processing, hidden, name, shortname);
183 }
184
185 public PluginOption(boolean hasArg,
186 Processing<PluginsHelper> processing, boolean hidden, String name) {
187 super(hasArg, processing, hidden, name, "");
188 }
189
190 public String resourcePrefix() {
191 return "plugin.opt.";
192 }
193 }
194
195 private final class PluginsHelper {
196
197 private Layer pluginsLayer = Layer.boot();
198 private final List<Plugin> plugins;
199 private String lastSorter;
200 private boolean listPlugins;
201 private Path existingImage;
202
203 // plugin to args maps. Each plugin may be used more than once in command line.
204 // Each such occurrence results in a Map of arguments. So, there could be multiple
205 // args maps per plugin instance.
206 private final Map<Plugin, List<Map<String, String>>> pluginToMaps = new HashMap<>();
207 private final List<PluginOption> pluginsOptions = new ArrayList<>();
481 private boolean hasArgument(String optionName) throws BadArgs {
482 Option<?> opt = getOption(optionName);
483 if (opt == null) {
484 opt = pluginOptions.getOption(optionName);
485 if (opt == null) {
486 throw new BadArgs("err.unknown.option", optionName).
487 showUsage(true);
488 }
489 }
490 return opt.hasArg;
491 }
492
493 public boolean shouldListPlugins() {
494 return pluginOptions.listPlugins;
495 }
496
497 private String getPluginsPath(String[] args) throws BadArgs {
498 return null;
499 }
500
501 // used by jimage. Return unhandled arguments like "create", "describe".
502 public List<String> handleOptions(T task, String[] args) throws BadArgs {
503 return handleOptions(task, args, true);
504 }
505
506 // used by jlink. No unhandled arguments like "create", "describe".
507 void handleOptionsNoUnhandled(T task, String[] args) throws BadArgs {
508 handleOptions(task, args, false);
509 }
510
511 // shared code that handles options for both jlink and jimage. jimage uses arguments like
512 // "create", "describe" etc. as "task names". Those arguments are unhandled here and returned
513 // as "unhandled arguments list". jlink does not want such arguments. "collectUnhandled" flag
514 // tells whether to allow for unhandled arguments or not.
515 private List<String> handleOptions(T task, String[] args, boolean collectUnhandled) throws BadArgs {
516 // findbugs warning, copy instead of keeping a reference.
517 command = Arrays.copyOf(args, args.length);
518
519 // Must extract it prior to do any option analysis.
520 // Required to interpret custom plugin options.
521 // Unit tests can call Task multiple time in same JVM.
522 pluginOptions = new PluginsHelper(null);
523
524 List<String> rest = collectUnhandled? new ArrayList<>() : null;
525 // process options
526 for (int i = 0; i < args.length; i++) {
527 if (args[i].startsWith("-")) {
528 String name = args[i];
529 PluginOption pluginOption = null;
530 Option<T> option = getOption(name);
531 if (option == null) {
532 pluginOption = pluginOptions.getOption(name);
533 if (pluginOption == null) {
534
535 throw new BadArgs("err.unknown.option", name).
536 showUsage(true);
537 }
538 }
539 Option<?> opt = pluginOption == null ? option : pluginOption;
540 String param = null;
541 if (opt.hasArg) {
542 if (name.startsWith("--") && name.indexOf('=') > 0) {
543 param = name.substring(name.indexOf('=') + 1,
544 name.length());
545 } else if (i + 1 < args.length) {
546 param = args[++i];
547 }
548 if (param == null || param.isEmpty()
549 || (param.length() >= 2 && param.charAt(0) == '-'
550 && param.charAt(1) == '-')) {
551 throw new BadArgs("err.missing.arg", name).
552 showUsage(true);
553 }
554 }
555 if (pluginOption != null) {
556 pluginOption.process(pluginOptions, name, param);
557 } else {
558 option.process(task, name, param);
559 }
560 if (opt.ignoreRest()) {
561 i = args.length;
562 }
563 } else {
564 if (collectUnhandled) {
565 rest.add(args[i]);
566 } else {
567 throw new BadArgs("err.orphan.argument", args[i]).
568 showUsage(true);
569 }
570 }
571 }
572 return rest;
573 }
574
575 private Option<T> getOption(String name) {
576 for (Option<T> o : options) {
577 if (o.matches(name)) {
578 return o;
579 }
580 }
581 return null;
582 }
583
584 public void showHelp(String progName) {
585 log.println(bundleHelper.getMessage("main.usage", progName));
586 Stream.concat(options.stream(), pluginOptions.mainOptions.stream())
587 .filter(option -> !option.isHidden())
588 .sorted()
589 .forEach(option -> {
590 log.println(bundleHelper.getMessage(option.resourceName()));
591 });
592
|
29 import java.io.PrintWriter;
30 import java.lang.module.Configuration;
31 import java.lang.module.ModuleFinder;
32 import java.lang.reflect.Layer;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.text.MessageFormat;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Locale;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.MissingResourceException;
47 import java.util.ResourceBundle;
48 import java.util.Set;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51
52 import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin;
53 import jdk.tools.jlink.plugin.Plugin;
54 import jdk.tools.jlink.plugin.Plugin.Category;
55 import jdk.tools.jlink.builder.DefaultImageBuilder;
56 import jdk.tools.jlink.builder.ImageBuilder;
57 import jdk.tools.jlink.plugin.PluginException;
58 import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
59 import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
60 import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
61 import jdk.tools.jlink.internal.plugins.StripDebugPlugin;
62 import jdk.internal.module.ModulePath;
63
64 /**
65 *
66 * JLink and JImage tools shared helper.
67 */
68 public final class TaskHelper {
69
85 public BadArgs showUsage(boolean b) {
86 showUsage = b;
87 return this;
88 }
89 public final String key;
90 public final Object[] args;
91 public boolean showUsage;
92 }
93
94 public static class Option<T> implements Comparable<T> {
95 public interface Processing<T> {
96
97 void process(T task, String opt, String arg) throws BadArgs;
98 }
99
100 final boolean hasArg;
101 final Processing<T> processing;
102 final boolean hidden;
103 final String name;
104 final String shortname;
105 final boolean terminalOption;
106
107 public Option(boolean hasArg,
108 Processing<T> processing,
109 boolean hidden,
110 String name,
111 String shortname,
112 boolean isTerminal)
113 {
114 if (!name.startsWith("--")) {
115 throw new RuntimeException("option name missing --, " + name);
116 }
117 if (!shortname.isEmpty() && !shortname.startsWith("-")) {
118 throw new RuntimeException("short name missing -, " + shortname);
119 }
120
121 this.hasArg = hasArg;
122 this.processing = processing;
123 this.hidden = hidden;
124 this.name = name;
125 this.shortname = shortname;
126 this.terminalOption = isTerminal;
127 }
128
129 public Option(boolean hasArg, Processing<T> processing, String name, String shortname, boolean isTerminal) {
130 this(hasArg, processing, false, name, shortname, isTerminal);
131 }
132
133 public Option(boolean hasArg, Processing<T> processing, String name, String shortname) {
134 this(hasArg, processing, false, name, shortname, false);
135 }
136
137 public Option(boolean hasArg, Processing<T> processing, boolean hidden, String name) {
138 this(hasArg, processing, hidden, name, "", false);
139 }
140
141 public Option(boolean hasArg, Processing<T> processing, String name) {
142 this(hasArg, processing, false, name, "", false);
143 }
144
145 public boolean isHidden() {
146 return hidden;
147 }
148
149 public boolean isTerminal() {
150 return terminalOption;
151 }
152
153 public boolean matches(String opt) {
154 return opt.equals(name) ||
155 opt.equals(shortname) ||
156 hasArg && opt.startsWith("--") && opt.startsWith(name + "=");
157 }
158
159 public boolean ignoreRest() {
160 return false;
161 }
162
163 void process(T task, String opt, String arg) throws BadArgs {
164 processing.process(task, opt, arg);
165 }
166
167 public String getName() {
168 return name;
169 }
170
171 public String resourceName() {
172 return resourcePrefix() + name.substring(2);
179 public String resourcePrefix() {
180 return "main.opt.";
181 }
182
183 @Override
184 public int compareTo(Object object) {
185 if (!(object instanceof Option<?>)) {
186 throw new RuntimeException("comparing non-Option");
187 }
188
189 Option<?> option = (Option<?>)object;
190
191 return name.compareTo(option.name);
192 }
193
194 }
195
196 private static class PluginOption extends Option<PluginsHelper> {
197 public PluginOption(boolean hasArg,
198 Processing<PluginsHelper> processing, boolean hidden, String name, String shortname) {
199 super(hasArg, processing, hidden, name, shortname, false);
200 }
201
202 public PluginOption(boolean hasArg,
203 Processing<PluginsHelper> processing, boolean hidden, String name) {
204 super(hasArg, processing, hidden, name, "", false);
205 }
206
207 public String resourcePrefix() {
208 return "plugin.opt.";
209 }
210 }
211
212 private final class PluginsHelper {
213
214 private Layer pluginsLayer = Layer.boot();
215 private final List<Plugin> plugins;
216 private String lastSorter;
217 private boolean listPlugins;
218 private Path existingImage;
219
220 // plugin to args maps. Each plugin may be used more than once in command line.
221 // Each such occurrence results in a Map of arguments. So, there could be multiple
222 // args maps per plugin instance.
223 private final Map<Plugin, List<Map<String, String>>> pluginToMaps = new HashMap<>();
224 private final List<PluginOption> pluginsOptions = new ArrayList<>();
498 private boolean hasArgument(String optionName) throws BadArgs {
499 Option<?> opt = getOption(optionName);
500 if (opt == null) {
501 opt = pluginOptions.getOption(optionName);
502 if (opt == null) {
503 throw new BadArgs("err.unknown.option", optionName).
504 showUsage(true);
505 }
506 }
507 return opt.hasArg;
508 }
509
510 public boolean shouldListPlugins() {
511 return pluginOptions.listPlugins;
512 }
513
514 private String getPluginsPath(String[] args) throws BadArgs {
515 return null;
516 }
517
518 /**
519 * Handles all options. This method stops processing the argument
520 * at the first non-option argument i.e. not starts with `-`, or
521 * at the first terminal option and returns the remaining arguments,
522 * if any.
523 */
524 public List<String> handleOptions(T task, String[] args) throws BadArgs {
525 // findbugs warning, copy instead of keeping a reference.
526 command = Arrays.copyOf(args, args.length);
527
528 // Must extract it prior to do any option analysis.
529 // Required to interpret custom plugin options.
530 // Unit tests can call Task multiple time in same JVM.
531 pluginOptions = new PluginsHelper(null);
532
533 // process options
534 for (int i = 0; i < args.length; i++) {
535 if (args[i].startsWith("-")) {
536 String name = args[i];
537 PluginOption pluginOption = null;
538 Option<T> option = getOption(name);
539 if (option == null) {
540 pluginOption = pluginOptions.getOption(name);
541 if (pluginOption == null) {
542 throw new BadArgs("err.unknown.option", name).
543 showUsage(true);
544 }
545 }
546 Option<?> opt = pluginOption == null ? option : pluginOption;
547 String param = null;
548 if (opt.hasArg) {
549 if (name.startsWith("--") && name.indexOf('=') > 0) {
550 param = name.substring(name.indexOf('=') + 1,
551 name.length());
552 } else if (i + 1 < args.length) {
553 param = args[++i];
554 }
555 if (param == null || param.isEmpty()
556 || (param.length() >= 2 && param.charAt(0) == '-'
557 && param.charAt(1) == '-')) {
558 throw new BadArgs("err.missing.arg", name).
559 showUsage(true);
560 }
561 }
562 if (pluginOption != null) {
563 pluginOption.process(pluginOptions, name, param);
564 } else {
565 option.process(task, name, param);
566 if (option.isTerminal()) {
567 return ++i < args.length
568 ? Stream.of(Arrays.copyOfRange(args, i, args.length))
569 .collect(Collectors.toList())
570 : Collections.emptyList();
571
572 }
573 }
574 if (opt.ignoreRest()) {
575 i = args.length;
576 }
577 } else {
578 return Stream.of(Arrays.copyOfRange(args, i, args.length))
579 .collect(Collectors.toList());
580 }
581 }
582 return Collections.emptyList();
583 }
584
585 private Option<T> getOption(String name) {
586 for (Option<T> o : options) {
587 if (o.matches(name)) {
588 return o;
589 }
590 }
591 return null;
592 }
593
594 public void showHelp(String progName) {
595 log.println(bundleHelper.getMessage("main.usage", progName));
596 Stream.concat(options.stream(), pluginOptions.mainOptions.stream())
597 .filter(option -> !option.isHidden())
598 .sorted()
599 .forEach(option -> {
600 log.println(bundleHelper.getMessage(option.resourceName()));
601 });
602
|