All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.swarm.cli.Option Maven / Gradle / Ivy

/**
 * Copyright 2015-2017 Red Hat, Inc, and individual contributors.
 *
 * Licensed 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.wildfly.swarm.cli;

import java.io.File;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.function.Supplier;

import javax.enterprise.inject.Vetoed;

import org.wildfly.swarm.internal.SwarmMessages;

/**
 * A single option specification.
 *
 * 

An option may have a short (single-dash) or long (double-dash) variant.

* *

Each option may optionally take a value, which may or may not be allowed * to be separated from the option.

* *

For instance, if {@link #valueMayBeSeparate(boolean)} is specified as false, * then only -Dwhatever would be allowed, while -D whatever * would not.

* *

Upon successful match, each Option has an {@link Action} associated * which is called with the value (if any) and the state-holding {@link CommandLine} * object.

* * @author Bob McWhirter */ @Vetoed public class Option { private static final String EQUALS = "="; private static final String HYPHEN = "-"; private static final String SPACE = " "; private static final String DOUBLE_HYPHEN = "--"; private static final int MAX_LINE_LENGTH = 50; /** * Construct an empty option. */ public Option() { } /** * Associate an action with this option. * * @param action The action to call when matched. * @return This Option object. */ public Option then(Action action) { this.action = action; return this; } /** * Display formatted help associated with this option. * * @param out The output stream to display the help upon. */ public void displayHelp(PrintStream out) { out.println(merge(summary(), description())); } private List summary() { List list = new ArrayList<>(); if (this.shortArg != null) { if (hasValue()) { if (this.valueDescription.contains(EQUALS)) { list.add(HYPHEN + this.shortArg + this.valueDescription); } else { list.add(HYPHEN + this.shortArg + EQUALS + this.valueDescription); } if (this.valueMayBeSeparate) { if (this.valueDescription.contains(EQUALS)) { list.add(HYPHEN + this.shortArg + this.valueDescription.replace(EQUALS, SPACE)); } else { list.add(HYPHEN + this.shortArg + SPACE + this.valueDescription); } } } else { list.add(HYPHEN + this.shortArg); } } if (this.longArg != null) { if (hasValue()) { if (this.valueDescription.contains(EQUALS)) { list.add(DOUBLE_HYPHEN + this.longArg + this.valueDescription); } else { list.add(DOUBLE_HYPHEN + this.longArg + EQUALS + this.valueDescription); } if (this.valueMayBeSeparate) { if (this.valueDescription.contains(EQUALS)) { list.add(DOUBLE_HYPHEN + this.longArg + this.valueDescription.replace(EQUALS, SPACE)); } else { list.add(DOUBLE_HYPHEN + this.longArg + SPACE + this.valueDescription); } } } else { list.add(DOUBLE_HYPHEN + this.longArg); } } return list; } private List description() { List list = new ArrayList<>(); StringTokenizer tokens = new StringTokenizer(this.description); StringBuilder line = new StringBuilder(); while (tokens.hasMoreElements()) { String token = tokens.nextToken(); if (line.length() + (SPACE + token).length() > MAX_LINE_LENGTH) { list.add(line.toString()); line = new StringBuilder(); } if (line.length() != 0) { line.append(SPACE); } line.append(token); } list.add(line.toString()); return list; } private String merge(List summary, List desc) { if (summary.size() > desc.size()) { int diff = summary.size() - desc.size(); for (int i = 0; i < diff; ++i) { desc.add(""); } } if (desc.size() > summary.size()) { int diff = desc.size() - summary.size(); for (int i = 0; i < diff; ++i) { summary.add(""); } } StringBuilder txt = new StringBuilder(); for (int i = 0; i < summary.size(); ++i) { txt.append(String.format(" %-30s %s", summary.get(i), desc.get(i))); txt.append("\n"); } return txt.toString(); } /** * Specify a short (single-dash) variant. * *

A single character following a single dash, such as -c.

* * @param shortArg The character. * @return This Option object. */ public Option withShort(Character shortArg) { this.shortArg = shortArg; return this; } /** * Specify a long (double-dash) variant. * *

A string following a double-dash, such as --help.

* * @param longArg The string (without dashes). * @return This Option object. */ public Option withLong(String longArg) { this.longArg = longArg; return this; } /** * Set a human-readable description of this option. * * @param description The description. * @return This Option object. */ public Option withDescription(String description) { this.description = description; return this; } /** * Specify that this option takes an argument value. * *

A non-null string passed to this method will be used to signify * that this option may take an argument, and to describe its format.

* *

For instance a string of name[=value]

would indicate * that a name is expected, with an optional value * which should follow an equal sign.

* * @param valueDescription * @return */ public Option hasValue(String valueDescription) { this.valueDescription = valueDescription; return this; } private boolean hasValue() { return this.valueDescription != null; } /** * Indicate if the value to the option may be separated by whitespace. (defaults to true). * *

In some cases, because of ambiguity, it may be required to indicate that an * argument may not be separate from the option. If an argument is to be * provided, it must immediately follow the option without whitespace.

* * @param valueMayBeSeparate true (the default) if value may be separate, or false to indicate it must be conjoined. * @return This Option object. */ public Option valueMayBeSeparate(boolean valueMayBeSeparate) { this.valueMayBeSeparate = valueMayBeSeparate; return this; } /** * Specify a default value supplier for this option. * * @param supplier The supplier. * @return This Option object. */ public Option withDefault(Supplier supplier) { this.supplier = supplier; return this; } T defaultValue() { if (this.supplier == null) { return null; } return this.supplier.get(); } boolean parse(ParseState state, CommandLine commandLine) throws Exception { String cur = state.la(); if (cur == null) { return false; } String value = null; String matchedArg = null; if (this.shortArg != null && cur.startsWith(HYPHEN + this.shortArg)) { matchedArg = HYPHEN + this.shortArg; if (hasValue() && cur.length() >= 3) { if (cur.charAt(2) == '=') { value = cur.substring(3); } else { value = cur.substring(2); } } } else if (this.longArg != null && cur.startsWith(DOUBLE_HYPHEN + this.longArg)) { matchedArg = DOUBLE_HYPHEN + this.longArg; if (hasValue() && cur.length() >= this.longArg.length() + 3) { if (cur.charAt(this.longArg.length() + 2) == '=') { value = cur.substring(this.longArg.length() + 3); } else { value = cur.substring(this.longArg.length() + 2); } } } else { return false; } state.consume(); if (value != null && value.trim().isEmpty()) { value = null; } if (hasValue() && value == null && !this.valueMayBeSeparate) { throw SwarmMessages.MESSAGES.argumentRequired(matchedArg); } if (hasValue() && value == null) { if (state.la() == null) { throw SwarmMessages.MESSAGES.argumentRequired(matchedArg); } value = state.consume(); } this.action.set(commandLine, this, value); return true; } public String toString() { return "[Option: short=" + this.shortArg + "; long=" + this.longArg + "; valueDescription=" + this.valueDescription + "]"; } /** * Helper to attempt forming a URL from a String in a sensible fashion. * * @param value The input string. * @return The correct URL, if possible. */ public static URL toURL(String value) throws MalformedURLException { try { URL url = new URL(value); return url; } catch (MalformedURLException e) { try { return new File(value).toURI().toURL(); } catch (MalformedURLException e2) { // throw the original throw e; } } } private Character shortArg; private String longArg; private String valueDescription; private boolean valueMayBeSeparate = true; private Action action; private String description; private Supplier supplier; /** * Callback functional interface for matched options. */ public interface Action { /** * Perform some action, being passed the value and the CommandLine. * * @param commandLine The state-holding CommandLine object. * @param option The matched option. * @param value The value to the option, if any. Possibly null. * @throws if an error occurs */ void set(CommandLine commandLine, Option option, String value) throws Exception; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy