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