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