001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui; 003 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Collection; 007import java.util.Collections; 008import java.util.EnumMap; 009import java.util.HashMap; 010import java.util.List; 011import java.util.Locale; 012import java.util.Map; 013import java.util.Optional; 014import java.util.logging.Level; 015import java.util.stream.Stream; 016 017import org.openstreetmap.josm.tools.Logging; 018import org.openstreetmap.josm.tools.OptionParser; 019import org.openstreetmap.josm.tools.OptionParser.OptionCount; 020 021/** 022 * This class holds the arguments passed on to {@link MainApplication#main}. 023 * @author Michael Zangl 024 * @since 10899 025 */ 026public class ProgramArguments { 027 028 /** 029 * JOSM command line options. 030 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a> 031 */ 032 public enum Option { 033 /** --help|-h Show this help */ 034 HELP(false), 035 /** --version Displays the JOSM version and exits */ 036 VERSION(false), 037 /** {@code --status-report} Show status report with useful information that can be attached to bugs */ 038 STATUS_REPORT(false), 039 /** --debug Print debugging messages to console */ 040 DEBUG(false), 041 /** --trace Print detailed debugging messages to console */ 042 TRACE(false), 043 /** --language=<language> Set the language */ 044 LANGUAGE(true), 045 /** --reset-preferences Reset the preferences to default */ 046 RESET_PREFERENCES(false), 047 /** --load-preferences=<url-to-xml> Changes preferences according to the XML file */ 048 LOAD_PREFERENCES(true), 049 /** --set=<key>=<value> Set preference key to value */ 050 SET(true), 051 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */ 052 GEOMETRY(true), 053 /** --no-maximize Do not launch in maximized mode */ 054 NO_MAXIMIZE(false), 055 /** --maximize Launch in maximized mode */ 056 MAXIMIZE(false), 057 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br> 058 * --download=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) <br> 059 * --download=<filename> Open a file (any file type that can be opened with File/Open) */ 060 DOWNLOAD(true), 061 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br> 062 * --downloadgps=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS */ 063 DOWNLOADGPS(true), 064 /** --selection=<searchstring> Select with the given search */ 065 SELECTION(true), 066 /** --offline=<OSM_API|JOSM_WEBSITE|CACHE_UPDATES|CERTIFICATES|ALL> Disable access to the given resource(s), delimited by comma */ 067 OFFLINE(true), 068 /** --skip-plugins */ 069 SKIP_PLUGINS(false); 070 071 private final String name; 072 private final boolean requiresArg; 073 074 Option(boolean requiresArgument) { 075 this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-'); 076 this.requiresArg = requiresArgument; 077 } 078 079 /** 080 * Replies the option name 081 * @return The option name, in lowercase 082 */ 083 public String getName() { 084 return name; 085 } 086 087 /** 088 * Determines if this option requires an argument. 089 * @return {@code true} if this option requires an argument, {@code false} otherwise 090 */ 091 public boolean requiresArgument() { 092 return requiresArg; 093 } 094 } 095 096 private final Map<Option, List<String>> argMap = new EnumMap<>(Option.class); 097 098 /** 099 * Construct the program arguments object 100 * @param args The args passed to main. 101 * @since 10936 102 */ 103 public ProgramArguments(String... args) { 104 Stream.of(Option.values()).forEach(o -> argMap.put(o, new ArrayList<>())); 105 buildCommandLineArgumentMap(args); 106 } 107 108 /** 109 * Builds the command-line argument map. 110 * @param args command-line arguments array 111 */ 112 private void buildCommandLineArgumentMap(String... args) { 113 OptionParser parser = new OptionParser("JOSM"); 114 for (Option o : Option.values()) { 115 if (o.requiresArgument()) { 116 parser.addArgumentParameter(o.getName(), OptionCount.MULTIPLE, p -> addOption(o, p)); 117 } else { 118 parser.addFlagParameter(o.getName(), () -> addOption(o, "")); 119 } 120 } 121 122 parser.addShortAlias(Option.HELP.getName(), "h"); 123 parser.addShortAlias(Option.VERSION.getName(), "v"); 124 125 List<String> remaining = parser.parseOptionsOrExit(Arrays.asList(args)); 126 127 // positional arguments are a shortcut for the --download ... option 128 for (String arg : remaining) { 129 addOption(Option.DOWNLOAD, arg); 130 } 131 } 132 133 private void addOption(Option opt, String optarg) { 134 argMap.get(opt).add(optarg); 135 } 136 137 /** 138 * Gets a single argument (the first) that was given for the given option. 139 * @param option The option to search 140 * @return The argument as optional value. 141 */ 142 public Optional<String> getSingle(Option option) { 143 return get(option).stream().findFirst(); 144 } 145 146 /** 147 * Gets all values that are given for a given option 148 * @param option The option 149 * @return The values that were given. May be empty. 150 */ 151 public Collection<String> get(Option option) { 152 return Collections.unmodifiableList(argMap.get(option)); 153 } 154 155 /** 156 * Test if a given option was used by the user. 157 * @param option The option to test for 158 * @return <code>true</code> if the user used it. 159 */ 160 public boolean hasOption(Option option) { 161 return !get(option).isEmpty(); 162 } 163 164 /** 165 * Helper method to indicate if version should be displayed. 166 * @return <code>true</code> to display version 167 */ 168 public boolean showVersion() { 169 return hasOption(Option.VERSION); 170 } 171 172 /** 173 * Helper method to indicate if help should be displayed. 174 * @return <code>true</code> to display version 175 */ 176 public boolean showHelp() { 177 return !get(Option.HELP).isEmpty(); 178 } 179 180 /** 181 * Get the log level the user wants us to use. 182 * @return The log level. 183 */ 184 public Level getLogLevel() { 185 if (hasOption(Option.TRACE)) { 186 return Logging.LEVEL_TRACE; 187 } else if (hasOption(Option.DEBUG)) { 188 return Logging.LEVEL_DEBUG; 189 } else { 190 return Logging.LEVEL_INFO; 191 } 192 } 193 194 /** 195 * Gets a map of all preferences the user wants to set. 196 * @return The preferences to set. It contains null values for preferences to unset 197 */ 198 public Map<String, String> getPreferencesToSet() { 199 HashMap<String, String> map = new HashMap<>(); 200 get(Option.SET).stream().map(i -> i.split("=", 2)).forEach(kv -> map.put(kv[0], getValue(kv))); 201 return map; 202 } 203 204 private static String getValue(String... kv) { 205 if (kv.length < 2) { 206 return ""; 207 } else if ("null".equals(kv[1])) { 208 return null; 209 } else { 210 return kv[1]; 211 } 212 } 213}