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 }