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}