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