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.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.invocation.InvocationMatch;
026 import org.crsh.cli.impl.lang.CommandFactory;
027 import org.crsh.cli.impl.lang.Instance;
028 import org.crsh.cli.spi.Completer;
029 import org.crsh.command.CommandContext;
030 import org.crsh.shell.impl.command.spi.CommandCreationException;
031 import org.crsh.lang.groovy.ast.ScriptLastStatementTransformer;
032 import org.crsh.shell.impl.command.spi.Command;
033 import org.crsh.shell.impl.command.spi.CommandInvoker;
034 import org.crsh.shell.impl.command.InvocationContextImpl;
035 import org.crsh.command.RuntimeContext;
036 import org.crsh.shell.impl.command.spi.ShellCommand;
037 import org.crsh.shell.ErrorType;
038 import org.crsh.util.Utils;
039
040 import java.io.IOException;
041 import java.lang.reflect.UndeclaredThrowableException;
042 import java.util.List;
043
044 /** @author Julien Viet */
045 public 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 }