001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019package org.crsh.lang.impl.script;
020
021import org.crsh.cli.impl.Delimiter;
022import org.crsh.cli.impl.completion.CompletionMatch;
023import org.crsh.cli.spi.Completion;
024import org.crsh.lang.spi.*;
025import org.crsh.shell.impl.command.RuntimeContextImpl;
026import org.crsh.shell.impl.command.ShellSession;
027import org.crsh.shell.impl.command.spi.CommandException;
028import org.crsh.shell.impl.command.spi.CommandInvoker;
029import org.crsh.shell.impl.command.spi.Command;
030import org.crsh.shell.ShellResponse;
031import org.crsh.util.Utils;
032
033import java.util.Map;
034import java.util.logging.Level;
035import java.util.logging.Logger;
036
037/** @author Julien Viet */
038public class ScriptRepl implements Repl {
039
040  /** . */
041  private static final ScriptRepl instance = new ScriptRepl();
042
043  /** . */
044  static final Logger log = Logger.getLogger(ScriptRepl.class.getName());
045
046  public static ScriptRepl getInstance() {
047    return instance;
048  }
049
050  /** . */
051  private final Language lang = new Language() {
052    @Override public String getName() { return "script"; }
053    @Override public String getDisplayName() { return "Script 1.0"; }
054    @Override public boolean isActive() { return true; }
055    @Override public Repl getRepl() { return ScriptRepl.this; }
056    @Override public org.crsh.lang.spi.Compiler getCompiler() { return ScriptCompiler.instance; }
057    @Override public void init(ShellSession session) { }
058    @Override public void destroy(ShellSession session) { }
059  };
060
061  private ScriptRepl() {
062  }
063
064  @Override
065  public Language getLanguage() {
066    return lang;
067  }
068
069  public String getName() {
070    return "script";
071  }
072
073  @Override
074  public String getDescription() {
075    return "The Script repl provides command line interpreter with a bash like syntax";
076  }
077
078  public ReplResponse eval(ShellSession session, String request) {
079    PipeLineFactory factory;
080    try {
081      factory = Token.parse(request).createFactory();
082    }
083    catch (CommandException e) {
084      return new ReplResponse.Response(ShellResponse.error(e.getErrorKind(), e.getMessage(), e.getCause()));
085    }
086    if (factory != null) {
087      try {
088        CommandInvoker<Void, Object> invoker = factory.create(session);
089        return new ReplResponse.Invoke(invoker);
090      }
091      catch (CommandNotFoundException e) {
092        log.log(Level.FINER, "Could not create command", e);
093        return new ReplResponse.Response(ShellResponse.unknownCommand(e.getName()));
094      }
095      catch (CommandException e) {
096        log.log(Level.FINER, "Could not create command", e);
097        return new ReplResponse.Response(ShellResponse.error(e.getErrorKind(), e.getMessage(), e));
098      }
099    } else {
100      return new ReplResponse.Response(ShellResponse.noCommand());
101    }
102  }
103
104  public CompletionMatch complete(ShellSession session, String prefix) {
105    Token ast = Token.parse(prefix);
106    String termPrefix;
107    if (ast != null) {
108      Token last = ast.getLast();
109      termPrefix = Utils.trimLeft(last.value);
110    } else {
111      termPrefix = "";
112    }
113
114    //
115    log.log(Level.FINE, "Retained term prefix is " + termPrefix);
116    CompletionMatch completion;
117    int pos = termPrefix.indexOf(' ');
118    if (pos == -1) {
119      Completion.Builder builder = Completion.builder(termPrefix);
120      for (Map.Entry<String, String> command : session.getCommands()) {
121        String name = command.getKey();
122        if (name.startsWith(termPrefix)) {
123          builder.add(name.substring(termPrefix.length()), true);
124        }
125      }
126      completion = new CompletionMatch(Delimiter.EMPTY, builder.build());
127    } else {
128      String commandName = termPrefix.substring(0, pos);
129      termPrefix = termPrefix.substring(pos);
130      try {
131        Command<?> command = session.getCommand(commandName);
132        if (command != null) {
133          completion = command.complete(new RuntimeContextImpl(session, session.getContext().getAttributes()), termPrefix);
134        } else {
135          completion = new CompletionMatch(Delimiter.EMPTY, Completion.create());
136        }
137      }
138      catch (CommandException e) {
139        log.log(Level.FINE, "Could not create command for completion of " + prefix, e);
140        completion = new CompletionMatch(Delimiter.EMPTY, Completion.create());
141      }
142    }
143
144    //
145    return completion;
146  }
147}