com.sun.enterprise.admin.cli.Parser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2018-2022] Payara Foundation and/or affiliates
package com.sun.enterprise.admin.cli;
import java.io.*;
import java.util.*;
import com.sun.enterprise.util.StringUtils;
import org.glassfish.api.admin.*;
import org.glassfish.api.admin.CommandModel.ParamModel;
import com.sun.enterprise.admin.util.CommandModelData.ParamModelData;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
/**
* The Parser
object is used to parse the
* command line and verify that the command line is CLIP compliant.
*/
public class Parser {
/** MultiMap of options and values from command-line */
private ParameterMap optionsMap = new ParameterMap();
/** Set of invalid options' names from command-line */
private Set invalidOptions = new HashSet<>();
/** Array of operands from command-line */
private List operands = new ArrayList();
/** The valid options for the command we're parsing */
private Collection options;
/** Ignore unknown options when parsing? */
private boolean ignoreUnknown;
private static final LocalStringsImpl strings = new LocalStringsImpl(Parser.class);
/*
* TODO:
* option types shouldn't be string literals here
*/
/**
* Parse the given command line arguments
*
* @param args command line arguments
* @param start index in args to start parsing
* @param options the valid options to consider while parsing
* @param ignoreUnknown if true, unknown options are considered operands
* instead of generating an exception
* @throws CommandValidationException if command line parsing fails
*/
public Parser(String[] args, int start,
Collection options, boolean ignoreUnknown)
throws CommandValidationException {
this.options = options;
this.ignoreUnknown = ignoreUnknown;
parseCommandLine(args, start);
}
/**
* Parse the command line arguments according to CLIP.
*
* @param argv command line arguments
* @throws CommandValidationException if command line is invalid
*/
private void parseCommandLine(final String[] argv, final int start)
throws CommandValidationException {
for (int si = start; si < argv.length; si++) {
String arg = argv[si];
if (arg.equals("--")) { // end of options
// if we're ignoring unknown options, we include this
// delimiter as an operand, it will be eliminated later
// when we process all remaining options
if (!ignoreUnknown) {
si++;
}
while (si < argv.length) {
operands.add(argv[si++]);
}
break;
}
// is it an operand or option value?
if (!arg.startsWith("-") || arg.length() <= 1) {
operands.add(StringUtils.removeEnclosingQuotes(arg));
if (ignoreUnknown) {
continue;
}
si++;
while (si < argv.length) {
operands.add(argv[si++]);
}
break;
}
// at this point it's got to be an option of some sort
ParamModel opt = null;
String name = null;
String value = null;
if (arg.charAt(1) == '-') { // long option
int ns = 2;
boolean sawno = false;
if (arg.startsWith("--no-")) {
sawno = true;
value = "false";
ns = 5; // skip prefix
}
// if of the form "--option=value", extract value
int ne = arg.indexOf('=');
if (ne < 0) {
name = arg.substring(ns);
} else {
if (value != null) {
throw new CommandValidationException(strings.get("parser.noValueAllowed", arg));
}
name = arg.substring(ns, ne);
value = arg.substring(ne + 1);
}
opt = lookupLongOption(name);
if (sawno && optionRequiresOperand(opt)) {
throw new CommandValidationException(strings.get("parser.illegalNo", opt.getName()));
}
} else { // short option
/*
* possibilities are:
* -f
* -f value
* -f=value
* -fxyz (multiple single letter boolean options
* with no arguments)
*/
if (arg.length() <= 2) { // one of the first two cases
opt = lookupShortOption(arg.charAt(1));
name = arg.substring(1);
} else { // one of the last two cases
if (arg.charAt(2) == '=') { // -f=value case
opt = lookupShortOption(arg.charAt(1));
value = arg.substring(3);
} else { // -fxyz case
for (int i = 1; i < arg.length(); i++) {
opt = lookupShortOption(arg.charAt(i));
if (opt == null) {
if (!ignoreUnknown) {
invalidOptions.add(arg);
}
// unknown option, skip all the rest
operands.add(arg);
break;
}
if (opt.getType() == Boolean.class || opt.getType() == boolean.class) {
setOption(opt, "true");
} else {
if (!ignoreUnknown) {
throw new CommandValidationException(strings.get("parser.nonbooleanNotAllowed",
Character.toString(arg.charAt(i)), arg));
}
// unknown option, skip all the rest
operands.add(arg);
break;
}
}
continue;
}
}
}
// is it a known option?
if (opt == null) {
if (!ignoreUnknown){
invalidOptions.add(arg);
}
// unknown option, skip it
operands.add(arg);
continue;
}
// find option value, if needed
if (value == null) {
// if no valid options were specified, we use the next argument
// as an option as long as it doesn't look like an option
if (options == null) {
if (si + 1 < argv.length && !argv[si + 1].startsWith("-")) {
value = argv[++si];
} else {
((ParamModelData)opt).type = Boolean.class; // fake it
}
} else if (optionRequiresOperand(opt)) {
if (++si >= argv.length){
throw new CommandValidationException(strings.get("parser.missingValue", name));
}
value = argv[si];
} else if (opt.getType() == Boolean.class || opt.getType() == boolean.class) {
/*
* If it's a boolean option, the following parameter
* might be the value for the option; peek ahead to
* see if it looks like a boolean value.
*/
if (si + 1 < argv.length) {
String val = argv[si + 1];
if (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false")) {
// yup, it's a boolean value, consume it
si++;
value = val;
}
}
}
}
if (invalidOptions.isEmpty()) {
setOption(opt, value);
}
}
if (invalidOptions.size() == 1) {
throw new CommandValidationException(strings.get("parser.invalidOption", invalidOptions.iterator().next()));
}
else if (invalidOptions.size() > 1) {
throw new CommandValidationException(strings.get("parser.invalidOptions", StringUtils.cat(" ", invalidOptions.toArray(new String[0]))));
}
}
/**
* Returns a Map with all the options.
* The Map is indexed by the long name of the option.
*
* @return options
*/
public ParameterMap getOptions() {
return optionsMap;
}
/**
* Returns the list of operands.
*
* @return list of operands
*/
public List getOperands() {
return operands;
}
@Override
public String toString() {
return "CLI parser: Options = " + optionsMap +
"; Operands = " + operands;
}
/**
* Get ParamModel for long option name.
*/
private ParamModel lookupLongOption(String s) {
if (s == null || s.length() == 0){
return null;
}
// XXX - for now, fake it if no options
if (options == null) {
// no valid options specified so everything is valid
return new ParamModelData(s, String.class, true, null);
}
for (ParamModel od : options) {
if (od.getParam().primary())
continue;
if (s.equalsIgnoreCase(od.getName()))
return od;
if (s.equalsIgnoreCase(od.getParam().alias()))
return od;
}
return null;
}
/**
* Get ParamModel for short option name.
*/
private ParamModel lookupShortOption(char c) {
// XXX - for now, fake it if no options
if (options == null){
return null;
}
String sc = Character.toString(c);
for (ParamModel od : options) {
if (od.getParam().shortName().equals(sc)){
return od;
}
}
return null;
}
/**
* Does this option require an operand?
*/
private static boolean optionRequiresOperand(ParamModel opt) {
return opt != null && opt.getType() != Boolean.class && opt.getType() != boolean.class;
}
/**
* Set the value for the option.
*/
private void setOption(ParamModel opt, String value) throws CommandValidationException {
// VERY basic validation
if (opt == null) {
throw new NullPointerException("null option name");
}
if (value != null) {
value = StringUtils.trimQuotes(value.trim());
}
String name = opt.getName();
if (opt.getType() == File.class) {
File f = new File(value);
// allow the pseudo-file name of "-" to mean stdin
if (!value.equals("-") && !(f.isFile() || f.canRead())) {
// get a real exception for why it's no good
InputStream is = null;
try {
is = new FileInputStream(f);
} catch (IOException ioex) {
throw new CommandValidationException(strings.get("parser.invalidFileEx",
name, ioex.toString()));
} finally {
if (is != null) {
try {
is.close();
} catch (IOException cex) {
}
}
}
throw new CommandValidationException(strings.get("parser.invalidFile", name, value));
}
} else if (opt.getType() == Boolean.class || opt.getType() == boolean.class) {
if (value == null) {
value = "true";
} else if (!(value.toLowerCase(Locale.ENGLISH).equals("true") || value.toLowerCase(Locale.ENGLISH).equals("false"))) {
throw new CommandValidationException(strings.get("parser.invalidBoolean", name, value));
}
} else if (opt.getParam().password()) {
throw new CommandValidationException(strings.get("parser.passwordNotAllowed", opt.getName()));
}
if (!opt.getParam().multiple()) {
// repeats not allowed
if (optionsMap.containsKey(name)) {
throw new CommandValidationException(strings.get("parser.noRepeats", name));
}
}
optionsMap.add(name, value);
}
}