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