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.Tuple;
025    import org.codehaus.groovy.runtime.MetaClassHelper;
026    
027    import java.util.ArrayList;
028    import java.util.Arrays;
029    import java.util.HashMap;
030    import java.util.List;
031    import java.util.Map;
032    
033    public class CommandClosure extends Closure {
034    
035      /** . */
036      protected HashMap<String, Object> options;
037    
038      /** . */
039      protected ArrayList<Object> args;
040    
041      public CommandClosure(Object owner) {
042        super(owner);
043    
044        //
045        this.options = null;
046        this.args = null;
047      }
048    
049      static Object[] unwrapArgs(Object arguments) {
050        if (arguments == null) {
051          return MetaClassHelper.EMPTY_ARRAY;
052        } else if (arguments instanceof Tuple) {
053          Tuple tuple = (Tuple) arguments;
054          return tuple.toArray();
055        } else if (arguments instanceof Object[]) {
056          return (Object[])arguments;
057        } else {
058          return new Object[]{arguments};
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          if ("with".equals(name)) {
069            Object[] array = unwrapArgs(args);
070            if (array.length == 0) {
071              return this;
072            } else if (array[0] instanceof Map) {
073              Map options = (Map)array[0];
074              if( array.length > 1) {
075                return options(options, Arrays.copyOfRange(array, 1, array.length));
076              } else {
077                return options(options, null);
078              }
079            } else {
080              return options(null, array);
081            }
082          } else {
083            throw e;
084          }
085        }
086      }
087    
088      private CommandClosure options(Map<?, ?> options, Object[] arguments) {
089        CommandClosure ret;
090        if (this instanceof MethodDispatcher) {
091          ret = new MethodDispatcher(((MethodDispatcher)this).dispatcher, ((MethodDispatcher)this).name);
092        } else {
093          ret = new ClassDispatcher(((ClassDispatcher)this).command, ((ClassDispatcher)this).owner);
094        }
095    
096        // We merge options
097        if (options != null && options.size() > 0) {
098          if (this.options == null) {
099            ret.options = new HashMap<String, Object>();
100          } else {
101            ret.options = new HashMap<String, Object>(this.options);
102          }
103          for (Map.Entry<?, ?> arg : options.entrySet()) {
104            ret.options.put(arg.getKey().toString(), arg.getValue());
105          }
106        }
107    
108        // We replace arguments
109        if (arguments != null) {
110          ret.args = new ArrayList<Object>(Arrays.asList(arguments));
111        }
112    
113        //
114        return ret;
115      }
116    }