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