Source code: com/tripi/asp/AspContext.java
1 /**
2 * ArrowHead ASP Server
3 * This is a source file for the ArrowHead ASP Server - an 100% Java
4 * VBScript interpreter and ASP server.
5 *
6 * For more information, see http://www.tripi.com/arrowhead
7 *
8 * Copyright (C) 2002 Terence Haddock
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 package com.tripi.asp;
26
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.util.Enumeration;
32 import java.util.Hashtable;
33 import java.util.Stack;
34 import java.util.Vector;
35
36 import org.apache.log4j.Category;
37
38 /**
39 * This class implements a class to hold the current state of ASP
40 * execution.
41 *
42 * @author Terence Haddock
43 */
44 public class AspContext
45 {
46 /** Debugging information */
47 private static final Category DBG = Category.getInstance(AspContext.class);
48
49 /** Universal scope */
50 Hashtable universalScope;
51
52 /** Global scope */
53 Hashtable globalScope;
54
55 /** Subroutine scope */
56 Stack subroutineScope;
57
58 /** Vector of objects to call OnPageEnd to */
59 Vector onPageEnd;
60
61 /** Are we option explicit? */
62 boolean optionExplicit = false;
63
64 /**
65 * Constructor.
66 * @param universalScope Universal scope
67 */
68 public AspContext(Hashtable universalScope)
69 {
70 this.universalScope = universalScope;
71 this.globalScope = new Hashtable();
72 this.subroutineScope = new Stack();
73 onPageEnd = new Vector();
74 }
75
76 /**
77 * This function clones this context.
78 * @return a cloned snapshop of this context.
79 */
80 protected Object clone()
81 {
82 AspContext ctx = new AspContext(universalScope);
83 ctx.globalScope = (Hashtable)globalScope.clone();
84 ctx.subroutineScope = (Stack)subroutineScope.clone();
85 ctx.onPageEnd = onPageEnd;
86 ctx.optionExplicit = optionExplicit;
87 return ctx;
88 }
89
90 /**
91 * This function determines if a variable is directly in scope.
92 * This is slightly different from inScope, as the variable is not
93 * tested against the global scope if we are in a subroutine.
94 *
95 * @param ident identifier to check
96 * @return <b>true</b> if the variable is in scope, <b>false</b> otherwise.
97 */
98 public boolean inDirectScope(IdentNode ident)
99 {
100 if (DBG.isDebugEnabled())
101 DBG.debug("inDirectScope(" + ident + ")");
102 if (!subroutineScope.empty())
103 {
104 Hashtable subScope = (Hashtable)subroutineScope.peek();
105 if (subScope.containsKey(ident))
106 return true;
107 } else {
108 if (globalScope.containsKey(ident))
109 return true;
110 }
111 if (universalScope.containsKey(ident))
112 return true;
113 return false;
114 }
115
116 /**
117 * This function determines if a variable is in scope.
118 *
119 * @param ident identifier to check
120 * @return <b>true</b> if the variable is in scope, <b>false</b>
121 * otherwise.
122 */
123 public boolean inScope(IdentNode ident)
124 {
125 if (DBG.isDebugEnabled())
126 DBG.debug("inScope(" + ident + ")");
127 if (!subroutineScope.empty())
128 {
129 Hashtable subScope = (Hashtable)subroutineScope.peek();
130 if (subScope.containsKey(ident))
131 return true;
132 }
133 if (globalScope.containsKey(ident))
134 return true;
135 if (universalScope.containsKey(ident))
136 return true;
137 return false;
138 }
139
140 /**
141 * This function forces an identifier's scope to the current context.
142 *
143 * @param ident Identifier to force
144 */
145 public void forceScope(IdentNode ident)
146 {
147 if (!subroutineScope.empty())
148 {
149 Hashtable subScope = (Hashtable)subroutineScope.peek();
150 subScope.put(ident, Constants.undefinedValueNode);
151 return;
152 }
153 globalScope.put(ident, Constants.undefinedValueNode);
154 }
155
156 /**
157 * This function obtains the value of a variable
158 *
159 * @param ident Identifier to obtain
160 * @return Value of the variable.
161 * @throws AspException on error
162 */
163 public Object getValue(IdentNode ident) throws AspException
164 {
165 if (DBG.isDebugEnabled())
166 DBG.debug("getValue(" + ident + ")");
167 if (!subroutineScope.empty())
168 {
169 Hashtable subScope = (Hashtable)subroutineScope.peek();
170 if (subScope.containsKey(ident))
171 return subScope.get(ident);
172 }
173 if (globalScope.containsKey(ident))
174 return globalScope.get(ident);
175 if (universalScope.containsKey(ident))
176 return universalScope.get(ident);
177 if (optionExplicit)
178 throw new AspException("Undefined variable: " + ident);
179 return Constants.undefinedValueNode;
180 }
181
182 /**
183 * Set the value of an identifier.
184 *
185 * @param ident Identifier name
186 * @param value New value of object
187 * @throws AspException on error
188 */
189 public void setValue(IdentNode ident, Object value) throws AspException
190 {
191 if (DBG.isDebugEnabled())
192 DBG.debug("setValue(" + ident + ")=" + value);
193 if (value == null) value = Constants.nullNode;
194 /**
195 * If the variable is in subroutine scope: store it in sub.
196 * Otherwise, if the variable is in global scope, store it in global.
197 * Otherwise, if subroutine scope exists, store it there.
198 * Otherwise, store it in global scope.
199 */
200 if (!subroutineScope.empty()) {
201 Hashtable subScope = (Hashtable)subroutineScope.peek();
202 if (subScope.containsKey(ident) || !globalScope.containsKey(ident))
203 {
204 subScope.put(ident, value);
205 return;
206 }
207 }
208 globalScope.put(ident, value);
209 return;
210 }
211
212 /**
213 * Sets the option explicit flag.
214 * @throws AspException on error
215 */
216 void setOptionExplicit() throws AspException
217 {
218 if (optionExplicit)
219 throw new AspException("Option Explicit can be called only once");
220 optionExplicit = true;
221 }
222
223 /**
224 * Pushes a new subroutine scope onto the scope stack.
225 * @param scope new subroutine scope
226 * @throws AspException on error
227 */
228 void pushSubroutineScope(Hashtable scope) throws AspException
229 {
230 subroutineScope.push(scope);
231 }
232
233 /**
234 * Pops a subroutine scope off of the scope stack.
235 * @return old subroutine scope, or null if there is no scopes left
236 * @throws AspException on error
237 */
238 Hashtable popSubroutineScope() throws AspException
239 {
240 return (Hashtable)subroutineScope.pop();
241 }
242
243 /**
244 * Utility function to obtain the Server object.
245 * XXX Should this be here, or in a separate class?
246 * @return Server object
247 * @throws AspException on error
248 */
249 public Server getAspServer() throws AspException
250 {
251 final IdentNode serverIdent = new IdentNode("Server");
252 JavaObjectNode jon = (JavaObjectNode)getValue(serverIdent);
253 return (Server)jon.getSubObject();
254 }
255
256 /**
257 * Utility function to obtain the Session object.
258 * @return Session object
259 * @throws AspException on error
260 */
261 public Session getAspSession() throws AspException
262 {
263 final IdentNode sessionIdent = new IdentNode("Session");
264 JavaObjectNode jon = (JavaObjectNode)getValue(sessionIdent);
265 return (Session)jon.getSubObject();
266 }
267
268 /**
269 * Utility function to obtain the Application object.
270 * @return Application object
271 * @throws AspException on error
272 */
273 public Application getAspApplication() throws AspException
274 {
275 final IdentNode appIdent = new IdentNode("Application");
276 JavaObjectNode jon = (JavaObjectNode)getValue(appIdent);
277 return (Application)jon.getSubObject();
278 }
279
280 /**
281 * Utility function to obtain the Request object.
282 * @return Request object
283 * @throws AspException on error
284 */
285 public Request getAspRequest() throws AspException
286 {
287 final IdentNode rspIdent = new IdentNode("Request");
288 JavaObjectNode jon = (JavaObjectNode)getValue(rspIdent);
289 return (Request)jon.getSubObject();
290 }
291
292 /**
293 * Utility function to obtain the Response object.
294 * @return Response object
295 * @throws AspException on error
296 */
297 public Response getAspResponse() throws AspException
298 {
299 final IdentNode rspIdent = new IdentNode("Response");
300 JavaObjectNode jon = (JavaObjectNode)getValue(rspIdent);
301 return (Response)jon.getSubObject();
302 }
303
304 /**
305 * Adds a class to call the OnPageEnd when the page ends.
306 * @param obj Object to call the OnPageEnd method.
307 */
308 public void addOnPageEnd(Object obj)
309 {
310 if (DBG.isDebugEnabled()) DBG.debug("Adding OnPageEnd for " + obj);
311 onPageEnd.add(obj);
312 }
313
314 /**
315 * Calls the OnPageEnd methods of the object.
316 */
317 public synchronized void callOnPageEnd()
318 {
319 if (DBG.isDebugEnabled()) DBG.debug("Calling all OnPageEnd");
320 for (Enumeration eItems = onPageEnd.elements(); eItems.hasMoreElements();)
321 {
322 Object obj = eItems.nextElement();
323 callOnPageEnd(obj);
324 }
325 onPageEnd.clear();
326 }
327
328 /**
329 * Calls the OnPageEnd method of a particular object.
330 * @param obj Object to call
331 */
332 void callOnPageEnd(Object obj)
333 {
334 if (DBG.isDebugEnabled()) DBG.debug("OnPageEnd for " + obj);
335 Class paramTypes[] = new Class[1];
336 paramTypes[0] = Object.class;
337 Object paramArguments[] = new Object[1];
338 paramArguments[0] = this;
339
340 Class cls = obj.getClass();
341 try {
342 Method met = cls.getMethod("OnPageEnd", paramTypes);
343 met.invoke(obj, paramArguments);
344 } catch (NoSuchMethodException ex)
345 {
346 } catch (IllegalAccessException ex)
347 {
348 } catch (InvocationTargetException ex)
349 {
350 }
351 }
352
353 /**
354 * Calls the OnPageStart method of a particular object.
355 * @param obj Object to call
356 */
357 void callOnPageStart(Object obj)
358 {
359 if (DBG.isDebugEnabled()) DBG.debug("OnPageStart for " + obj);
360 Class paramTypes[] = new Class[1];
361 paramTypes[0] = Object.class;
362 Object paramArguments[] = new Object[1];
363 paramArguments[0] = this;
364
365 Class cls = obj.getClass();
366 try {
367 Method met = cls.getMethod("OnPageStart", paramTypes);
368 met.invoke(obj, paramArguments);
369 } catch (NoSuchMethodException ex)
370 {
371 } catch (IllegalAccessException ex)
372 {
373 } catch (InvocationTargetException ex)
374 {
375 }
376 }
377
378 /**
379 * This utility function checks if ResumeNext is in effect.
380 *
381 * @param context Current Context
382 * @return true if resume next is in effect, false otherwise
383 * @throws AspException on error
384 */
385 boolean resumeNext() throws AspException
386 {
387 final IdentNode ident = new IdentNode("_err");
388 if (DBG.isDebugEnabled()) DBG.debug("Resume next test");
389 if (inScope(ident)) {
390 Object obj = getValue(ident);
391 if (DBG.isDebugEnabled()) DBG.debug("Resume next: " + obj);
392 if (obj == null || obj instanceof NullNode) {
393 return true;
394 }
395 }
396 return false;
397 }
398
399 /**
400 * This function handles error processing.
401 *
402 * @param ex Exception thrown
403 * @param ctx Current debugging context
404 * @throws AspException on error
405 */
406 public void processException(Exception ex, DebugContext ctx)
407 throws AspException
408 {
409 if (ex instanceof AspExitException) throw (AspExitException)ex;
410
411 final IdentNode Err = new IdentNode("Err");
412 AspException aspEx = new AspNestedException(ex, ctx);
413 setValue(Err, new JavaObjectNode(aspEx));
414 if (!resumeNext())
415 {
416 throw aspEx;
417 }
418 }
419
420 /**
421 * This function obtains the URL associated with the given filename.
422 *
423 * @param filename Full pathname to file
424 * @return URL associated with the given filename
425 * @throws AspException on error
426 */
427 public URL getURL(String filename) throws AspException
428 {
429 /* Get the server object */
430 final IdentNode serverIdent = new IdentNode("server");
431 JavaObjectNode serverNode = (JavaObjectNode)getValue(serverIdent);
432 Server server = (Server)serverNode.getSubObject();
433
434 try {
435 if (DBG.isDebugEnabled()) DBG.debug("Getting URL for: " + filename);
436 /* Get the URL for the file */
437 URL url = server.servletContext.getResource(filename);
438
439 if (DBG.isDebugEnabled()) DBG.debug("URL for: " + filename + "=" + url);
440
441 if (url == null) url = new URL("file://" + filename);
442
443 return url;
444 } catch (MalformedURLException ex) {
445 throw new AspNestedException(ex);
446 }
447 }
448 }
449