com.aliyun.openservices.odps.console.utils.CommandParserUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.aliyun.openservices.odps.console.utils.CommandParserUtils.java

Source

/*
 * 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 com.aliyun.openservices.odps.console.utils;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;

import com.aliyun.odps.OdpsDeprecatedLogger;
import com.aliyun.odps.utils.StringUtils;
import com.aliyun.openservices.odps.console.ExecutionContext;
import com.aliyun.openservices.odps.console.ODPSConsoleException;
import com.aliyun.openservices.odps.console.commands.AbstractCommand;
import com.aliyun.openservices.odps.console.commands.CompositeCommand;
import com.aliyun.openservices.odps.console.commands.InstancePriorityCommand;
import com.aliyun.openservices.odps.console.commands.InteractiveCommand;
import com.aliyun.openservices.odps.console.commands.LoginCommand;
import com.aliyun.openservices.odps.console.commands.UseProjectCommand;
import com.aliyun.openservices.odps.console.constants.ODPSConsoleConstants;
import com.aliyun.openservices.odps.console.utils.antlr.AntlrObject;

/**
 * ??????
 *
 * @author shuman.gansm
 */
public class CommandParserUtils {

    private static final String COMMAND_PACKAGE = "com.aliyun.openservices.odps.console.commands";

    /**
     * ??CommandCommand??
     * ?ExecuteCommand-e?-e??ExecuteCommand?
     */
    private static final String[] COMMANDLINE_COMMANDS = new String[] { "SetEndpointCommand", "LoginCommand",
            "UseProjectCommand", "DryRunCommand", "MachineReadableCommand", "FinanceJsonCommand",
            "AsyncModeCommand", "InstancePriorityCommand", "SkipCommand", "SetRetryCommand", "InteractiveCommand",
            "ExecuteCommand", "HelpCommand", "ShowVersionCommand" };

    /**
     * ??CommandCommand??
     */
    private static final String[] INTERACTIVE_COMMANDS = new String[] { "QuitCommand", "HelpCommand",
            "UseProjectCommand", "SetCommand", "ExportMetaCommand", "ShowTablesCommand", // list tables
            "ShowPartitionsCommand", // list partitions
            "DescribeCommand", // describe table metas
            "UnSetCommand" };

    private static final String HELP_TAGS_FIELD = "HELP_TAGS";
    private static final String HELP_PRINT_METHOD = "printUsage";

    public static String[] getCommandTokens(String cmd) throws ODPSConsoleException {
        AntlrObject antlr = new AntlrObject(cmd);
        String[] args = antlr.getTokenStringArray();

        if (args == null) {
            throw new ODPSConsoleException("Invalid parameters - Generic options must be specified.");
        }
        return args;
    }

    public static CommandLine getCommandLine(String[] args, Options opts) throws ODPSConsoleException {

        CommandLineParser clp = new GnuParser();
        CommandLine cl;
        try {
            cl = clp.parse(opts, args, false);
        } catch (Exception e) {
            throw new ODPSConsoleException("Unknown exception from client - " + e.getMessage(), e);
        }

        return cl;
    }

    public static CommandLine getCommandLine(String cmd, Options opts) throws ODPSConsoleException {
        return getCommandLine(getCommandTokens(cmd), opts);
    }

    /**
     * ???
     *
     * @param args
     * @return
     * @throws ODPSConsoleException
     */
    public static AbstractCommand parseOptions(String args[], ExecutionContext sessionContext)
            throws ODPSConsoleException {

        // option listoptionlist????
        List<String> optionList = new LinkedList<String>(Arrays.asList(args));

        // ??-e"", --project=??option
        optionList = populateOptions(optionList);
        removeHook(optionList, sessionContext);

        // commandList?parse?command?
        List<AbstractCommand> commandList = new LinkedList<AbstractCommand>();
        parseCommandLineCommand(commandList, optionList, sessionContext);

        // ????, ??
        if (optionList.size() > 0) {
            throw new ODPSConsoleException(ODPSConsoleConstants.BAD_COMMAND);
        }

        boolean hasLoginCommand = false;
        for (AbstractCommand command : commandList) {
            if (command instanceof LoginCommand) {
                hasLoginCommand = true;
                break;
            }
        }
        // account_providerlogincomandlogincomand?
        if (!hasLoginCommand && sessionContext.getAccountProvider() != null) {
            commandList.add(0, new LoginCommand(sessionContext.getAccountProvider(), sessionContext.getAccessId(),
                    null, null, "--account_provider", sessionContext));
        }

        checkUseProject(commandList, sessionContext);
        CompositeCommand compositeCommand = new CompositeCommand(commandList, "", sessionContext);

        return compositeCommand;
    }

