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.command;
020
021 import groovy.lang.Binding;
022 import groovy.lang.Closure;
023 import groovy.lang.MissingMethodException;
024 import groovy.lang.MissingPropertyException;
025 import groovy.lang.Script;
026 import org.codehaus.groovy.runtime.InvokerInvocationException;
027 import org.crsh.cmdline.CommandCompletion;
028 import org.crsh.cmdline.Delimiter;
029 import org.crsh.cmdline.spi.ValueCompletion;
030 import org.crsh.shell.impl.command.CRaSH;
031 import org.crsh.text.RenderPrintWriter;
032 import org.crsh.util.Strings;
033
034 import java.io.IOException;
035 import java.util.LinkedList;
036 import java.util.List;
037 import java.util.Map;
038
039 public abstract class GroovyScriptCommand extends Script implements ShellCommand, CommandInvoker<Object, Object> {
040
041 /** . */
042 private LinkedList<InvocationContext<?>> stack;
043
044 /** The current context. */
045 protected CommandContext context;
046
047 /** The current output. */
048 protected RenderPrintWriter out;
049
050 /** . */
051 private String[] args;
052
053 protected GroovyScriptCommand() {
054 this.stack = null;
055 this.context = null;
056 }
057
058 public final void pushContext(InvocationContext<?> context) throws NullPointerException {
059 if (context == null) {
060 throw new NullPointerException();
061 }
062
063 //
064 if (stack == null) {
065 stack = new LinkedList<InvocationContext<?>>();
066 }
067
068 // Save current context (is null the first time)
069 stack.addLast((InvocationContext)this.context);
070
071 // Set new context
072 this.context = context;
073 this.out = context.getWriter();
074 }
075
076 public final InvocationContext<?> popContext() {
077 if (stack == null || stack.isEmpty()) {
078 throw new IllegalStateException("Cannot pop a context anymore from the stack");
079 }
080 InvocationContext context = (InvocationContext)this.context;
081 this.context = stack.removeLast();
082 this.out = this.context != null ? ((InvocationContext)this.context).getWriter() : null;
083 return context;
084 }
085
086 public final InvocationContext<?> peekContext() {
087 return (InvocationContext<?>)context;
088 }
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 @Override
099 public Object invokeMethod(String name, Object args) {
100
101 //
102 try {
103 return super.invokeMethod(name, args);
104 }
105 catch (MissingMethodException e) {
106 if (context instanceof InvocationContext) {
107 InvocationContext ic = (InvocationContext)context;
108 CRaSH crash = (CRaSH)context.getSession().get("crash");
109 if (crash != null) {
110 ShellCommand cmd;
111 try {
112 cmd = crash.getCommand(name);
113 }
114 catch (NoSuchCommandException ce) {
115 throw new InvokerInvocationException(ce);
116 }
117 if (cmd != null) {
118 ClassDispatcher dispatcher = new ClassDispatcher(cmd, this);
119 return dispatcher.dispatch("", CommandClosure.unwrapArgs(args));
120 }
121 }
122 }
123
124 //
125 throw e;
126 }
127 }
128
129 @Override
130 public final Object getProperty(String property) {
131 if ("out".equals(property)) {
132 if (context instanceof InvocationContext<?>) {
133 return ((InvocationContext<?>)context).getWriter();
134 } else {
135 return null;
136 }
137 } else if ("context".equals(property)) {
138 return context;
139 } else {
140 if (context instanceof InvocationContext<?>) {
141 CRaSH crash = (CRaSH)context.getSession().get("crash");
142 if (crash != null) {
143 try {
144 ShellCommand cmd = crash.getCommand(property);
145 if (cmd != null) {
146 return new ClassDispatcher(cmd, this);
147 }
148 } catch (NoSuchCommandException e) {
149 throw new InvokerInvocationException(e);
150 }
151 }
152 }
153
154 //
155 try {
156 return super.getProperty(property);
157 }
158 catch (MissingPropertyException e) {
159 return null;
160 }
161 }
162 }
163
164 public final CommandCompletion complete(CommandContext context, String line) {
165 return new CommandCompletion(Delimiter.EMPTY, ValueCompletion.create());
166 }
167
168 public String describe(String line, DescriptionFormat mode) {
169 return null;
170 }
171
172 public final PipeCommand<Object> invoke(final InvocationContext<Object> context) throws ScriptException {
173 return new PipeCommand<Object>() {
174
175 @Override
176 public void open() throws ScriptException {
177 // Set up current binding
178 Binding binding = new Binding(context.getSession());
179
180 // Set the args on the script
181 binding.setProperty("args", args);
182
183 //
184 setBinding(binding);
185
186 //
187 GroovyScriptCommand.this.context = context;
188 try {
189 //
190 Object res = run();
191
192 // Evaluate the closure
193 if (res instanceof Closure) {
194 Closure closure = (Closure)res;
195 res = closure.call(args);
196 }
197
198 //
199 if (res != null) {
200 RenderPrintWriter writer = context.getWriter();
201 if (writer.isEmpty()) {
202 writer.print(res);
203 }
204 }
205 }
206 catch (Exception t) {
207 throw CRaSHCommand.toScript(t);
208 }
209 finally {
210 GroovyScriptCommand.this.context = null;
211 }
212 }
213
214 @Override
215 public void provide(Object element) throws IOException {
216 // Should never be called
217 }
218
219 @Override
220 public void flush() throws IOException {
221 context.flush();
222 }
223 };
224 }
225
226 public final CommandInvoker<?, ?> resolveInvoker(String line) {
227 List<String> chunks = Strings.chunks(line);
228 this.args = chunks.toArray(new String[chunks.size()]);
229 return this;
230 }
231
232 public CommandInvoker<?, ?> resolveInvoker(String name, Map<String, ?> options, List<?> args) {
233 String[] tmp = new String[args.size()];
234 for (int i = 0;i < tmp.length;i++) {
235 tmp[i] = args.get(i).toString();
236 }
237 this.args = tmp;
238 return this;
239 }
240 }