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