    public static void removeHook(List<String> optionList, ExecutionContext sessionContext) {

        // hook?command??optioncontext
        if (optionList.contains("--enablehook")) {

            if (optionList.indexOf("--enablehook") + 1 < optionList.size()) {

                int index = optionList.indexOf("--enablehook");
                // command
                String hook = optionList.get(index + 1);

                // --enablehook ??
                optionList.remove(optionList.indexOf("--enablehook"));
                optionList.remove(optionList.indexOf(hook));

                if (!Boolean.valueOf(hook)) {
                    sessionContext.setOdpsHooks(null);
                }
            }
        }

    }

    /**
     * ??
     *
     * @param commandLines
     * @return
     * @throws ODPSConsoleException
     */
    public static AbstractCommand parseCommand(String commandLines, ExecutionContext sessionContext)
            throws ODPSConsoleException {

        commandLines = commandLines.trim();
        if (commandLines.equals("")) {
            throw new ODPSConsoleException(ODPSConsoleConstants.INVALID_PARAMETER_E);
        }

        int index = commandLines.lastIndexOf(";");
        if (index != commandLines.length() - 1) {
            throw new ODPSConsoleException(ODPSConsoleConstants.COMMAND_END_WITH);
        }

        OdpsDeprecatedLogger.getDeprecatedCalls().put("USER_COMMANDS :" + commandLines, 1L);

        // ??

        List<String> commandLinelist = new AntlrObject(commandLines).splitCommands();

        // SqlLinesParser??--????,?,query?command?
        if (commandLinelist.size() == 0 && !commandLines.trim().startsWith("--")) {

            commandLinelist.add(commandLines.substring(0, index));
        }

        // 
        List<AbstractCommand> odpsCommandList = new LinkedList<AbstractCommand>();

        // query number
        int i = 0;
        // commandskip
        int step = sessionContext.getStep();
        for (String command : commandLinelist) {

            i++;
            if (step > i) {
                continue;
            }

            command = command.trim();
            // for null command
            if (command.equals("")) {
                continue;
            }

            parseInteractiveCommand(odpsCommandList, command, sessionContext, i);
        }

        // query
        if (step > i) {
            throw new ODPSConsoleException(
                    "[Error] invalid NUM for option k, total query count inlcude empty query: " + i);
        }

        if (odpsCommandList.size() == 1) {
            return odpsCommandList.get(0);
        } else {
            CompositeCommand compositeCommand = new CompositeCommand(odpsCommandList, "", sessionContext);

            return compositeCommand;
        }
    }

    private static void checkUseProject(List<AbstractCommand> commandList, ExecutionContext sessionContext)
            throws ODPSConsoleException {
        if (sessionContext.getProjectName() == null || sessionContext.getProjectName().equals("")) {
            return;
        }
        boolean useProjectFlag = false;
        for (AbstractCommand command : commandList) {
            if (command instanceof UseProjectCommand || command instanceof InteractiveCommand) {
                useProjectFlag = true;
            }
        }
        if (!useProjectFlag) {
            String commandText = "--project=" + sessionContext.getProjectName();
            UseProjectCommand useProjectCommand = new UseProjectCommand(sessionContext.getProjectName(),
                    commandText, sessionContext);
            int index = 0;
            boolean loginCommandExist = false;
            for (AbstractCommand command : commandList) {
                index++;
                if (command instanceof LoginCommand) {
                    loginCommandExist = true;
                    break;
                }

            }
            if (loginCommandExist) {
                commandList.add(index, useProjectCommand);
            } else {
                commandList.add(0, useProjectCommand);
            }
        }

        // ?use project?priorty
        // ?? InstancePriorityCommand
        boolean ipcExist = false;
        int useCommandIndex = 0;
        for (int i = 0; i < commandList.size(); i++) {
            AbstractCommand command = commandList.get(i);
            if (command instanceof InstancePriorityCommand) {
                ipcExist = true;
            } else if (command instanceof UseProjectCommand) {
                useCommandIndex = i + 1;
            }
        }
        if (!ipcExist) {
            // ??InstancePriorityCommand?Priority,?
            InstancePriorityCommand ipc = new InstancePriorityCommand(sessionContext.getPriority(),
                    "--instance_priority", sessionContext);
            commandList.add(useCommandIndex, ipc);
        }

    }

