blob: 00a297b29474566ec548a92841fa6ae2d324383d [file] [log] [blame]
Richard S. Hallfbd735b2009-06-11 16:07:20 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package aQute.shell.runtime;
20
21import java.util.*;
22
23import org.osgi.service.command.*;
24
25public class Closure extends Reflective implements Function {
26 private static final long serialVersionUID = 1L;
27 final CharSequence source;
28 final Closure parent;
29 CommandSessionImpl session;
30 List<Object> parms;
31
32 Closure(CommandSessionImpl session, Closure parent, CharSequence source) {
33 this.session = session;
34 this.parent = parent;
35 this.source = source;
36 }
37
38 public Object execute(CommandSession x, List<Object> values)
39 throws Exception {
40 parms = values;
41 Parser parser = new Parser(source);
42 ArrayList<Pipe> pipes = new ArrayList<Pipe>();
43 List<List<List<CharSequence>>> program = parser.program();
44
45 for (List<List<CharSequence>> statements : program) {
46 Pipe current = new Pipe(this, statements);
47
48 if (pipes.isEmpty()) {
49 current.setIn(session.in);
50 current.setOut(session.out);
51 } else {
52 Pipe previous = pipes.get(pipes.size() - 1);
53 previous.connect(current);
54 }
55 pipes.add(current);
56 }
57 if (pipes.size() == 0)
58 return null;
59
60 if (pipes.size() == 1) {
61 pipes.get(0).run();
62 } else {
63 for (Pipe pipe : pipes) {
64 pipe.start();
65 }
66 for (Pipe pipe : pipes) {
67 pipe.join();
68 }
69 }
70
71 Pipe last = pipes.get(pipes.size() - 1);
72 if (last.exception != null)
73 throw last.exception;
74
75 if (last.result instanceof Object[]) {
76 return Arrays.asList((Object[]) last.result);
77 }
78 return last.result;
79 }
80
81 Object executeStatement(List<CharSequence> statement) throws Exception {
82 Object result;
83 List<Object> values = new ArrayList<Object>();
84 Object cmd = eval(statement.remove(0));
85 for (CharSequence token : statement)
86 values.add(eval(token));
87
88 result = execute(cmd, values);
89 return result;
90 }
91
92 private Object execute(Object cmd, List<Object> values) throws Exception {
93 if (cmd == null) {
94 if (values.isEmpty())
95 return null;
96 else
97 throw new IllegalArgumentException(
98 "Command name evaluates to null");
99 }
100
101 // Now there are the following cases
102 // <string> '=' statement // complex assignment
103 // <string> statement // cmd call
104 // <object> // value of <object>
105 // <object> statement // method call
106
107 if (cmd instanceof CharSequence) {
108 String scmd = cmd.toString();
109
110 if (values.size() > 0 && "=".equals(values.get(0))) {
111 if (values.size() == 0)
112 return session.variables.remove(scmd);
113 else {
114 Object value = execute(values.get(1), values.subList(2,
115 values.size()));
116 return assignment(scmd, value);
117 }
118 } else {
119 String scopedFunction = scmd;
120 Object x = get(scmd);
121 if ( !(x instanceof Function) ) {
122 if (scmd.indexOf(':') < 0) {
123 scopedFunction = "*:" + scmd;
124 }
125 x = get(scopedFunction);
126 if (x == null || !(x instanceof Function)) {
127 if (values.isEmpty())
128 return scmd;
129 throw new IllegalArgumentException("Command not found: "
130 + scopedFunction);
131 }
132 }
133 return ((Function) x).execute(session, values);
134 }
135 } else {
136 if (values.isEmpty())
137 return cmd;
138 else
139 return method(session, cmd, values.remove(0).toString(), values);
140 }
141 }
142
143 private Object assignment(Object name, Object value) {
144 session.variables.put(name, value);
145 return value;
146 }
147
148 private Object eval(CharSequence seq) throws Exception {
149 int end = seq.length();
150 switch (seq.charAt(0)) {
151 case '$':
152 return var(seq);
153 case '<':
154 Closure c = new Closure(session, this, seq.subSequence(1, end - 1));
155 return c.execute(session, parms);
156 case '[':
157 return array(seq.subSequence(1, end - 1));
158
159 case '{':
160 return new Closure(session, this, seq.subSequence(1, end - 1));
161
162 default:
163 String result = new Parser(seq).unescape();
164 if ("null".equals(result))
165 return null;
166 if ("true".equalsIgnoreCase(result))
167 return true;
168 if ("false".equalsIgnoreCase(result))
169 return false;
170 return seq;
171 }
172 }
173
174 private Object array(CharSequence array) throws Exception {
175 List<Object> list = new ArrayList<Object>();
176 Map<Object, Object> map = new LinkedHashMap<Object, Object>();
177 Parser p = new Parser(array);
178
179 while (!p.eof()) {
180 CharSequence token = p.value();
181
182 p.ws();
183 if (p.peek() == '=') {
184 p.next();
185 p.ws();
186 if (!p.eof()) {
187 CharSequence value = p.messy();
188 map.put(eval(token), eval(value));
189 }
190 } else
191 list.add(eval(token));
192
193 if (p.peek() == ',')
194 p.next();
195 p.ws();
196 }
197 p.ws();
198 if (!p.eof())
199 throw new IllegalArgumentException("Invalid array syntax: " + array);
200
201 if (map.size() != 0 && list.size() != 0)
202 throw new IllegalArgumentException(
203 "You can not mix maps and arrays: " + array);
204
205 if (map.size() > 0)
206 return map;
207 else
208 return list;
209 }
210
211 private Object var(CharSequence var) throws Exception {
212 String name = eval(var.subSequence(1, var.length())).toString();
213 return get(name);
214 }
215
216 /**
217 *
218 * @param name
219 * @return
220 */
221 private Object get(String name) {
222 if (parms != null) {
223 if ("it".equals(name))
224 return parms.get(0);
225 if ("args".equals(name))
226 return parms;
227
228 if (name.length() == 1 && Character.isDigit(name.charAt(0)))
229 return parms.get(name.charAt(0) - '0');
230 }
231 return session.get(name);
232 }
233}