001: package jaxx.compiler;
002:
003: import java.io.*;
004: import java.lang.reflect.Modifier;
005: import java.util.*;
006:
007: import jaxx.*;
008: import jaxx.parser.*;
009: import jaxx.reflect.*;
010: import jaxx.tags.*;
011:
012: public class ScriptManager {
013: private JAXXCompiler compiler;
014:
015: ScriptManager(JAXXCompiler compiler) {
016: this .compiler = compiler;
017: }
018:
019: /** Strips unnecessary curly braces from around the script, generating a warning if they are found. */
020: public String trimScript(String script) {
021: script = script.trim();
022: if (script.startsWith("{") && script.endsWith("}")) {
023: compiler
024: .reportWarning("curly braces are unnecessary for script '"
025: + script + "'");
026: script = script.substring(1, script.length() - 1);
027: }
028: return script;
029: }
030:
031: public void checkParse(String script) throws CompilerException {
032: script = trimScript(script);
033: JavaParser p = new JavaParser(new StringReader(script));
034: while (!p.Line())
035: ;
036: }
037:
038: public String preprocessScript(String script)
039: throws CompilerException {
040: script = trimScript(script);
041: StringBuffer result = new StringBuffer();
042: JavaParser p = new JavaParser(new StringReader(script + ";"));
043: while (!p.Line()) {
044: SimpleNode node = p.popNode();
045: if (node != null) {
046: preprocessScriptNode(node, false);
047: result.append(node.getText());
048: }
049: }
050: return result.toString();
051: }
052:
053: /** Scans through a compound symbol (foo.bar.baz) to identify and compile the JAXX class it refers to, if any.
054: */
055: private void scanCompoundSymbol(String symbol) {
056: String[] tokens = symbol.split("\\.");
057: StringBuffer currentSymbol = new StringBuffer();
058: for (int j = 0; j < tokens.length; j++) {
059: if (currentSymbol.length() > 0)
060: currentSymbol.append('.');
061: currentSymbol.append(tokens[j].trim());
062:
063: String contextClass = TagManager.resolveClassName(
064: currentSymbol.toString(), compiler);
065: if (contextClass != null) {
066: compiler.addDependencyClass(contextClass);
067: }
068: }
069: }
070:
071: private void preprocessScriptNode(SimpleNode node,
072: boolean staticContext) throws CompilerException {
073: // identify static methods and initializers -- we can't fire events statically
074: if (node.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION) {
075: if (node.getParent().getChild(0).getText()
076: .indexOf("static") != -1)
077: staticContext = true;
078: } else if (node.getId() == JavaParserTreeConstants.JJTINITIALIZER)
079: if (node.getText().trim().startsWith("static"))
080: staticContext = true;
081:
082: int count = node.jjtGetNumChildren();
083: for (int i = 0; i < count; i++)
084: preprocessScriptNode(node.getChild(i), staticContext);
085:
086: int id = node.getId();
087: if (id == JavaParserTreeConstants.JJTNAME
088: || id == JavaParserTreeConstants.JJTCLASSORINTERFACETYPE)
089: scanCompoundSymbol(node.getText());
090: if (!staticContext) {
091: String lhs = null;
092: if (id == JavaParserTreeConstants.JJTASSIGNMENTEXPRESSION
093: || (id == JavaParserTreeConstants.JJTPOSTFIXEXPRESSION && node
094: .jjtGetNumChildren() == 2))
095: lhs = ((SimpleNode) node.jjtGetChild(0)).getText()
096: .trim();
097: else if (id == JavaParserTreeConstants.JJTPREINCREMENTEXPRESSION
098: || id == JavaParserTreeConstants.JJTPREDECREMENTEXPRESSION)
099: lhs = ((SimpleNode) node.jjtGetChild(0)).getText()
100: .trim();
101: if (lhs != null) {
102: FieldDescriptor[] fields = compiler.getScriptFields();
103: for (int i = 0; i < fields.length; i++) {
104: if (fields[i].getName().equals(lhs)) {
105: lhs.substring(lhs.lastIndexOf(".") + 1);
106: node.firstToken.image = "jaxx.runtime.Util.assignment("
107: + node.firstToken.image;
108: String outputClassName = compiler
109: .getOutputClassName();
110: node.lastToken.image = node.lastToken.image
111: + ", \"" + lhs + "\", "
112: + outputClassName + ".this)";
113: }
114: }
115: }
116: }
117: }
118:
119: /** Examines a Line to determine its real type. As all tokens returned by the parser are Lines, and
120: * they are just a tiny wrapper around the real node, this method strips off the wrapper layers to identify
121: * the real type of a node.
122: */
123: private int getLineType(SimpleNode line) {
124: if (line.jjtGetNumChildren() == 1) {
125: SimpleNode node = line.getChild(0);
126: if (node.getId() == JavaParserTreeConstants.JJTBLOCKSTATEMENT) {
127: if (node.jjtGetNumChildren() == 1)
128: return node.getChild(0).getId();
129: } else if (node.getId() == JavaParserTreeConstants.JJTCLASSORINTERFACEBODYDECLARATION) {
130: int id = node.getChild(0).getId();
131: if (id == JavaParserTreeConstants.JJTMODIFIERS)
132: return node.getChild(1).getId();
133: else if (id == JavaParserTreeConstants.JJTINITIALIZER)
134: return id;
135: }
136: return node.getId();
137: } else
138: return JavaParserTreeConstants.JJTLINE; // generic value implying that it's okay to put into the initializer block
139: }
140:
141: private SimpleNode findExplicitConstructorInvocation(
142: SimpleNode parent) {
143: if (parent.getId() == JavaParserTreeConstants.JJTEXPLICITCONSTRUCTORINVOCATION)
144: return parent;
145:
146: int count = parent.jjtGetNumChildren();
147: for (int i = 0; i < count; i++) {
148: SimpleNode result = findExplicitConstructorInvocation(parent
149: .getChild(i));
150: if (result != null)
151: return result;
152: }
153: return null;
154: }
155:
156: private void processConstructor(String modifiers, SimpleNode node) {
157: assert node.getId() == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION : "expected node to be ConstructorDeclaration, found "
158: + JavaParserTreeConstants.jjtNodeName[node.getId()]
159: + " instead";
160: assert node.getChild(0).getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS : "expected node 0 to be FormalParameters, found "
161: + JavaParserTreeConstants.jjtNodeName[node.getChild(1)
162: .getId()] + " instead";
163: if (node.getChild(0).jjtGetNumChildren() == 0)
164: compiler
165: .reportError("The default no-argument constructor may not be redefined");
166: else {
167: SimpleNode explicitConstructorInvocation = findExplicitConstructorInvocation(node);
168: if (explicitConstructorInvocation == null
169: || explicitConstructorInvocation.getText().trim()
170: .startsWith("super(")) {
171: String code = "$initialize();"
172: + JAXXCompiler.getLineSeparator();
173: if (explicitConstructorInvocation == null)
174: node.getChild(1).firstToken.image = code
175: + node.getChild(1).firstToken.image;
176: else
177: explicitConstructorInvocation.lastToken.image += code;
178: }
179: }
180:
181: compiler.bodyCode.append(modifiers + " " + node.getText());
182: compiler.bodyCode.append(";\n");
183: }
184:
185: private void scanScriptNode(SimpleNode node)
186: throws CompilerException {
187: int nodeType = getLineType(node);
188: if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { // have to handle imports early so the preprocessing takes them into account
189: String text = node.getChild(0).getText().trim();
190: if (text.startsWith("import"))
191: text = text.substring("import".length()).trim();
192: if (text.endsWith(";"))
193: text = text.substring(0, text.length() - 1);
194: compiler.addImport(text);
195: }
196:
197: preprocessScriptNode(node, false);
198:
199: if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) {
200: // do nothing, already handled above
201: } else if (nodeType == JavaParserTreeConstants.JJTMETHODDECLARATION) {
202: String returnType = null;
203: String name = null;
204: List/*<String>*/parameterTypes = new ArrayList/*<String>*/();
205: List/*<String>*/parameterNames = new ArrayList/*<String>*/();
206: SimpleNode methodDeclaration = node.getChild(0).getChild(1);
207: assert methodDeclaration.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION;
208: for (int i = 0; i < methodDeclaration.jjtGetNumChildren(); i++) {
209: SimpleNode child = methodDeclaration.getChild(i);
210: int type = child.getId();
211: if (type == JavaParserTreeConstants.JJTRESULTTYPE) {
212: String rawReturnType = child.getText().trim();
213: returnType = TagManager.resolveClassName(
214: rawReturnType, compiler);
215: // FIXME: this check fails for inner classes defined in this file
216: //if (returnType == null)
217: // throw new CompilerException("could not find class '" + rawReturnType + "'");
218: } else if (type == JavaParserTreeConstants.JJTMETHODDECLARATOR) {
219: name = child.firstToken.image.trim();
220: SimpleNode formalParameters = child.getChild(0);
221: assert formalParameters.getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS;
222: for (int j = 0; j < formalParameters
223: .jjtGetNumChildren(); j++) {
224: SimpleNode parameter = formalParameters
225: .getChild(j);
226: String rawParameterType = parameter.getChild(1)
227: .getText().trim().replaceAll(
228: "\\.\\.\\.", "[]");
229: String parameterType = TagManager
230: .resolveClassName(rawParameterType,
231: compiler);
232: // FIXME: this check fails for inner classes defined in this file
233: //if (parameterType == null)
234: // throw new CompilerException("could not find class '" + rawParameterType + "'");
235: parameterTypes.add(parameterType);
236: parameterNames.add(parameter.getChild(2)
237: .getText().trim());
238: }
239: }
240: }
241: compiler.bodyCode.append(node.getText());
242: compiler.bodyCode.append(";\n");
243: compiler
244: .addScriptMethod(new MethodDescriptor(name,
245: Modifier.PUBLIC, returnType,
246: (String[]) parameterTypes
247: .toArray(new String[parameterTypes
248: .size()]), compiler
249: .getClassLoader()));
250: } else if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION
251: || nodeType == JavaParserTreeConstants.JJTINITIALIZER) {
252: compiler.bodyCode.append(node.getText());
253: compiler.bodyCode.append(";\n");
254: } else if (nodeType == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION) {
255: processConstructor(node.getChild(0).getChild(0).getText(),
256: node.getChild(0).getChild(1));
257: } else if (nodeType == JavaParserTreeConstants.JJTLOCALVARIABLEDECLARATION
258: || nodeType == JavaParserTreeConstants.JJTFIELDDECLARATION) {
259: // the "local" variable declarations in this expression aren't actually local -- they are flagged local
260: // just because there isn't an enclosing class scope visible to the parser. "Real" local variable
261: // declarations won't show up here, because they will be buried inside of methods.
262: String text = node.getText();
263: String declaration = text;
264: int equals = text.indexOf("=");
265: if (equals != -1)
266: declaration = declaration.substring(0, equals);
267: declaration = declaration.trim();
268: String[] declarationTokens = declaration.split("\\s");
269: boolean isFinal = Arrays.asList(declarationTokens)
270: .contains("final");
271: boolean isStatic = Arrays.asList(declarationTokens)
272: .contains("static");
273: String name = declarationTokens[declarationTokens.length - 1];
274: if (name.endsWith(";"))
275: name = name.substring(0, name.length() - 1).trim();
276: String className = declarationTokens[declarationTokens.length - 2];
277: String type = TagManager.resolveClassName(className,
278: compiler);
279: compiler.addScriptField(new FieldDescriptor(name,
280: Modifier.PUBLIC, type, compiler.getClassLoader())); // TODO: determine the actual modifiers
281: if (equals != -1 && !isFinal && !isStatic) { // declare the field in the class body, but wait to actually initialize it
282: compiler.bodyCode.append(text.substring(0, equals)
283: .trim()
284: + ";");
285: String initializer = text.substring(equals + 1).trim();
286: if (type.endsWith("[]"))
287: initializer = "new " + type + " " + initializer;
288: final String finalInitializer = name + " = "
289: + initializer;
290: compiler.registerInitializer(new Runnable() {
291: public void run() {
292: compiler
293: .registerCompiledObject(new ScriptInitializer(
294: finalInitializer, compiler));
295: }
296: });
297: } else {
298: compiler.bodyCode.append(text);
299: }
300: compiler.bodyCode.append(";\n");
301: } else {
302: String text = node.getText().trim();
303: if (text.length() > 0) {
304: compiler.initializer.append(text);
305: compiler.initializer.append(";\n");
306: }
307: }
308: }
309:
310: public void registerScript(String script) throws CompilerException {
311: JavaParser p = new JavaParser(new StringReader(script + ";"));
312: while (!p.Line()) {
313: SimpleNode node = p.popNode();
314: if (node != null) {
315: scanScriptNode(node);
316: }
317: }
318: }
319: }
|