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.groovy.command;
020
021import groovy.lang.Binding;
022import groovy.lang.Closure;
023import org.crsh.cli.impl.Delimiter;
024import org.crsh.cli.impl.completion.CompletionMatch;
025import org.crsh.cli.spi.Completion;
026import org.crsh.command.CommandContext;
027import org.crsh.command.CommandCreationException;
028import org.crsh.command.CommandInvoker;
029import org.crsh.command.DescriptionFormat;
030import org.crsh.command.InvocationContextImpl;
031import org.crsh.command.RuntimeContext;
032import org.crsh.command.ShellCommand;
033import org.crsh.shell.ErrorType;
034import org.crsh.text.RenderPrintWriter;
035import org.crsh.util.Strings;
036
037import java.io.IOException;
038import java.lang.reflect.UndeclaredThrowableException;
039import java.util.List;
040import java.util.Map;
041
042/** @author Julien Viet */
043public class GroovyScriptShellCommand<C extends GroovyScriptCommand> implements ShellCommand {
044
045  /** . */
046  private final Class<C> clazz;
047
048  public GroovyScriptShellCommand(Class<C> clazz) {
049    this.clazz = clazz;
050  }
051
052  private C createCommand() throws CommandCreationException {
053    C command;
054    try {
055      command = clazz.newInstance();
056    }
057    catch (Exception e) {
058      String name = clazz.getSimpleName();
059      throw new CommandCreationException(name, ErrorType.INTERNAL, "Could not create command " + name + " instance", e);
060    }
061    return command;
062  }
063
064  public final CompletionMatch complete(RuntimeContext context, String line) {
065    return new CompletionMatch(Delimiter.EMPTY, Completion.create());
066  }
067
068  public final String describe(String line, DescriptionFormat mode) {
069    return null;
070  }
071
072  public final CommandInvoker<?, ?> resolveInvoker(String line) throws CommandCreationException {
073    List<String> chunks = Strings.chunks(line);
074    String[] args = chunks.toArray(new String[chunks.size()]);
075    return getInvoker(args);
076  }
077
078  public final CommandInvoker<?, ?> resolveInvoker(String name, Map<String, ?> options, List<?> args) throws CommandCreationException {
079    String[] tmp = new String[args.size()];
080    for (int i = 0;i < tmp.length;i++) {
081      tmp[i] = args.get(i).toString();
082    }
083    return getInvoker(tmp);
084  }
085
086  private CommandInvoker<Object, Object> getInvoker(final String[] args) throws CommandCreationException {
087    final C command = createCommand();
088    return new CommandInvoker<Object, Object>() {
089
090      public final Class<Object> getProducedType() {
091        return Object.class;
092      }
093
094      public final Class<Object> getConsumedType() {
095        return Object.class;
096      }
097
098      public void open(CommandContext<? super Object> consumer) {
099        // Set up current binding
100        Binding binding = new Binding(consumer.getSession());
101
102        // Set the args on the script
103        binding.setProperty("args", args);
104
105        //
106        command.setBinding(binding);
107
108        //
109        command.pushContext(new InvocationContextImpl<Object>((CommandContext<Object>)consumer));
110
111        //
112        try {
113          //
114          Object res = command.run();
115
116          // Evaluate the closure
117          if (res instanceof Closure) {
118            Closure closure = (Closure)res;
119            res = closure.call(args);
120          }
121
122          //
123          if (res != null) {
124            RenderPrintWriter writer = command.peekContext().getWriter();
125            if (writer.isEmpty()) {
126              writer.print(res);
127            }
128          }
129        }
130        catch (Exception t) {
131          throw GroovyCommand.unwrap(t);
132        }
133      }
134
135      public void provide(Object element) throws IOException {
136        // Should never be called
137      }
138
139      public void flush() throws IOException {
140        command.peekContext().flush();
141      }
142
143      public void close() throws IOException, UndeclaredThrowableException {
144        command.popContext();
145      }
146    };
147  }
148
149
150}