    private static void addCommand(List<AbstractCommand> commandList, AbstractCommand command,
            ExecutionContext sessionContext) {

        if (command != null) {

            // command.setContext(sessionContext);
            commandList.add(command);
        }
    }

    static List<PluginPriorityCommand> ecList;

    public static void printHelpInfo(List<String> keywords) {
        Map<String, Integer> matched = new HashMap<String, Integer>() {
        };
        List<String> allCommands = new ArrayList<String>();

        if (ecList == null) {
            ecList = PluginUtil.getExtendCommandList();
        }

        for (PluginPriorityCommand command : ecList) {
            allCommands.add(command.getCommandName());
        }

        for (String commandName : allCommands) {
            try {
                Class<?> commandClass = Class.forName(commandName, false, classLoader);
                Field tags_field = commandClass.getField(HELP_TAGS_FIELD);
                String[] tags = (String[]) tags_field.get(null);

                int count = 0;
                for (String tag : tags) {
                    for (String keyword : keywords) {
                        if (tag.equalsIgnoreCase(keyword)) {
                            count++;
                            break;
                        }
                    }
                }
                if (count != 0) {
                    matched.put(commandName, count);
                }
            } catch (ClassNotFoundException e) {
                // Console??AssertionError
                //throw new AssertionError("Cannot find the command:" + commandName);
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                // Ignore
            } catch (IllegalAccessException e) {
                // Ignore
            }
        }

        // Print the help info of the command(s) whose tags match most keywords
        boolean found = false;
        System.out.println();
        for (int i = keywords.size(); i >= 1; i--) {
            for (Map.Entry<String, Integer> entry : matched.entrySet()) {
                if (i == entry.getValue()) {
                    try {
                        Class<?> commandClass = Class.forName(entry.getKey(), false, classLoader);
                        Method printMethod = commandClass.getDeclaredMethod(HELP_PRINT_METHOD,
                                new Class<?>[] { PrintStream.class });
                        printMethod.invoke(null, System.out);
                        found = true;
                    } catch (ClassNotFoundException e) {
                        // Console??AssertionError
                        throw new AssertionError("Cannot find the command:" + entry.getKey());
                    } catch (NoSuchMethodException e) {
                        // Ignore
                    } catch (IllegalAccessException e) {
                        // Ignore
                    } catch (InvocationTargetException e) {
                        // Ignore
                    }
                }
            }
            if (found) {
                break;
            }
        }
        System.out.println();
    }

    private static void parseCommandLineCommand(List<AbstractCommand> commandList, List<String> optionList,
            ExecutionContext sessionContext) throws ODPSConsoleException {

        for (int i = 0; i < COMMANDLINE_COMMANDS.length; i++) {
            String commandName = COMMAND_PACKAGE + "." + COMMANDLINE_COMMANDS[i];
            AbstractCommand cmd = reflectCommandObject(commandName,
                    new Class<?>[] { List.class, ExecutionContext.class }, optionList, sessionContext);

            addCommand(commandList, cmd, sessionContext);
        }
        if (ecList == null) {
            ecList = PluginUtil.getExtendCommandList();
        }

        for (PluginPriorityCommand command : ecList) {
            String commandName = command.getCommandName();
            if (commandName != null && !"".equals(commandName.trim())) {
                AbstractCommand cmd = null;
                try {
                    cmd = reflectCommandObject(commandName, new Class<?>[] { List.class, ExecutionContext.class },
                            optionList, sessionContext);
                } catch (AssertionError e) {
                    // ?,console???
                    sessionContext.getOutputWriter().writeDebug(e.getMessage());
                    System.err.println("fail to load user command, pls check:" + commandName);
                }

                if (cmd != null) {
                    addCommand(commandList, cmd, sessionContext);
                    return;
                }
            }
        }
    }

