Package org.waveprotocol.box.server.robots.agent

Source Code of org.waveprotocol.box.server.robots.agent.AbstractCliRobotAgent

/**
* 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.waveprotocol.box.server.robots.agent;

import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.appendLine;
import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.lastEnteredLineOf;

import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
import com.google.wave.api.Blip;
import com.google.wave.api.event.DocumentChangedEvent;
import com.google.wave.api.event.WaveletSelfAddedEvent;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.waveprotocol.box.server.CoreSettings;
import org.waveprotocol.box.server.persistence.AccountStore;
import org.waveprotocol.box.server.robots.register.RobotRegistrar;
import org.waveprotocol.box.server.robots.register.RobotRegistrarImpl;
import org.waveprotocol.wave.model.id.TokenGenerator;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
* The base for robot agents that run on the WIAB server and interact with users
* by entering commands as text in the blips.
*
* @author yurize@apache.org (Yuri Zelikov)
*/
@SuppressWarnings("serial")
public abstract class AbstractCliRobotAgent extends AbstractBaseRobotAgent {

  /** The options for the command. */
  private final Options options;
  private final CommandLineParser parser;
  private final HelpFormatter helpFormatter;

  /**
   * Constructor. Initializes the agent to serve on the URI provided by
   * {@link #getRobotUri()} and ensures that the agent is registered in the
   * Account store.
   *
   * @param injector the injector instance.
   */
  public AbstractCliRobotAgent(Injector injector) {
    this(injector.getInstance(Key.get(String.class, Names.named(CoreSettings.WAVE_SERVER_DOMAIN))),
        injector.getInstance(TokenGenerator.class), injector
            .getInstance(ServerFrontendAddressHolder.class), injector
            .getInstance(AccountStore.class), injector.getInstance(RobotRegistrarImpl.class),
        injector.getInstance(Key.get(Boolean.class, Names.named(CoreSettings.ENABLE_SSL))));
  }

  /**
   * Constructor. Initializes the agent to serve on the URI provided by
   * {@link #getRobotUri()} and ensures that the agent is registered in the
   * Account store.
   */
  AbstractCliRobotAgent(String waveDomain, TokenGenerator tokenGenerator,
      ServerFrontendAddressHolder frontendAddressHolder, AccountStore accountStore,
      RobotRegistrar robotRegistrar, Boolean sslEnabled) {
    super(waveDomain, tokenGenerator, frontendAddressHolder, accountStore, robotRegistrar, sslEnabled);
    parser = new PosixParser();
    helpFormatter = new HelpFormatter();
    options = initOptions();
  }

  /**
   * Displays a short description when the robot is added to a wave.
   */
  @Override
  public void onWaveletSelfAdded(WaveletSelfAddedEvent event) {
    String robotAddress = event.getWavelet().getRobotAddress();
    // Display a short description.
    appendLine(event.getBlip(), "\n" + robotAddress + ": I am listening.\n" + getShortDescription()
        + "\nFor help type " + "\"" + getCommandName()
        + " -help\" on a new line and hit \"Enter\".");
  }

  @Override
  public void onDocumentChanged(DocumentChangedEvent event) {
    Blip blip = event.getBlip();
    String modifiedBy = event.getModifiedBy();
    CommandLine commandLine = null;
    try {
      commandLine = preprocessCommand(blip.getContent());
    } catch (IllegalArgumentException e) {
      appendLine(blip, e.getMessage());
    }
    if (commandLine != null) {
      if (commandLine.hasOption("help")
          // Or if only options.
          || (commandLine.getArgs().length - commandLine.getOptions().length <= 1)) {
        appendLine(blip, getFullDescription());
      } else {
        String robotMessage = maybeExecuteCommand(commandLine, modifiedBy);
        appendLine(blip, robotMessage);
      }
    }
  }

