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 */
019package org.crsh.lang.impl.java;
020
021import org.crsh.cli.impl.invocation.CommandInvoker;
022import org.crsh.cli.impl.invocation.InvocationException;
023import org.crsh.cli.impl.lang.Instance;
024import org.crsh.command.BaseCommand;
025import org.crsh.command.CommandContext;
026import org.crsh.command.InvocationContext;
027import org.crsh.command.Pipe;
028import org.crsh.keyboard.KeyHandler;
029import org.crsh.shell.ErrorKind;
030import org.crsh.text.ScreenContext;
031import org.crsh.shell.impl.command.InvocationContextImpl;
032import org.crsh.shell.impl.command.spi.CommandException;
033import org.crsh.util.Utils;
034
035import java.io.IOException;
036import java.lang.reflect.Type;
037
038/**
039* @author Julien Viet
040*/
041class 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}