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.descriptor.CommandDescriptor;
024import org.crsh.cli.impl.descriptor.HelpDescriptor;
025import org.crsh.cli.impl.invocation.InvocationMatch;
026import org.crsh.cli.impl.lang.CommandFactory;
027import org.crsh.cli.impl.lang.Instance;
028import org.crsh.cli.spi.Completer;
029import org.crsh.command.CommandContext;
030import org.crsh.shell.impl.command.spi.CommandCreationException;
031import org.crsh.lang.groovy.ast.ScriptLastStatementTransformer;
032import org.crsh.shell.impl.command.spi.Command;
033import org.crsh.shell.impl.command.spi.CommandInvoker;
034import org.crsh.shell.impl.command.InvocationContextImpl;
035import org.crsh.command.RuntimeContext;
036import org.crsh.shell.impl.command.spi.ShellCommand;
037import org.crsh.shell.ErrorType;
038import org.crsh.util.Utils;
039
040import java.io.IOException;
041import java.lang.reflect.UndeclaredThrowableException;
042import java.util.List;
043
044/** @author Julien Viet */
045public class GroovyScriptShellCommand<T extends GroovyScriptCommand> extends ShellCommand<Instance<T>> {
046
047  /** . */
048  private final Class<T> clazz;
049
050  /** . */
051  private final boolean hasExplicitReturn;
052
053  /** . */
054  private final CommandDescriptor<Instance<T>> descriptor;
055
056  public GroovyScriptShellCommand(Class<T> clazz) {
057
058    //
059    CommandFactory factory = new CommandFactory(getClass().getClassLoader());
060
061    boolean hasExplicitReturn;
062    try {
063      clazz.getDeclaredField(ScriptLastStatementTransformer.FIELD_NAME);
064      hasExplicitReturn = true;
065    }
066    catch (NoSuchFieldException e) {
067      hasExplicitReturn = false;
068    }
069
070    //
071    this.clazz = clazz;
072    this.descriptor = HelpDescriptor.create(factory.create(clazz));
073    this.hasExplicitReturn = hasExplicitReturn;
074  }
075
076  @Override
077  public CommandDescriptor<Instance<T>> getDescriptor() {
078    return descriptor;
079  }
080
081  @Override
082  protected Command<?, ?> resolveCommand(final InvocationMatch<Instance<T>> match) {
083    return new Command<Void, Object>() {
084      @Override
085      public CommandInvoker<Void, Object> getInvoker() throws CommandCreationException {
086        List<String> chunks = Utils.chunks(match.getRest());
087        String[] args = chunks.toArray(new String[chunks.size()]);
088        return GroovyScriptShellCommand.this.getInvoker(args);
089      }
090
091      @Override
092      public InvocationMatch<?> getMatch() {
093        return match;
094      }
095
096      @Override
097      public Class<Object> getProducedType() {
098        return Object.class;
099      }
100
101      @Override
102      public Class<Void> getConsumedType() {
103        return Void.class;
104      }
105    };
106  }
107
108  private T createCommand() throws CommandCreationException {
109    T command;
110    try {
111      command = clazz.newInstance();
112    }
113    catch (Exception e) {
114      String name = clazz.getSimpleName();
115      throw new CommandCreationException(name, ErrorType.INTERNAL, "Could not create command " + name + " instance", e);
116    }
117    return command;
118  }
119
120  @Override
121  protected Completer getCompleter(RuntimeContext context) throws CommandCreationException {
122    return null;
123  }
124
125  private CommandInvoker<Void, Object> getInvoker(final String[] args) throws CommandCreationException {
126    final T command = createCommand();
127    return new CommandInvoker<Void, Object>() {
128
129      /** . */
130      private org.crsh.command.InvocationContext<Object> context;
131
132      public final Class<Object> getProducedType() {
133        return Object.class;
134      }
135
136      public final Class<Void> getConsumedType() {
137        return Void.class;
138      }
139
140      public void open(CommandContext<? super Object> consumer) {
141
142        // Set the context
143        context = new InvocationContextImpl<Object>((CommandContext<Object>)consumer);
144
145        // Set up current binding
146        Binding binding = new Binding(consumer.getSession());
147
148        // Set the args on the script
149        binding.setProperty("args", args);
150
151        //
152        command.setBinding(binding);
153
154
155        //
156        command.pushContext(context);
157
158        //
159        try {
160          //
161          Object ret = command.run();
162
163          // Evaluate the closure
164          if (ret instanceof Closure) {
165            Closure closure = (Closure)ret;
166            ret = closure.call(args);
167          }
168
169          //
170          if (ret != null) {
171            if (hasExplicitReturn) {
172              context.provide(ret);
173            }
174          }
175        }
176        catch (Exception t) {
177          throw GroovyCommand.unwrap(t);
178        }
179      }
180
181      public void provide(Void element) throws IOException {
182        // Should never be called
183      }
184
185      public void flush() throws IOException {
186        context.flush();
187      }
188
189      public void close() throws IOException, UndeclaredThrowableException {
190        context = null;
191        command.popContext();
192      }
193    };
194  }
195
196
197}