blob: d4973cd717d8af2739d3e46c21a374563276c246 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// DWB5: session.err is not redirected when creating pipeline
// DWB6: add 'set -x' trace feature if echo is set
// DWB7: removing variable via 'execute("name=") throws OutOfBoundsException
package aQute.shell.runtime;
import java.util.*;
import org.osgi.service.command.*;
public class Closure extends Reflective implements Function {
private static final long serialVersionUID = 1L;
final CharSequence source;
final Closure parent;
CommandSessionImpl session;
List<Object> parms;
Closure(CommandSessionImpl session, Closure parent, CharSequence source) {
this.session = session;
this.parent = parent;
this.source = source;
}
public Object execute(CommandSession x, List<Object> values)
throws Exception {
parms = values;
Parser parser = new Parser(source);
ArrayList<Pipe> pipes = new ArrayList<Pipe>();
List<List<List<CharSequence>>> program = parser.program();
for (List<List<CharSequence>> statements : program) {
Pipe current = new Pipe(this, statements);
if (pipes.isEmpty()) {
current.setIn(session.in);
current.setOut(session.out);
current.setErr(session.err); // XXX: derek.baum@paremus.com
} else {
Pipe previous = pipes.get(pipes.size() - 1);
previous.connect(current);
}
pipes.add(current);
}
if (pipes.size() == 0)
return null;
if (pipes.size() == 1) {
pipes.get(0).run();
} else {
for (Pipe pipe : pipes) {
pipe.start();
}
for (Pipe pipe : pipes) {
pipe.join();
}
}
Pipe last = pipes.get(pipes.size() - 1);
if (last.exception != null)
throw last.exception;
if (last.result instanceof Object[]) {
return Arrays.asList((Object[]) last.result);
}
return last.result;
}
Object executeStatement(List<CharSequence> statement) throws Exception {
Object result;
List<Object> values = new ArrayList<Object>();
CharSequence statement0 = statement.remove(0);
// derek: FEATURE: add set -x facility if echo is set
StringBuilder buf = new StringBuilder("+ ");
buf.append(statement0);
Object cmd = eval(statement0);
for (CharSequence token : statement) {
buf.append(' ');
buf.append(token);
values.add(eval(token));
}
if (Boolean.TRUE.equals(session.get("echo")))
System.err.println(buf);
result = execute(cmd, values);
return result;
}
private Object execute(Object cmd, List<Object> values) throws Exception {
if (cmd == null) {
if (values.isEmpty())
return null;
else
throw new IllegalArgumentException(
"Command name evaluates to null");
}
// Now there are the following cases
// <string> '=' statement // complex assignment
// <string> statement // cmd call
// <object> // value of <object>
// <object> statement // method call
if (cmd instanceof CharSequence) {
String scmd = cmd.toString();
if (values.size() > 0 && "=".equals(values.get(0))) {
//if (values.size() == 0)
if (values.size() == 1) // derek: BUGFIX
return session.variables.remove(scmd);
else {
Object value = execute(values.get(1), values.subList(2,
values.size()));
return assignment(scmd, value);
}
} else {
String scopedFunction = scmd;
Object x = get(scmd);
if ( !(x instanceof Function) ) {
if (scmd.indexOf(':') < 0) {
scopedFunction = "*:" + scmd;
}
x = get(scopedFunction);
if (x == null || !(x instanceof Function)) {
if (values.isEmpty())
return scmd;
throw new IllegalArgumentException("Command not found: "
+ scopedFunction);
}
}
return ((Function) x).execute(session, values);
}
} else {
if (values.isEmpty())
return cmd;
else
return method(session, cmd, values.remove(0).toString(), values);
}
}
private Object assignment(Object name, Object value) {
session.variables.put(name, value);
return value;
}
private Object eval(CharSequence seq) throws Exception {
int end = seq.length();
switch (seq.charAt(0)) {
case '$':
return var(seq);
case '<':
Closure c = new Closure(session, this, seq.subSequence(1, end - 1));
return c.execute(session, parms);
case '[':
return array(seq.subSequence(1, end - 1));
case '{':
return new Closure(session, this, seq.subSequence(1, end - 1));
default:
String result = new Parser(seq).unescape();
if ("null".equals(result))
return null;
if ("true".equalsIgnoreCase(result))
return true;
if ("false".equalsIgnoreCase(result))
return false;
return seq;
}
}
private Object array(CharSequence array) throws Exception {
List<Object> list = new ArrayList<Object>();
Map<Object, Object> map = new LinkedHashMap<Object, Object>();
Parser p = new Parser(array);
while (!p.eof()) {
CharSequence token = p.value();
p.ws();
if (p.peek() == '=') {
p.next();
p.ws();
if (!p.eof()) {
CharSequence value = p.messy();
map.put(eval(token), eval(value));
}
} else
list.add(eval(token));
if (p.peek() == ',')
p.next();
p.ws();
}
p.ws();
if (!p.eof())
throw new IllegalArgumentException("Invalid array syntax: " + array);
if (map.size() != 0 && list.size() != 0)
throw new IllegalArgumentException(
"You can not mix maps and arrays: " + array);
if (map.size() > 0)
return map;
else
return list;
}
private Object var(CharSequence var) throws Exception {
String name = eval(var.subSequence(1, var.length())).toString();
return get(name);
}
/**
*
* @param name
* @return
*/
private Object get(String name) {
if (parms != null) {
if ("it".equals(name))
return parms.get(0);
if ("args".equals(name))
return parms;
if (name.length() == 1 && Character.isDigit(name.charAt(0)))
return parms.get(name.charAt(0) - '0');
}
return session.get(name);
}
}