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 020 package org.crsh.command; 021 022 import groovy.lang.Closure; 023 import groovy.lang.MissingMethodException; 024 import groovy.lang.MissingPropertyException; 025 import org.codehaus.groovy.runtime.InvokerInvocationException; 026 import org.crsh.io.Consumer; 027 import org.crsh.shell.InteractionContext; 028 029 import java.io.IOException; 030 import java.util.ArrayList; 031 import java.util.Collections; 032 import java.util.HashMap; 033 import java.util.List; 034 import java.util.Map; 035 036 final class ClassDispatcher extends CommandClosure { 037 038 /** . */ 039 final Object owner; 040 041 /** . */ 042 final ShellCommand command; 043 044 ClassDispatcher(ShellCommand command, Object owner) { 045 super(new Object()); 046 047 // 048 this.command = command; 049 this.owner = owner; 050 } 051 052 @Override 053 public Object getProperty(String property) { 054 try { 055 return super.getProperty(property); 056 } 057 catch (MissingPropertyException e) { 058 return new MethodDispatcher(this, property); 059 } 060 } 061 062 @Override 063 public Object invokeMethod(String name, Object args) { 064 try { 065 return super.invokeMethod(name, args); 066 } 067 catch (MissingMethodException e) { 068 return dispatch(name, unwrapArgs(args)); 069 } 070 } 071 072 /** 073 * Closure invocation. 074 * 075 * @param arguments the closure arguments 076 */ 077 public Object call(Object[] arguments) { 078 return dispatch("", arguments); 079 } 080 081 Object dispatch(String methodName, Object[] arguments) { 082 PipeCommandProxy pipe = resolvePipe(methodName, arguments); 083 084 // 085 try { 086 pipe.fire(); 087 pipe.close(); 088 return null; 089 } 090 catch (ScriptException e) { 091 Throwable cause = e.getCause(); 092 if (cause != null) { 093 throw new InvokerInvocationException(cause); 094 } else { 095 throw e; 096 } 097 } 098 } 099 100 private PipeCommandProxy<?, Object> resolvePipe(String name, Object[] args) { 101 final Closure closure; 102 int to = args.length; 103 if (to > 0 && args[to - 1] instanceof Closure) { 104 closure = (Closure)args[--to]; 105 } else { 106 closure = null; 107 } 108 109 // 110 Map<String, Object> invokerOptions = this.options != null ? this.options : Collections.<String, Object>emptyMap(); 111 List<Object> invokerArgs = this.args != null ? this.args : Collections.emptyList(); 112 113 // 114 if (to > 0) { 115 Object first = args[0]; 116 int from; 117 if (first instanceof Map<?, ?>) { 118 from = 1; 119 Map<?, ?> options = (Map<?, ?>)first; 120 if (options.size() > 0) { 121 invokerOptions = new HashMap<String, Object>(invokerOptions); 122 for (Map.Entry<?, ?> option : options.entrySet()) { 123 String optionName = option.getKey().toString(); 124 Object optionValue = option.getValue(); 125 invokerOptions.put(optionName, optionValue); 126 } 127 } 128 } else { 129 from = 0; 130 } 131 132 if (from < to) { 133 invokerArgs = new ArrayList<Object>(invokerArgs); 134 while (from < to) { 135 Object o = args[from++]; 136 if (o != null) { 137 invokerArgs.add(o); 138 } 139 } 140 } 141 } 142 143 // 144 CommandInvoker<Void, Void> invoker = (CommandInvoker<Void, Void>)command.resolveInvoker(name, invokerOptions, invokerArgs); 145 146 // 147 InvocationContext context; 148 if (owner instanceof CRaSHCommand) { 149 context = ((CRaSHCommand)owner).peekContext(); 150 } else if (owner instanceof GroovyScriptCommand) { 151 context = (InvocationContext)((GroovyScriptCommand)owner).peekContext(); 152 } else { 153 throw new UnsupportedOperationException("todo"); 154 } 155 156 // 157 Consumer producer; 158 if (closure != null) { 159 CommandInvoker producerPipe; 160 if (closure instanceof MethodDispatcher) { 161 MethodDispatcher commandClosure = (MethodDispatcher)closure; 162 producerPipe = commandClosure.dispatcher.resolvePipe(commandClosure.name, new Object[0]); 163 } else if (closure instanceof ClassDispatcher) { 164 ClassDispatcher dispatcherClosure = (ClassDispatcher)closure; 165 producerPipe = dispatcherClosure.resolvePipe(name, new Object[0]); 166 } else { 167 168 // That's the type we cast to 169 Class[] pt = closure.getParameterTypes(); 170 final Class type; 171 if (pt.length > 0) { 172 type = pt[0]; 173 } else { 174 type = Void.class; 175 } 176 177 // 178 producerPipe = new CommandInvoker<Object, Void>() { 179 public Class<Void> getProducedType() { 180 return Void.class; 181 } 182 public Class<Object> getConsumedType() { 183 return type; 184 } 185 public void setSession(CommandContext session) { 186 } 187 public void setPiped(boolean piped) { 188 } 189 public void open(InteractionContext<Void> consumer) { 190 } 191 public void close() { 192 } 193 public void provide(Object element) throws IOException { 194 if (type.isInstance(element)) { 195 closure.call(element); 196 } 197 } 198 public void flush() throws IOException { 199 } 200 }; 201 } 202 producerPipe.setPiped(true); 203 producer = producerPipe; 204 } else { 205 producer = context; 206 } 207 208 // 209 InnerInvocationContext inner = new InnerInvocationContext(context, producer); 210 PipeCommandProxy pipe = new PipeCommandProxy(inner, invoker, producer); 211 pipe.setSession(context); 212 return pipe; 213 } 214 }