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