001    /*
002     * Copyright (C) 2010 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    
020    package org.crsh.shell.impl;
021    
022    import org.crsh.command.CommandInvoker;
023    import org.crsh.command.NoSuchCommandException;
024    import org.crsh.command.ScriptException;
025    import org.crsh.command.ShellCommand;
026    import org.crsh.shell.ErrorType;
027    import org.crsh.shell.ShellResponse;
028    import org.crsh.shell.ShellProcessContext;
029    
030    import java.util.ArrayList;
031    import java.util.regex.Pattern;
032    
033    /**
034     * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
035     * @version $Revision$
036     */
037    abstract class AST {
038    
039      abstract Term lastTerm();
040    
041      static class Expr extends AST {
042    
043        /** . */
044        final Term term;
045    
046        /** . */
047        final Expr next;
048    
049        Expr(Term term) {
050          this.term = term;
051          this.next = null;
052        }
053    
054        Expr(Term term, Expr next) {
055          this.term = term;
056          this.next = next;
057        }
058    
059        final CRaSHProcess create(CRaSHSession crash, String request) throws NoSuchCommandException {
060          term.create(crash);
061          if (next != null) {
062            next.create(crash);
063          }
064          return new CRaSHProcess(crash, request) {
065            @Override
066            ShellResponse doInvoke(ShellProcessContext context) throws InterruptedException {
067              return Expr.this.execute(crash, context, null);
068            }
069          };
070        }
071    
072        private void create(CRaSHSession crash) throws NoSuchCommandException {
073          term.create(crash);
074          if (next != null) {
075            next.create(crash);
076          }
077        }
078    
079        protected ShellResponse execute(CRaSHSession crash, ShellProcessContext context, ArrayList consumed) throws InterruptedException {
080    
081          // What will be produced by this expression
082          ArrayList produced = new ArrayList();
083    
084          //
085          StringBuilder out = new StringBuilder();
086    
087          // Iterate over all terms
088          for (Term current = term;current != null;current = current.next) {
089    
090            // Build command context
091            InvocationContextImpl ctx;
092            if (current.invoker.getConsumedType() == Void.class) {
093              ctx = new InvocationContextImpl(context, null, crash.attributes);
094            } else {
095              // For now we assume we have compatible consumed/produced types
096              ctx = new InvocationContextImpl(context, consumed, crash.attributes);
097            }
098    
099            //
100            try {
101              current.invoker.invoke(ctx);
102            } catch (ScriptException e) {
103    
104              // Should we handle InterruptedException here ?
105    
106              return current.build(e);
107            } catch (Throwable t) {
108              return current.build(t);
109            }
110    
111            // Append anything that was in the buffer
112            if (ctx.getBuffer() != null) {
113              out.append(ctx.getBuffer().toString());
114            }
115    
116            // Append produced if possible
117            if (current.invoker.getProducedType() == Void.class) {
118              // Do nothing
119            } else {
120              produced.addAll(ctx.getProducedItems());
121            }
122          }
123    
124          //
125          if (next != null) {
126            return next.execute(crash, context, produced);
127          } else {
128            ShellResponse response;
129            if (out.length() > 0) {
130              response = ShellResponse.display(produced, out.toString());
131            } else {
132              response = ShellResponse.ok(produced);
133            }
134            return response;
135          }
136        }
137    
138        @Override
139        Term lastTerm() {
140          if (next != null) {
141            return next.lastTerm();
142          }
143          if (term != null) {
144            return term.lastTerm();
145          }
146          return null;
147        }
148      }
149    
150      static class Term extends AST {
151    
152        /** . */
153        final String line;
154    
155        /** . */
156        final Term next;
157    
158        /** . */
159        final String name;
160    
161        /** . */
162        final String rest;
163    
164        /** . */
165        private ShellCommand command;
166    
167        /** . */
168        private CommandInvoker invoker;
169    
170        Term(String line) {
171          this(line, null);
172        }
173    
174        Term(String line, Term next) {
175    
176          Pattern p = Pattern.compile("^\\s*(\\S+)");
177          java.util.regex.Matcher m = p.matcher(line);
178          String name = null;
179          String rest = null;
180          if (m.find()) {
181            name = m.group(1);
182            rest = line.substring(m.end());
183          }
184    
185          //
186          this.name = name;
187          this.rest = rest;
188          this.line = line;
189          this.next = next;
190        }
191    
192        private void create(CRaSHSession session) throws NoSuchCommandException {
193          CommandInvoker invoker = null;
194          if (name != null) {
195            command = session.crash.getCommand(name);
196            if (command != null) {
197              invoker = command.createInvoker(rest);
198            }
199          }
200    
201          //
202          if (invoker == null) {
203            throw new NoSuchCommandException(name);
204          } else {
205            this.invoker = invoker;
206          }
207    
208          //
209          if (next != null) {
210            next.create(session);
211          }
212        }
213    
214        String getLine() {
215          return line;
216        }
217    
218        @Override
219        Term lastTerm() {
220          if (next != null) {
221            return next.lastTerm();
222          } else {
223            return this;
224          }
225        }
226    
227        private ShellResponse.Error build(Throwable throwable) {
228          ErrorType errorType;
229          if (throwable instanceof ScriptException) {
230            errorType = ErrorType.EVALUATION;
231            Throwable cause = throwable.getCause();
232            if (cause != null) {
233              throwable = cause;
234            }
235          } else {
236            errorType = ErrorType.INTERNAL;
237          }
238          String result;
239          String msg = throwable.getMessage();
240          if (throwable instanceof ScriptException) {
241            if (msg == null) {
242              result = name + ": failed";
243            } else {
244              result = name + ": " + msg;
245            }
246            return ShellResponse.error(errorType, result, throwable);
247          } else {
248            if (msg == null) {
249              msg = throwable.getClass().getSimpleName();
250            }
251            if (throwable instanceof RuntimeException) {
252              result = name + ": exception: " + msg;
253            } else if (throwable instanceof Exception) {
254              result = name + ": exception: " + msg;
255            } else if (throwable instanceof java.lang.Error) {
256              result = name + ": error: " + msg;
257            } else {
258              result = name + ": unexpected throwable: " + msg;
259            }
260            return ShellResponse.error(errorType, result, throwable);
261          }
262        }
263      }
264    }