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.script;
020    
021    import org.crsh.cli.descriptor.CommandDescriptor;
022    import org.crsh.cli.descriptor.Description;
023    import org.crsh.cli.impl.SyntaxException;
024    import org.crsh.cli.impl.descriptor.IntrospectionException;
025    import org.crsh.cli.impl.invocation.InvocationException;
026    import org.crsh.cli.impl.invocation.InvocationMatch;
027    import org.crsh.cli.spi.Completer;
028    import org.crsh.command.CommandContext;
029    import org.crsh.command.RuntimeContext;
030    import org.crsh.lang.spi.CommandResolution;
031    import org.crsh.lang.spi.Compiler;
032    import org.crsh.lang.spi.ReplResponse;
033    import org.crsh.shell.ErrorKind;
034    import org.crsh.shell.impl.command.ShellSession;
035    import org.crsh.shell.impl.command.spi.Command;
036    import org.crsh.shell.impl.command.spi.CommandException;
037    import org.crsh.shell.impl.command.spi.CommandInvoker;
038    import org.crsh.shell.impl.command.spi.CommandMatch;
039    
040    import java.io.BufferedReader;
041    import java.io.ByteArrayInputStream;
042    import java.io.IOException;
043    import java.io.InputStreamReader;
044    import java.lang.reflect.Type;
045    import java.util.Collections;
046    import java.util.Map;
047    import java.util.Set;
048    
049    /**
050     * @author Julien Viet
051     */
052    public 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    }