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.util.Safe; 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, false); 083 084 // 085 try { 086 pipe.fire(); 087 return null; 088 } 089 catch (ScriptException e) { 090 Throwable cause = e.getCause(); 091 if (cause != null) { 092 throw new InvokerInvocationException(cause); 093 } else { 094 throw e; 095 } 096 } 097 finally { 098 Safe.close(pipe); 099 } 100 } 101 102 private PipeCommandProxy<?, Object> resolvePipe(String name, Object[] args, boolean piped) { 103 final Closure closure; 104 int to = args.length; 105 if (to > 0 && args[to - 1] instanceof Closure) { 106 closure = (Closure)args[--to]; 107 } else { 108 closure = null; 109 } 110 111 // 112 Map<String, Object> invokerOptions = this.options != null ? this.options : Collections.<String, Object>emptyMap(); 113 List<Object> invokerArgs = this.args != null ? this.args : Collections.emptyList(); 114 115 // 116 if (to > 0) { 117 Object first = args[0]; 118 int from; 119 if (first instanceof Map<?, ?>) { 120 from = 1; 121 Map<?, ?> options = (Map<?, ?>)first; 122 if (options.size() > 0) { 123 invokerOptions = new HashMap<String, Object>(invokerOptions); 124 for (Map.Entry<?, ?> option : options.entrySet()) { 125 String optionName = option.getKey().toString(); 126 Object optionValue = option.getValue(); 127 invokerOptions.put(optionName, optionValue); 128 } 129 } 130 } else { 131 from = 0; 132 } 133 134 if (from < to) { 135 invokerArgs = new ArrayList<Object>(invokerArgs); 136 while (from < to) { 137 Object o = args[from++]; 138 if (o != null) { 139 invokerArgs.add(o); 140 } 141 } 142 } 143 } 144 145 // 146 CommandInvoker<Void, Void> invoker = (CommandInvoker<Void, Void>)command.resolveInvoker(name, invokerOptions, invokerArgs); 147 148 // 149 InvocationContext context; 150 if (owner instanceof CRaSHCommand) { 151 context = ((CRaSHCommand)owner).peekContext(); 152 } else if (owner instanceof GroovyScriptCommand) { 153 context = (InvocationContext)((GroovyScriptCommand)owner).peekContext(); 154 } else { 155 throw new UnsupportedOperationException("todo"); 156 } 157 158 // 159 Consumer producer; 160 if (closure != null) { 161 CommandInvoker producerPipe; 162 if (closure instanceof MethodDispatcher) { 163 MethodDispatcher commandClosure = (MethodDispatcher)closure; 164 producerPipe = commandClosure.dispatcher.resolvePipe(commandClosure.name, new Object[0], true); 165 } else if (closure instanceof ClassDispatcher) { 166 ClassDispatcher dispatcherClosure = (ClassDispatcher)closure; 167 producerPipe = dispatcherClosure.resolvePipe(name, new Object[0], true); 168 } else { 169 170 // That's the type we cast to 171 Class[] pt = closure.getParameterTypes(); 172 final Class type; 173 if (pt.length > 0) { 174 type = pt[0]; 175 } else { 176 type = Void.class; 177 } 178 179 // 180 producerPipe = new CommandInvoker<Object, Void>() { 181 public Class<Void> getProducedType() { 182 return Void.class; 183 } 184 public Class<Object> getConsumedType() { 185 return type; 186 } 187 public void open(CommandContext<Void> consumer) { 188 } 189 public void close() { 190 } 191 public void provide(Object element) throws IOException { 192 if (type.isInstance(element)) { 193 closure.call(element); 194 } 195 } 196 public void flush() throws IOException { 197 } 198 }; 199 } 200 producer = producerPipe; 201 } else { 202 producer = context; 203 } 204 205 // 206 InnerInvocationContext inner = new InnerInvocationContext(context, producer, piped); 207 return new PipeCommandProxy(inner, invoker, producer); 208 } 209 }