    private static void parseInteractiveCommand(List<AbstractCommand> commandList, String commandText,
            ExecutionContext sessionContext, int queryNumber) throws ODPSConsoleException {

        if (ecList == null) {
            ecList = PluginUtil.getExtendCommandList();
        }

        for (int i = 0; i < INTERACTIVE_COMMANDS.length; i++) {

            String commandName = COMMAND_PACKAGE + "." + INTERACTIVE_COMMANDS[i];
            // ?
            ecList.add(new PluginPriorityCommand(commandName, PluginPriorityCommand.MAX_PRIORITY));
        }

        // console?

        String userCommands = sessionContext.getUserCommands();

        if (userCommands != null) {
            // ????
            for (String commandString : Arrays.asList(userCommands.split(","))) {
                ecList.add(new PluginPriorityCommand(commandString, PluginPriorityCommand.MAX_PRIORITY));
            }

        }
        // QueryCommand,
        ecList.add(new PluginPriorityCommand(COMMAND_PACKAGE + "." + "QueryCommand",
                PluginPriorityCommand.MIN_PRIORITY));
        Collections.sort(ecList);

        for (PluginPriorityCommand command : ecList) {
            String commandName = command.getCommandName();
            if (commandName != null && !"".equals(commandName.trim())) {
                AbstractCommand cmd = null;
                try {
                    cmd = reflectCommandObject(commandName, new Class<?>[] { String.class, ExecutionContext.class },
                            commandText, sessionContext);
                } catch (AssertionError e) {
                    // ?,console???
                    sessionContext.getOutputWriter().writeDebug(e.getMessage());
                    System.err.println("fail to load user command, pls check:" + commandName);
                }

                if (cmd != null) {
                    cmd.setCommandStep(queryNumber);
                    addCommand(commandList, cmd, sessionContext);
                    return;
                }
            }
        }
    }

    static URLClassLoader classLoader;

    public static void loadPlugins() {
        List<URL> pluginJarList = PluginUtil.getPluginsJarList();
        URL[] urls = (URL[]) pluginJarList.toArray(new URL[pluginJarList.size()]);
        classLoader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
    }

    private static AbstractCommand reflectCommandObject(String commandName, Class<?>[] argTypes, Object... args)
            throws ODPSConsoleException {

        Class<?> commandClass = null;
        try {

            if (classLoader == null) {
                loadPlugins();
            }

            commandClass = Class.forName(commandName, false, classLoader);
            Method parseMethod = commandClass.getDeclaredMethod("parse", argTypes);

            Object commandObject = parseMethod.invoke(null, args);

            if (commandObject != null) {
                return (AbstractCommand) commandObject;
            } else {
                return null;
            }
        } catch (ClassNotFoundException e) {
            // Console??AssertionError
            throw new AssertionError("Cannot find the command:" + commandName);
        } catch (SecurityException e) {
            throw new AssertionError("Cannot find the parse method on the command: " + commandName);
        } catch (NoSuchMethodException e) {
            //FOR there's two kind of command,not throw exception
            return null;
        } catch (IllegalArgumentException e) {
            throw new AssertionError("Failed to invoke the parse method on the command:" + commandName);
        } catch (IllegalAccessException e) {
            throw new AssertionError("Failed to invoke the parse method on the command:" + commandName);
        } catch (InvocationTargetException e) {
            if (e.getCause() instanceof ODPSConsoleException) {
                String msg = e.getCause().getMessage();
                if (!StringUtils.isNullOrEmpty(msg) && msg.contains(ODPSConsoleConstants.BAD_COMMAND)
                        && commandClass != null) {
                    String output = getCommandUsageString(commandClass);
                    if (output != null) {
                        throw new ODPSConsoleException(e.getCause().getMessage() + "\n" + output);
                    }
                }
                throw (ODPSConsoleException) e.getCause();
            } else {
                throw new ODPSConsoleException(e.getCause());
            }
        }
    }

    private static String getCommandUsageString(Class<?> commandClass) {
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(os);
            Method printMethod = commandClass.getDeclaredMethod("printUsage", new Class<?>[] { PrintStream.class });
            printMethod.invoke(null, ps);
            return os.toString("UTF8");

        } catch (Exception e) {
            return null;
        }
    }

    // ??-e"", --project=??option
    private static List<String> populateOptions(List<String> optionList) {

        List<String> resultList = new LinkedList<String>();

        for (String optionStr : optionList) {

            if (!StringUtils.isNullOrEmpty(optionStr)) {
                String option = optionStr.trim();

                if (option.matches("^--[^= ]+ *= *[^ ]+")) {

                    // ^--[^-= ]+ *= *[^ ]+
                    String[] cmds = option.split("=", 2);

                    // ??command?
                    if (cmds[0].trim().equals("--username")) {
                        resultList.add("-u");
                    } else if (cmds[0].trim().equals("--password")) {
                        resultList.add("-p");
                    } else {
                        resultList.add(cmds[0].trim());
                    }

                    resultList.add(cmds[1].trim());

                } else if (option.matches("-[a-z].+")) {

                    resultList.add(option.substring(0, 2));
                    resultList.add(option.substring(2));

                } else {
                    resultList.add(option);
                }
            }
        }

        return resultList;
    }
}