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                // Build command context
103                InvocationContextImpl ctx;
104                if (current.invoker.getConsumedType() == Void.class) {
105                  ctx = new InvocationContextImpl(context, null, crash.attributes);
106                } else {
107                  // For now we assume we have compatible consumed/produced types
108                  ctx = new InvocationContextImpl(context, consumed, crash.attributes);
109                }
110    
111                //
112                try {
113                  current.invoker.invoke(ctx);
114                } catch (Throwable t) {
115                  return new ShellResponse.Error(ErrorType.EVALUATION, t);
116                }
117    
118                // Append anything that was in the buffer
119                if (ctx.getBuffer() != null) {
120                  out.append(ctx.getBuffer().toString());
121                }
122    
123                // Append produced if possible
124                if (current.invoker.getProducedType() == Void.class) {
125                  // Do nothing
126                } else {
127                  produced.addAll(ctx.getProducedItems());
128                }
129              }
130    
131              //
132              if (expr.next != null) {
133                return execute(context, expr.next, produced);
134              } else {
135                ShellResponse response;
136                if (out.length() > 0) {
137                  response = new ShellResponse.Display(produced, out.toString());
138                } else {
139                  response = new ShellResponse.Ok(produced);
140                }
141                return response;
142              }
143          }
144        }
145    
146        @Override
147        Term lastTerm() {
148          if (next != null) {
149            return next.lastTerm();
150          }
151          if (term != null) {
152            return term.lastTerm();
153          }
154          return null;
155        }
156      }
157    
158      static class Term extends AST {
159    
160        /** . */
161        final String line;
162    
163        /** . */
164        final Term next;
165    
166        /** . */
167        final String name;
168    
169        /** . */
170        final String rest;
171    
172        /** . */
173        private ShellCommand command;
174    
175        /** . */
176        private CommandInvoker invoker;
177    
178        Term(String line) {
179          this(line, null);
180        }
181    
182        Term(String line, Term next) {
183    
184          Pattern p = Pattern.compile("^\\s*(\\S+)");
185          java.util.regex.Matcher m = p.matcher(line);
186          String name = null;
187          String rest = null;
188          if (m.find()) {
189            name = m.group(1);
190            rest = line.substring(m.end());
191          }
192    
193          //
194          this.name = name;
195          this.rest = rest;
196          this.line = line;
197          this.next = next;
198        }
199    
200        private void create(CRaSH crash) throws CreateCommandException {
201          CommandInvoker invoker = null;
202          if (name != null) {
203            command = crash.getCommand(name);
204            if (command != null) {
205              invoker = command.createInvoker(rest);
206            }
207          }
208    
209          //
210          if (invoker == null) {
211            throw new CreateCommandException(new ShellResponse.UnknownCommand(name));
212          } else {
213            this.invoker = invoker;
214          }
215    
216          //
217          if (next != null) {
218            next.create(crash);
219          }
220        }
221    
222        String getLine() {
223          return line;
224        }
225    
226        @Override
227        Term lastTerm() {
228          if (next != null) {
229            return next.lastTerm();
230          } else {
231            return this;
232          }
233        }
234      }
235    }