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.java;
020    
021    import org.crsh.cli.descriptor.CommandDescriptor;
022    import org.crsh.cli.impl.descriptor.HelpDescriptor;
023    import org.crsh.cli.impl.invocation.InvocationMatch;
024    import org.crsh.cli.impl.lang.CommandFactory;
025    import org.crsh.cli.impl.lang.Instance;
026    import org.crsh.cli.impl.lang.ObjectCommandInvoker;
027    import org.crsh.cli.spi.Completer;
028    import org.crsh.command.BaseCommand;
029    import org.crsh.shell.impl.command.spi.CommandCreationException;
030    import org.crsh.command.InvocationContext;
031    import org.crsh.command.Pipe;
032    import org.crsh.command.RuntimeContext;
033    import org.crsh.shell.ErrorType;
034    import org.crsh.shell.impl.command.spi.Command;
035    import org.crsh.shell.impl.command.spi.ShellCommand;
036    import org.crsh.util.Utils;
037    
038    import java.lang.reflect.Type;
039    
040    /** @author Julien Viet */
041    public class ShellCommandImpl<T extends BaseCommand> extends ShellCommand<Instance<T>> {
042    
043      /** . */
044      private final Class<T> clazz;
045    
046      /** . */
047      private final CommandDescriptor<Instance<T>> descriptor;
048    
049      public ShellCommandImpl(Class<T> clazz) {
050        CommandFactory factory = new CommandFactory(getClass().getClassLoader());
051        this.clazz = clazz;
052        this.descriptor = HelpDescriptor.create(factory.create(clazz));
053      }
054    
055      public CommandDescriptor<Instance<T>> getDescriptor() {
056        return descriptor;
057      }
058    
059      protected Completer getCompleter(final RuntimeContext context) throws CommandCreationException {
060        final T command = createCommand();
061        if (command instanceof Completer) {
062          command.context = context;
063          return (Completer)command;
064        } else {
065          return null;
066        }
067      }
068    
069      @Override
070      protected Command<?, ?> resolveCommand(InvocationMatch<Instance<T>> match) {
071    
072        // Cast to the object invoker
073        org.crsh.cli.impl.invocation.CommandInvoker<Instance<T>,?> invoker = match.getInvoker();
074    
075        // Do we have a pipe command or not ?
076        if (Pipe.class.isAssignableFrom(invoker.getReturnType())) {
077          org.crsh.cli.impl.invocation.CommandInvoker tmp = invoker;
078          return getPipeInvoker(tmp);
079        } else {
080    
081          // Determine the produced type
082          Class<?> producedType;
083          if (void.class.equals(invoker.getReturnType())) {
084            producedType = Object.class;
085          } else {
086            producedType = invoker.getReturnType();
087          }
088    
089          // Override produced type from InvocationContext<P> if any
090          if (invoker instanceof ObjectCommandInvoker) {
091            ObjectCommandInvoker<T, ?> objectInvoker = (ObjectCommandInvoker<T, ?>)invoker;
092            Class<?>[] parameterTypes = objectInvoker.getParameterTypes();
093            for (int i = 0;i < parameterTypes.length;i++) {
094              Class<?> parameterType = parameterTypes[i];
095              if (InvocationContext.class.isAssignableFrom(parameterType)) {
096                Type contextGenericParameterType = objectInvoker.getGenericParameterTypes()[i];
097                producedType = Utils.resolveToClass(contextGenericParameterType, InvocationContext.class, 0);
098                break;
099              }
100            }
101          }
102    
103          //
104          return getProducerInvoker(invoker, producedType);
105        }
106      }
107    
108      T createCommand() throws CommandCreationException {
109        T command;
110        try {
111          command = clazz.newInstance();
112        }
113        catch (Exception e) {
114          String name = clazz.getSimpleName();
115          throw new CommandCreationException(name, ErrorType.INTERNAL, "Could not create command " + name + " instance", e);
116        }
117        return command;
118      }
119    
120      private <C, P, PC extends Pipe<C, P>> Command<C, P> getPipeInvoker(final org.crsh.cli.impl.invocation.CommandInvoker<Instance<T>, PC> invoker) {
121        return new PipeCommandImpl<T, C, P, PC>(this, invoker);
122      }
123    
124      private <P> Command<Void, P> getProducerInvoker(final org.crsh.cli.impl.invocation.CommandInvoker<Instance<T>, ?> invoker, final Class<P> producedType) {
125        return new ProducerCommandImpl<T, P>(this, invoker, producedType);
126      }
127    
128    }