  /**
   * Validates and parses the input for the command.
   *
   * @param blipContent the blip contents.
   * @return the command line {@link CommandLine} object with parsed data from
   *         the blip contents or null in case the content doesn't contain a
   *         command.
   * @throws IllegalArgumentException if illegal arguments passed to the
   *         command.
   */
  protected CommandLine preprocessCommand(String blipContent) throws IllegalArgumentException {
    CommandLine commandLine = null;
    String lastLine = lastEnteredLineOf(blipContent);
    if (lastLine != null) {
      try {
        commandLine = parse(lastLine.split(" "));
      } catch (ParseException e) {
        throw new IllegalArgumentException(e);
      }
      String[] args = commandLine.getArgs();
      if (!args[0].equals(getCommandName())) {
        return null;
      }
      int argsNum = args.length - commandLine.getOptions().length - 1;
      // If there are only options in the command - then it is also invalid and
      // have to display usage anyway.
      if ((argsNum > 0)
          && (argsNum < getMinNumOfArguments() || argsNum > getMaxNumOfArguments())) {
        String message = null;
        if (getMinNumOfArguments() == getMaxNumOfArguments()) {
          message =
            String.format("Invalid number of arguments. Expected: %d , actual: %d %s",
                getMinNumOfArguments(), argsNum, getUsage());
        } else {
          message =
            String.format(
                "Invalid number of arguments. Expected between %d and %d, actual: %d. %s",
                getMinNumOfArguments(), getMaxNumOfArguments(), argsNum, getUsage());
        }
        throw new IllegalArgumentException(message);
      }
    }
    return commandLine;
  }

  @Override
  protected String getRobotProfilePageUrl() {
    return null;
  }

  /**
   * Returns the command options usage.
   */
  public String getUsage() {
    StringWriter stringWriter = new StringWriter();
    PrintWriter pw = new PrintWriter(stringWriter);
    // HelpFormatter doesn't provide other ways to access defaultWidth, so we
    // forced to access it in a deprecated way.
    // TODO (user) Update this code to remove access of deprecated fields when
    // it will be possible.
    helpFormatter.printHelp(pw, helpFormatter.defaultWidth, getCommandName() + " "
        + getCmdLineSyntax() + " \n", null, options, helpFormatter.defaultLeftPad,
        helpFormatter.defaultDescPad, "", false);
    pw.flush();
    String usageStr = stringWriter.toString();
    return usageStr;
  }

  /**
   * Initializes basic options. Override if more options needed.
   *
   * @return the command options.
   */
  protected Options initOptions() {
    // Create Options.
    Options options = new Options();
    // The robot has only "help" option.
    @SuppressWarnings({"static-access", "static"})
    Option help = OptionBuilder.withDescription("Displays help for the command.").create("help");
    options.addOption(help);
    return options;
  }

  protected CommandLine parse(String... args) throws ParseException {
    return getParser().parse(getOptions(), args);
  }

  /**
   * Returns the command line parser.
   */
  protected CommandLineParser getParser() {
    return parser;
  }

  /**
   * Returns the command options.
   */
  protected Options getOptions() {
    return options;
  }

  /**
   * Attempts to execute the command.
   *
   * @param commandLine the commandLine with arguments and/or options entered by
   *        the user.
   * @param modifiedBy the user that entered the content.
   * @return the result message: success or failure.
   */
  protected abstract String maybeExecuteCommand(CommandLine commandLine, String modifiedBy);

  /**
   * Returns the short description of the robot.
   */
  public abstract String getShortDescription();

  /**
   * Returns the full robot description.
   */
  public abstract String getFullDescription();

  /**
   * Returns the command name for the robot.
   */
  public abstract String getCommandName();

  /**
   * Returns the command line syntax.
   */
  public abstract String getCmdLineSyntax();

  /**
   * Returns the command use example.
   */
  public abstract String getExample();

  /**
   * Returns the minimum number of arguments this command accepts. Should be
   * greater than zero and less or equal to {@link #getMaxNumOfArguments()}.
   */
  public abstract int getMinNumOfArguments();

  /**
   * Returns the maximum number of arguments this command accepts.
   */
  public abstract int getMaxNumOfArguments();
}
TOP

Related Classes of org.waveprotocol.box.server.robots.agent.AbstractCliRobotAgent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.