public <T> T askChoice(String title, String message, List<T> choices, T defaultValue)
			throws CancelledException {

		// The value parser function is only a two parameter list (String,T).  We have another
		// parameter for this method's parse function.  So, curry it.
		StringTransformer<T> curry = s -> parseChoice(s, choices);

		String key = join(title, message);
		T existingValue = loadAskValue(defaultValue, curry, key);
		if (isRunningHeadless()) {
			return existingValue;
		}

		Class<?> clazz = choices.get(0).getClass();
		T choice = doAsk(clazz, title, message, existingValue, lastValue -> {

			AskDialog<T> dialog =
				new AskDialog<>(null, title, message, AskDialog.STRING, choices, lastValue);
			if (dialog.isCanceled()) {
				throw new CancelledException();
			}

			T value = dialog.getChoiceValue();
			return value;
		});

		return choice;
	}
Returns an object that represents one of the choices in the given list. The actual behavior
of the method depends on your environment, which can be GUI or headless.
<p>
Regardless of environment -- if script arguments have been set, this method will use the
next argument in the array and advance the array index so the next call to an ask method
will get the next argument.  If there are no script arguments and a .properties file
sharing the same base name as the Ghidra Script exists (i.e., Script1.properties for
Script1.java), then this method will then look there for the String value to return.
The method will look in the .properties file by searching for a property name that is a
space-separated concatenation of the input String parameters (title + " " + message).
If that property name exists and its value represents a valid choice, then the
.properties value will be used in the following way:
<ol>
		<li>In the GUI environment, this method displays a popup dialog that prompts the user
			to choose from the given list of objects. The pre-chosen choice will be the last
			user-chosen value (if the dialog has been run before). If that does not exist, the
			pre-chosen value is the .properties value. If that does not exist or is invalid,
			then the 'defaultValue' parameter is used (as long as it is not null).</li>
		<li>In the headless environment, this method returns an object representing the
			.properties value (if it exists and is a valid choice), or throws an Exception if
			there is an invalid or missing .properties value.</li>
</ol>

@param title the title of the dialog (in GUI mode) or the first part of the variable name
			(in headless mode or when using .properties file)
@param message the message to display next to the input field (in GUI mode) or the second
			part of the variable name (in headless mode or when using .properties file)
@param choices set of choices (toString() value of each object will be displayed in the dialog)
@param defaultValue the default value to display in the input field; may be
                    null, but must be a valid choice if non-null.
@return the user-selected value
@throws CancelledException if the user hit the 'cancel' button
@throws IllegalArgumentException if in headless mode, there was a missing or invalid	choice
			specified in the .properties file
Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java