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.lang.impl.groovy.closure; 020 021 import groovy.lang.Closure; 022 import groovy.lang.GroovyObjectSupport; 023 import groovy.lang.MissingMethodException; 024 import groovy.lang.MissingPropertyException; 025 import groovy.lang.Tuple; 026 import org.codehaus.groovy.runtime.MetaClassHelper; 027 import org.crsh.shell.impl.command.spi.Command; 028 import org.crsh.shell.impl.command.spi.CommandException; 029 import org.crsh.shell.impl.command.spi.CommandInvoker; 030 import org.crsh.command.InvocationContext; 031 import org.crsh.util.Utils; 032 033 import java.io.IOException; 034 import java.util.ArrayList; 035 import java.util.Arrays; 036 import java.util.Collections; 037 import java.util.HashMap; 038 import java.util.LinkedList; 039 import java.util.List; 040 import java.util.Map; 041 042 /** @author Julien Viet */ 043 public class PipeLineClosure extends Closure { 044 045 /** . */ 046 private static final Object[] EMPTY_ARGS = new Object[0]; 047 048 /** . */ 049 private final InvocationContext<Object> context; 050 051 /** . */ 052 private PipeLineElement[] elements; 053 054 public PipeLineClosure(InvocationContext<Object> context, String name, Command<?> command) { 055 this(context, new CommandElement[]{new CommandElement(name, command, null)}); 056 } 057 058 public PipeLineClosure(InvocationContext<Object> context, PipeLineElement[] elements) { 059 super(new Object()); 060 061 // 062 this.context = context; 063 this.elements = elements; 064 } 065 066 public Object find() { 067 return _gdk("find", EMPTY_ARGS); 068 } 069 070 public Object find(Closure closure) { 071 return _gdk("find", new Object[]{closure}); 072 } 073 074 private Object _gdk(String name, Object[] args) { 075 PipeLineClosure find = _sub(name); 076 if (find != null) { 077 return find.call(args); 078 } else { 079 throw new MissingMethodException(name, PipeLineClosure.class, args); 080 } 081 } 082 083 public Object or(Object t) { 084 if (t instanceof PipeLineClosure) { 085 PipeLineClosure next = (PipeLineClosure)t; 086 PipeLineElement[] combined = Arrays.copyOf(elements, elements.length + next.elements.length); 087 System.arraycopy(next.elements, 0, combined, elements.length, next.elements.length); 088 return new PipeLineClosure(context, combined); 089 } else if (t instanceof Closure) { 090 Closure closure = (Closure)t; 091 PipeLineElement[] combined = new PipeLineElement[elements.length + 1]; 092 System.arraycopy(elements, 0, combined, 0, elements.length); 093 combined[elements.length] = new ClosureElement(closure); 094 return new PipeLineClosure(context, combined); 095 } else { 096 throw new IllegalArgumentException("Cannot append to a pipeline: " + t); 097 } 098 } 099 100 private PipeLineClosure _sub(String name) { 101 if (elements.length == 1) { 102 CommandElement element = (CommandElement)elements[0]; 103 if (element.subordinate == null) { 104 return new PipeLineClosure(context, new CommandElement[]{ 105 element.subordinate(name) 106 }); 107 } 108 } 109 return null; 110 } 111 112 public Object getProperty(String property) { 113 try { 114 return super.getProperty(property); 115 } 116 catch (MissingPropertyException e) { 117 PipeLineClosure sub = _sub(property); 118 if (sub != null) { 119 return sub; 120 } else { 121 throw e; 122 } 123 } 124 } 125 126 @Override 127 public Object invokeMethod(String name, Object args) { 128 try { 129 return super.invokeMethod(name, args); 130 } 131 catch (MissingMethodException e) { 132 PipeLineClosure sub = _sub(name); 133 if (sub != null) { 134 return sub.call((Object[])args); 135 } else { 136 throw e; 137 } 138 } 139 } 140 141 private static Object[] unwrapArgs(Object arguments) { 142 if (arguments == null) { 143 return MetaClassHelper.EMPTY_ARRAY; 144 } else if (arguments instanceof Tuple) { 145 Tuple tuple = (Tuple) arguments; 146 return tuple.toArray(); 147 } else if (arguments instanceof Object[]) { 148 return (Object[])arguments; 149 } else { 150 return new Object[]{arguments}; 151 } 152 } 153 154 private PipeLineClosure options(Map<String, ?> options, Object[] arguments) { 155 CommandElement first = (CommandElement)elements[0]; 156 PipeLineElement[] ret = elements.clone(); 157 ret[0] = first.merge(options, arguments != null && arguments.length > 0 ? Arrays.asList(arguments) : Collections.emptyList()); 158 return new PipeLineClosure(context, ret); 159 } 160 161 @Override 162 public Object call(Object... args) { 163 164 final Closure closure; 165 int to = args.length; 166 if (to > 0 && args[to - 1] instanceof Closure) { 167 closure = (Closure)args[--to]; 168 } else { 169 closure = null; 170 } 171 172 // Configure the command with the closure 173 if (closure != null) { 174 final HashMap<String, Object> closureOptions = new HashMap<String, Object>(); 175 GroovyObjectSupport delegate = new GroovyObjectSupport() { 176 @Override 177 public void setProperty(String property, Object newValue) { 178 closureOptions.put(property, newValue); 179 } 180 }; 181 closure.setResolveStrategy(Closure.DELEGATE_ONLY); 182 closure.setDelegate(delegate); 183 Object ret = closure.call(); 184 Object[] closureArgs; 185 if (ret != null) { 186 if (ret instanceof Object[]) { 187 closureArgs = (Object[])ret; 188 } 189 else if (ret instanceof Iterable) { 190 closureArgs = Utils.list((Iterable)ret).toArray(); 191 } 192 else { 193 boolean use = true; 194 for (Object value : closureOptions.values()) { 195 if (value == ret) { 196 use = false; 197 break; 198 } 199 } 200 // Avoid the case : foo { bar = "juu" } that will make "juu" as an argument 201 closureArgs = use ? new Object[]{ret} : EMPTY_ARGS; 202 } 203 } else { 204 closureArgs = EMPTY_ARGS; 205 } 206 return options(closureOptions, closureArgs); 207 } else { 208 if (context != null) { 209 try { 210 PipeLineInvoker binding = bind(args); 211 binding.invoke(context); 212 return null; 213 } 214 catch (IOException e) { 215 return throwRuntimeException(e); 216 } 217 catch (CommandException e) { 218 return throwRuntimeException(e.getCause()); 219 } 220 } else { 221 return super.call(args); 222 } 223 } 224 } 225 226 public PipeLineClosure bind(InvocationContext<Object> context) { 227 return new PipeLineClosure(context, elements); 228 } 229 230 public PipeLineInvoker bind(Object args) { 231 return bind(unwrapArgs(args)); 232 } 233 234 public PipeLineInvoker bind(Object[] args) { 235 return new PipeLineInvoker(this, args); 236 } 237 238 LinkedList<CommandInvoker> resolve2(Object[] args) throws CommandException { 239 240 // Resolve options and arguments 241 Map<String, Object> invokerOptions = Collections.emptyMap(); 242 List<Object> invokerArgs = Collections.emptyList(); 243 if (args.length > 0) { 244 Object first = args[0]; 245 int from; 246 if (first instanceof Map<?, ?>) { 247 from = 1; 248 Map<?, ?> options = (Map<?, ?>)first; 249 if (options.size() > 0) { 250 invokerOptions = new HashMap<String, Object>(invokerOptions); 251 for (Map.Entry<?, ?> option : options.entrySet()) { 252 String optionName = option.getKey().toString(); 253 Object optionValue = option.getValue(); 254 invokerOptions.put(optionName, optionValue); 255 } 256 } 257 } else { 258 from = 0; 259 } 260 if (from < args.length) { 261 invokerArgs = new ArrayList<Object>(invokerArgs); 262 while (from < args.length) { 263 Object o = args[from++]; 264 if (o != null) { 265 invokerArgs.add(o); 266 } 267 } 268 } 269 } 270 271 // 272 CommandElement first = (CommandElement)elements[0]; 273 PipeLineElement[] a = elements.clone(); 274 a[0] = first.merge(invokerOptions, invokerArgs); 275 276 // 277 LinkedList<CommandInvoker> ret = new LinkedList<CommandInvoker>(); 278 for (PipeLineElement _elt : a) { 279 ret.add(_elt.create()); 280 } 281 282 // 283 return ret; 284 } 285 286 @Override 287 public String toString() { 288 StringBuilder sb = new StringBuilder(); 289 for (int i = 0;i < elements.length;i++) { 290 if (i > 0) { 291 sb.append(" | "); 292 } 293 elements[i].toString(sb); 294 } 295 return sb.toString(); 296 } 297 }