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 }