1 /* GenericInterpreter.java
2
3 {{IS_NOTE
4 Purpose:
5
6 Description:
7
8 History:
9 Thu Feb 8 23:47:29 2007, Created by tomyeh
10 }}IS_NOTE
11
12 Copyright (C) 2007 Potix Corporation. All Rights Reserved.
13
14 {{IS_RIGHT
15 This program is distributed under GPL Version 2.0 in the hope that
16 it will be useful, but WITHOUT ANY WARRANTY.
17 }}IS_RIGHT
18 */
19 package org.zkoss.zk.scripting.util;
20
21 import java.util.Set;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.LinkedList;
25
26 import org.zkoss.lang.Objects;
27 import org.zkoss.xel.Function;
28
29 import org.zkoss.zk.ui.Page;
30 import org.zkoss.zk.ui.sys.PageCtrl;
31 import org.zkoss.zk.scripting.Interpreter;
32 import org.zkoss.zk.scripting.Namespace;
33 import org.zkoss.zk.scripting.Namespaces;
34 import org.zkoss.zk.scripting.NamespaceChangeListener;
35
36 /**
37 * A skeletal class for implementing a interpreter ({@link Interpreter}) that
38 * support {@link Namespace}.
39 *
40 * <p>Derive classes usually override {@link #exec} instead of {@link #interpret};
41 * In addition, don't override {@link #getVariable},
42 * {@link #setVariable} and {@link #unsetVariable}.
43 * Instead, override {@link #get(String)}, {@link #contains(String)},
44 * {@link #set(String,Object)} and {@link #unset(String)} instead.
45 *
46 * <p>If an interpreter doesn't support hierachical scopes,
47 * it can simply implement a global scope, and then use
48 * {@link #getFromNamespace} to
49 * retrieve variables from ZK's hierachical namespaces.
50 *
51 * <p>If it supports hierachical scopes
52 * (example: {@link org.zkoss.zk.scripting.bsh.BSHInterpreter}), it
53 * can maintain a one-to-one relationship among interpreter's scopes
54 * and ZK's {@link Namespace}. Thus, it can retrieve
55 * the correct scope by giving ZK's {@link Namespace}, and vice versa.
56 * It also has to implement {@link #get(Namespace,String)}, {@link #contains(Namespace,String)}
57 * {@link #set(Namespace,String,Object)} and {@link #unset(Namespace,String)}.
58 *
59 * <p>Whether to support hierachical namespaces is optional.
60 *
61 * @author tomyeh
62 */
63 abstract public class GenericInterpreter implements Interpreter {
64 /** Used by {@link #getFromNamespace} to denote a variable is not defined.
65 * @since 2.4.0
66 */
67 public static final Object UNDEFINED = new Object();
68
69 /** A list of {@link Namespace}.
70 * Top of it is the current one (if null, it means Namespaces.getCurrent)
71 */
72 private final List _nss = new LinkedList();
73 private Page _owner;
74 private String _zslang;
75
76 protected GenericInterpreter() {
77 }
78
79 //interface to override//
80 /** Executes the specified script.
81 * Deriving class shall provide an implementation of this method, rather
82 * than overriding {@link #interpret}.
83 */
84 abstract protected void exec(String script);
85
86 /** Tests whether a variable is defined in this interpreter.
87 * Optional. Implement it if the interpreter can tell the difference
88 * between null and undefined.
89 *
90 * <p>By default, it tests whether {@link #get(String)} returns non-null.
91 * @since 2.4.0
92 */
93 protected boolean contains(String name) {
94 return get(name) != null;
95 }
96 /** Gets the variable from the interpreter.
97 * Optional. Implement it if you want to expose variables defined
98 * in the interpreter to Java codes.
99 *
100 * <p>{@link #beforeExec} is called first, before this method is invoked.
101 *
102 * <p>An empty (and fake) namespace is pushed so {@link #getFromNamespace}
103 * always returns null.
104 */
105 protected Object get(String name) {
106 return null;
107 }
108 /** Sets the variable to the interpreter.
109 * Optional. Implement it if you want to allow Java codes to define
110 * a variable in the interpreter.
111 *
112 * <p>{@link #beforeExec} is called first, before this method is invoked.
113 */
114 protected void set(String name, Object value) {
115 }
116 /** Removes the variable from the interpreter.
117 * Optional. Implement it if you want to allow Java codes to undefine
118 * a variable from the interpreter.
119 *
120 * <p>{@link #beforeExec} is called first, before this method is invoked.
121 */
122 protected void unset(String name) {
123 }
124
125 /** Tests whether a variable is defined in the interpreter's scope
126 * associated with the specified namespace.
127 * Optional. Implement it if the interpreter can tell the difference
128 * between null and undefined.
129 *
130 * <p>By default, it tests whether {@link #get(Namespace, String)}
131 * returns non-null.
132 * @since 2.4.0
133 */
134 protected boolean contains(Namespace ns, String name) {
135 return get(ns, name) != null;
136 }
137 /** Gets the variable from the interpreter's scope associated with
138 * the giving namespace.
139 * Optional. Implement it if you want to expose variables defined
140 * in the interpreter to Java codes.
141 *
142 * <p>This method is implemented only if the interpreter that supports
143 * hierachical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
144 *
145 * <p>Default: the same as {@link #get(String)}.
146 *
147 * <p>{@link #beforeExec} is called first, before this method is invoked.
148 *
149 * <p>An empty (and fake) namespace is pushed so {@link #getFromNamespace}
150 * always returns null.
151 */
152 protected Object get(Namespace ns, String name) {
153 return get(name);
154 }
155 /** Sets the variable to the interpreter's scope associated with the
156 * giving namespace.
157 * Optional. Implement it if you want to allow Java codes to define
158 * a variable in the interpreter.
159 *
160 * <p>This method is implemented only if the interpreter that supports
161 * hierachical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
162 *
163 * <p>Default: the same as {@link #set(String, Object)}.
164 *
165 * <p>{@link #beforeExec} is called first, before this method is invoked.
166 * @since 2.4.0
167 */
168 protected void set(Namespace ns, String name, Object value) {
169 set(name, value);
170 }
171 /** Removes the variable from the interpreter.
172 * Optional. Implement it if you want to allow Java codes to undefine
173 * a variable from the interpreter.
174 *
175 * <p>This method is implemented only if the interpreter that supports
176 * hierachical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
177 *
178 * <p>Default: the same as {@link #unset(String)}.
179 *
180 * <p>{@link #beforeExec} is called first, before this method is invoked.
181 * @since 2.4.0
182 */
183 protected void unset(Namespace ns, String name) {
184 unset(name);
185 }
186
187 /** Called before {@link #exec}.
188 * <p>Default: call {@link #beforeExec} and push the namespace
189 * as the active one.
190 */
191 protected void beforeInterpret(Namespace ns) {
192 beforeExec();
193 push(ns); //getFromNamespace will handle null
194 }
195 /** Called after {@link #exec}.
196 * <p>Default: call {@link #afterExec}.
197 */
198 protected void afterInterpret(Namespace ns) {
199 pop();
200 afterExec();
201 }
202 /** Called before {@link #exec}, {@link #get} and many others.
203 * <p>Default: does nothing.
204 */
205 protected void beforeExec() {
206 }
207 /** Called after {@link #exec}, {@link #get} and many others.
208 * <p>Default: does nothing.
209 */
210 protected void afterExec() {
211 }
212
213 //utilities//
214 /** Returns the variable through namespaces and variable resolvers,
215 * or {@link #UNDEFINED} if the variable not defined.
216 *
217 * <p>It is usually called to search namespaces and variable resolvers,
218 * when the real interpreter failed to find a variable in its own scope.
219 *
220 * <p>Note: We use {@link #UNDEFINED} to denote undefined since 2.4.0,
221 * while null is a valid value.
222 */
223 protected Object getFromNamespace(String name) {
224 final Namespace ns = getCurrent();
225 if (ns != null) {
226 Object val = ns.getVariable(name, false);
227 if (val != null || ns.containsVariable(name, false))
228 return val;
229 }
230 return UNDEFINED;
231 }
232 /** Returns the variable through the specified namespaces and
233 * variable resolvers, or {@link #UNDEFINED} if the variable is not
234 * defined.
235 *
236 * <p>It is usually called to search namespaces and variable resolvers,
237 * when the real interpreter failed to find a variable in its own scope.
238 *
239 * <p>This method is used with the interpreter that supports
240 * hierachical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
241 *
242 * @param ns the namespace to search from (never null).
243 * Note: if {@link #getCurrent} returns null, this method simply returns
244 * null (i.e., ignoring ns).
245 */
246 protected Object getFromNamespace(Namespace ns, String name) {
247 if (getCurrent() != null) {
248 Object val = ns.getVariable(name, false);
249 if (val != null || ns.containsVariable(name, false))
250 return val;
251 }
252 return UNDEFINED;
253 }
254
255 //Interpreter//
256 public void init(Page owner, String zslang) {
257 _owner = owner;
258 _zslang = zslang;
259 }
260 /** Reset the owner ({@link #getOwner}) to null.
261 */
262 public void destroy() {
263 _owner = null;
264 }
265 public Page getOwner() {
266 return _owner;
267 }
268 public String getLanguage() {
269 return _zslang;
270 }
271
272 /** Handles the namespace and then invoke {@link #exec}.
273 * <p>Don't override this method, rather, override {@link #exec}.
274 */
275 public void interpret(String script, Namespace ns) {
276 final String each =
277 _owner.getLanguageDefinition().getEachTimeScript(_zslang);
278 if (each != null)
279 script = each + '\n' + script;
280
281 beforeInterpret(ns);
282 try {
283 exec(script);
284 } finally {
285 afterInterpret(ns);
286 }
287 }
288 /** Returns null since retrieving class is not supported.
289 */
290 public Class getClass(String clsnm) {
291 return null;
292 }
293 /** Returns null since retrieving methods is not supported.
294 * @since 3.0.0
295 */
296 public Function getFunction(String name, Class[] argTypes) {
297 return null;
298 }
299 /** Returns null since retrieving methods is not supported.
300 * @since 3.0.0
301 */
302 public Function getFunction(Namespace ns, String name, Class[] argTypes) {
303 return null;
304 }
305
306 /** Tests whether the variable exists.
307 *
308 * <p>Deriving class shall override {@link #contains(String)}, instead of this method.
309 * @since 2.4.0
310 */
311 public boolean containsVariable(String name) {
312 beforeExec();
313 push(Objects.UNKNOWN);
314 //don't use null since it means Namespaces#getCurrent, see below
315 try {
316 return contains(name);
317 } finally {
318 pop();
319 afterExec();
320 }
321 }
322 /** Retrieve the variable.
323 *
324 * <p>Deriving class shall override {@link #get(String)}, instead of this method.
325 */
326 public Object getVariable(String name) {
327 beforeExec();
328 push(Objects.UNKNOWN);
329 //don't use null since it means Namespaces#getCurrent, see below
330 try {
331 return get(name);
332 } finally {
333 pop();
334 afterExec();
335 }
336 }
337 /** Sets the variable to this interpreter.
338 *
339 * <p>Deriving class shall override {@link #set(String,Object)}, instead of this method.
340 */
341 public final void setVariable(String name, Object value) {
342 beforeExec();
343 try {
344 set(name, value);
345 } finally {
346 afterExec();
347 }
348 }
349 /** Removes the variable from this interpreter.
350 *
351 * <p>Deriving class shall override {@link #unset(String)}, instead of this method.
352 */
353 public final void unsetVariable(String name) {
354 beforeExec();
355 try {
356 unset(name);
357 } finally {
358 afterExec();
359 }
360 }
361
362 /** Tests whether the variable exists by using the specified name
363 * as a reference to identify the interpreter's scope.
364 *
365 * <p>Deriving class shall override {@link #contains(Namespace,String)}, instead of this method.
366 * @since 2.4.0
367 */
368 public boolean containsVariable(Namespace ns, String name) {
369 beforeExec();
370 push(Objects.UNKNOWN);
371 //don't use null since it means Namespaces#getCurrent, see below
372 try {
373 return contains(ns, name);
374 } finally {
375 pop();
376 afterExec();
377 }
378 }
379 /** Returns the value of a variable defined in this interpreter's
380 * scope identified by the specified namespace.
381 * Note: it doesn't search the specified namespace ({@link Namespace}).
382 *
383 * <p>Deriving class shall override {@link #get(Namespace,String)},
384 * instead of this method.
385 *
386 * <p>This method is part of {@link org.zkoss.zk.scripting.HierachicalAware}.
387 * It is defined here to simplify the implementation of the
388 * deriving classes, if they support the hierachical scopes.
389 */
390 public Object getVariable(Namespace ns, String name) {
391 beforeExec();
392 push(Objects.UNKNOWN);
393 //don't use null since it means Namespaces#getCurrent, see below
394 try {
395 return get(ns, name);
396 } finally {
397 pop();
398 afterExec();
399 }
400 }
401 /** Sets the value of a variable to this interpreter's scope
402 * identified by the specified namespace.
403 *
404 * <p>Deriving class shall override {@link #set(Namespace,String,Object)},
405 * instead of this method.
406 * @since 2.4.0
407 */
408 public final void setVariable(Namespace ns, String name, Object value) {
409 beforeExec();
410 try {
411 set(ns, name, value);
412 } finally {
413 afterExec();
414 }
415 }
416 /** Removes the value of a variable defined in the interpreter's
417 * scope identified by the specified namespace.
418 *
419 * <p>Deriving class shall override {@link #unset(Namespace,String)}, instead of this method.
420 * @since 2.4.0
421 */
422 public final void unsetVariable(Namespace ns, String name) {
423 beforeExec();
424 try {
425 unset(ns, name);
426 } finally {
427 afterExec();
428 }
429 }
430
431 /** Use the specified namespace as the active namespace.
432 */
433 private void push(Object ns) {
434 _nss.add(0, ns);
435 }
436 /** Remove the active namespace.
437 */
438 private void pop() {
439 _nss.remove(0);
440 }
441 /** Returns the current namespace, or null if no namespace is allowed.
442 * Note: if this method returns null, it means the interpreter shall
443 * not search variables defined in ZK namespace.
444 *
445 * <p>This method will handle {@link Namespaces#getCurrent} automatically.
446 */
447 protected Namespace getCurrent() {
448 if (!_nss.isEmpty()) {
449 Object o = _nss.get(0);
450 if (o == Objects.UNKNOWN)
451 return null; //no namespace allowed
452 if (o != null)
453 return (Namespace)o;
454 //we assume owner's namespace if null, because zscript might
455 //define a class that will be invoke thru, say, event listener
456 //In other words, interpret is not called, so ns is not specified
457 }
458 return Namespaces.getCurrent(_owner); //never null
459 }
460 }