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