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

com.sun.enterprise.admin.cli.Parser Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * 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);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy