Source code: com/tripi/asp/parse/NestedTokenManager.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.parse;
26
27 import com.tripi.asp.*;
28
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.StringReader;
32 import java.util.Stack;
33 import jregex.*;
34 import org.apache.log4j.Category;
35
36 /**
37 * This class is a nested token manager, and handles VBScript tokens
38 * embedded within ASP files.
39 */
40 public class NestedTokenManager implements TokenManager
41 {
42 /** Debugging class */
43 Category DBG = Category.getInstance(NestedTokenManager.class);
44
45 /** This inner class contains information about a file being loaded */
46 static class FileInfo
47 {
48 /** Abosolute filename for the file. */
49 String filename;
50
51 /** Active token manager for the file. */
52 AspParseTokenManager tokManager;
53 }
54
55 /** Stack of current parsers */
56 Stack fileInfoStack = new Stack();
57
58 /** Are we in VBScript or looking as ASP tokens */
59 boolean inScript;
60
61 /** The token factory we use to read VBScript tokens */
62 TokenManager subTokenManager;
63
64 /** File Factory used to obtain input streams from fiels */
65 FileFactory fileFactory;
66
67 /** Next token */
68 Token nextToken = null;
69
70 /**
71 * Constructor, with an initial filename and file factory.
72 * @param filename Initial filename
73 * @param fileFactory factory used to obtain files
74 * @throws IOException on error
75 */
76 public NestedTokenManager(String filename, FileFactory fileFactory)
77 throws AspException
78 {
79 this.fileFactory = fileFactory;
80 this.inScript = false;
81 FileInfo fileInfo = getFileInfo(filename);
82 fileInfoStack.push(fileInfo);
83 }
84
85 /**
86 * Get the FileInfo structure for the file.
87 * @param base Base filename
88 * @param filename Relative filename to open
89 * @return FileInfo class containing filename and parser.
90 * @throws IOException on input/output error
91 */
92 FileInfo getFileInfo(String filename) throws AspException
93 {
94 FileInfo fileInfo = new FileInfo();
95 fileInfo.filename = filename;
96
97 InputStream is = fileFactory.getResource(fileInfo.filename);
98 SimpleCharStream stream = new SimpleCharStream(is);
99 fileInfo.tokManager = new AspParseTokenManager(stream);
100 return fileInfo;
101 }
102
103 /**
104 * Internal function to find the contents of an SGML token.
105 * @param str String to find contents of
106 * @return contents of SGML token
107 */
108 private String getTokenContents(String str)
109 {
110 final Pattern p = new Pattern("^<[^>]*>(.*)<[^>]*>$",
111 REFlags.MULTILINE | REFlags.IGNORE_SPACES | REFlags.DOTALL);
112 Matcher m = p.matcher(str);
113 if (!m.matches()) throw new AspRuntimeException("Internal error");
114 return m.group(1);
115 }
116
117 /**
118 * Obtains a token manager for the data contained within the token.
119 * The token should be an ASPScript token, the <%, %> characters will
120 * be stripped.
121 * @param tok ASP Token to parse code from.
122 * @return TokenManager for parsing the code.
123 */
124 TokenManager getTokenManager(Token tok)
125 {
126 String subStr;
127 if (tok.kind == AspParseConstants.ASPSCRIPT ||
128 tok.kind == AspParseConstants.OUTPUTSCRIPT)
129 {
130 subStr = tok.image.substring(2, tok.image.length() - 2);
131 } else if (tok.kind == AspParseConstants.SERVERSCRIPT)
132 {
133 subStr = getTokenContents(tok.image);
134 } else {
135 DebugContext ctx = new DebugContext();
136 tok.fillDebugContext(ctx);
137 throw new AspRuntimeException("Internal parse error: " +
138 "Unexptected token type " + tok.kind, ctx);
139 }
140 StringReader sr = new StringReader(subStr);
141 SimpleCharStream stream = new SimpleCharStream(sr, tok.beginLine,
142 tok.beginColumn + 2);
143 return new VBScriptTokenManagerInterface(stream);
144 }
145
146 /**
147 * Obtain the filename from an include specification.
148 * @param include include specification which to obtain file from.
149 * @return filename for the include specification.
150 */
151 String getFilenameFromInclude(String baseFilename, String include)
152 throws AspException
153 {
154 final Pattern p = new Pattern("<!--[ \t]*#include[ \t]*(file|virtual)" +
155 "[ \t]*=[ \t]*\"([^\"]*)\"[ \t]*-->", REFlags.IGNORE_CASE);
156 Matcher m = p.matcher(include);
157 if (!m.matches())
158 {
159 throw new AspRuntimeException("Internal error");
160 }
161 String virtual = m.group(1);
162 String relativeFilename = m.group(2);
163 String filename = fileFactory.resolveFile(baseFilename,
164 relativeFilename, virtual.equalsIgnoreCase("virtual"));
165 return filename;
166 }
167
168 /**
169 * Clone the token, keeping debugging information and image.
170 * @param tok Token to close
171 */
172 public Token cloneToken(Token tok)
173 {
174 Token retTok = new Token();
175 retTok.image = tok.image;
176 retTok.kind = 0;
177 retTok.beginLine = tok.beginLine;
178 retTok.beginColumn = tok.beginColumn;
179 retTok.endLine = tok.endLine;
180 retTok.endColumn = tok.endColumn;
181 retTok.filename = tok.filename;
182 return retTok;
183 }
184
185 /**
186 * Obtain the next token from the stream.
187 * @return next token from the stream.
188 */
189 public Token getNextToken()
190 {
191 if (DBG.isDebugEnabled()) DBG.debug("getNextToken");
192 if (nextToken != null) {
193 Token retTok = nextToken;
194 nextToken = null;
195 if (DBG.isDebugEnabled()) DBG.debug("nextToken: " + retTok.kind);
196 return retTok;
197 }
198 if (DBG.isDebugEnabled()) DBG.debug("Peek");
199 FileInfo fileInfo = (FileInfo)fileInfoStack.peek();
200 if (DBG.isDebugEnabled()) DBG.debug("inScript: " + inScript);
201 if (inScript)
202 try {
203 if (DBG.isDebugEnabled()) DBG.debug("subTokenManager: " + subTokenManager);
204 Token tok = subTokenManager.getNextToken();
205 if (DBG.isDebugEnabled()) DBG.debug("filename: " + fileInfo.filename);
206 tok.filename = fileInfo.filename;
207 if (DBG.isDebugEnabled()) DBG.debug("Language token: " + tok.kind);
208 if (tok.kind != 0)
209 return tok;
210 inScript = false;
211 } catch (TokenMgrError e) {
212 DBG.error(e);
213 e.setFilename(fileInfo.filename);
214 throw e;
215 }
216 Token tok = null;
217 try {
218 AspParseTokenManager tm = fileInfo.tokManager;
219 if (DBG.isDebugEnabled()) DBG.debug("Asp getNextToken");
220 tok = tm.getNextToken();
221 if (DBG.isDebugEnabled()) DBG.debug("ASP Token: " + tok.kind);
222 tok.filename = fileInfo.filename;
223 if (tok.kind == 0) {
224 fileInfoStack.pop();
225 if (fileInfoStack.empty())
226 {
227 return tok;
228 }
229 return getNextToken();
230 }
231 switch(tok.kind)
232 {
233 case AspParseConstants.ASPSCRIPT:
234 subTokenManager = getTokenManager(tok);
235 inScript = true;
236 return getNextToken();
237 case AspParseConstants.SERVERSCRIPT:
238 subTokenManager = getTokenManager(tok);
239 inScript = true;
240 return getNextToken();
241 case AspParseConstants.OUTPUTSCRIPT:
242 subTokenManager = getTokenManager(tok);
243 inScript = true;
244 nextToken = cloneToken(tok);
245 nextToken.kind = VBScriptConstants.OUTPUT;
246
247 Token retTok = cloneToken(tok);
248 retTok.kind = VBScriptConstants.NL;
249 retTok.image = "\n";
250 return retTok;
251 case AspParseConstants.INCLUDE:
252 String filename = getFilenameFromInclude(
253 fileInfo.filename, tok.image);
254 FileInfo subFileInfo = getFileInfo(filename);
255 fileInfoStack.push(subFileInfo);
256 return getNextToken();
257 case AspParseConstants.HTML:
258 if (!containsOnlyWS(tok.image))
259 {
260 Token thisTok = cloneToken(tok);
261
262 nextToken = cloneToken(tok);
263 nextToken.kind = VBScriptConstants.HTML;
264 }
265
266 Token subTok = cloneToken(tok);
267 subTok.kind = VBScriptConstants.NL;
268 subTok.image = "\n";
269 return subTok;
270 case AspParseConstants.LANGUAGEDEF:
271 /* Skipped */
272 return getNextToken();
273 default:
274 DebugContext ctx = new DebugContext();
275 tok.fillDebugContext(ctx);
276 throw new AspRuntimeException("Unknown token: " + tok.kind,
277 ctx);
278 }
279 } catch (TokenMgrError e)
280 {
281 e.setFilename(fileInfo.filename);
282 throw e;
283 } catch (AspException ex)
284 {
285 DBG.debug("AspException thrown");
286 if (!ex.hasContext() && tok != null) {
287 DebugContext ctx = new DebugContext();
288 tok.fillDebugContext(ctx);
289 ex.setContext(ctx);
290 }
291 throw new AspRuntimeSubException(ex);
292 }
293 }
294
295 /**
296 * This function tests if the string contains only whitespace.
297 * @param str String to test for white space
298 * @return <b>true</b> if the string contains only whitespace, <b>false</b>
299 * otherwise.
300 */
301 public boolean containsOnlyWS(String str)
302 {
303 for (int i = 0; i < str.length(); i++)
304 {
305 if (!Character.isWhitespace(str.charAt(i)))
306 return false;
307 }
308 return true;
309 }
310 }
311