org.netbeans.spi.sendopts.Option Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.spi.sendopts;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Level;
import org.netbeans.api.sendopts.CommandException;
import org.netbeans.api.sendopts.CommandLine;
import org.netbeans.modules.sendopts.DefaultProcessor;
import org.netbeans.modules.sendopts.OptionImpl;
/** Represents possible option that can appear on {@link org.netbeans.api.sendopts.CommandLine}
* and contains factory methods to create them. Consider using {@link Arg}
* annotation instead.
*
* An option can have letter short version, long name. It can
* accept arguments, one argument or an array of additional arguments.
*
* @author Jaroslav Tulach
*/
public final class Option {
/** character of single command of (char)-1
*/
private final int shortName;
/** long name or null */
private final String longName;
/** implementation of this option */
final OptionImpl impl;
/** bundle with message*/
private final String[] keys;
private final String[] bundles;
/** Constant that represents no short name indicator.
*/
public static final char NO_SHORT_NAME = (char)-1;
private static String[] EMPTY = new String[2];
/** Use factory method */
private Option(char shortName, String longName, int type) {
this.shortName = shortName == NO_SHORT_NAME ? -1 : (int)shortName;
this.longName = longName;
switch (type) {
case 0: this.impl = OptionImpl.createNoArg(this); break;
case 1: this.impl = OptionImpl.createOneArg(this, false); break;
case 2: this.impl = OptionImpl.createOneArg(this, true); break;
case 3: this.impl = OptionImpl.createAdd(this, false); break;
case 4: this.impl = OptionImpl.createAdd(this, true); break;
case 5: this.impl = OptionImpl.createAlways(this); break;
default: throw new IllegalArgumentException("Type: " + type); // NOI18N
}
this.keys = EMPTY;
this.bundles = EMPTY;
}
/** Complex option */
Option(int type, Option[] arr) {
this.shortName = -1;
this.longName = null;
this.impl = OptionImpl.create(this, type, Arrays.asList(arr));
this.keys = EMPTY;
this.bundles = EMPTY;
}
/** clone with some description
*/
private Option(Option old, int typeOfDescription, String bundle, String description) {
this.shortName = old.shortName;
this.longName = old.longName;
this.impl = OptionImpl.cloneImpl(old.impl, this, null);
this.keys = (String[])old.keys.clone();
this.bundles = (String[])old.bundles.clone();
this.keys[typeOfDescription] = description;
this.bundles[typeOfDescription] = bundle;
}
/** Programmatic textual representation of the option. Format is subject to change
* in future.
* @return textual description of the option
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(this))); // NOI18N
sb.append('[');
sb.append(shortName);
sb.append(',');
sb.append(longName);
sb.append(',');
impl.append(sb);
sb.append(']');
return sb.toString();
}
/** Options with the same functionality, regardless of their descriptions
* {@link Option#shortDescription} and {@link Option#displayName} are always the same.
*/
public boolean equals(Object o) {
if (o instanceof Option) {
Option option = (Option)o;
return impl.root == option.impl.root;
}
return false;
}
public int hashCode() {
return System.identityHashCode(impl.root);
}
/** Factory method that creates an option without any arguments.
* For example to create an option that handles --help
or
* -h
one can create it using:
* Option helpOption = Option.withoutArgument('h', "help");
* and inside of the {@link OptionProcessor} declaring this
* option use:
* protected void process(Env env, Map<Option,String[]> values) throws CommandException {
* if (values.containsKey(helpOption)) {
* printHelp(env.getErrorStream());
* }
* }
* The values.get(helpOption)
is always null
to signal
* that this options does not have any associated value.
*
* @param shortName character code or {@link Option#NO_SHORT_NAME}
* @param longName long name or null
* @return option representing the created definition
*/
public static Option withoutArgument(char shortName, String longName) {
return new Option(shortName, longName, 0);
}
/** Factory method for option that may, but does not need to have an argument.
* For example to have option that increments by one or by specified number
* one could write:
* Option incrementOption = Option.optionalArgument('i', "increment");
* and inside of the {@link OptionProcessor} declaring this
* option use:
* public void process(Env env, Map<Option,String[]> values) throws CommandException {
* if (values.containsKey(incrementOption)) {
* String[] inc = values.get(incrementOption);
* int increment = inc == null ? 1 : Integer.parseInt(inc[0]);
* // do what is necessary
* }
* }
* The values
map always contains the incrementOption
* if it appeared on the command line. If it had associated value, then
* the map.get(incrementOption)
returns array of length one,
* with item on position 0 being the value of the option. However if the
* option appeared without argument, then the value associated with the
* option is null
.
*
* If registered into to system using {@link OptionProcessor} then users could
* use command lines like -i=5
or --increment=5
to
* increase the value by five or just -i
and --increment
* to increment by default - e.g. one.
*
* @param shortName the character to be used as a shortname or {@link Option#NO_SHORT_NAME}
* @param longName the long name or null
*/
public static Option optionalArgument(char shortName, String longName) {
return new Option(shortName, longName, 1);
}
/** Factory method for option has to be followed by one argument.
* For example to have option that opens a file
* one could write:
* Option openOption = Option.optionalArgument('o', "open");
* and inside of the {@link OptionProcessor} declaring this
* option use:
* public void process(Env env, Map<Option,String[]> values) throws CommandException {
* if (values.containsKey(openOption)) {
* String fileName = values.get(openOption)[0];
* File file = new File({@link Env#getCurrentDirectory}, fileName);
* // do what is necessary
* }
* }
* The values
map always contains the openOption
* if it appeared on the command line. Its value is then always string
* array of length one and its 0 element contains the argument for the
* option.
*
* If registered into to system using {@link OptionProcessor} then users could
* use command lines like -oX.java
or --open Y.java
to
* invoke the open functionality.
*
* @param shortName the character to be used as a shortname or {@link Option#NO_SHORT_NAME}
* @param longName the long name or null
*/
public static Option requiredArgument(char shortName, String longName) {
return new Option(shortName, longName, 2);
}
/** Creates an option that can accept additional arguments
.
* For example to have option that opens few files
* one could write:
* Option openOption = Option.additionalArguments('o', "open");
* and inside of the {@link OptionProcessor} declaring this
* option use:
* public void process(Env env, Map<Option,String[]> values) throws CommandException {
* if (values.containsKey(openOption)) {
* for (String fileName : values.get(openOption)) {
* File file = new File({@link Env#getCurrentDirectory}, fileName);
* // do what is necessary
* }
* }
* }
* The values
map always contains the openOption
* if it appeared on the command line. Its value is then always string
* array of arbitrary length containing all elements on the command line
* that were not recognised as options (or their arguments).
* For example line
* X.java -o Y.java Z.txt
* will invoke the {@link OptionProcessor} with
* { "X.java", "Y.java", "Z.txt" }
.
*
* Obviously only one such {@link Option#additionalArguments} can be
* used at once on a command line. If there was not only the open
* but also edit
option
* taking the additional arguments,
* then command line like:
* --edit X.java --open Y.java Z.txt
* would be rejected.
*
* @param shortName the character to be used as a shortname or {@link Option#NO_SHORT_NAME}
* @param longName the long name or null
*/
public static Option additionalArguments(char shortName, String longName) {
return new Option(shortName, longName, 3);
}
/** Creates a default option that accepts additional arguments
* not claimed by any other option.
* For example to have option that opens few files
* one could write:
* Option openOption = Option.defaultArguments();
* and inside of the {@link OptionProcessor} declaring this
* option use:
* public void process(Env env, Map<Option,String[]> values) throws CommandException {
* if (values.containsKey(openOption)) {
* for (fileName : values.get(openOption)) {
* File file = new File({@link Env#getCurrentDirectory}, fileName);
* // do what is necessary
* }
* }
* }
* The values
map always contains the openOption
* if there were some arguments on the command line that were not parsed
* by any other option. Its value is then always string
* array of arbitrary length containing all elements on the command line
* that were not recognised as options (or their arguments).
* For example line
* X.java Y.java Z.txt
* will invoke the {@link OptionProcessor} with
* { "X.java", "Y.java", "Z.txt" }
.
*
* Obviously only one such {@link Option#defaultArguments} can defined.
* If there are two, then an error is reported when one tries to parse
* any command line with arguments not claimed by any other option.
* That is why it is always good idea to not define just {@link Option#defaultArguments}
* option, but also appropriate {@link Option#additionalArguments} one:
* Option openOption1 = Option.defaultArguments();
* Option openOption2 = Option.additionalArguments('o', "open");
* and handle both of them in the {@link OptionProcessor}. Then if the
* command line:
* X.java Y.java Z.txt
is rejected due to ambiguities one can use
* X.java Y.java --open Z.txt
to invoke the same functionality.
*/
public static Option defaultArguments() {
return new Option(NO_SHORT_NAME, null, 4);
}
/** Creates an option that is always present. This can be useful for
* processors that want to be notified everytime the command line
* is successfuly parsed.
*
* Option always = Option.always();
* and inside of the {@link OptionProcessor} declaring this
* option use:
* public void process(Env env, Map<Option,String[]> values) throws CommandException {
* assert values.contains(always);
* }
*
* @return the option that always matches correct command line
* @since 2.1
*/
public static Option always() {
return new Option(NO_SHORT_NAME, null, 5);
}
/** Associates a name with given option. By default
* the option display name is generated by the infrastructure from the
* short and long name plus generic description of options arguments, this
* method allows to completely replace the default behaviour.
*
* @param option the option to add description for
* @param bundleName name of a bundle to create
* @param key the bundle key to get the message from
* @return option with same behaviour as the old one plus with associated display name
*/
public static Option displayName(Option option, String bundleName, String key) {
return new Option(option, 0, bundleName, key);
}
/** Associates a short textual description with given option. This message
* is going to be printed during {@link org.netbeans.api.sendopts.CommandLine#usage} next to the
* option name. Usually should be one liner comment.
*
* @param option the option to add description for
* @param bundleName name of a bundle to create
* @param key the bundle key to get the message from
* @return option with same behaviour as the old one plus with associated short description message
*/
public static Option shortDescription(Option option, String bundleName, String key) {
return new Option(option, 1, bundleName, key);
}
static {
OptionImpl.Trampoline.DEFAULT = new OptionImpl.Trampoline() {
public OptionImpl impl(Option o) {
return o.impl;
}
public Env create(CommandLine cmd, InputStream is, OutputStream os, OutputStream err, File currentDir) {
return new Env(cmd, is, os, err, currentDir);
}
public void usage(PrintWriter w, Option o, int max) {
if (o.keys[1] != null) {
w.print(key(o.bundles[1], o.keys[1], Locale.getDefault()));
}
}
public Option[] getOptions(OptionProcessor p) {
return p.getOptions().toArray(new Option[0]);
}
public void process(OptionProcessor provider, Env env, Map