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
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 }