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 }