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.script;
020
021import org.crsh.cli.descriptor.CommandDescriptor;
022import org.crsh.cli.descriptor.Description;
023import org.crsh.cli.impl.SyntaxException;
024import org.crsh.cli.impl.descriptor.IntrospectionException;
025import org.crsh.cli.impl.invocation.InvocationException;
026import org.crsh.cli.impl.invocation.InvocationMatch;
027import org.crsh.cli.spi.Completer;
028import org.crsh.command.CommandContext;
029import org.crsh.command.RuntimeContext;
030import org.crsh.lang.spi.CommandResolution;
031import org.crsh.lang.spi.Compiler;
032import org.crsh.lang.spi.ReplResponse;
033import org.crsh.shell.ErrorKind;
034import org.crsh.shell.impl.command.ShellSession;
035import org.crsh.shell.impl.command.spi.Command;
036import org.crsh.shell.impl.command.spi.CommandException;
037import org.crsh.shell.impl.command.spi.CommandInvoker;
038import org.crsh.shell.impl.command.spi.CommandMatch;
039
040import java.io.BufferedReader;
041import java.io.ByteArrayInputStream;
042import java.io.IOException;
043import java.io.InputStreamReader;
044import java.lang.reflect.Type;
045import java.util.Collections;
046import java.util.Map;
047import java.util.Set;
048
049/**
050 * @author Julien Viet
051 */
052public class ScriptCompiler implements Compiler {
053
054  /** . */
055  private static final Set<String> EXT = Collections.singleton("script");
056
057  /** . */
058  static final ScriptCompiler instance = new ScriptCompiler();
059
060  public static ScriptCompiler getInstance() {
061    return instance;
062  }
063
064  @Override
065  public Set<String> getExtensions() {
066    return EXT;
067  }
068
069  @Override
070  public CommandResolution compileCommand(final String name, final byte[] source) throws CommandException, NullPointerException {
071
072    return new CommandResolution() {
073      @Override
074      public String getDescription() {
075        return "";
076      }
077      @Override
078      public Command<?> getCommand() throws CommandException {
079
080        //
081        final CommandDescriptor<Object> descriptor;
082        try {
083          descriptor = new CommandDescriptor<Object>(name, new Description()) {
084            @Override
085            public CommandDescriptor<Object> getOwner() {
086              return null;
087            }
088
089            @Override
090            public Map<String, ? extends CommandDescriptor<Object>> getSubordinates() {
091              return Collections.emptyMap();
092            }
093
094            @Override
095            public org.crsh.cli.impl.invocation.CommandInvoker<Object, ?> getInvoker(InvocationMatch<Object> match) {
096              return new org.crsh.cli.impl.invocation.CommandInvoker<Object, Object>(match) {
097                @Override
098                public Class<Object> getReturnType() {
099                  return Object.class;
100                }
101
102                @Override
103                public Type getGenericReturnType() {
104                  return Object.class;
105                }
106
107                @Override
108                public Object invoke(Object command) throws InvocationException, SyntaxException {
109                  throw new UnsupportedOperationException("Not used");
110                }
111              };
112            }
113          };
114        }
115        catch (IntrospectionException e) {
116          throw new CommandException(ErrorKind.SYNTAX, "Script " + name + " failed unexpectedly", e);
117        }
118
119        return new Command<Object>() {
120          @Override
121          public CommandDescriptor<Object> getDescriptor() {
122            return descriptor;
123          }
124          @Override
125          protected Completer getCompleter(RuntimeContext context) {
126            return null;
127          }
128          @Override
129          protected CommandMatch<?, ?> resolve(InvocationMatch<Object> match) {
130            return new CommandMatch<Void, Object>() {
131              @Override
132              public CommandInvoker<Void, Object> getInvoker() {
133                return new CommandInvoker<Void, Object>() {
134
135                  /** . */
136                  private CommandContext<?> consumer;
137
138                  @Override
139                  public void provide(Void element) throws IOException {
140                  }
141
142                  @Override
143                  public Class<Void> getConsumedType() {
144                    return Void.class;
145                  }
146
147                  @Override
148                  public void flush() throws IOException {
149                    consumer.flush();
150                  }
151
152                  @Override
153                  public Class<Object> getProducedType() {
154                    return Object.class;
155                  }
156
157                  @Override
158                  public void open(CommandContext<? super Object> consumer) {
159                    this.consumer = consumer;
160                  }
161
162                  @Override
163                  public void close() throws IOException, CommandException {
164
165                    // Execute sequentially the script
166                    BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(source)));
167
168                    // A bit nasty but well it's ok
169                    ShellSession session = (ShellSession)consumer.getSession();
170
171                    while (true) {
172                      String request = reader.readLine();
173                      if (request == null) {
174                        break;
175                      }
176                      request = request.trim();
177                      if (request.length() == 0) {
178                        break;
179                      }
180                      ReplResponse response = ScriptRepl.getInstance().eval(session, request);
181                      if (response instanceof ReplResponse.Response) {
182                        ReplResponse.Response shellResponse = (ReplResponse.Response)response;
183                        Exception ex = new Exception("Was not expecting response " + shellResponse.response);
184                        throw new CommandException(ErrorKind.EVALUATION, "Failure when evaluating '" + request + "'  in script " + name, ex);
185                      } else if (response instanceof ReplResponse.Invoke) {
186                        ReplResponse.Invoke invokeResponse = (ReplResponse.Invoke)response;
187                        CommandInvoker invoker =  invokeResponse.invoker;
188                        invoker.invoke(consumer);
189                      }
190                    }
191
192                    //
193                    try {
194                      consumer.close();
195                    }
196                    catch (Exception e) {
197                      // ?
198                    }
199
200                    //
201                    this.consumer = null;
202                  }
203                };
204              }
205              @Override
206              public Class<Object> getProducedType() {
207                return Object.class;
208              }
209              @Override
210              public Class<Void> getConsumedType() {
211                return Void.class;
212              }
213            };
214          }
215        };
216      }
217    };
218  }
219
220  @Override
221  public String doCallBack(ShellSession session, String name, String defaultValue) {
222    return null;
223  }
224}