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.ShellCommand;
024    import org.crsh.shell.ErrorType;
025    import org.crsh.shell.ShellProcess;
026    import org.crsh.shell.ShellResponse;
027    import org.crsh.shell.ShellProcessContext;
028    
029    import java.util.ArrayList;
030    import java.util.Map;
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(CRaSH crash, String request) throws CreateCommandException {
060          term.create(crash);
061          if (next != null) {
062            next.create(crash);
063          }
064          return new ShellProcessImpl(crash, request, this);
065        }
066    
067        private void create(CRaSH crash) throws CreateCommandException {
068          term.create(crash);
069          if (next != null) {
070            next.create(crash);
071          }
072        }
073    
074        private static class ShellProcessImpl extends CRaSHProcess {
075    
076          /** . */
077          private final AST.Expr expr;
078    
079          private ShellProcessImpl(CRaSH crash, String request, AST.Expr expr) {
080            super(crash, request);
081    
082            //
083            this.expr = expr;
084          }
085    
086          @Override
087          ShellResponse invoke(ShellProcessContext context) {
088            return execute(context, expr, null);
089          }
090    
091          private ShellResponse execute(ShellProcessContext context, AST.Expr expr, ArrayList consumed) {
092    
093              // What will be produced by this expression
094              ArrayList produced = new ArrayList();
095    
096              //
097              StringBuilder out = new StringBuilder();
098    
099              // Iterate over all terms
100              for (Term current = expr.term;current != null;current = current.next) {
101    
102    
103    
104    
105    
106                // Build command context
107                InvocationContextImpl ctx;
108                if (current.invoker.getConsumedType() == Void.class) {
109                  ctx = new InvocationContextImpl(context, null, crash.attributes);
110                } else {
111                  // For now we assume we have compatible consumed/produced types
112                  ctx = new InvocationContextImpl(context, consumed, crash.attributes);
113                }
114    
115                //
116                try {
117                  current.invoker.invoke(ctx);
118                } catch (Throwable t) {
119                  return new ShellResponse.Error(ErrorType.EVALUATION, t);
120                }
121    
122                // Append anything that was in the buffer
123                if (ctx.getBuffer() != null) {
124                  out.append(ctx.getBuffer().toString());
125                }
126    
127                // Append produced if possible
128                if (current.invoker.getProducedType() == Void.class) {
129                  // Do nothing
130                } else {
131                  produced.addAll(ctx.getProducedItems());
132                }
133              }
134    
135              //
136              if (expr.next != null) {
137                return execute(context, expr.next, produced);
138              } else {
139                ShellResponse response;
140                if (out.length() > 0) {
141                  response = new ShellResponse.Display(produced, out.toString());
142                } else {
143                  response = new ShellResponse.Ok(produced);
144                }
145                return response;
146              }
147          }
148        }
149    
150        @Override
151        Term lastTerm() {
152          if (next != null) {
153            return next.lastTerm();
154          }
155          if (term != null) {
156            return term.lastTerm();
157          }
158          return null;
159        }
160      }
161    
162      static class Term extends AST {
163    
164        /** . */
165        final String line;
166    
167        /** . */
168        final Term next;
169    
170        /** . */
171        final String name;
172    
173        /** . */
174        final String rest;
175    
176        /** . */
177        private ShellCommand command;
178    
179        /** . */
180        private CommandInvoker invoker;
181    
182        Term(String line) {
183          this(line, null);
184        }
185    
186        Term(String line, Term next) {
187    
188          Pattern p = Pattern.compile("^\\s*(\\S+)");
189          java.util.regex.Matcher m = p.matcher(line);
190          String name = null;
191          String rest = null;
192          if (m.find()) {
193            name = m.group(1);
194            rest = line.substring(m.end());
195          }
196    
197          //
198          this.name = name;
199          this.rest = rest;
200          this.line = line;
201          this.next = next;
202        }
203    
204        private void create(CRaSH crash) throws CreateCommandException {
205          CommandInvoker invoker = null;
206          if (name != null) {
207            command = crash.getCommand(name);
208            if (command != null) {
209              invoker = command.createInvoker(rest);
210            }
211          }
212    
213          //
214          if (invoker == null) {
215            throw new CreateCommandException(new ShellResponse.UnknownCommand(name));
216          } else {
217            this.invoker = invoker;
218          }
219    
220          //
221          if (next != null) {
222            next.create(crash);
223          }
224        }
225    
226        String getLine() {
227          return line;
228        }
229    
230        @Override
231        Term lastTerm() {
232          if (next != null) {
233            return next.lastTerm();
234          } else {
235            return this;
236          }
237        }
238      }
239    }