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.java;
020    
021    import org.crsh.cli.impl.invocation.CommandInvoker;
022    import org.crsh.cli.impl.invocation.InvocationException;
023    import org.crsh.cli.impl.lang.Instance;
024    import org.crsh.command.BaseCommand;
025    import org.crsh.command.CommandContext;
026    import org.crsh.command.InvocationContext;
027    import org.crsh.command.Pipe;
028    import org.crsh.keyboard.KeyHandler;
029    import org.crsh.shell.ErrorKind;
030    import org.crsh.text.ScreenContext;
031    import org.crsh.shell.impl.command.InvocationContextImpl;
032    import org.crsh.shell.impl.command.spi.CommandException;
033    import org.crsh.util.Utils;
034    
035    import java.io.IOException;
036    import java.lang.reflect.Type;
037    
038    /**
039    * @author Julien Viet
040    */
041    class PipeCommandMatch<T extends BaseCommand, C, P, PC extends Pipe<C, P>> extends BaseCommandMatch<T, C, P> {
042    
043      /** . */
044      final Type ret;
045    
046      /** . */
047      final Class<C> consumedType;
048    
049      /** . */
050      final Class<P> producedType;
051    
052      /** . */
053      private final CommandInvoker<Instance<T>, PC> invoker;
054    
055      /** . */
056      private final String name;
057    
058      public PipeCommandMatch(ClassShellCommand<T> baseShellCommand, CommandInvoker<Instance<T>, PC> invoker) {
059        super(baseShellCommand);
060        this.invoker = invoker;
061        ret = invoker.getGenericReturnType();
062        consumedType = (Class<C>)Utils.resolveToClass(ret, Pipe.class, 0);
063        producedType = (Class<P>)Utils.resolveToClass(ret, Pipe.class, 1);
064        name = baseShellCommand.getDescriptor().getName();
065      }
066    
067      @Override
068      public Class<P> getProducedType() {
069        return producedType;
070      }
071    
072      @Override
073      public Class<C> getConsumedType() {
074        return consumedType;
075      }
076    
077      @Override
078      BaseInvoker getInvoker(T command) throws CommandException {
079    
080        //
081        return new BaseInvoker(command) {
082    
083          Pipe<C, P> real;
084          InvocationContext<P> invocationContext;
085    
086          public Class<P> getProducedType() {
087            return producedType;
088          }
089    
090          public Class<C> getConsumedType() {
091            return consumedType;
092          }
093    
094          public void open(CommandContext<? super P> consumer) throws CommandException {
095            // Java is fine with that but not intellij....
096            CommandContext<P> consumer2 = (CommandContext<P>)consumer;
097            open2(consumer2);
098          }
099    
100          @Override
101          public ScreenContext getScreenContext() {
102            return real instanceof ScreenContext ? (ScreenContext)real : null;
103          }
104    
105          @Override
106          public KeyHandler getKeyHandler() {
107            return real instanceof KeyHandler ? (KeyHandler)real : null;
108          }
109    
110          public void open2(final CommandContext<P> consumer) throws CommandException {
111    
112            //
113            invocationContext = new InvocationContextImpl<P>(consumer);
114    
115            // Push context
116            command.pushContext(invocationContext);
117    
118            //  Set the unmatched part
119            command.unmatched = invoker.getMatch().getRest();
120    
121            //
122            PC ret;
123            try {
124              ret = invoker.invoke(this);
125            }
126            catch (org.crsh.cli.impl.SyntaxException e) {
127              throw new CommandException(ErrorKind.SYNTAX, "Syntax exception when executing command " + name, e);
128            } catch (InvocationException e) {
129              throw new CommandException(ErrorKind.EVALUATION, "Command " + name + " failed", e.getCause());
130            }
131    
132            // It's a pipe command
133            if (ret != null) {
134              real = ret;
135              try {
136                real.open(invocationContext);
137              }
138              catch (Exception e) {
139                throw new CommandException(ErrorKind.EVALUATION, "Command " + name + " failed", e);
140              }
141            }
142          }
143    
144          public void provide(C element) throws IOException, CommandException {
145            if (real != null) {
146              try {
147                real.provide(element);
148              }
149              catch (Exception e) {
150                throw new CommandException(ErrorKind.EVALUATION, "Command " + name + " failed", e);
151              }
152            }
153          }
154    
155          public void flush() throws IOException {
156            if (real != null) {
157              real.flush();
158            } else {
159              invocationContext.flush();
160            }
161          }
162    
163          public void close() throws IOException, CommandException {
164            try {
165              try {
166                if (real != null) {
167                  real.close();
168                }
169              }
170              catch (Exception e) {
171                throw new CommandException(ErrorKind.EVALUATION, "Command " + name + " failed", e);
172              } finally {
173                try {
174                  invocationContext.close();
175                }
176                catch (Exception e) {
177                  throw new CommandException(ErrorKind.EVALUATION, e);
178                }
179              }
180            } finally {
181              command.popContext();
182              command.unmatched = null;
183            }
184          }
185        };
186      }
187    }