Author: tchemit Date: 2008-10-19 19:44:06 +0000 (Sun, 19 Oct 2008) New Revision: 977 Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompilerHelper.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/ScriptManager.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/css/Stylesheet.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/ApplicationHandler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JInternalFrameHandler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JWindowHandler.java lutinjaxx/trunk/maven-jaxx-plugin/src/test/java/org/codelutin/jaxx/CompilerTest.java Log: refactor JAXXCompiler : now use two others class : - JAXXCompilerHelper which contains all static methods - JAXXObjectGenerator which manage the generation of the java file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java 2008-10-19 19:44:06 UTC (rev 977) @@ -420,7 +420,7 @@ eventHandlers.add(handler); if (getJavaCode().indexOf(".") != -1) { // object lives in another JAXX file and consequently its initialization code won't be output - compiler.initializer.append(getInitializationCode(handler, compiler)); + compiler.appendInitializerCode(getInitializationCode(handler, compiler)); } } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java 2008-10-19 19:44:06 UTC (rev 977) @@ -66,13 +66,13 @@ if (!result && quickNoDependencies) { if (!dest.endsWith(".layout")) // layout is specially handled early in the chain - compiler.initDataBindings.append(assignment).append(JAXXCompiler.getLineSeparator()); + compiler.appendInitDataBindings(assignment+ JAXXCompiler.getLineSeparator()); return false; // no dependencies, just a static expression - } else { - if (compiler.processDataBinding.length() > 0) - compiler.processDataBinding.append("else "); - compiler.processDataBinding.append("if ($dest.equals(").append(TypeManager.getJavaCode(id)).append(")) { ").append(assignment).append("}\n").append(JAXXCompiler.getLineSeparator()); - return true; } + if (compiler.haveProcessDataBinding()) { + compiler.appendProcessDataBinding("else "); + } + compiler.appendProcessDataBinding("if ($dest.equals(" + TypeManager.getJavaCode(id) + ")) { " + assignment + "}\n" + JAXXCompiler.getLineSeparator()); + return true; } } \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java 2008-10-19 19:44:06 UTC (rev 977) @@ -82,8 +82,9 @@ * @throws CompilerException if a compilation error occurs */ public boolean compile(String propertyChangeListenerCode) throws CompilerException { - if (compiled) + if (compiled) { throw new IllegalStateException(this + " has already been compiled"); + } String id = compiler.getAutoId(ClassDescriptorLoader.getClassDescriptor(getClass())); JavaParser p = new JavaParser(new StringReader(source + ";")); while (!p.Line()) { @@ -91,8 +92,9 @@ scanNode(node, id); } - if (dependencySymbols.size() > 0) - compiler.bodyCode.append("private PropertyChangeListener ").append(id).append(" = ").append(propertyChangeListenerCode).append(";\n"); + if (dependencySymbols.size() > 0) { + compiler.appendBodyCode("private PropertyChangeListener " + id + " = " + propertyChangeListenerCode + ";\n"); + } compileListeners(); compiled = true; @@ -121,8 +123,9 @@ default: int count = node.jjtGetNumChildren(); - for (int i = 0; i < count; i++) + for (int i = 0; i < count; i++) { scanNode(node.getChild(i), listenerId); + } determineNodeType(node, listenerId); } } @@ -131,33 +134,33 @@ assert node.getId() == JavaParserTreeConstants.JJTLITERAL; if (node.jjtGetNumChildren() == 1) { int id = node.getChild(0).getId(); - if (id == JavaParserTreeConstants.JJTBOOLEANLITERAL) + if (id == JavaParserTreeConstants.JJTBOOLEANLITERAL) { return ClassDescriptorLoader.getClassDescriptor(boolean.class); - else if (id == JavaParserTreeConstants.JJTNULLLITERAL) + } + if (id == JavaParserTreeConstants.JJTNULLLITERAL) { return ClassDescriptorLoader.getClassDescriptor(NULL.class); - else - throw new RuntimeException("Expected BooleanLiteral or NullLiteral, found " + JavaParserTreeConstants.jjtNodeName[id]); - } else { - int id = node.firstToken.kind; - switch (id) { - case JavaParserConstants.INTEGER_LITERAL: - if (node.firstToken.image.toLowerCase().endsWith("l")) - return ClassDescriptorLoader.getClassDescriptor(long.class); - else - return ClassDescriptorLoader.getClassDescriptor(int.class); - case JavaParserConstants.CHARACTER_LITERAL: - return ClassDescriptorLoader.getClassDescriptor(char.class); - case JavaParserConstants.FLOATING_POINT_LITERAL: - if (node.firstToken.image.toLowerCase().endsWith("f")) - return ClassDescriptorLoader.getClassDescriptor(float.class); - else - return ClassDescriptorLoader.getClassDescriptor(double.class); - case JavaParserConstants.STRING_LITERAL: - return ClassDescriptorLoader.getClassDescriptor(String.class); - default: - throw new RuntimeException("Expected literal token, found " + JavaParserConstants.tokenImage[id]); } + throw new RuntimeException("Expected BooleanLiteral or NullLiteral, found " + JavaParserTreeConstants.jjtNodeName[id]); } + int id = node.firstToken.kind; + switch (id) { + case JavaParserConstants.INTEGER_LITERAL: + if (node.firstToken.image.toLowerCase().endsWith("l")) { + return ClassDescriptorLoader.getClassDescriptor(long.class); + } + return ClassDescriptorLoader.getClassDescriptor(int.class); + case JavaParserConstants.CHARACTER_LITERAL: + return ClassDescriptorLoader.getClassDescriptor(char.class); + case JavaParserConstants.FLOATING_POINT_LITERAL: + if (node.firstToken.image.toLowerCase().endsWith("f")) { + return ClassDescriptorLoader.getClassDescriptor(float.class); + } + return ClassDescriptorLoader.getClassDescriptor(double.class); + case JavaParserConstants.STRING_LITERAL: + return ClassDescriptorLoader.getClassDescriptor(String.class); + default: + throw new RuntimeException("Expected literal token, found " + JavaParserConstants.tokenImage[id]); + } } /** @@ -179,11 +182,13 @@ for (int j = 0; j < tokens.length - (isMethod ? 1 : 0); j++) { accepted = false; - if (tokensSeenSoFar.length() > 0) + if (tokensSeenSoFar.length() > 0) { tokensSeenSoFar.append('.'); + } tokensSeenSoFar.append(tokens[j]); - if (currentSymbol.length() > 0) + if (currentSymbol.length() > 0) { currentSymbol.append('.'); + } currentSymbol.append(tokens[j]); if (currentSymbol.indexOf(".") == -1) { @@ -234,8 +239,9 @@ return null; } } - if (!accepted) + if (!accepted) { return null; + } } return contextClass; @@ -254,11 +260,13 @@ SimpleNode prefix = expression.getChild(0); if (prefix.jjtGetNumChildren() == 1) { int type = prefix.getChild(0).getId(); - if (type == JavaParserTreeConstants.JJTLITERAL || type == JavaParserTreeConstants.JJTEXPRESSION) + if (type == JavaParserTreeConstants.JJTLITERAL || type == JavaParserTreeConstants.JJTEXPRESSION) { prefix.setJavaType(prefix.getChild(0).getJavaType()); - else + } else if (type == JavaParserTreeConstants.JJTNAME && expression.jjtGetNumChildren() == 1) // name with no arguments after it + { prefix.setJavaType(scanCompoundSymbol(prefix.getText().trim(), compiler.getRootObject().getObjectClass(), false, listenerId)); + } } if (expression.jjtGetNumChildren() == 1) { @@ -266,8 +274,9 @@ } ClassDescriptor contextClass = prefix.getJavaType(); - if (contextClass == null) + if (contextClass == null) { contextClass = compiler.getRootObject().getObjectClass(); + } String lastNode = prefix.getText().trim(); for (int i = 1; i < expression.jjtGetNumChildren(); i++) { @@ -275,14 +284,17 @@ if (suffix.jjtGetNumChildren() == 1 && suffix.getChild(0).getId() == JavaParserTreeConstants.JJTARGUMENTS) { if (suffix.getChild(0).jjtGetNumChildren() == 0) { // at the moment only no-argument methods are trackable contextClass = scanCompoundSymbol(lastNode, contextClass, true, listenerId); - if (contextClass == null) + if (contextClass == null) { return null; + } int dotPos = lastNode.lastIndexOf("."); String objectCode = dotPos == -1 ? "" : lastNode.substring(0, dotPos); - for (int j = i - 2; j >= 0; j--) + for (int j = i - 2; j >= 0; j--) { objectCode = expression.getChild(j).getText() + objectCode; - if (objectCode.length() == 0) + } + if (objectCode.length() == 0) { objectCode = compiler.getRootObject().getJavaCode(); + } String methodName = lastNode.substring(dotPos + 1).trim(); try { MethodDescriptor method = contextClass.getMethodDescriptor(methodName, new ClassDescriptor[0]); @@ -292,10 +304,11 @@ catch (NoSuchMethodException e) { // happens for methods defined in the current JAXX file via scripts String propertyName = null; - if (methodName.startsWith("is")) + if (methodName.startsWith("is")) { propertyName = Introspector.decapitalize(methodName.substring("is".length())); - else if (methodName.startsWith("get")) + } else if (methodName.startsWith("get")) { propertyName = Introspector.decapitalize(methodName.substring("get".length())); + } if (propertyName != null) { MethodDescriptor[] newMethods = compiler.getScriptMethods(); for (MethodDescriptor newMethod : newMethods) { @@ -313,8 +326,9 @@ } } lastNode = suffix.getText().trim(); - if (lastNode.startsWith(".")) + if (lastNode.startsWith(".")) { lastNode = lastNode.substring(1); + } } return null; @@ -322,7 +336,9 @@ private void trackMemberIfPossible(String objectCode, ClassDescriptor objectClass, String memberName, boolean method, String listenerId) { if (objectClass.isInterface()) // might be technically possible to track in some cases, but for now + { return; // we can't create a DefaultObjectHandler for interfaces + } DefaultObjectHandler handler = TagManager.getTagHandler(objectClass); try { @@ -344,12 +360,13 @@ * can be tracked. * * @param node node to scan - * @param listenerId + * @param listenerId the listener id */ private void determineNodeType(SimpleNode node, String listenerId) { ClassDescriptor type = null; - if (node.jjtGetNumChildren() == 1) + if (node.jjtGetNumChildren() == 1) { type = node.getChild(0).getJavaType(); + } switch (node.getId()) { case JavaParserTreeConstants.JJTCLASSORINTERFACETYPE: type = ClassDescriptorLoader.getClassDescriptor(Class.class); @@ -375,34 +392,48 @@ addListenerCode.append(" "); } addListenerCode.append(addCode); - if (objectCode != null) + if (objectCode != null) { addListenerCode.append("}"); + } if (objectCode != null) { removeListenerCode.append("if (").append(objectCode).append(" != null) {").append(JAXXCompiler.getLineSeparator()); removeListenerCode.append(" "); } removeListenerCode.append(removeCode); - if (objectCode != null) + if (objectCode != null) { removeListenerCode.append("}"); + } } } private void compileListeners() { if (addListenerCode.length() > 0) { - if (compiler.applyDataBinding.length() > 0) - compiler.applyDataBinding.append("else "); - compiler.applyDataBinding.append("if ($binding.equals(").append(TypeManager.getJavaCode(id)).append(")) {").append(JAXXCompiler.getLineSeparator()); - compiler.applyDataBinding.append(" ").append(addListenerCode).append(JAXXCompiler.getLineSeparator()); - compiler.applyDataBinding.append("}").append(JAXXCompiler.getLineSeparator()); + if (compiler.haveApplyDataBinding()) { + compiler.appendApplyDataBinding("else "); + } + compiler.appendApplyDataBinding("if ($binding.equals(" + TypeManager.getJavaCode(id) + ")) {" + JAXXCompiler.getLineSeparator()); + compiler.appendApplyDataBinding(" " + addListenerCode + JAXXCompiler.getLineSeparator()); + compiler.appendApplyDataBinding("}" + JAXXCompiler.getLineSeparator()); + //if (compiler.applyDataBinding.length() > 0) + // compiler.applyDataBinding.append("else "); + //compiler.applyDataBinding.append("if ($binding.equals(").append(TypeManager.getJavaCode(id)).append(")) {").append(JAXXCompiler.getLineSeparator()); + //compiler.applyDataBinding.append(" ").append(addListenerCode).append(JAXXCompiler.getLineSeparator()); + //compiler.applyDataBinding.append("}").append(JAXXCompiler.getLineSeparator()); } if (removeListenerCode.length() > 0) { - if (compiler.removeDataBinding.length() > 0) - compiler.removeDataBinding.append("else "); - compiler.removeDataBinding.append("if ($binding.equals(").append(TypeManager.getJavaCode(id)).append(")) {").append(JAXXCompiler.getLineSeparator()); - compiler.removeDataBinding.append(" ").append(removeListenerCode).append(JAXXCompiler.getLineSeparator()); - compiler.removeDataBinding.append("}").append(JAXXCompiler.getLineSeparator()); + if (compiler.haveRemoveDataBinding()) { + compiler.appendRemoveDataBinding("else "); + } + compiler.appendRemoveDataBinding("if ($binding.equals(" + TypeManager.getJavaCode(id) + ")) {" + JAXXCompiler.getLineSeparator()); + compiler.appendRemoveDataBinding(" " + removeListenerCode + JAXXCompiler.getLineSeparator()); + compiler.appendRemoveDataBinding("}" + JAXXCompiler.getLineSeparator()); + //if (compiler.removeDataBinding.length() > 0) + // compiler.removeDataBinding.append("else "); + //compiler.removeDataBinding.append("if ($binding.equals(").append(TypeManager.getJavaCode(id)).append(")) {").append(JAXXCompiler.getLineSeparator()); + //compiler.removeDataBinding.append(" ").append(removeListenerCode).append(JAXXCompiler.getLineSeparator()); + //compiler.removeDataBinding.append("}").append(JAXXCompiler.getLineSeparator()); } } } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java 2008-10-19 19:44:06 UTC (rev 977) @@ -15,265 +15,141 @@ import jaxx.reflect.FieldDescriptor; import jaxx.reflect.MethodDescriptor; import jaxx.runtime.ComponentDescriptor; -import jaxx.runtime.JAXXObject; import jaxx.runtime.JAXXObjectDescriptor; -import jaxx.runtime.swing.Application; -import jaxx.spi.Initializer; import jaxx.tags.DefaultObjectHandler; import jaxx.tags.TagHandler; import jaxx.tags.TagManager; import jaxx.tags.validator.BeanValidatorHandler.CompiledBeanValidator; -import jaxx.types.TypeManager; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.AttributesImpl; -import org.xml.sax.helpers.XMLFilterImpl; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.ErrorListener; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.sax.SAXSource; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.io.ObjectOutputStream; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.ServiceLoader; import java.util.Set; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.zip.GZIPOutputStream; -/** Compiles JAXX files into Java classes. */ -public class JAXXCompiler { - /** log */ - protected static final Log log = LogFactory.getLog(JAXXCompiler.class); +/** + * Compiles JAXX files into Java classes. + * <p/> + * extends the {@link JAXXCompilerHelper} class wich contains all static usefull method. + * <p/> + * In that way, this class should not contains any static code, but use by inheritance the ones defined in the {@link JAXXCompilerHelper} + * <p/> + * use also a {@link #generator} ({@link JAXXObjectGenerator} to generate the java file, + * after the compilation. + */ +public class JAXXCompiler extends JAXXCompilerHelper { - /** - * True to throw exceptions when we encounter unresolvable classes, false to ignore. - * This is currently set to false until JAXX has full support for inner classes - * (including enumerations), because currently they don't always resolve (but will - * generally compile without error anyway). - */ - public static final boolean STRICT_CHECKS = false; + /*---------------------------------------------------------------------------------*/ + /*-- compiler fields --------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ - public static final String JAXX_NAMESPACE = "http://www.jaxxframework.org/"; - public static final String JAXX_INTERNAL_NAMESPACE = "http://www.jaxxframework.org/internal"; + /** flag to detec if an error occurs while compiling jaxx file */ + protected boolean failed; - /** Maximum length of an inlinable creation method. */ - private static final int INLINE_THRESHOLD = 300; - - /** Contains import declarations (of the form "javax.swing.") which are always imported in all compiler instances. */ - private static List<String> staticImports = new ArrayList<String>(); - - static { - //TODO humm, we should be able to import only what is needed - staticImports.add("java.awt.*"); - staticImports.add("java.awt.event.*"); - staticImports.add("java.beans.*"); - staticImports.add("java.io.*"); - staticImports.add("java.lang.*"); - staticImports.add("java.util.*"); - staticImports.add("javax.swing.*"); - staticImports.add("javax.swing.border.*"); - staticImports.add("javax.swing.event.*"); - staticImports.add("jaxx.runtime.swing.JAXXButtonGroup"); - staticImports.add("jaxx.runtime.swing.HBox"); - staticImports.add("jaxx.runtime.swing.VBox"); - staticImports.add("jaxx.runtime.swing.Table"); - staticImports.add("static org.codelutin.i18n.I18n._"); - staticImports.add("static org.codelutin.jaxx.util.UIHelper.createImageIcon"); - } - - private static DefaultObjectHandler firstPassClassTagHandler = new DefaultObjectHandler(ClassDescriptorLoader.getClassDescriptor(Object.class)); - - /** - * A list of Runnables which will be run after the first compilation pass. This is primarily used - * to trigger the creation of CompiledObjects, which cannot be created during the first pass and must be - * created in document order. - */ - private List<Runnable> initializers = new ArrayList<Runnable>(); - - /** Files being compiled during the compilation session, may be modified as compilation progresses and additional dependencies are found. */ - private static List<File> jaxxFiles = new ArrayList<File>(); - - /** Class names corresponding to the files in the jaxxFiles list. */ - private static List<String> jaxxFileClassNames = new ArrayList<String>(); - - /** Maps the names of classes being compiled to the compiler instance handling the compilation. */ - private static Map<String, JAXXCompiler> compilers = new HashMap<String, JAXXCompiler>(); - - /** Maps the names of classes being compiled to their symbol tables (created after the first compiler pass). */ - private static Map<File, SymbolTable> symbolTables = new HashMap<File, SymbolTable>(); - - private CompilerOptions options; - - /** Used for error reporting purposes, so we can report the right line number. */ - private Stack<Element> tagsBeingCompiled = new Stack<Element>(); - - /** Used for error reporting purposes, so we can report the right source file. */ - private Stack<File> sourceFiles = new Stack<File>(); - - /** Maps object ID strings to the objects themselves. These are created during the second compilation pass. */ - private Map<String, CompiledObject> objects = new LinkedHashMap<String, CompiledObject>(); - - /** Maps objects to their ID strings. These are created during the second compilation pass. */ - private Map<CompiledObject, String> ids = new LinkedHashMap<CompiledObject, String>(); - - private static int errorCount; - private static int warningCount; - - private boolean failed; - /** Object corresponding to the root tag in the document. */ - private CompiledObject root; + protected CompiledObject root; /** Contains strings of the form "javax.swing." */ - private Set<String> importedPackages = new HashSet<String>(); + protected Set<String> importedPackages = new HashSet<String>(); /** Contains strings of the form "javax.swing.Timer" */ - private Set<String> importedClasses = new HashSet<String>(); + protected Set<String> importedClasses = new HashSet<String>(); /** Keeps track of open components (components still having children added). */ - private Stack<CompiledObject> openComponents = new Stack<CompiledObject>(); + protected Stack<CompiledObject> openComponents = new Stack<CompiledObject>(); /** Sequence number used to create automatic variable names. */ - private int autogenID = 0; + protected int autogenID = 0; - private List<DataBinding> dataBindings = new ArrayList<DataBinding>(); + protected List<DataBinding> dataBindings = new ArrayList<DataBinding>(); - private JavaFile javaFile = new JavaFile(); - - // true if a main() method has been declared in a script - boolean mainDeclared; - /** list of validators */ protected List<CompiledBeanValidator> validators = new ArrayList<CompiledBeanValidator>(); /** list of objectId attached to a validator * */ protected List<String> validatedComponents = new ArrayList<String>(); - private SymbolTable symbolTable = new SymbolTable(); + protected SymbolTable symbolTable = new SymbolTable(); - // TODO: replace these public StringBuffers with something a little less stupid - - /** Extra code to be added to the instance initializer. */ - public StringBuffer initializer = new StringBuffer(); - - /** Extra code to be added at the end of the instance initializer. */ - public StringBuffer lateInitializer = new StringBuffer(); - - /** Extra code to be added to the class body. */ - public StringBuffer bodyCode = new StringBuffer(); - - /** Code to initialize data bindings. */ - public StringBuffer initDataBindings = new StringBuffer(); - - /** Body of the applyDataBinding method. */ - public StringBuffer applyDataBinding = new StringBuffer(); - - /** Body of the removeDataBinding method. */ - public StringBuffer removeDataBinding = new StringBuffer(); - - /** Body of the processDataBinding method. */ - public StringBuffer processDataBinding = new StringBuffer(); - /** Base directory used for path resolution (normally the directory in which the .jaxx file resides). */ - private File baseDir; + protected File baseDir; /** .jaxx file being compiled. */ - private File src; + protected File src; - /** Generated .java file. */ - private File dest; - /** Parsed XML of src file. */ - private Document document; + protected Document document; /** Name of class being compiled. */ - private String outputClassName; + protected String outputClassName; - private ScriptManager scriptManager = new ScriptManager(this); + protected ScriptManager scriptManager = new ScriptManager(this); /** Combination of all stylesheets registered using {@link #registerStylesheet}. */ - private Stylesheet stylesheet; + protected Stylesheet stylesheet; /** Contains all attributes defined inline on class tags. */ - private List<Rule> inlineStyles = new ArrayList<Rule>(); + protected List<Rule> inlineStyles = new ArrayList<Rule>(); /** * Maps objects (expressed in Java code) to event listener classes (e.g. MouseListener) to Lists of EventHandlers. The final list * contains all event handlers of a particular type attached to a particular object (again, as represented by a Java expression). */ - private Map<String, Map<ClassDescriptor, List<EventHandler>>> eventHandlers = new HashMap<String, Map<ClassDescriptor, List<EventHandler>>>(); + protected Map<String, Map<ClassDescriptor, List<EventHandler>>> eventHandlers = new HashMap<String, Map<ClassDescriptor, List<EventHandler>>>(); - private Map<Object, String> uniqueIds = new HashMap<Object, String>(); + /** Maps of uniqued id for objects used in compiler */ + protected Map<Object, String> uniqueIds = new HashMap<Object, String>(); - private Map<EventHandler, String> eventHandlerMethodNames = new HashMap<EventHandler, String>(); + /** Map of event handler method names used in compiler */ + protected Map<EventHandler, String> eventHandlerMethodNames = new HashMap<EventHandler, String>(); /** ClassLoader which searches the user-specified class path in addition to the normal class path */ - private ClassLoader classLoader; + protected ClassLoader classLoader; - private static final int PASS_1 = 0; - private static final int PASS_2 = 1; - private static int currentPass; + /** the generator of the java file */ + protected final JAXXObjectGenerator generator; - public static void init() { - // forces static initializer to run if it hasn't yet - } + /** + * A list of Runnables which will be run after the first compilation pass. This is primarily used + * to trigger the creation of CompiledObjects, which cannot be created during the first pass and must be + * created in document order. + */ + protected List<Runnable> initializers = new ArrayList<Runnable>(); - public static void loadLibraries(boolean verbose) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { - ClassLoader classloader = Thread.currentThread().getContextClassLoader(); - if (verbose) { - log.info("with cl " + classloader); - } - ServiceLoader<Initializer> loader = ServiceLoader.load(Initializer.class, classloader); - for (Initializer initializer : loader) { - if (verbose) { - log.info("load initializer " + initializer); - } - initializer.initialize(); - } - } + /** left brace matcher */ + protected Matcher leftBraceMatcher = Pattern.compile("^(\\{)|[^\\\\](\\{)").matcher(""); - private JAXXCompiler(ClassLoader classLoader) { + /** right brace matcher */ + protected Matcher rightBraceMatcher = Pattern.compile("^(\\})|[^\\\\](\\})").matcher(""); + + /*---------------------------------------------------------------------------------*/ + /*-- Constructor methods ----------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + protected JAXXCompiler(ClassLoader classLoader) { this.options = new CompilerOptions(); this.classLoader = classLoader; + this.generator = new JAXXObjectGenerator(this); addImport("java.lang.*"); } - /** * Creates a new JAXXCompiler. * @@ -288,61 +164,18 @@ sourceFiles.push(src); this.outputClassName = outputClassName; this.options = options; + this.generator = new JAXXObjectGenerator(this); addImport(outputClassName.substring(0, outputClassName.lastIndexOf(".") + 1) + "*"); for (Object staticImport : staticImports) { addImport((String) staticImport); } } + /*---------------------------------------------------------------------------------*/ + /*-- Initializer methods -----------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ - /** - * Creates a dummy JAXXCompiler for use in unit testing. - * - * @return the compiler - */ - public static JAXXCompiler createDummyCompiler() { - return createDummyCompiler(JAXXCompiler.class.getClassLoader()); - } - - - /** - * Creates a dummy JAXXCompiler for use in unit testing. - * - * @param classLoader class loader to use - * @return the compiler - */ - public static JAXXCompiler createDummyCompiler(ClassLoader classLoader) { - return new JAXXCompiler(classLoader); - } - - - public CompilerOptions getOptions() { - return options; - } - - - public JavaFile getJavaFile() { - return javaFile; - } - - - private void compileFirstPass() throws IOException { - try { - InputStream in = new FileInputStream(src); - document = parseDocument(in); - in.close(); - compileFirstPass(document.getDocumentElement()); - } - catch (SAXParseException e) { - reportError(e.getLineNumber(), "Invalid XML: " + e.getMessage()); - } - catch (SAXException e) { - reportError(null, "Error parsing XML document: " + e); - } - } - - - private void runInitializers() { + protected void runInitializers() { for (Runnable runnable : initializers) { if (log.isDebugEnabled()) { log.debug(runnable); @@ -369,735 +202,10 @@ initializers.add(r); } + /*---------------------------------------------------------------------------------*/ + /*-- Compile methods --------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ - private void compileSecondPass() throws IOException { - if (!tagsBeingCompiled.isEmpty()) { - throw new RuntimeException("Internal error: starting pass two, but tagsBeingCompiled is not empty: " + tagsBeingCompiled); - } - compileSecondPass(document.getDocumentElement()); - } - - - private void applyStylesheets() { - for (Object o : new ArrayList<CompiledObject>(objects.values())) { - CompiledObject object = (CompiledObject) o; - TagManager.getTagHandler(object.getObjectClass()).applyStylesheets(object, this); - } - } - - - private void generateCode() throws IOException { - if (options.getTargetDirectory() != null) { - dest = new File(options.getTargetDirectory(), outputClassName.replace('.', File.separatorChar) + ".java"); - } else { - dest = new File(baseDir, outputClassName.substring(outputClassName.lastIndexOf(".") + 1) + ".java"); - } - if (dest.exists() && !dest.setLastModified(System.currentTimeMillis())) { - log.warn("could not touch file " + dest); - } - PrintWriter out = new PrintWriter(new FileWriter(dest)); - createJavaSource(out); - out.close(); - } - - private void createJavaSource(PrintWriter out) throws IOException { - int dotPos = outputClassName.lastIndexOf("."); - String packageName = dotPos != -1 ? outputClassName.substring(0, dotPos) : null; - String simpleClassName = outputClassName.substring(dotPos + 1); - outputClass(packageName, simpleClassName, out); - } - - - public String getOutputClassName() { - return outputClassName; - } - - public static SAXParser getSAXParser() { - try { - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - SAXParser parser; - parser = factory.newSAXParser(); - return parser; - } - catch (SAXException e) { - throw new RuntimeException(e); - } - catch (ParserConfigurationException e) { - throw new RuntimeException(e); - } - } - - public static Document parseDocument(InputStream in) throws IOException, SAXException { - try { - TransformerFactory factory = TransformerFactory.newInstance(); - Transformer transformer = factory.newTransformer(); - transformer.setErrorListener(new ErrorListener() { - public void warning(TransformerException ex) throws TransformerException { - throw ex; - } - - public void error(TransformerException ex) throws TransformerException { - throw ex; - } - - public void fatalError(TransformerException ex) throws TransformerException { - throw ex; - } - }); - - DOMResult result = new DOMResult(); - transformer.transform(new SAXSource(new XMLFilterImpl(getSAXParser().getXMLReader()) { - Locator locator; - - @Override - public void setDocumentLocator(Locator locator) { - this.locator = locator; - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { - AttributesImpl resultAtts = new AttributesImpl(atts); - resultAtts.addAttribute(JAXX_INTERNAL_NAMESPACE, "line", "internal:line", "CDATA", String.valueOf(locator.getLineNumber())); - getContentHandler().startElement(uri, localName, qName, resultAtts); - } - }, new InputSource(in)), result); - return (Document) result.getNode(); - } - catch (TransformerConfigurationException e) { - throw new RuntimeException(e); - } - catch (TransformerException e) { - Throwable ex = e; - while (ex.getCause() != null) { - ex = ex.getCause(); - } - if (ex instanceof IOException) { - throw (IOException) ex; - } - if (ex instanceof SAXException) { - throw (SAXException) ex; - } - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - throw new RuntimeException(ex); - } - } - - - public File getBaseDir() { - return baseDir; - } - - - public Set<String> getImportedClasses() { - return importedClasses; - } - - - public Set<String> getImportedPackages() { - return importedPackages; - } - - - private boolean inlineCreation(CompiledObject object) { - return object.getId().startsWith("$") && object.getInitializationCode(this).length() < INLINE_THRESHOLD; - } - - - public void checkOverride(CompiledObject object) throws CompilerException { - if (object.getId().startsWith("$")) { - return; - } - ClassDescriptor ancestor = root.getObjectClass(); - if (ancestor == object.getObjectClass()) { - return; - } - while (ancestor != null) { - try { - FieldDescriptor f = ancestor.getDeclaredFieldDescriptor(object.getId()); - if (!f.getType().isAssignableFrom(object.getObjectClass())) { - reportError("attempting to redefine superclass member '" + object.getId() + "' as incompatible type (was " + f.getType() + ", redefined as " + object.getObjectClass() + ")"); - } - object.setOverride(true); - break; - } - catch (NoSuchFieldException e) { - ancestor = ancestor.getSuperclass(); - } - } - } - - - private Iterator<CompiledObject> getObjectCreationOrder() { - return objects.values().iterator(); - } - - - protected JavaMethod createConstructor(String className) throws CompilerException { - StringBuffer code = new StringBuffer(); - String constructorParams = root.getConstructorParams(); - if (constructorParams != null) { - code.append(" super(").append(constructorParams).append(");"); - code.append(getLineSeparator()); - } - code.append("$initialize();"); - code.append(getLineSeparator()); - return new JavaMethod(Modifier.PUBLIC, null, className, null, null, code.toString()); - } - - - protected JavaMethod createInitializer(String className) throws CompilerException { - StringBuffer code = new StringBuffer(); - code.append("$objectMap.put(").append(TypeManager.getJavaCode(root.getId())).append(", this);"); - code.append(getLineSeparator()); - - Iterator<CompiledObject> i = getObjectCreationOrder(); - boolean lastWasMethodCall = false; - while (i.hasNext()) { - CompiledObject object = i.next(); - if (object != root && !object.isOverride()) { - if (inlineCreation(object)) { - if (lastWasMethodCall) { - lastWasMethodCall = false; - code.append(getLineSeparator()); - } - code.append(getCreationCode(object)); - code.append(getLineSeparator()); - } else { - code.append(object.getCreationMethodName()).append("();"); - code.append(getLineSeparator()); - lastWasMethodCall = true; - } - } - } - String rootCode = root.getInitializationCode(this); - if (rootCode != null && rootCode.length() > 0) { - code.append(rootCode); - code.append(getLineSeparator()); - } - code.append(getLineSeparator()); - if (initializer.length() > 0) { - code.append(initializer); - code.append(getLineSeparator()); - } - code.append("$completeSetup();"); - code.append(getLineSeparator()); - return new JavaMethod(Modifier.PRIVATE, "void", "$initialize", null, null, code.toString()); - } - - - protected JavaMethod createCompleteSetupMethod() { - StringBuffer code = new StringBuffer(); - code.append("allComponentsCreated = true;"); - code.append(getLineSeparator()); - for (CompiledObject object : objects.values()) { - //TC - 20081017 only generate the method if not empty ? - if (object.getId().startsWith("$")) { - code.append(object.getAdditionCode()).append(getLineSeparator()); - } else { - String additionCode = object.getAdditionCode(); - if (additionCode.length() > 0) { - code.append(object.getAdditionMethodName()).append("();").append(getLineSeparator()); - additionCode = "if (allComponentsCreated) {" + getLineSeparator() + additionCode + "}"; - javaFile.addMethod(new JavaMethod(Modifier.PROTECTED, "void", object.getAdditionMethodName(), null, null, additionCode)); - } - } - //code.append(getLineSeparator()); - } - - code.append(initDataBindings); - - if (lateInitializer.length() > 0) { - code.append(lateInitializer); - code.append(getLineSeparator()); - } - - if (hasValidator()) { - // register validator - for (CompiledBeanValidator validator : validators) { - String id = TypeManager.getJavaCode(validator.getId()); - code.append("validatorIds.add(").append(id).append(");"); - code.append(getLineSeparator()); - code.append("getValidator(").append(id).append(").installUIs();"); - code.append("getValidator(").append(id).append(").validate();"); - code.append(getLineSeparator()); - } - } - return new JavaMethod(Modifier.PRIVATE, "void", "$completeSetup", null, null, code.toString()); - } - - - protected JavaMethod createProcessDataBindingMethod() { - StringBuffer code = new StringBuffer(); - boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(root.getObjectClass()); - // the force parameter forces the update to happen even if it is already in activeBindings. This - // is used on superclass invocations b/c by the time the call gets to the superclass, it is already - // marked active and would otherwise be skipped - if (processDataBinding.length() > 0) { - code.append(" if (!$force && $activeBindings.contains($dest)) return;"); - code.append(getLineSeparator()); - code.append(" $activeBindings.add($dest);"); - code.append(getLineSeparator()); - code.append(" try {"); - code.append(getLineSeparator()); - if (processDataBinding.length() > 0) { - code.append(processDataBinding); - code.append(getLineSeparator()); - } - if (superclassIsJAXXObject) { - code.append(" else"); - code.append(getLineSeparator()); - code.append(" super.processDataBinding($dest, true);"); - code.append(getLineSeparator()); - } - code.append(" }"); - code.append(getLineSeparator()); - code.append(" finally {"); - code.append(getLineSeparator()); - code.append(" $activeBindings.remove($dest);"); - code.append(getLineSeparator()); - code.append(" }"); - code.append(getLineSeparator()); - } else if (superclassIsJAXXObject) { - code.append(" super.processDataBinding($dest, true);"); - code.append(getLineSeparator()); - } - return new JavaMethod(Modifier.PUBLIC, "void", "processDataBinding", - new JavaArgument[]{new JavaArgument("String", "$dest"), new JavaArgument("boolean", "$force")}, - null, code.toString()); - } - - - protected void createJavaFile(String packageName, String className) throws CompilerException { - String fullClassName = packageName != null ? packageName + "." + className : className; - if (root == null) { - throw new CompilerException("root tag must be a class tag"); - } - ClassDescriptor superclass = root.getObjectClass(); - boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(superclass); - javaFile.setModifiers(Modifier.PUBLIC); - javaFile.setClassName(fullClassName); - javaFile.setSuperClass(getCanonicalName(superclass)); - javaFile.setInterfaces(new String[]{getCanonicalName(JAXXObject.class)}); - - for (CompiledObject object : objects.values()) { - if (!object.isOverride() && !(object instanceof ScriptInitializer)) { - int access = object.getId().startsWith("$") ? Modifier.PRIVATE : Modifier.PROTECTED; - if (object == root) { - javaFile.addField(new JavaField(access, fullClassName, object.getId(), "this")); - } else { - //TC -20081017 can have generic on compiled Object - javaFile.addField(new JavaField(access, getCanonicalName(object), object.getId())); - //javaFile.addField(new JavaField(access, getCanonicalName(object.getObjectClass()), object.getId())); - } - } - } - - if (!superclassIsJAXXObject) { - // add logger - if (getOptions().isAddLogger()) { - javaFile.addImport("org.apache.commons.logging.Log"); - javaFile.addImport("org.apache.commons.logging.LogFactory"); - javaFile.addField(createLoggerField(fullClassName)); - } - - javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<Object>", "$activeBindings", "new ArrayList<Object>()")); - javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.Map<String,Object>", "$bindingSources", "new HashMap<String,Object>()")); - } - - javaFile.addImport("jaxx.runtime.validator.BeanValidator"); - javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<String>", "validatorIds", "new ArrayList<String>()")); - - if (stylesheet != null) { - javaFile.addField(new JavaField(0, "java.util.Map", "$previousValues", "new java.util.HashMap()")); - } - - javaFile.addMethod(createConstructor(className)); - javaFile.addMethod(createInitializer(className)); - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "BeanValidator<?>", "getValidator", new JavaArgument[]{new JavaArgument("String", "validatorId")}, - null, "return (BeanValidator)(validatorIds.contains(validatorId)?getObjectById(validatorId):null);")); - - for (DataBinding dataBinding : dataBindings) { - if (dataBinding.compile(true)) { - initDataBindings.append("applyDataBinding(").append(TypeManager.getJavaCode(dataBinding.getId())).append(");").append(JAXXCompiler.getLineSeparator()); - } - } - - javaFile.addBodyCode(bodyCode.toString()); - - for (CompiledObject object : objects.values()) { - if (!inlineCreation(object) && object != root) { - javaFile.addMethod(new JavaMethod(Modifier.PROTECTED, "void", object.getCreationMethodName(), null, null, getCreationCode(object))); - } - } - - javaFile.addField(new JavaField(Modifier.PRIVATE, "boolean", "allComponentsCreated")); - - javaFile.addMethod(createCompleteSetupMethod()); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "applyDataBinding", new JavaArgument[]{new JavaArgument("String", "$binding")}, - null, applyDataBinding.toString() + getLineSeparator() + " processDataBinding($binding);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removeDataBinding", new JavaArgument[]{new JavaArgument("String", "$binding")}, - null, removeDataBinding.toString())); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "processDataBinding", new JavaArgument[]{new JavaArgument("String", "dest")}, - null, "processDataBinding(dest, false);")); - - javaFile.addMethod(createProcessDataBindingMethod()); - - if (!superclassIsJAXXObject) { - javaFile.addField(createObjectMap()); - javaFile.addMethod(createGetObjectByIdMethod()); - } - - javaFile.addField(createJAXXObjectDescriptorField()); - javaFile.addMethod(createGetJAXXObjectDescriptorMethod()); - - /* - * Gestion du context - */ - javaFile.addField(createContextField()); - javaFile.addMethod(createSetContextValueMethod()); - javaFile.addMethod(createSetContextValueNameMethod()); - javaFile.addMethod(createGetContextValueMethod()); - javaFile.addMethod(createGetContextValueNameMethod()); - javaFile.addMethod(createGetParentContainer()); - javaFile.addMethod(createGetParentContainerMore()); - ClassDescriptor currentClass = root.getObjectClass(); - MethodDescriptor firePropertyChange = null; - while (firePropertyChange == null && currentClass != null) { - try { - firePropertyChange = currentClass.getDeclaredMethodDescriptor("firePropertyChange", new ClassDescriptor[]{ - ClassDescriptorLoader.getClassDescriptor(String.class), - ClassDescriptorLoader.getClassDescriptor(Object.class), - ClassDescriptorLoader.getClassDescriptor(Object.class) - }); - - } - catch (NoSuchMethodException e) { - currentClass = currentClass.getSuperclass(); - } - } - - int modifiers = firePropertyChange != null ? firePropertyChange.getModifiers() : 0; - if (Modifier.isPublic(modifiers)) { - // we have all the support we need - } - if (Modifier.isProtected(modifiers)) { - // there is property change support but the firePropertyChange method is protected - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "firePropertyChange", new JavaArgument[]{ - new JavaArgument("java.lang.String", "propertyName"), new JavaArgument("java.lang.Object", "oldValue"), new JavaArgument("java.lang.Object", "newValue")}, - null, "super.firePropertyChange(propertyName, oldValue, newValue);")); - } else { - // either no support at all or firePropertyChange isn't accessible - addPropertyChangeSupport(javaFile); - } - - addEventHandlers(javaFile); - - if (ClassDescriptorLoader.getClassDescriptor(Application.class).isAssignableFrom(root.getObjectClass()) && !mainDeclared) { - // TODO: check for existing main method first - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC | Modifier.STATIC, "void", "main", - new JavaArgument[]{new JavaArgument("String[]", "arg")}, null, - "SwingUtilities.invokeLater(new Runnable() { public void run() { new " + className + "().setVisible(true); } });")); - } - } - - - protected void outputClass(String packageName, String className, PrintWriter out) throws CompilerException { - createJavaFile(packageName, className); - out.println(javaFile.toString()); - } - - - public void addImport(String text) { - if (text.endsWith("*")) { - importedPackages.add(text.substring(0, text.length() - 1)); - } else { - importedClasses.add(text); - } - - if (!text.equals("*")) { - getJavaFile().addImport(text); - } - } - - private JavaField createLoggerField(String className) { - return new JavaField(Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL, "Log", "log", "LogFactory.getLog(" + className + ".class)"); - } - - /* - * Gestion du context - */ - private JavaField createContextField() { - return new JavaField(Modifier.PROTECTED, "Map<Object,String>", "$contextMap", "new HashMap<Object,String>()"); - } - - private JavaMethod createSetContextValueMethod() { - - return new JavaMethod(Modifier.PUBLIC, "void", "setContextValue", - new JavaArgument[]{new JavaArgument("Object", "clazz")}, null, getSetContextValueMethodCode()); - } - - private JavaMethod createSetContextValueNameMethod() { - return new JavaMethod(Modifier.PUBLIC, "void", "setContextValue", - new JavaArgument[]{new JavaArgument("Object", "clazz"), new JavaArgument("String", "name")}, null, getSetContextValueNameMethodCode()); - } - - private JavaMethod createGetContextValueMethod() { - return new JavaMethod(Modifier.PUBLIC, "<T> T", "getContextValue", - new JavaArgument[]{new JavaArgument("Class<T>", "clazz")}, null, getGetContextValueMethodCode()); - } - - private JavaMethod createGetContextValueNameMethod() { - return new JavaMethod(Modifier.PUBLIC, "<T> T", "getContextValue", - new JavaArgument[]{new JavaArgument("Class<T>", "clazz"), new JavaArgument("String", "name")}, null, getGetContextValueNameMethodCode()); - } - - private JavaMethod createGetParentContainer() { - return new JavaMethod(Modifier.PUBLIC, "<O extends Container> O", "getParentContainer", - new JavaArgument[]{new JavaArgument("Class<O>", "clazz")}, null, getGetParentContenerMethodCode()); - } - - private JavaMethod createGetParentContainerMore() { - return new JavaMethod(Modifier.PUBLIC, "<O extends Container> O", "getParentContainer", - new JavaArgument[]{new JavaArgument("Object", "source"), new JavaArgument("Class<O>", "clazz")}, null, getGetParentContenerMethodMoreCode()); - } - - private String getSetContextValueMethodCode() { - return "this.setContextValue(clazz, null);"; - } - - private String getSetContextValueNameMethodCode() { - StringBuffer result = new StringBuffer(); - result.append("$contextMap.put(clazz, name);"); - return result.toString(); - } - - private String getGetContextValueMethodCode() { - return "return this.getContextValue(clazz, null);"; - } - - private String getGetContextValueNameMethodCode() { - StringBuffer result = new StringBuffer(); - result.append("for (Map.Entry<Object,String> entry : $contextMap.entrySet()) {"); - result.append("if (clazz.isAssignableFrom(entry.getKey().getClass()) && (name == null || name == entry.getValue())) {"); - result.append("return (T) entry.getKey();}}"); - result.append("return null;"); - return result.toString(); - } - - private String getGetParentContenerMethodCode() { - StringBuffer result = new StringBuffer(); - result.append("return this.getParentContainer(this, clazz);"); - return result.toString(); - } - - private String getGetParentContenerMethodMoreCode() { - StringBuffer result = new StringBuffer(); - result.append("if (!Container.class.isAssignableFrom(source.getClass())) {return null;}"); - result.append(getLineSeparator()); - result.append("Container parent = ((Container)source).getParent();"); - result.append(getLineSeparator()); - result.append("if (parent != null && !clazz.isAssignableFrom(parent.getClass())){parent = getParentContainer(parent, clazz);}"); - result.append(getLineSeparator()); - result.append("return (O)parent;"); - return result.toString(); - } - - private JavaField createObjectMap() { - return new JavaField(Modifier.PROTECTED, "Map<String,Object>", "$objectMap", "new HashMap<String,Object>()"); - } - - - protected JavaMethod createGetObjectByIdMethod() { - return new JavaMethod(Modifier.PUBLIC, "java.lang.Object", "getObjectById", - new JavaArgument[]{new JavaArgument("String", "id")}, null, - "return $objectMap.get(id);"); - } - - - public JAXXObjectDescriptor getJAXXObjectDescriptor() { - runInitializers(); - CompiledObject[] components = new ArrayList<CompiledObject>(objects.values()).toArray(new CompiledObject[objects.size()]); - assert initializers.isEmpty() : "there are pending initializers remaining"; - assert root != null : "root object has not been defined"; - assert Arrays.asList(components).contains(root) : "root object is not registered"; - ComponentDescriptor[] descriptors = new ComponentDescriptor[components.length]; - // as we print, sort the array so that component's parents are always before the components themselves - for (int i = 0; i < components.length; i++) { - CompiledObject parent = components[i].getParent(); - while (parent != null) { - boolean found = false; - for (int j = i + 1; j < components.length; j++) { // found parent after component, swap them - if (components[j] == parent) { - components[j] = components[i]; - components[i] = parent; - found = true; - break; - } - } - if (!found) { - break; - } - parent = components[i].getParent(); - } - int parentIndex = -1; - if (parent != null) { - for (int j = 0; j < i; j++) { - if (components[j] == parent) { - parentIndex = j; - break; - } - } - } - descriptors[i] = new ComponentDescriptor(components[i].getId(), components[i] == root ? outputClassName : components[i].getObjectClass().getName(), - components[i].getStyleClass(), parentIndex != -1 ? descriptors[parentIndex] : null); - } - - Stylesheet stylesheet = getStylesheet(); - if (stylesheet == null) { - stylesheet = new Stylesheet(); - } - - return new JAXXObjectDescriptor(descriptors, stylesheet); - } - - - protected JavaField createJAXXObjectDescriptorField() { - try { - JAXXObjectDescriptor descriptor = getJAXXObjectDescriptor(); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(buffer)); - out.writeObject(descriptor); - out.close(); - // the use of the weird deprecated constructor is deliberate -- we need to store the data as a String - // in the compiled class file, since byte array initialization is horribly inefficient compared to - // String initialization. So we store the bytes in the String, and we quite explicitly want a 1:1 - // mapping between bytes and chars, with the high byte of the char set to zero. We can then safely - // reconstitute the original byte[] at a later date. This is unquestionably an abuse of the String - // type, but if we could efficiently store a byte[] we wouldn't have to do this. - String data = new String(buffer.toByteArray(), 0); - - int sizeLimit = 65000; // constant strings are limited to 64K, and I'm not brave enough to push right up to the limit - if (data.length() < sizeLimit) { - return new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", TypeManager.getJavaCode(data)); - } else { - StringBuffer initializer = new StringBuffer(); - for (int i = 0; i < data.length(); i += sizeLimit) { - String name = "$jaxxObjectDescriptor" + i; - javaFile.addField(new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", name, - TypeManager.getJavaCode(data.substring(i, Math.min(i + sizeLimit, data.length()))))); - if (initializer.length() > 0) { - initializer.append(" + "); - } - initializer.append("String.valueOf(").append(name).append(")"); - } - return new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", initializer.toString()); - } - } - catch (IOException e) { - throw new RuntimeException("Internal error: can't-happen error", e); - } - } - - - protected JavaMethod createGetJAXXObjectDescriptorMethod() { - return new JavaMethod(Modifier.PUBLIC | Modifier.STATIC, "jaxx.runtime.JAXXObjectDescriptor", "$getJAXXObjectDescriptor", - null, null, "return jaxx.runtime.Util.decodeCompressedJAXXObjectDescriptor($jaxxObjectDescriptor);"); - } - - - public String getEventHandlerMethodName(EventHandler handler) { - String result = eventHandlerMethodNames.get(handler); - if (result == null) { - result = "$ev" + eventHandlerMethodNames.size(); - eventHandlerMethodNames.put(handler, result); - } - return result; - } - - protected void addEventHandlers(JavaFile javaFile) { - for (Map.Entry<String, Map<ClassDescriptor, List<EventHandler>>> e1 : eventHandlers.entrySet()) { - // outer loop is iterating over different objects (well, technically, different Java expressions) - for (Map.Entry<ClassDescriptor, List<EventHandler>> e2 : e1.getValue().entrySet()) { - // iterate over different types of listeners for this particular object (MouseListener, ComponentListener, etc.) - for (EventHandler handler : e2.getValue()) { - // iterate over individual event handlers of a single type - String methodName = getEventHandlerMethodName(handler); - MethodDescriptor listenerMethod = handler.getListenerMethod(); - if (listenerMethod.getParameterTypes().length != 1) { - throw new CompilerException("Expected event handler " + listenerMethod.getName() + " of class " + handler.getListenerClass() + " to have exactly one argument"); - } - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", methodName, - new JavaArgument[]{new JavaArgument(getCanonicalName(listenerMethod.getParameterTypes()[0]), "event")}, null, - handler.getJavaCode())); - - } - } - } - } - - protected String getCreationCode(CompiledObject object) throws CompilerException { - if (object instanceof ScriptInitializer) { - return object.getInitializationCode(this); - } - StringBuffer result = new StringBuffer(); - result.append(object.getId()); - result.append(" = "); - String constructorParams = object.getConstructorParams(); - if (constructorParams != null) { - //TC - 20081017 compiledObject can have generics - result.append(" new ").append(getCanonicalName(object)).append("(").append(constructorParams).append(");"); - //result.append("(").append(getCanonicalName(object.getObjectClass())).append(") new ").append(getCanonicalName(object.getObjectClass())).append("(").append(constructorParams).append(");"); - } else { - //TC - 20081017 compiledObject can have generics - result.append("new ").append(getCanonicalName(object)).append("();"); - } - result.append(getLineSeparator()); - String initCode = object.getInitializationCode(this); - if (initCode != null && initCode.length() > 0) { - result.append(initCode); - } - result.append("$objectMap.put(").append(TypeManager.getJavaCode(object.getId())).append(", ").append(object.getId()).append(");"); - - return result.toString(); - } - - - protected void addPropertyChangeSupport(JavaFile javaFile) throws CompilerException { - javaFile.addField(new JavaField(0, "java.beans.PropertyChangeSupport", "$propertyChangeSupport")); - - javaFile.addMethod(new JavaMethod(0, "java.beans.PropertyChangeSupport", "$getPropertyChangeSupport", null, null, - "if ($propertyChangeSupport == null)\n" + - " $propertyChangeSupport = new PropertyChangeSupport(this);\n" + - "return $propertyChangeSupport;")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "addPropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().addPropertyChangeListener(listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "addPropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.lang.String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().addPropertyChangeListener(property, listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removePropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().removePropertyChangeListener(listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removePropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.lang.String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().removePropertyChangeListener(property, listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "firePropertyChange", new JavaArgument[]{ - new JavaArgument("java.lang.String", "propertyName"), new JavaArgument("java.lang.Object", "oldValue"), new JavaArgument("java.lang.Object", "newValue")}, - null, "$getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue);")); - } - - public void compileFirstPass(final Element tag) throws IOException { tagsBeingCompiled.push(tag); @@ -1194,144 +302,36 @@ } - - // 1.5 adds getCanonicalName; unfortunately we can't depend on 1.5 features yet - public static String getCanonicalName(Class clazz) { - if (clazz.isArray()) { - String canonicalName = getCanonicalName(clazz.getComponentType()); - if (canonicalName != null) { - return canonicalName + "[]"; - } - return null; + protected void compileFirstPass() throws IOException { + try { + InputStream in = new FileInputStream(src); + document = parseDocument(in); + in.close(); + compileFirstPass(document.getDocumentElement()); } - return clazz.getName().replace('$', '.'); - } - - - public static String getCanonicalName(ClassDescriptor clazz) { - if (clazz.isArray()) { - String canonicalName = getCanonicalName(clazz.getComponentType()); - if (canonicalName != null) { - return canonicalName + "[]"; - } - return null; + catch (SAXParseException e) { + reportError(e.getLineNumber(), "Invalid XML: " + e.getMessage()); } - return clazz.getName().replace('$', '.'); - } - - - public static String getCanonicalName(CompiledObject compiled) { - ClassDescriptor clazz = compiled.getObjectClass(); - if (clazz.isArray()) { - String canonicalName = getCanonicalName(clazz.getComponentType()); - if (canonicalName != null) { - if (compiled.getGenericTypes().length > 0) { - canonicalName += "<"; - for (int i = 0; i < compiled.getGenericTypes().length; i++) { - ClassDescriptor classDescriptor = compiled.getGenericTypes()[i]; - if (i > 0) { - canonicalName += " ,"; - } - canonicalName += classDescriptor.getName(); - } - canonicalName += ">"; - - } - return canonicalName + "[]"; - } - return null; + catch (SAXException e) { + reportError(null, "Error parsing XML document: " + e); } - - String canonicalName = clazz.getName().replace('$', '.'); - if (compiled.getGenericTypes().length > 0) { - canonicalName += "<"; - for (int i = 0; i < compiled.getGenericTypes().length; i++) { - ClassDescriptor classDescriptor = compiled.getGenericTypes()[i]; - if (i > 0) { - canonicalName += " ,"; - } - canonicalName += classDescriptor.getName(); - } - canonicalName += ">"; - - } - return canonicalName; } - - public static String capitalize(String s) { - if (s.length() == 0) { - return s; + protected void compileSecondPass() throws IOException { + if (!tagsBeingCompiled.isEmpty()) { + throw new RuntimeException("Internal error: starting pass two, but tagsBeingCompiled is not empty: " + tagsBeingCompiled); } - return Character.toUpperCase(s.charAt(0)) + s.substring(1); + compileSecondPass(document.getDocumentElement()); } + /*---------------------------------------------------------------------------------*/ + /*-- CompiledObject methods -------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ - public String[] parseParameterList(String parameters) throws CompilerException { - List<String> result = new ArrayList<String>(); - StringBuffer current = new StringBuffer(); - int state = 0; // normal - for (int i = 0; i < parameters.length(); i++) { - char c = parameters.charAt(i); - switch (state) { - case 0: // normal - switch (c) { - case '"': - current.append(c); - state = 1; - break; // in quoted string - case '\\': - current.append(c); - state = 2; - break; // immediately after backslash - case ',': - if (current.length() > 0) { - result.add(current.toString()); - current.setLength(0); - break; - } else { - reportError("error parsing parameter list: " + parameters); - } - default: - current.append(c); - } - break; - case 1: // in quoted string - switch (c) { - case '"': - current.append(c); - state = 0; - break; // normal - case '\\': - current.append(c); - state = 3; - break; // immediate after backslash in quoted string - default: - current.append(c); - } - break; - case 2: // immediately after backslash - current.append(c); - state = 0; // normal - break; - case 3: // immediately after backslash in quoted string - current.append(c); - state = 1; // in quoted string - break; - } - } - if (current.length() > 0) { - result.add(current.toString()); - } - return result.toArray(new String[result.size()]); - } - - public void openComponent(CompiledObject component) throws CompilerException { openComponent(component, null); } - public void openComponent(CompiledObject component, String constraints) throws CompilerException { CompiledObject parent = getOpenComponent(); openInvisibleComponent(component); @@ -1340,7 +340,6 @@ } } - public void openInvisibleComponent(CompiledObject component) { if (!ids.containsKey(component)) { registerCompiledObject(component); @@ -1348,7 +347,6 @@ openComponents.push(component); } - public CompiledObject getOpenComponent() { if (openComponents.isEmpty()) { return null; @@ -1356,19 +354,12 @@ return openComponents.peek(); } - public void closeComponent(CompiledObject component) { if (openComponents.pop() != component) { throw new IllegalArgumentException("can only close the topmost open object"); } } - - public CompiledObject getRootObject() { - return root; - } - - public void registerCompiledObject(CompiledObject object) { assert symbolTables.values().contains(symbolTable) : "attempting to register CompiledObject before pass 1 is complete"; if (root == null) { @@ -1386,33 +377,6 @@ ids.put(object, id); } - - public String getAutoId(ClassDescriptor objectClass) { - if (options.getOptimize()) { - return "$" + Integer.toString(autogenID++, 36); - } else { - String name = objectClass.getName(); - name = name.substring(name.lastIndexOf(".") + 1); - return "$" + name + autogenID++; - } - } - - - public String getUniqueId(Object object) { - String result = uniqueIds.get(object); - if (result == null) { - result = "$u" + uniqueIds.size(); - uniqueIds.put(object, result); - } - return result; - } - - - public SymbolTable getSymbolTable() { - return symbolTable; - } - - public CompiledObject getCompiledObject(String id) { runInitializers(); assert symbolTables.values().contains(symbolTable) : "attempting to retrieve CompiledObject before pass 1 is complete"; @@ -1420,40 +384,36 @@ } - private Matcher leftBraceMatcher = Pattern.compile("^(\\{)|[^\\\\](\\{)").matcher(""); - - private int getNextLeftBrace(String string, int pos) { - leftBraceMatcher.reset(string); - return leftBraceMatcher.find(pos) ? Math.max(leftBraceMatcher.start(1), leftBraceMatcher.start(2)) : -1; + protected boolean inlineCreation(CompiledObject object) { + return object.getId().startsWith("$") && object.getInitializationCode(this).length() < INLINE_THRESHOLD; } - - private Matcher rightBraceMatcher = Pattern.compile("^(\\})|[^\\\\](\\})").matcher(""); - - private int getNextRightBrace(String string, int pos) { - leftBraceMatcher.reset(string); - rightBraceMatcher.reset(string); - int openCount = 1; - int rightPos; - while (openCount > 0) { - pos++; - int leftPos = leftBraceMatcher.find(pos) ? Math.max(leftBraceMatcher.start(1), leftBraceMatcher.start(2)) : -1; - rightPos = rightBraceMatcher.find(pos) ? Math.max(rightBraceMatcher.start(1), rightBraceMatcher.start(2)) : -1; - assert leftPos == -1 || leftPos >= pos; - assert rightPos == -1 || rightPos >= pos; - if (leftPos != -1 && leftPos < rightPos) { - pos = leftPos; - openCount++; - } else if (rightPos != -1) { - pos = rightPos; - openCount--; - } else { - openCount = 0; + public void checkOverride(CompiledObject object) throws CompilerException { + if (object.getId().startsWith("$")) { + return; + } + ClassDescriptor ancestor = root.getObjectClass(); + if (ancestor == object.getObjectClass()) { + return; + } + while (ancestor != null) { + try { + FieldDescriptor f = ancestor.getDeclaredFieldDescriptor(object.getId()); + if (!f.getType().isAssignableFrom(object.getObjectClass())) { + reportError("attempting to redefine superclass member '" + object.getId() + "' as incompatible type (was " + f.getType() + ", redefined as " + object.getObjectClass() + ")"); + } + object.setOverride(true); + break; } + catch (NoSuchFieldException e) { + ancestor = ancestor.getSuperclass(); + } } - return pos; } + /*---------------------------------------------------------------------------------*/ + /*-- DataBinding methods ----------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ /** * Examine an attribute value for data binding expressions. Returns a 'cooked' expression which @@ -1525,27 +485,6 @@ } } - - public ScriptManager getScriptManager() { - return scriptManager; - } - - - /** - * Verifies that a snippet of Java code parses correctly. A warning is generated if the string has enclosing - * curly braces. - * - * @param javaCode the Java code snippet to test - * @return a "cooked" version of the string which has enclosing curly braces removed. - * @throws CompilerException if the code cannot be parsed - */ - public String checkJavaCode(String javaCode) { - javaCode = scriptManager.trimScript(javaCode); - scriptManager.checkParse(javaCode); - return javaCode; - } - - public void registerEventHandler(EventHandler handler) { String objectCode = handler.getObjectCode(); Map<ClassDescriptor, List<EventHandler>> listeners = eventHandlers.get(objectCode); @@ -1562,37 +501,34 @@ handlerList.add(handler); } - - public FieldDescriptor[] getScriptFields() { - List<FieldDescriptor> scriptFields = symbolTable.getScriptFields(); - return scriptFields.toArray(new FieldDescriptor[scriptFields.size()]); + public String getEventHandlerMethodName(EventHandler handler) { + String result = eventHandlerMethodNames.get(handler); + if (result == null) { + result = "$ev" + eventHandlerMethodNames.size(); + eventHandlerMethodNames.put(handler, result); + } + return result; } + /*---------------------------------------------------------------------------------*/ + /*-- Script methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ public void addScriptField(FieldDescriptor field) { symbolTable.getScriptFields().add(field); } - - public MethodDescriptor[] getScriptMethods() { - List<MethodDescriptor> scriptMethods = symbolTable.getScriptMethods(); - return scriptMethods.toArray(new MethodDescriptor[scriptMethods.size()]); - } - - public void addScriptMethod(MethodDescriptor method) { if (method.getName().equals("main") && method.getParameterTypes().length == 1 && method.getParameterTypes()[0].getName().equals("[Ljava.lang.String;")) { - mainDeclared = true; + generator.setMainDeclared(true); } symbolTable.getScriptMethods().add(method); } - public void registerScript(String script) throws CompilerException { registerScript(script, null); } - public void registerScript(String script, File sourceFile) throws CompilerException { if (sourceFile != null) { sourceFiles.push(sourceFile); @@ -1611,12 +547,21 @@ } } - public String preprocessScript(String script) throws CompilerException { return scriptManager.preprocessScript(script); } + /*---------------------------------------------------------------------------------*/ + /*-- StyleSheet methods -----------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + protected void applyStylesheets() { + for (Object o : new ArrayList<CompiledObject>(objects.values())) { + CompiledObject object = (CompiledObject) o; + TagManager.getTagHandler(object.getObjectClass()).applyStylesheets(object, this); + } + } + public void registerStylesheet(Stylesheet stylesheet) { if (this.stylesheet == null) { this.stylesheet = stylesheet; @@ -1625,26 +570,13 @@ } } - - public Stylesheet getStylesheet() { - Stylesheet merged = new Stylesheet(); - if (stylesheet != null) { - merged.add(stylesheet.getRules()); - } - merged.add(inlineStyles.toArray(new Rule[inlineStyles.size()])); - return merged; - } - - - public Stack<File> getSourceFiles() { - return sourceFiles; - } - - public void addInlineStyle(CompiledObject object, String propertyName, boolean dataBinding) { inlineStyles.add(Rule.inlineAttribute(object, propertyName, dataBinding)); } + /*---------------------------------------------------------------------------------*/ + /*-- Report methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ public void reportWarning(String warning) { Element currentTag = null; @@ -1688,12 +620,10 @@ reportError(currentTag, error); } - public void reportError(CompilerException ex) { reportError(null, ex); } - public void reportError(String extraMessage, CompilerException ex) { String message = ex.getMessage(); if (ex.getClass() == UnsupportedAttributeException.class || ex.getClass() == UnsupportedTagException.class) { @@ -1712,12 +642,10 @@ reportError(currentTag, extraMessage != null ? extraMessage + message : message, lineOffset); } - public void reportError(Element tag, String error) { reportError(tag, error, 0); } - public void reportError(Element tag, String error, int lineOffset) { int lineNumber = 0; if (tag != null) { @@ -1730,7 +658,6 @@ reportError(lineNumber, error); } - public void reportError(int lineNumber, String error) { File src = sourceFiles.isEmpty() ? null : sourceFiles.peek(); try { @@ -1751,50 +678,129 @@ failed = true; } + /*---------------------------------------------------------------------------------*/ + /*-- Validator methods ------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ /** - * Escapes a string using standard Java escape sequences, generally in preparation to including it in a string literal - * in a compiled Java file. + * Register in this compiler a new compiled validator. * - * @param raw the raw string to be escape - * @return a string in which all 'dangerous' characters have been replaced by equivalent Java escape sequences + * @param validator the compiled validator to register */ - public static String escapeJavaString(String raw) { - StringBuffer out = new StringBuffer(raw); - for (int i = 0; i < out.length(); i++) { - char c = out.charAt(i); - if (c == '\\' || c == '"') { - out.insert(i, '\\'); - i++; - } else if (c == '\n') { - out.replace(i, i + 1, "\\n"); - i++; - } else if (c == '\r') { - out.replace(i, i + 1, "\\r"); - i++; - } else if (c < 32 || c > 127) { - String value = Integer.toString((int) c, 16); - while (value.length() < 4) { - value = "0" + value; - } - out.replace(i, i + 1, "\\u" + value); - i += 5; - } - } - return out.toString(); + public void registerValidator(CompiledBeanValidator validator) { + validators.add(validator); + validatedComponents.addAll(validator.getFields().values()); } + /** @return <code>true</code> if some validators were detected, <code>false</code> otherwise */ + public boolean hasValidator() { + return !validators.isEmpty(); + } /** - * Returns the system line separator string. + * Test if a given CompiledObject is attached to a validator. * - * @return the string used to separate lines + * @param componentId the compiled object to test + * @return <code>true</code> if the given compiled object is attached to a validator, <code>false</code> otherwise */ - public static String getLineSeparator() { - return System.getProperty("line.separator", "\n"); + public boolean isComponentUsedByValidator(String componentId) { + return validatedComponents.contains(componentId); } + /** + * Test if a given bean is attached to a validator. + * + * @param beanId the bean to test + * @return <code>true</code> if the given bean is attached to a validator, <code>false</code> otherwise + */ + public boolean isBeanUsedByValidator(String beanId) { + for (CompiledBeanValidator validator : validators) { + if (beanId.equals(validator.getBean())) { + return true; + } + } + return false; + } + /*---------------------------------------------------------------------------------*/ + /*-- Getter methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + public Map<String, CompiledObject> getObjects() { + return objects; + } + + public List<DataBinding> getDataBindings() { + return dataBindings; + } + + public List<CompiledBeanValidator> getValidators() { + return validators; + } + + public Map<String, Map<ClassDescriptor, List<EventHandler>>> getEventHandlers() { + return eventHandlers; + } + + public CompilerOptions getOptions() { + return options; + } + + public String getOutputClassName() { + return outputClassName; + } + + public File getBaseDir() { + return baseDir; + } + + public Set<String> getImportedClasses() { + return importedClasses; + } + + public Set<String> getImportedPackages() { + return importedPackages; + } + + protected Iterator<CompiledObject> getObjectCreationOrder() { + return objects.values().iterator(); + } + + public CompiledObject getRootObject() { + return root; + } + + public Stack<File> getSourceFiles() { + return sourceFiles; + } + + public ScriptManager getScriptManager() { + return scriptManager; + } + + public SymbolTable getSymbolTable() { + return symbolTable; + } + + public Stylesheet getStylesheet() { + Stylesheet merged = new Stylesheet(); + if (stylesheet != null) { + merged.add(stylesheet.getRules()); + } + merged.add(inlineStyles.toArray(new Rule[inlineStyles.size()])); + return merged; + } + + public FieldDescriptor[] getScriptFields() { + List<FieldDescriptor> scriptFields = symbolTable.getScriptFields(); + return scriptFields.toArray(new FieldDescriptor[scriptFields.size()]); + } + + public MethodDescriptor[] getScriptMethods() { + List<MethodDescriptor> scriptMethods = symbolTable.getScriptMethods(); + return scriptMethods.toArray(new MethodDescriptor[scriptMethods.size()]); + } + /** * Returns a <code>ClassLoader</code> which searches the user-specified class path in addition * to the normal system class path. @@ -1823,48 +829,132 @@ return classLoader; } + public JAXXObjectDescriptor getJAXXObjectDescriptor() { + runInitializers(); + CompiledObject[] components = new ArrayList<CompiledObject>(objects.values()).toArray(new CompiledObject[objects.size()]); + assert initializers.isEmpty() : "there are pending initializers remaining"; + assert root != null : "root object has not been defined"; + assert Arrays.asList(components).contains(root) : "root object is not registered"; + ComponentDescriptor[] descriptors = new ComponentDescriptor[components.length]; + // as we print, sort the array so that component's parents are always before the components themselves + for (int i = 0; i < components.length; i++) { + CompiledObject parent = components[i].getParent(); + while (parent != null) { + boolean found = false; + for (int j = i + 1; j < components.length; j++) { // found parent after component, swap them + if (components[j] == parent) { + components[j] = components[i]; + components[i] = parent; + found = true; + break; + } + } + if (!found) { + break; + } + parent = components[i].getParent(); + } + int parentIndex = -1; + if (parent != null) { + for (int j = 0; j < i; j++) { + if (components[j] == parent) { + parentIndex = j; + break; + } + } + } + descriptors[i] = new ComponentDescriptor(components[i].getId(), components[i] == root ? outputClassName : components[i].getObjectClass().getName(), + components[i].getStyleClass(), parentIndex != -1 ? descriptors[parentIndex] : null); + } - /** - * @param className the name of the class to use - * @return the compiler instance which is processing the specified JAXX class. Each class is compiled by a - * different compiler instance. - */ - public static JAXXCompiler getJAXXCompiler(String className) { - return compilers != null ? compilers.get(className) : null; + Stylesheet stylesheet = getStylesheet(); + if (stylesheet == null) { + stylesheet = new Stylesheet(); + } + + return new JAXXObjectDescriptor(descriptors, stylesheet); } + /*---------------------------------------------------------------------------------*/ + /*-- Setter methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ - /** - * @param className the name of the class to use - * @return the symbol table for the specified JAXX class. Must be called during the second compiler pass. - * Returns <code>null</code> if no such symbol table could be found. - */ - public static SymbolTable getSymbolTable(String className) { - JAXXCompiler compiler = getJAXXCompiler(className); - if (compiler == null) { - return null; - } - return compiler.getSymbolTable(); + public void setFailed(boolean failed) { + this.failed = failed; } - public static File URLtoFile(URL url) { - return URLtoFile(url.toString()); + /*---------------------------------------------------------------------------------*/ + /*-- Delegate methods from JAXXObjectGenerator ------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + public void appendInitializerCode(String code) { + generator.getInitializer().append(code); } - public static File URLtoFile(String urlString) { - if (!urlString.startsWith("file:")) { - throw new IllegalArgumentException("url must start with 'file:'"); + public void appendBodyCode(String code) { + generator.getBodyCode().append(code); + } + + public void appendInitDataBindings(String code) { + generator.getInitDataBindings().append(code); + } + + public void appendProcessDataBinding(String code) { + generator.getProcessDataBinding().append(code); + } + + public void appendApplyDataBinding(String code) { + generator.getApplyDataBinding().append(code); + } + + public void appendRemoveDataBinding(String code) { + generator.getRemoveDataBinding().append(code); + } + + public void appendLateInitializer(String code) { + generator.getLateInitializer().append(code); + } + + public boolean haveProcessDataBinding() { + return generator.getProcessDataBinding().length() > 0; + } + + public boolean haveApplyDataBinding() { + return generator.getApplyDataBinding().length() > 0; + } + + public boolean haveRemoveDataBinding() { + return generator.getRemoveDataBinding().length() > 0; + } + + public void addMethodToJavaFile(JavaMethod method) { + generator.getJavaFile().addMethod(method); + } + + public boolean hasMethod(String methodName) { + JavaMethod[] methods = generator.getJavaFile().getMethods(); + for (JavaMethod method : methods) { + if (methodName.equals(method.getName())) { + return true; + } } - urlString = urlString.substring("file:".length()); - if (urlString.startsWith("/") && System.getProperty("os.name").startsWith("Windows")) { - urlString = urlString.substring(1); + return false; + } + + /*---------------------------------------------------------------------------------*/ + /*-- Other methods ----------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + public void addImport(String text) { + if (text.endsWith("*")) { + importedPackages.add(text.substring(0, text.length() - 1)); + } else { + importedClasses.add(text); } - try { - return new File(URLDecoder.decode(urlString.replace('/', File.separatorChar), "utf-8")); + + if (!text.equals("*")) { + generator.getJavaFile().addImport(text); } - catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } } public void addDependencyClass(String className) { @@ -1905,335 +995,21 @@ } } - /** - * Compiled a set of files, expressed as paths relative to a base directory. The class names of the compiled files are derived - * from the relative path strings (e.g. "example/Foo.jaxx" compiles into a class named "example.Foo"). Returns <code>true</code> - * if compilation succeeds, <code>false</code> if it fails. Warning and error messages are sent to <code>System.err</code>. + * Verifies that a snippet of Java code parses correctly. A warning is generated if the string has enclosing + * curly braces. * - * @param base the directory against which to resolve relative paths - * @param relativePaths a list of relative paths to .jaxx files being compiled - * @param options the compiler options to use - * @return <code>true</code> if compilation succeeds, <code>false</code> otherwise + * @param javaCode the Java code snippet to test + * @return a "cooked" version of the string which has enclosing curly braces removed. + * @throws CompilerException if the code cannot be parsed */ - public static synchronized boolean compile(File base, String[] relativePaths, CompilerOptions options) { - File[] files = new File[relativePaths.length]; - String[] classNames = new String[relativePaths.length]; - for (int i = 0; i < files.length; i++) { - files[i] = new File(base, relativePaths[i]); - classNames[i] = relativePaths[i].substring(0, relativePaths[i].lastIndexOf(".")); - classNames[i] = classNames[i].replace(File.separatorChar, '.'); - classNames[i] = classNames[i].replace('/', '.'); - classNames[i] = classNames[i].replace('\\', '.'); - classNames[i] = classNames[i].replace(':', '.'); - } - return compile(files, classNames, options); + public String checkJavaCode(String javaCode) { + javaCode = scriptManager.trimScript(javaCode); + scriptManager.checkParse(javaCode); + return javaCode; } - - /** Resets all state in preparation for a new compilation session. */ - private static void reset() { - errorCount = 0; - warningCount = 0; - jaxxFiles.clear(); - jaxxFileClassNames.clear(); - symbolTables.clear(); - compilers.clear(); - } - - /** - * Compiled a set of files, with the class names specified explicitly. The class compiled from files[i] will be named classNames[i]. - * Returns <code>true</code> if compilation succeeds, <code>false</code> if it fails. Warning and error messages are sent to - * <code>System.err</code>. - * - * @param files the .jaxx files to compile - * @param classNames the names of the classes being compiled - * @param options the compiler options to use - * @return <code>true</code> if compilation succeeds, <code>false</code> otherwise - */ - public static synchronized boolean compile(File[] files, String[] classNames, CompilerOptions options) { - reset(); // just to be safe... - jaxxFiles.addAll(Arrays.asList(files)); - jaxxFileClassNames.addAll(Arrays.asList(classNames)); - try { - boolean success = true; - - // pass 1 - currentPass = PASS_1; - boolean compiled; - do { - compiled = false; - assert jaxxFiles.size() == jaxxFileClassNames.size(); - java.util.Iterator<File> filesIterator = new ArrayList<File>(jaxxFiles).iterator(); // clone it so it can safely be modified while we're iterating - java.util.Iterator<String> classNamesIterator = new ArrayList<String>(jaxxFileClassNames).iterator(); - while (filesIterator.hasNext()) { - File file = filesIterator.next(); - String className = classNamesIterator.next(); - if (options.isVerbose()) { - log.info("compile first pass for " + className); - } - if (symbolTables.get(file) == null) { - compiled = true; - if (compilers.containsKey(className)) { - throw new CompilerException("Internal error: " + className + " is already being compiled, attempting to compile it again"); - } - - File destDir = options.getTargetDirectory(); - if (destDir != null) { - int dotPos = className.lastIndexOf("."); - if (dotPos != -1) { - destDir = new File(destDir, className.substring(0, dotPos).replace('.', File.separatorChar)); - } - destDir.mkdirs(); - } else { - //destDir = file.getParentFile(); - } - JAXXCompiler compiler = new JAXXCompiler(file.getParentFile(), file, className, options); - compilers.put(className, compiler); - compiler.compileFirstPass(); - assert !symbolTables.values().contains(compiler.getSymbolTable()) : "symbolTable is already registered"; - symbolTables.put(file, compiler.getSymbolTable()); - if (compiler.failed) { - success = false; - } - } - } - - } while (compiled); - - // pass 2 - currentPass = PASS_2; - if (success) { - assert jaxxFiles.size() == jaxxFileClassNames.size(); - List<File> jaxxFilesClone = new ArrayList<File>(jaxxFiles); - for (String className : jaxxFileClassNames) { - JAXXCompiler compiler = compilers.get(className); - if (compiler == null) { - throw new CompilerException("Internal error: could not find compiler for " + className + " during second pass"); - } - if (options.isVerbose()) { - - log.info("runInitializers for " + className); - } - if (!compiler.failed) { - compiler.runInitializers(); - } - if (options.isVerbose()) { - - log.info("compile second pass for " + className); - } - compiler.compileSecondPass(); - if (options.isVerbose()) { - - log.info("done with result [" + !compiler.failed + "] for " + className); - } - if (!compiler.failed) { - - } else { - success = false; - } - } - if (!jaxxFilesClone.equals(jaxxFiles)) { - throw new AssertionError("Internal error: compilation set altered during pass 2 (was " + jaxxFilesClone + ", modified to " + jaxxFiles + ")"); - } - } - - // stylesheet application - if (success) { - assert jaxxFiles.size() == jaxxFileClassNames.size(); - for (String className : jaxxFileClassNames) { - JAXXCompiler compiler = compilers.get(className); - if (compiler == null) { - throw new CompilerException("Internal error: could not find compiler for " + className + " during stylesheet application"); - } - compiler.applyStylesheets(); - if (compiler.failed) { - success = false; - } - } - } - - // code generation - if (success) { - assert jaxxFiles.size() == jaxxFileClassNames.size(); - for (String className : jaxxFileClassNames) { - JAXXCompiler compiler = compilers.get(className); - if (compiler == null) { - throw new CompilerException("Internal error: could not find compiler for " + className + " during code generation"); - } - compiler.generateCode(); - if (compiler.failed) { - success = false; - } - } - } - - if (warningCount == 1) { - System.err.println("1 warning"); - } else if (warningCount > 0) { - System.err.println(warningCount + " warnings"); - } - if (errorCount == 1) { - System.err.println("1 error"); - } else if (errorCount > 0) { - System.err.println(errorCount + " errors"); - } - return success; - } - catch (CompilerException e) { - System.err.println(e.getMessage()); - e.printStackTrace(); - return false; - } - catch (Throwable e) { - e.printStackTrace(); - return false; - } - finally { - //TC - 20081018 only reset when no error was detected - if (errorCount==0) { - reset(); - } - } - } - - - private static void showUsage() { - System.out.println("Usage: jaxxc <options> <source files>"); - System.out.println(); - System.out.println("Source files must end in extension .jaxx"); - System.out.println("Use JAXX_OPTS environment variable to pass arguments to Java runtime"); - System.out.println(); - System.out.println("Supported options include:"); - System.out.println(" -classpath <paths> paths to search for user classes"); - System.out.println(" -cp <paths> same as -classpath"); - System.out.println(" -d <directory> target directory for generated class files"); - System.out.println(" -java or -j produce .java files, but do not compile them"); - System.out.println(" -keep or -k preserve generated .java files after compilation"); - System.out.println(" -optimize or -o optimize during compilation"); - System.out.println(" -version display version information"); - System.out.println(); - System.out.println("See http://www.jaxxframework.org/ for full documentation."); - } - - - public static String getVersion() { - return "1.0.4"; - } - - - public static void main(String[] arg) throws Exception { - boolean success = true; - - CompilerOptions options = new CompilerOptions(); - List<String> files = new ArrayList<String>(); - for (int i = 0; i < arg.length; i++) { - if (arg[i].endsWith(".jaxx")) { - files.add(arg[i]); - } else if (arg[i].equals("-d")) { - if (++i < arg.length) { - File targetDirectory = new File(arg[i]); - if (!targetDirectory.exists()) { - System.err.println("Error: could not find target directory: " + targetDirectory); - errorCount++; - success = false; - } - options.setTargetDirectory(targetDirectory); - } else { - success = false; - } - } else if (arg[i].equals("-cp") || arg[i].equals("-classpath")) { - if (++i < arg.length) { - options.setClassPath(arg[i]); - } else { - success = false; - } - } else if (arg[i].equals("-javac_opts")) { - if (++i < arg.length) { - options.setJavacOpts(arg[i]); - } else { - success = false; - } - } else if (arg[i].equals("-k") || arg[i].equals("-keep")) { - options.setKeepJavaFiles(true); - } else if (arg[i].equals("-j") || arg[i].equals("-java")) { - options.setKeepJavaFiles(true); - } else if (arg[i].equals("-o") || arg[i].equals("-optimize")) { - options.setOptimize(true); - } else if (arg[i].equals("-version")) { - System.err.println("jaxxc version " + getVersion() + " by Ethan Nicholas"); - System.err.println("http://www.jaxxframework.org/"); - System.exit(0); - } else if (arg[i].equals("-internalDumpVersion")) { // used by ant to extract the version info - System.out.println("jaxx.version=" + getVersion()); - return; - } else { - success = false; - } - } - - success &= (errorCount == 0 && files.size() > 0); - - if (success) { - success = compile(new File("."), files.toArray(new String[files.size()]), options); - } else { - showUsage(); - System.exit(1); - } - - System.exit(success ? 0 : 1); - } - - public File getDest() { - return dest; - } - - public void setFailed(boolean failed) { - this.failed = failed; - } - - /** - * Register in this compiler a new compiled validator. - * - * @param validator the compiled validator to register - */ - public void registerValidator(CompiledBeanValidator validator) { - validators.add(validator); - validatedComponents.addAll(validator.getFields().values()); - } - - /** @return <code>true</code> if some validators were detected, <code>false</code> otherwise */ - public boolean hasValidator() { - return !validators.isEmpty(); - } - - /** - * Test if a given CompiledObject is attached to a validator. - * - * @param componentId the compiled object to test - * @return <code>true</code> if the given compiled object is attached to a validator, <code>false</code> otherwise - */ - public boolean isComponentUsedByValidator(String componentId) { - return validatedComponents.contains(componentId); - } - - /** - * Test if a given bean is attached to a validator. - * - * @param beanId the bean to test - * @return <code>true</code> if the given bean is attached to a validator, <code>false</code> otherwise - */ - public boolean isBeanUsedByValidator(String beanId) { - for (CompiledBeanValidator validator : validators) { - if(beanId.equals(validator.getBean())) { - return true; - } - } - return false; - } - - /** * Check that a reference exists in symbol table on second compil pass * * @param tag the current tag @@ -2258,4 +1034,112 @@ } return true; } + + protected int getNextLeftBrace(String string, int pos) { + leftBraceMatcher.reset(string); + return leftBraceMatcher.find(pos) ? Math.max(leftBraceMatcher.start(1), leftBraceMatcher.start(2)) : -1; + } + + protected int getNextRightBrace(String string, int pos) { + leftBraceMatcher.reset(string); + rightBraceMatcher.reset(string); + int openCount = 1; + int rightPos; + while (openCount > 0) { + pos++; + int leftPos = leftBraceMatcher.find(pos) ? Math.max(leftBraceMatcher.start(1), leftBraceMatcher.start(2)) : -1; + rightPos = rightBraceMatcher.find(pos) ? Math.max(rightBraceMatcher.start(1), rightBraceMatcher.start(2)) : -1; + assert leftPos == -1 || leftPos >= pos; + assert rightPos == -1 || rightPos >= pos; + if (leftPos != -1 && leftPos < rightPos) { + pos = leftPos; + openCount++; + } else if (rightPos != -1) { + pos = rightPos; + openCount--; + } else { + openCount = 0; + } + } + return pos; + } + + public String[] parseParameterList(String parameters) throws CompilerException { + List<String> result = new ArrayList<String>(); + StringBuffer current = new StringBuffer(); + int state = 0; // normal + for (int i = 0; i < parameters.length(); i++) { + char c = parameters.charAt(i); + switch (state) { + case 0: // normal + switch (c) { + case '"': + current.append(c); + state = 1; + break; // in quoted string + case '\\': + current.append(c); + state = 2; + break; // immediately after backslash + case ',': + if (current.length() > 0) { + result.add(current.toString()); + current.setLength(0); + break; + } else { + reportError("error parsing parameter list: " + parameters); + } + default: + current.append(c); + } + break; + case 1: // in quoted string + switch (c) { + case '"': + current.append(c); + state = 0; + break; // normal + case '\\': + current.append(c); + state = 3; + break; // immediate after backslash in quoted string + default: + current.append(c); + } + break; + case 2: // immediately after backslash + current.append(c); + state = 0; // normal + break; + case 3: // immediately after backslash in quoted string + current.append(c); + state = 1; // in quoted string + break; + } + } + if (current.length() > 0) { + result.add(current.toString()); + } + return result.toArray(new String[result.size()]); + } + + public String getAutoId(ClassDescriptor objectClass) { + if (options.getOptimize()) { + return "$" + Integer.toString(autogenID++, 36); + } else { + String name = objectClass.getName(); + name = name.substring(name.lastIndexOf(".") + 1); + return "$" + name + autogenID++; + } + } + + public String getUniqueId(Object object) { + String result = uniqueIds.get(object); + if (result == null) { + result = "$u" + uniqueIds.size(); + uniqueIds.put(object, result); + } + return result; + } + } Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompilerHelper.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompilerHelper.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompilerHelper.java 2008-10-19 19:44:06 UTC (rev 977) @@ -0,0 +1,657 @@ +package jaxx.compiler; + +import jaxx.CompilerException; +import jaxx.reflect.ClassDescriptor; +import jaxx.reflect.ClassDescriptorLoader; +import jaxx.spi.Initializer; +import jaxx.tags.DefaultObjectHandler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.XMLFilterImpl; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXSource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Stack; + +/** @author chemit */ +public class JAXXCompilerHelper { + + /** log */ + protected static final Log log = LogFactory.getLog(JAXXCompiler.class); + + /** + * True to throw exceptions when we encounter unresolvable classes, false to ignore. + * This is currently set to false until JAXX has full support for inner classes + * (including enumerations), because currently they don't always resolve (but will + * generally compile without error anyway). + */ + public static final boolean STRICT_CHECKS = false; + + public static final String JAXX_NAMESPACE = "http://www.jaxxframework.org/"; + + public static final String JAXX_INTERNAL_NAMESPACE = "http://www.jaxxframework.org/internal"; + + /** Maximum length of an inlinable creation method. */ + protected static final int INLINE_THRESHOLD = 300; + + protected static final int PASS_1 = 0; + + protected static final int PASS_2 = 1; + + /** Contains import declarations (of the form "javax.swing.") which are always imported in all compiler instances. */ + protected static List<String> staticImports = new ArrayList<String>(); + + static { + //TODO humm, we should be able to import only what is needed + staticImports.add("java.awt.*"); + staticImports.add("java.awt.event.*"); + staticImports.add("java.beans.*"); + staticImports.add("java.io.*"); + staticImports.add("java.lang.*"); + staticImports.add("java.util.*"); + staticImports.add("javax.swing.*"); + staticImports.add("javax.swing.border.*"); + staticImports.add("javax.swing.event.*"); + staticImports.add("jaxx.runtime.swing.JAXXButtonGroup"); + staticImports.add("jaxx.runtime.swing.HBox"); + staticImports.add("jaxx.runtime.swing.VBox"); + staticImports.add("jaxx.runtime.swing.Table"); + staticImports.add("static org.codelutin.i18n.I18n._"); + staticImports.add("static org.codelutin.jaxx.util.UIHelper.createImageIcon"); + } + + protected static DefaultObjectHandler firstPassClassTagHandler = new DefaultObjectHandler(ClassDescriptorLoader.getClassDescriptor(Object.class)); + + /** Files being compiled during the compilation session, may be modified as compilation progresses and additional dependencies are found. */ + protected static List<File> jaxxFiles = new ArrayList<File>(); + + /** Class names corresponding to the files in the jaxxFiles list. */ + protected static List<String> jaxxFileClassNames = new ArrayList<String>(); + + /** Maps the names of classes being compiled to the compiler instance handling the compilation. */ + protected static Map<String, JAXXCompiler> compilers = new HashMap<String, JAXXCompiler>(); + + /** Maps the names of classes being compiled to their symbol tables (created after the first compiler pass). */ + protected static Map<File, SymbolTable> symbolTables = new HashMap<File, SymbolTable>(); + + protected static int currentPass; + + protected CompilerOptions options; + + /** Used for error reporting purposes, so we can report the right line number. */ + protected Stack<Element> tagsBeingCompiled = new Stack<Element>(); + + /** Used for error reporting purposes, so we can report the right source file. */ + protected Stack<File> sourceFiles = new Stack<File>(); + + /** Maps object ID strings to the objects themselves. These are created during the second compilation pass. */ + protected Map<String, CompiledObject> objects = new LinkedHashMap<String, CompiledObject>(); + + /** Maps objects to their ID strings. These are created during the second compilation pass. */ + protected Map<CompiledObject, String> ids = new LinkedHashMap<CompiledObject, String>(); + + protected static int errorCount; + protected static int warningCount; + + public static void loadLibraries(boolean verbose) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { + ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + if (verbose) { + log.info("with cl " + classloader); + } + ServiceLoader<Initializer> loader = ServiceLoader.load(Initializer.class, classloader); + for (Initializer initializer : loader) { + if (verbose) { + log.info("load initializer " + initializer); + } + initializer.initialize(); + } + } + + public static void init() { + // forces static initializer to run if it hasn't yet + } + + /** Resets all state in preparation for a new compilation session. */ + protected static void reset() { + errorCount = warningCount = 0; + jaxxFiles.clear(); + jaxxFileClassNames.clear(); + symbolTables.clear(); + compilers.clear(); + } + + public static String getVersion() { + return "1.0.4"; + } + + /** + * Creates a dummy JAXXCompiler for use in unit testing. + * + * @return the compiler + */ + public static JAXXCompiler createDummyCompiler() { + return createDummyCompiler(JAXXCompiler.class.getClassLoader()); + } + + /** + * Creates a dummy JAXXCompiler for use in unit testing. + * + * @param classLoader class loader to use + * @return the compiler + */ + public static JAXXCompiler createDummyCompiler(ClassLoader classLoader) { + return new JAXXCompiler(classLoader); + } + + /** + * @param className the name of the class to use + * @return the compiler instance which is processing the specified JAXX class. Each class is compiled by a + * different compiler instance. + */ + public static JAXXCompiler getJAXXCompiler(String className) { + return compilers != null ? compilers.get(className) : null; + } + + /** + * @param className the name of the class to use + * @return the symbol table for the specified JAXX class. Must be called during the second compiler pass. + * Returns <code>null</code> if no such symbol table could be found. + */ + public static SymbolTable getSymbolTable(String className) { + JAXXCompiler compiler = getJAXXCompiler(className); + if (compiler == null) { + return null; + } + return compiler.getSymbolTable(); + } + + /** + * Returns the system line separator string. + * + * @return the string used to separate lines + */ + public static String getLineSeparator() { + return System.getProperty("line.separator", "\n"); + } + + /** + * Compiled a set of files, expressed as paths relative to a base directory. The class names of the compiled files are derived + * from the relative path strings (e.g. "example/Foo.jaxx" compiles into a class named "example.Foo"). Returns <code>true</code> + * if compilation succeeds, <code>false</code> if it fails. Warning and error messages are sent to <code>System.err</code>. + * + * @param base the directory against which to resolve relative paths + * @param relativePaths a list of relative paths to .jaxx files being compiled + * @param options the compiler options to use + * @return <code>true</code> if compilation succeeds, <code>false</code> otherwise + */ + public static synchronized boolean compile(File base, String[] relativePaths, CompilerOptions options) { + File[] files = new File[relativePaths.length]; + String[] classNames = new String[relativePaths.length]; + for (int i = 0; i < files.length; i++) { + files[i] = new File(base, relativePaths[i]); + classNames[i] = relativePaths[i].substring(0, relativePaths[i].lastIndexOf(".")); + classNames[i] = classNames[i].replace(File.separatorChar, '.'); + classNames[i] = classNames[i].replace('/', '.'); + classNames[i] = classNames[i].replace('\\', '.'); + classNames[i] = classNames[i].replace(':', '.'); + } + return compile(files, classNames, options); + } + + /** + * Compiled a set of files, with the class names specified explicitly. The class compiled from files[i] will be named classNames[i]. + * Returns <code>true</code> if compilation succeeds, <code>false</code> if it fails. Warning and error messages are sent to + * <code>System.err</code>. + * + * @param files the .jaxx files to compile + * @param classNames the names of the classes being compiled + * @param options the compiler options to use + * @return <code>true</code> if compilation succeeds, <code>false</code> otherwise + */ + public static synchronized boolean compile(File[] files, String[] classNames, CompilerOptions options) { + reset(); // just to be safe... + jaxxFiles.addAll(Arrays.asList(files)); + jaxxFileClassNames.addAll(Arrays.asList(classNames)); + try { + boolean success = true; + + // pass 1 + currentPass = PASS_1; + boolean compiled; + do { + compiled = false; + assert jaxxFiles.size() == jaxxFileClassNames.size(); + java.util.Iterator<File> filesIterator = new ArrayList<File>(jaxxFiles).iterator(); // clone it so it can safely be modified while we're iterating + java.util.Iterator<String> classNamesIterator = new ArrayList<String>(jaxxFileClassNames).iterator(); + while (filesIterator.hasNext()) { + File file = filesIterator.next(); + String className = classNamesIterator.next(); + if (options.isVerbose()) { + log.info("compile first pass for " + className); + } + if (symbolTables.get(file) == null) { + compiled = true; + if (compilers.containsKey(className)) { + throw new CompilerException("Internal error: " + className + " is already being compiled, attempting to compile it again"); + } + + File destDir = options.getTargetDirectory(); + if (destDir != null) { + int dotPos = className.lastIndexOf("."); + if (dotPos != -1) { + destDir = new File(destDir, className.substring(0, dotPos).replace('.', File.separatorChar)); + } + destDir.mkdirs(); + } else { + //destDir = file.getParentFile(); + } + JAXXCompiler compiler = new JAXXCompiler(file.getParentFile(), file, className, options); + compilers.put(className, compiler); + compiler.compileFirstPass(); + assert !symbolTables.values().contains(compiler.getSymbolTable()) : "symbolTable is already registered"; + symbolTables.put(file, compiler.getSymbolTable()); + if (compiler.failed) { + success = false; + } + } + } + + } while (compiled); + + // pass 2 + currentPass = PASS_2; + if (success) { + assert jaxxFiles.size() == jaxxFileClassNames.size(); + List<File> jaxxFilesClone = new ArrayList<File>(jaxxFiles); + for (String className : jaxxFileClassNames) { + JAXXCompiler compiler = compilers.get(className); + if (compiler == null) { + throw new CompilerException("Internal error: could not find compiler for " + className + " during second pass"); + } + if (options.isVerbose()) { + + log.info("runInitializers for " + className); + } + if (!compiler.failed) { + compiler.runInitializers(); + } + if (options.isVerbose()) { + + log.info("compile second pass for " + className); + } + compiler.compileSecondPass(); + if (options.isVerbose()) { + + log.info("done with result [" + !compiler.failed + "] for " + className); + } + if (!compiler.failed) { + + } else { + success = false; + } + } + if (!jaxxFilesClone.equals(jaxxFiles)) { + throw new AssertionError("Internal error: compilation set altered during pass 2 (was " + jaxxFilesClone + ", modified to " + jaxxFiles + ")"); + } + } + + // stylesheet application + if (success) { + assert jaxxFiles.size() == jaxxFileClassNames.size(); + for (String className : jaxxFileClassNames) { + JAXXCompiler compiler = compilers.get(className); + if (compiler == null) { + throw new CompilerException("Internal error: could not find compiler for " + className + " during stylesheet application"); + } + compiler.applyStylesheets(); + if (compiler.failed) { + success = false; + } + } + } + + // code generation + if (success) { + assert jaxxFiles.size() == jaxxFileClassNames.size(); + for (String className : jaxxFileClassNames) { + JAXXCompiler compiler = compilers.get(className); + if (compiler == null) { + throw new CompilerException("Internal error: could not find compiler for " + className + " during code generation"); + } + compiler.generator.generateCode(); + //compiler.generateCode(); + if (compiler.failed) { + success = false; + } + } + } + + if (warningCount == 1) { + System.err.println("1 warning"); + } else if (warningCount > 0) { + System.err.println(warningCount + " warnings"); + } + if (errorCount == 1) { + System.err.println("1 error"); + } else if (errorCount > 0) { + System.err.println(errorCount + " errors"); + } + return success; + } + catch (CompilerException e) { + System.err.println(e.getMessage()); + e.printStackTrace(); + return false; + } + catch (Throwable e) { + e.printStackTrace(); + return false; + } + finally { + //TC - 20081018 only reset when no error was detected + if (errorCount == 0) { + reset(); + } + } + } + + // 1.5 adds getCanonicalName; unfortunately we can't depend on 1.5 features yet + public static String getCanonicalName(Class clazz) { + if (clazz.isArray()) { + String canonicalName = getCanonicalName(clazz.getComponentType()); + if (canonicalName != null) { + return canonicalName + "[]"; + } + return null; + } + return clazz.getName().replace('$', '.'); + } + + public static String getCanonicalName(ClassDescriptor clazz) { + if (clazz.isArray()) { + String canonicalName = getCanonicalName(clazz.getComponentType()); + if (canonicalName != null) { + return canonicalName + "[]"; + } + return null; + } + return clazz.getName().replace('$', '.'); + } + + public static String getCanonicalName(CompiledObject compiled) { + ClassDescriptor clazz = compiled.getObjectClass(); + if (clazz.isArray()) { + String canonicalName = getCanonicalName(clazz.getComponentType()); + if (canonicalName != null) { + if (compiled.getGenericTypes().length > 0) { + canonicalName += "<"; + for (int i = 0; i < compiled.getGenericTypes().length; i++) { + ClassDescriptor classDescriptor = compiled.getGenericTypes()[i]; + if (i > 0) { + canonicalName += " ,"; + } + canonicalName += classDescriptor.getName(); + } + canonicalName += ">"; + } + return canonicalName + "[]"; + } + return null; + } + + String canonicalName = clazz.getName().replace('$', '.'); + if (compiled.getGenericTypes().length > 0) { + canonicalName += "<"; + for (int i = 0; i < compiled.getGenericTypes().length; i++) { + ClassDescriptor classDescriptor = compiled.getGenericTypes()[i]; + if (i > 0) { + canonicalName += " ,"; + } + canonicalName += classDescriptor.getName(); + } + canonicalName += ">"; + } + return canonicalName; + } + + public static String capitalize(String s) { + if (s.length() == 0) { + return s; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + /** + * Escapes a string using standard Java escape sequences, generally in preparation to including it in a string literal + * in a compiled Java file. + * + * @param raw the raw string to be escape + * @return a string in which all 'dangerous' characters have been replaced by equivalent Java escape sequences + */ + public static String escapeJavaString(String raw) { + StringBuffer out = new StringBuffer(raw); + for (int i = 0; i < out.length(); i++) { + char c = out.charAt(i); + if (c == '\\' || c == '"') { + out.insert(i, '\\'); + i++; + } else if (c == '\n') { + out.replace(i, i + 1, "\\n"); + i++; + } else if (c == '\r') { + out.replace(i, i + 1, "\\r"); + i++; + } else if (c < 32 || c > 127) { + String value = Integer.toString((int) c, 16); + while (value.length() < 4) { + value = "0" + value; + } + out.replace(i, i + 1, "\\u" + value); + i += 5; + } + } + return out.toString(); + } + + public static File URLtoFile(URL url) { + return URLtoFile(url.toString()); + } + + public static File URLtoFile(String urlString) { + if (!urlString.startsWith("file:")) { + throw new IllegalArgumentException("url must start with 'file:'"); + } + urlString = urlString.substring("file:".length()); + if (urlString.startsWith("/") && System.getProperty("os.name").startsWith("Windows")) { + urlString = urlString.substring(1); + } + try { + return new File(URLDecoder.decode(urlString.replace('/', File.separatorChar), "utf-8")); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + public static SAXParser getSAXParser() { + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + SAXParser parser; + parser = factory.newSAXParser(); + return parser; + } + catch (SAXException e) { + throw new RuntimeException(e); + } + catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + } + + public static Document parseDocument(InputStream in) throws IOException, SAXException { + try { + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + transformer.setErrorListener(new ErrorListener() { + public void warning(TransformerException ex) throws TransformerException { + throw ex; + } + + public void error(TransformerException ex) throws TransformerException { + throw ex; + } + + public void fatalError(TransformerException ex) throws TransformerException { + throw ex; + } + }); + + DOMResult result = new DOMResult(); + transformer.transform(new SAXSource(new XMLFilterImpl(getSAXParser().getXMLReader()) { + Locator locator; + + @Override + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { + AttributesImpl resultAtts = new AttributesImpl(atts); + resultAtts.addAttribute(JAXX_INTERNAL_NAMESPACE, "line", "internal:line", "CDATA", String.valueOf(locator.getLineNumber())); + getContentHandler().startElement(uri, localName, qName, resultAtts); + } + }, new InputSource(in)), result); + return (Document) result.getNode(); + } + catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } + catch (TransformerException e) { + Throwable ex = e; + while (ex.getCause() != null) { + ex = ex.getCause(); + } + if (ex instanceof IOException) { + throw (IOException) ex; + } + if (ex instanceof SAXException) { + throw (SAXException) ex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + throw new RuntimeException(ex); + } + } + + protected static void showUsage() { + System.out.println("Usage: jaxxc <options> <source files>"); + System.out.println(); + System.out.println("Source files must end in extension .jaxx"); + System.out.println("Use JAXX_OPTS environment variable to pass arguments to Java runtime"); + System.out.println(); + System.out.println("Supported options include:"); + System.out.println(" -classpath <paths> paths to search for user classes"); + System.out.println(" -cp <paths> same as -classpath"); + System.out.println(" -d <directory> target directory for generated class files"); + System.out.println(" -java or -j produce .java files, but do not compile them"); + System.out.println(" -keep or -k preserve generated .java files after compilation"); + System.out.println(" -optimize or -o optimize during compilation"); + System.out.println(" -version display version information"); + System.out.println(); + System.out.println("See http://www.jaxxframework.org/ for full documentation."); + } + + public static void main(String[] arg) throws Exception { + boolean success = true; + + CompilerOptions options = new CompilerOptions(); + List<String> files = new ArrayList<String>(); + for (int i = 0; i < arg.length; i++) { + if (arg[i].endsWith(".jaxx")) { + files.add(arg[i]); + } else if (arg[i].equals("-d")) { + if (++i < arg.length) { + File targetDirectory = new File(arg[i]); + if (!targetDirectory.exists()) { + System.err.println("Error: could not find target directory: " + targetDirectory); + JAXXCompiler.errorCount++; + success = false; + } + options.setTargetDirectory(targetDirectory); + } else { + success = false; + } + } else if (arg[i].equals("-cp") || arg[i].equals("-classpath")) { + if (++i < arg.length) { + options.setClassPath(arg[i]); + } else { + success = false; + } + } else if (arg[i].equals("-javac_opts")) { + if (++i < arg.length) { + options.setJavacOpts(arg[i]); + } else { + success = false; + } + } else if (arg[i].equals("-k") || arg[i].equals("-keep")) { + options.setKeepJavaFiles(true); + } else if (arg[i].equals("-j") || arg[i].equals("-java")) { + options.setKeepJavaFiles(true); + } else if (arg[i].equals("-o") || arg[i].equals("-optimize")) { + options.setOptimize(true); + } else if (arg[i].equals("-version")) { + System.err.println("jaxxc version " + getVersion() + " by Ethan Nicholas"); + System.err.println("http://www.jaxxframework.org/"); + System.exit(0); + } else if (arg[i].equals("-internalDumpVersion")) { // used by ant to extract the version info + System.out.println("jaxx.version=" + getVersion()); + return; + } else { + success = false; + } + } + + success &= (JAXXCompiler.errorCount == 0 && files.size() > 0); + + if (success) { + success = JAXXCompiler.compile(new File("."), files.toArray(new String[files.size()]), options); + } else { + showUsage(); + System.exit(1); + } + + System.exit(success ? 0 : 1); + } + +} Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java 2008-10-19 19:44:06 UTC (rev 977) @@ -0,0 +1,657 @@ +package jaxx.compiler; + +import jaxx.CompilerException; +import jaxx.reflect.ClassDescriptor; +import jaxx.reflect.ClassDescriptorLoader; +import jaxx.reflect.MethodDescriptor; +import jaxx.runtime.JAXXObject; +import jaxx.runtime.JAXXObjectDescriptor; +import jaxx.runtime.swing.Application; +import jaxx.tags.validator.BeanValidatorHandler.CompiledBeanValidator; +import jaxx.types.TypeManager; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPOutputStream; + +/** + * This class is a refactoring of the {@link jaxx.compiler.JAXXCompiler}. + * <p/> + * We delegate now the generation of a {@link jaxx.runtime.JAXXObject} to this class, the + * {@link jaxx.compiler.JAXXCompiler} now only deals with the compilation of files. + * + * @author chemit + */ +public class JAXXObjectGenerator { + + /** log */ + protected static final Log log = LogFactory.getLog(JAXXObjectGenerator.class); + + /** the compile used to parse the file to be generated here. */ + protected JAXXCompiler compiler; + + /** the file to be generated */ + protected JavaFile javaFile = new JavaFile(); + + /** Generated .java file. */ + protected File dest; + + /** Extra code to be added to the instance initializer. */ + protected StringBuffer initializer = new StringBuffer(); + + /** Extra code to be added at the end of the instance initializer. */ + protected StringBuffer lateInitializer = new StringBuffer(); + + /** Extra code to be added to the class body. */ + protected StringBuffer bodyCode = new StringBuffer(); + + /** Code to initialize data bindings. */ + protected StringBuffer initDataBindings = new StringBuffer(); + + /** Body of the applyDataBinding method. */ + protected StringBuffer applyDataBinding = new StringBuffer(); + + /** Body of the removeDataBinding method. */ + protected StringBuffer removeDataBinding = new StringBuffer(); + + /** Body of the processDataBinding method. */ + protected StringBuffer processDataBinding = new StringBuffer(); + + /** true if a main() method has been declared in a script */ + protected boolean mainDeclared; + + public JAXXObjectGenerator(JAXXCompiler compiler) { + this.compiler = compiler; + } + + protected void generateCode() throws IOException { + if (getOptions().getTargetDirectory() != null) { + dest = new File(getOptions().getTargetDirectory(), getOutputClassName().replace('.', File.separatorChar) + ".java"); + } else { + dest = new File(getBaseDir(), getOutputClassName().substring(getOutputClassName().lastIndexOf(".") + 1) + ".java"); + } + if (dest.exists() && !dest.setLastModified(System.currentTimeMillis())) { + log.warn("could not touch file " + dest); + } + PrintWriter out = new PrintWriter(new FileWriter(dest)); + createJavaSource(out); + out.close(); + } + + protected void createJavaSource(PrintWriter out) throws IOException { + int dotPos = getOutputClassName().lastIndexOf("."); + String packageName = dotPos != -1 ? getOutputClassName().substring(0, dotPos) : null; + String simpleClassName = getOutputClassName().substring(dotPos + 1); + outputClass(packageName, simpleClassName, out); + } + + protected void outputClass(String packageName, String className, PrintWriter out) throws CompilerException { + createJavaFile(packageName, className); + out.println(javaFile.toString()); + } + + protected void createJavaFile(String packageName, String className) throws CompilerException { + String fullClassName = packageName != null ? packageName + "." + className : className; + CompiledObject root = compiler.getRootObject(); + if (root == null) { + throw new CompilerException("root tag must be a class tag"); + } + Map<String, CompiledObject> objects = compiler.getObjects(); + ClassDescriptor superclass = root.getObjectClass(); + boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(superclass); + javaFile.setModifiers(Modifier.PUBLIC); + javaFile.setClassName(fullClassName); + javaFile.setSuperClass(JAXXCompiler.getCanonicalName(superclass)); + javaFile.setInterfaces(new String[]{JAXXCompiler.getCanonicalName(JAXXObject.class)}); + + for (CompiledObject object : compiler.getObjects().values()) { + if (!object.isOverride() && !(object instanceof ScriptInitializer)) { + int access = object.getId().startsWith("$") ? Modifier.PRIVATE : Modifier.PROTECTED; + if (object == root) { + javaFile.addField(new JavaField(access, fullClassName, object.getId(), "this")); + } else { + //TC -20081017 can have generic on compiled Object + javaFile.addField(new JavaField(access, JAXXCompiler.getCanonicalName(object), object.getId())); + //javaFile.addField(new JavaField(access, getCanonicalName(object.getObjectClass()), object.getId())); + } + } + } + + if (!superclassIsJAXXObject) { + // add logger + if (getOptions().isAddLogger()) { + javaFile.addImport("org.apache.commons.logging.Log"); + javaFile.addImport("org.apache.commons.logging.LogFactory"); + javaFile.addField(createLoggerField(fullClassName)); + } + + javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<Object>", "$activeBindings", "new ArrayList<Object>()")); + javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.Map<String,Object>", "$bindingSources", "new HashMap<String,Object>()")); + javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<String>", "validatorIds", "new ArrayList<String>()")); + } + + javaFile.addImport("jaxx.runtime.validator.BeanValidator"); + + if (compiler.getStylesheet() != null) { + javaFile.addField(new JavaField(0, "java.util.Map", "$previousValues", "new java.util.HashMap()")); + } + + javaFile.addMethod(createConstructor(className)); + javaFile.addMethod(createInitializer(className)); + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "BeanValidator<?>", "getValidator", new JavaArgument[]{new JavaArgument("String", "validatorId")}, + null, "return (BeanValidator)(validatorIds.contains(validatorId)?getObjectById(validatorId):null);")); + + for (DataBinding dataBinding : compiler.getDataBindings()) { + if (dataBinding.compile(true)) { + initDataBindings.append("applyDataBinding(").append(TypeManager.getJavaCode(dataBinding.getId())).append(");").append(JAXXCompiler.getLineSeparator()); + } + } + + javaFile.addBodyCode(bodyCode.toString()); + + for (CompiledObject object : objects.values()) { + if (!compiler.inlineCreation(object) && object != root) { + javaFile.addMethod(new JavaMethod(Modifier.PROTECTED, "void", object.getCreationMethodName(), null, null, getCreationCode(object))); + } + } + + javaFile.addField(new JavaField(Modifier.PRIVATE, "boolean", "allComponentsCreated")); + + javaFile.addMethod(createCompleteSetupMethod()); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "applyDataBinding", new JavaArgument[]{new JavaArgument("String", "$binding")}, + null, applyDataBinding.toString() + JAXXCompiler.getLineSeparator() + " processDataBinding($binding);")); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removeDataBinding", new JavaArgument[]{new JavaArgument("String", "$binding")}, + null, removeDataBinding.toString())); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "processDataBinding", new JavaArgument[]{new JavaArgument("String", "dest")}, + null, "processDataBinding(dest, false);")); + + javaFile.addMethod(createProcessDataBindingMethod()); + + if (!superclassIsJAXXObject) { + javaFile.addField(createObjectMap()); + javaFile.addMethod(createGetObjectByIdMethod()); + } + + javaFile.addField(createJAXXObjectDescriptorField()); + javaFile.addMethod(createGetJAXXObjectDescriptorMethod()); + + /* + * Gestion du context + */ + javaFile.addField(createContextField()); + javaFile.addMethod(createSetContextValueMethod()); + javaFile.addMethod(createSetContextValueNameMethod()); + javaFile.addMethod(createGetContextValueMethod()); + javaFile.addMethod(createGetContextValueNameMethod()); + javaFile.addMethod(createGetParentContainer()); + javaFile.addMethod(createGetParentContainerMore()); + ClassDescriptor currentClass = root.getObjectClass(); + MethodDescriptor firePropertyChange = null; + while (firePropertyChange == null && currentClass != null) { + try { + firePropertyChange = currentClass.getDeclaredMethodDescriptor("firePropertyChange", new ClassDescriptor[]{ + ClassDescriptorLoader.getClassDescriptor(String.class), + ClassDescriptorLoader.getClassDescriptor(Object.class), + ClassDescriptorLoader.getClassDescriptor(Object.class) + }); + + } + catch (NoSuchMethodException e) { + currentClass = currentClass.getSuperclass(); + } + } + + int modifiers = firePropertyChange != null ? firePropertyChange.getModifiers() : 0; + if (Modifier.isPublic(modifiers)) { + // we have all the support we need + } + if (Modifier.isProtected(modifiers)) { + // there is property change support but the firePropertyChange method is protected + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "firePropertyChange", new JavaArgument[]{ + new JavaArgument("java.lang.String", "propertyName"), new JavaArgument("java.lang.Object", "oldValue"), new JavaArgument("java.lang.Object", "newValue")}, + null, "super.firePropertyChange(propertyName, oldValue, newValue);")); + } else { + // either no support at all or firePropertyChange isn't accessible + addPropertyChangeSupport(javaFile); + } + + addEventHandlers(javaFile); + + if (ClassDescriptorLoader.getClassDescriptor(Application.class).isAssignableFrom(root.getObjectClass()) && !isMainDeclared()) { + // TODO: check for existing main method first + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC | Modifier.STATIC, "void", "main", + new JavaArgument[]{new JavaArgument("String[]", "arg")}, null, + "SwingUtilities.invokeLater(new Runnable() { public void run() { new " + className + "().setVisible(true); } });")); + } + } + + /*---------------------------------------------------------------------------------*/ + /*-- Create fields ----------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + private JavaField createObjectMap() { + return new JavaField(Modifier.PROTECTED, "Map<String,Object>", "$objectMap", "new HashMap<String,Object>()"); + } + + private JavaField createContextField() { + return new JavaField(Modifier.PROTECTED, "Map<Object,String>", "$contextMap", "new HashMap<Object,String>()"); + } + + private JavaField createLoggerField(String className) { + return new JavaField(Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL, "Log", "log", "LogFactory.getLog(" + className + ".class)"); + } + + protected JavaField createJAXXObjectDescriptorField() { + try { + JAXXObjectDescriptor descriptor = compiler.getJAXXObjectDescriptor(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(buffer)); + out.writeObject(descriptor); + out.close(); + // the use of the weird deprecated constructor is deliberate -- we need to store the data as a String + // in the compiled class file, since byte array initialization is horribly inefficient compared to + // String initialization. So we store the bytes in the String, and we quite explicitly want a 1:1 + // mapping between bytes and chars, with the high byte of the char set to zero. We can then safely + // reconstitute the original byte[] at a later date. This is unquestionably an abuse of the String + // type, but if we could efficiently store a byte[] we wouldn't have to do this. + String data = new String(buffer.toByteArray(), 0); + + int sizeLimit = 65000; // constant strings are limited to 64K, and I'm not brave enough to push right up to the limit + if (data.length() < sizeLimit) { + return new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", TypeManager.getJavaCode(data)); + } else { + StringBuffer initializer = new StringBuffer(); + for (int i = 0; i < data.length(); i += sizeLimit) { + String name = "$jaxxObjectDescriptor" + i; + javaFile.addField(new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", name, + TypeManager.getJavaCode(data.substring(i, Math.min(i + sizeLimit, data.length()))))); + if (initializer.length() > 0) { + initializer.append(" + "); + } + initializer.append("String.valueOf(").append(name).append(")"); + } + return new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", initializer.toString()); + } + } catch (IOException e) { + throw new RuntimeException("Internal error: can't-happen error", e); + } + } + + /*---------------------------------------------------------------------------------*/ + /*-- Create methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + protected JavaMethod createGetJAXXObjectDescriptorMethod() { + return new JavaMethod(Modifier.PUBLIC | Modifier.STATIC, "jaxx.runtime.JAXXObjectDescriptor", "$getJAXXObjectDescriptor", + null, null, "return jaxx.runtime.Util.decodeCompressedJAXXObjectDescriptor($jaxxObjectDescriptor);"); + } + + protected JavaMethod createGetObjectByIdMethod() { + return new JavaMethod(Modifier.PUBLIC, "java.lang.Object", "getObjectById", + new JavaArgument[]{new JavaArgument("String", "id")}, null, + "return $objectMap.get(id);"); + } + + private JavaMethod createSetContextValueMethod() { + + return new JavaMethod(Modifier.PUBLIC, "void", "setContextValue", + new JavaArgument[]{new JavaArgument("Object", "clazz")}, null, getSetContextValueMethodCode()); + } + + private JavaMethod createSetContextValueNameMethod() { + return new JavaMethod(Modifier.PUBLIC, "void", "setContextValue", + new JavaArgument[]{new JavaArgument("Object", "clazz"), new JavaArgument("String", "name")}, null, getSetContextValueNameMethodCode()); + } + + private JavaMethod createGetContextValueMethod() { + return new JavaMethod(Modifier.PUBLIC, "<T> T", "getContextValue", + new JavaArgument[]{new JavaArgument("Class<T>", "clazz")}, null, getGetContextValueMethodCode()); + } + + private JavaMethod createGetContextValueNameMethod() { + return new JavaMethod(Modifier.PUBLIC, "<T> T", "getContextValue", + new JavaArgument[]{new JavaArgument("Class<T>", "clazz"), new JavaArgument("String", "name")}, null, getGetContextValueNameMethodCode()); + } + + private JavaMethod createGetParentContainer() { + return new JavaMethod(Modifier.PUBLIC, "<O extends Container> O", "getParentContainer", + new JavaArgument[]{new JavaArgument("Class<O>", "clazz")}, null, getGetParentContenerMethodCode()); + } + + private JavaMethod createGetParentContainerMore() { + return new JavaMethod(Modifier.PUBLIC, "<O extends Container> O", "getParentContainer", + new JavaArgument[]{new JavaArgument("Object", "source"), new JavaArgument("Class<O>", "clazz")}, null, getGetParentContenerMethodMoreCode()); + } + + + protected void addPropertyChangeSupport(JavaFile javaFile) throws CompilerException { + javaFile.addField(new JavaField(0, "java.beans.PropertyChangeSupport", "$propertyChangeSupport")); + + javaFile.addMethod(new JavaMethod(0, "java.beans.PropertyChangeSupport", "$getPropertyChangeSupport", null, null, + "if ($propertyChangeSupport == null)\n" + + " $propertyChangeSupport = new PropertyChangeSupport(this);\n" + + "return $propertyChangeSupport;")); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "addPropertyChangeListener", new JavaArgument[]{ + new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, + "$getPropertyChangeSupport().addPropertyChangeListener(listener);")); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "addPropertyChangeListener", new JavaArgument[]{ + new JavaArgument("java.lang.String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, + "$getPropertyChangeSupport().addPropertyChangeListener(property, listener);")); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removePropertyChangeListener", new JavaArgument[]{ + new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, + "$getPropertyChangeSupport().removePropertyChangeListener(listener);")); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removePropertyChangeListener", new JavaArgument[]{ + new JavaArgument("java.lang.String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, + "$getPropertyChangeSupport().removePropertyChangeListener(property, listener);")); + + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "firePropertyChange", new JavaArgument[]{ + new JavaArgument("java.lang.String", "propertyName"), new JavaArgument("java.lang.Object", "oldValue"), new JavaArgument("java.lang.Object", "newValue")}, + null, "$getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue);")); + } + + + protected void addEventHandlers(JavaFile javaFile) { + for (Map.Entry<String, Map<ClassDescriptor, List<EventHandler>>> e1 : compiler.getEventHandlers().entrySet()) { + // outer loop is iterating over different objects (well, technically, different Java expressions) + for (Map.Entry<ClassDescriptor, List<EventHandler>> e2 : e1.getValue().entrySet()) { + // iterate over different types of listeners for this particular object (MouseListener, ComponentListener, etc.) + for (EventHandler handler : e2.getValue()) { + // iterate over individual event handlers of a single type + String methodName = compiler.getEventHandlerMethodName(handler); + MethodDescriptor listenerMethod = handler.getListenerMethod(); + if (listenerMethod.getParameterTypes().length != 1) { + throw new CompilerException("Expected event handler " + listenerMethod.getName() + " of class " + handler.getListenerClass() + " to have exactly one argument"); + } + javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", methodName, + new JavaArgument[]{new JavaArgument(JAXXCompiler.getCanonicalName(listenerMethod.getParameterTypes()[0]), "event")}, null, + handler.getJavaCode())); + } + } + } + } + + protected JavaMethod createConstructor(String className) throws CompilerException { + StringBuffer code = new StringBuffer(); + String constructorParams = compiler.getRootObject().getConstructorParams(); + if (constructorParams != null) { + code.append(" super(").append(constructorParams).append(");"); + code.append(JAXXCompiler.getLineSeparator()); + } + code.append("$initialize();"); + code.append(JAXXCompiler.getLineSeparator()); + return new JavaMethod(Modifier.PUBLIC, null, className, null, null, code.toString()); + } + + + protected JavaMethod createInitializer(String className) throws CompilerException { + StringBuffer code = new StringBuffer(); + CompiledObject root = compiler.getRootObject(); + code.append("$objectMap.put(").append(TypeManager.getJavaCode(root.getId())).append(", this);"); + code.append(JAXXCompiler.getLineSeparator()); + + Iterator<CompiledObject> i = compiler.getObjectCreationOrder(); + boolean lastWasMethodCall = false; + while (i.hasNext()) { + CompiledObject object = i.next(); + if (object != root && !object.isOverride()) { + if (compiler.inlineCreation(object)) { + if (lastWasMethodCall) { + lastWasMethodCall = false; + code.append(JAXXCompiler.getLineSeparator()); + } + code.append(getCreationCode(object)); + code.append(JAXXCompiler.getLineSeparator()); + } else { + code.append(object.getCreationMethodName()).append("();"); + code.append(JAXXCompiler.getLineSeparator()); + lastWasMethodCall = true; + } + } + } + String rootCode = root.getInitializationCode(compiler); + if (rootCode != null && rootCode.length() > 0) { + code.append(rootCode); + code.append(JAXXCompiler.getLineSeparator()); + } + code.append(JAXXCompiler.getLineSeparator()); + if (initializer.length() > 0) { + code.append(initializer); + code.append(JAXXCompiler.getLineSeparator()); + } + code.append("$completeSetup();"); + code.append(JAXXCompiler.getLineSeparator()); + return new JavaMethod(Modifier.PRIVATE, "void", "$initialize", null, null, code.toString()); + } + + + protected JavaMethod createCompleteSetupMethod() { + StringBuffer code = new StringBuffer(); + code.append("allComponentsCreated = true;"); + code.append(JAXXCompiler.getLineSeparator()); + for (CompiledObject object : compiler.getObjects().values()) { + //TC - 20081017 only generate the method if not empty ? + if (object.getId().startsWith("$")) { + code.append(object.getAdditionCode()).append(JAXXCompiler.getLineSeparator()); + } else { + String additionCode = object.getAdditionCode(); + if (additionCode.length() > 0) { + code.append(object.getAdditionMethodName()).append("();").append(JAXXCompiler.getLineSeparator()); + additionCode = "if (allComponentsCreated) {" + JAXXCompiler.getLineSeparator() + additionCode + "}"; + javaFile.addMethod(new JavaMethod(Modifier.PROTECTED, "void", object.getAdditionMethodName(), null, null, additionCode)); + } + } + //code.append(getLineSeparator()); + } + + code.append(initDataBindings); + + if (lateInitializer.length() > 0) { + code.append(lateInitializer); + code.append(JAXXCompiler.getLineSeparator()); + } + + if (compiler.hasValidator()) { + // register validator + for (CompiledBeanValidator validator : compiler.getValidators()) { + String id = TypeManager.getJavaCode(validator.getId()); + code.append("validatorIds.add(").append(id).append(");"); + code.append(JAXXCompiler.getLineSeparator()); + code.append("getValidator(").append(id).append(").installUIs();"); + code.append("getValidator(").append(id).append(").validate();"); + code.append(JAXXCompiler.getLineSeparator()); + } + } + return new JavaMethod(Modifier.PRIVATE, "void", "$completeSetup", null, null, code.toString()); + } + + protected JavaMethod createProcessDataBindingMethod() { + StringBuffer code = new StringBuffer(); + boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(compiler.getRootObject().getObjectClass()); + // the force parameter forces the update to happen even if it is already in activeBindings. This + // is used on superclass invocations b/c by the time the call gets to the superclass, it is already + // marked active and would otherwise be skipped + if (processDataBinding.length() > 0) { + code.append(" if (!$force && $activeBindings.contains($dest)) return;"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" $activeBindings.add($dest);"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" try {"); + code.append(JAXXCompiler.getLineSeparator()); + if (processDataBinding.length() > 0) { + code.append(processDataBinding); + code.append(JAXXCompiler.getLineSeparator()); + } + if (superclassIsJAXXObject) { + code.append(" else"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" super.processDataBinding($dest, true);"); + code.append(JAXXCompiler.getLineSeparator()); + } + code.append(" }"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" finally {"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" $activeBindings.remove($dest);"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" }"); + code.append(JAXXCompiler.getLineSeparator()); + } else if (superclassIsJAXXObject) { + code.append(" super.processDataBinding($dest, true);"); + code.append(JAXXCompiler.getLineSeparator()); + } + return new JavaMethod(Modifier.PUBLIC, "void", "processDataBinding", + new JavaArgument[]{new JavaArgument("String", "$dest"), new JavaArgument("boolean", "$force")}, + null, code.toString()); + } + + /*---------------------------------------------------------------------------------*/ + /*-- Create methods code ----------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + private String getSetContextValueMethodCode() { + return "this.setContextValue(clazz, null);"; + } + + private String getSetContextValueNameMethodCode() { + StringBuffer result = new StringBuffer(); + result.append("$contextMap.put(clazz, name);"); + return result.toString(); + } + + private String getGetContextValueMethodCode() { + return "return this.getContextValue(clazz, null);"; + } + + private String getGetContextValueNameMethodCode() { + StringBuffer result = new StringBuffer(); + result.append("for (Map.Entry<Object,String> entry : $contextMap.entrySet()) {"); + result.append("if (clazz.isAssignableFrom(entry.getKey().getClass()) && (name == null || name == entry.getValue())) {"); + result.append("return (T) entry.getKey();}}"); + result.append("return null;"); + return result.toString(); + } + + private String getGetParentContenerMethodCode() { + StringBuffer result = new StringBuffer(); + result.append("return this.getParentContainer(this, clazz);"); + return result.toString(); + } + + private String getGetParentContenerMethodMoreCode() { + StringBuffer result = new StringBuffer(); + result.append("if (!Container.class.isAssignableFrom(source.getClass())) {return null;}"); + result.append(JAXXCompiler.getLineSeparator()); + result.append("Container parent = ((Container)source).getParent();"); + result.append(JAXXCompiler.getLineSeparator()); + result.append("if (parent != null && !clazz.isAssignableFrom(parent.getClass())){parent = getParentContainer(parent, clazz);}"); + result.append(JAXXCompiler.getLineSeparator()); + result.append("return (O)parent;"); + return result.toString(); + } + + protected String getCreationCode(CompiledObject object) throws CompilerException { + if (object instanceof ScriptInitializer) { + return object.getInitializationCode(compiler); + } + StringBuffer result = new StringBuffer(); + result.append(object.getId()); + result.append(" = "); + String constructorParams = object.getConstructorParams(); + if (constructorParams != null) { + //TC - 20081017 compiledObject can have generics + result.append(" new ").append(JAXXCompiler.getCanonicalName(object)).append("(").append(constructorParams).append(");"); + //result.append("(").append(getCanonicalName(object.getObjectClass())).append(") new ").append(getCanonicalName(object.getObjectClass())).append("(").append(constructorParams).append(");"); + } else { + //TC - 20081017 compiledObject can have generics + result.append("new ").append(JAXXCompiler.getCanonicalName(object)).append("();"); + } + result.append(JAXXCompiler.getLineSeparator()); + String initCode = object.getInitializationCode(compiler); + if (initCode != null && initCode.length() > 0) { + result.append(initCode); + } + result.append("$objectMap.put(").append(TypeManager.getJavaCode(object.getId())).append(", ").append(object.getId()).append(");"); + + return result.toString(); + } + + /*---------------------------------------------------------------------------------*/ + /*-- Delegate methods from JAXXCompiler -------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + public String getOutputClassName() { + return compiler.getOutputClassName(); + } + + public File getBaseDir() { + return compiler.getBaseDir(); + } + + public CompilerOptions getOptions() { + return compiler.getOptions(); + } + + /*---------------------------------------------------------------------------------*/ + /*-- Getter methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + public JavaFile getJavaFile() { + return javaFile; + } + + public StringBuffer getInitializer() { + return initializer; + } + + public StringBuffer getLateInitializer() { + return lateInitializer; + } + + public StringBuffer getBodyCode() { + return bodyCode; + } + + public StringBuffer getInitDataBindings() { + return initDataBindings; + } + + public StringBuffer getApplyDataBinding() { + return applyDataBinding; + } + + public StringBuffer getRemoveDataBinding() { + return removeDataBinding; + } + + public StringBuffer getProcessDataBinding() { + return processDataBinding; + } + + public boolean isMainDeclared() { + return mainDeclared; + } + + /*---------------------------------------------------------------------------------*/ + /*-- Setter methods ---------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + + public void setMainDeclared(boolean mainDeclared) { + this.mainDeclared = mainDeclared; + } +} Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/ScriptManager.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/ScriptManager.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/ScriptManager.java 2008-10-19 19:44:06 UTC (rev 977) @@ -1,181 +1,181 @@ -package jaxx.compiler; - -import jaxx.CompilerException; -import jaxx.parser.JavaParser; -import jaxx.parser.JavaParserTreeConstants; -import jaxx.parser.SimpleNode; -import jaxx.reflect.FieldDescriptor; -import jaxx.reflect.MethodDescriptor; -import jaxx.tags.TagManager; - -import java.io.StringReader; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class ScriptManager { - private JAXXCompiler compiler; - - - ScriptManager(JAXXCompiler compiler) { - this.compiler = compiler; - } - - - /** - * Strips unnecessary curly braces from around the script, generating a warning if they are found. - * - * @param script script to trim - * @return the trimed script - */ - public String trimScript(String script) { - script = script.trim(); - if (script.startsWith("{") && script.endsWith("}")) { - compiler.reportWarning("curly braces are unnecessary for script '" + script + "'"); - script = script.substring(1, script.length() - 1); - } - return script; - } - - - public void checkParse(String script) throws CompilerException { - script = trimScript(script); - JavaParser p = new JavaParser(new StringReader(script)); - while (!p.Line()) { - // ??? - } - } - - - public String preprocessScript(String script) throws CompilerException { - script = trimScript(script); - StringBuffer result = new StringBuffer(); - JavaParser p = new JavaParser(new StringReader(script)); - //JavaParser p = new JavaParser(new StringReader(script + ";")); - while (!p.Line()) { - SimpleNode node = p.popNode(); - if (node != null) { - preprocessScriptNode(node, false); - result.append(node.getText()); - } - } - return result.toString(); - } - - - /** - * Scans through a compound symbol (foo.bar.baz) to identify and compile the JAXX class it refers to, if any. - * - * @param symbol symbol to scan - */ - private void scanCompoundSymbol(String symbol) { - String[] tokens = symbol.split("\\."); - StringBuffer currentSymbol = new StringBuffer(); - for (String token : tokens) { - if (currentSymbol.length() > 0) - currentSymbol.append('.'); - currentSymbol.append(token.trim()); - - String contextClass = TagManager.resolveClassName(currentSymbol.toString(), compiler); - if (contextClass != null) { - compiler.addDependencyClass(contextClass); - } - } - } - - - private void preprocessScriptNode(SimpleNode node, boolean staticContext) throws CompilerException { - // identify static methods and initializers -- we can't fire events statically - if (node.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION) { - if (node.getParent().getChild(0).getText().indexOf("static") != -1) { - staticContext = true; - } - } else if (node.getId() == JavaParserTreeConstants.JJTINITIALIZER) - if (node.getText().trim().startsWith("static")) { - staticContext = true; - } - - int count = node.jjtGetNumChildren(); - for (int i = 0; i < count; i++) { - preprocessScriptNode(node.getChild(i), staticContext); - } - - int id = node.getId(); - if (id == JavaParserTreeConstants.JJTNAME || id == JavaParserTreeConstants.JJTCLASSORINTERFACETYPE) { - scanCompoundSymbol(node.getText()); - } - if (!staticContext) { - String lhs = null; - if (id == JavaParserTreeConstants.JJTASSIGNMENTEXPRESSION || (id == JavaParserTreeConstants.JJTPOSTFIXEXPRESSION && node.jjtGetNumChildren() == 2)) { - lhs = ((SimpleNode) node.jjtGetChild(0)).getText().trim(); - } - else - if (id == JavaParserTreeConstants.JJTPREINCREMENTEXPRESSION || id == JavaParserTreeConstants.JJTPREDECREMENTEXPRESSION) { - lhs = ((SimpleNode) node.jjtGetChild(0)).getText().trim(); - } - if (lhs != null) { - FieldDescriptor[] fields = compiler.getScriptFields(); - for (FieldDescriptor field : fields) { - if (field.getName().equals(lhs)) { - //lhs.substring(lhs.lastIndexOf(".") + 1); - node.firstToken.image = "jaxx.runtime.Util.assignment(" + node.firstToken.image; - String outputClassName = compiler.getOutputClassName(); - node.lastToken.image = node.lastToken.image + ", \"" + lhs + "\", " + outputClassName + ".this)"; - } - } - } - } - } - - - /** - * Examines a Line to determine its real type. As all tokens returned by the parser are Lines, and - * they are just a tiny wrapper around the real node, this method strips off the wrapper layers to identify - * the real type of a node. - * - * @param line line to scan - * @return the line type - */ - private int getLineType(SimpleNode line) { - if (line.jjtGetNumChildren() == 1) { - SimpleNode node = line.getChild(0); - if (node.getId() == JavaParserTreeConstants.JJTBLOCKSTATEMENT) { - if (node.jjtGetNumChildren() == 1) { - return node.getChild(0).getId(); - } - } else - if (node.getId() == JavaParserTreeConstants.JJTCLASSORINTERFACEBODYDECLARATION) { - int id = node.getChild(0).getId(); - if (id == JavaParserTreeConstants.JJTMODIFIERS) { - return node.getChild(1).getId(); - } - if (id == JavaParserTreeConstants.JJTINITIALIZER) { - return id; - } - } - return node.getId(); - } - return JavaParserTreeConstants.JJTLINE; // generic value implying that it's okay to put into the initializer block - } - - - private SimpleNode findExplicitConstructorInvocation(SimpleNode parent) { - if (parent.getId() == JavaParserTreeConstants.JJTEXPLICITCONSTRUCTORINVOCATION) { - return parent; - } - - int count = parent.jjtGetNumChildren(); - for (int i = 0; i < count; i++) { - SimpleNode result = findExplicitConstructorInvocation(parent.getChild(i)); - if (result != null) { - return result; - } - } - return null; - } - - +package jaxx.compiler; + +import jaxx.CompilerException; +import jaxx.parser.JavaParser; +import jaxx.parser.JavaParserTreeConstants; +import jaxx.parser.SimpleNode; +import jaxx.reflect.FieldDescriptor; +import jaxx.reflect.MethodDescriptor; +import jaxx.tags.TagManager; + +import java.io.StringReader; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ScriptManager { + private JAXXCompiler compiler; + + + ScriptManager(JAXXCompiler compiler) { + this.compiler = compiler; + } + + + /** + * Strips unnecessary curly braces from around the script, generating a warning if they are found. + * + * @param script script to trim + * @return the trimed script + */ + public String trimScript(String script) { + script = script.trim(); + if (script.startsWith("{") && script.endsWith("}")) { + compiler.reportWarning("curly braces are unnecessary for script '" + script + "'"); + script = script.substring(1, script.length() - 1); + } + return script; + } + + + public void checkParse(String script) throws CompilerException { + script = trimScript(script); + JavaParser p = new JavaParser(new StringReader(script)); + while (!p.Line()) { + // ??? + } + } + + + public String preprocessScript(String script) throws CompilerException { + script = trimScript(script); + StringBuffer result = new StringBuffer(); + JavaParser p = new JavaParser(new StringReader(script)); + //JavaParser p = new JavaParser(new StringReader(script + ";")); + while (!p.Line()) { + SimpleNode node = p.popNode(); + if (node != null) { + preprocessScriptNode(node, false); + result.append(node.getText()); + } + } + return result.toString(); + } + + + /** + * Scans through a compound symbol (foo.bar.baz) to identify and compile the JAXX class it refers to, if any. + * + * @param symbol symbol to scan + */ + private void scanCompoundSymbol(String symbol) { + String[] tokens = symbol.split("\\."); + StringBuffer currentSymbol = new StringBuffer(); + for (String token : tokens) { + if (currentSymbol.length() > 0) + currentSymbol.append('.'); + currentSymbol.append(token.trim()); + + String contextClass = TagManager.resolveClassName(currentSymbol.toString(), compiler); + if (contextClass != null) { + compiler.addDependencyClass(contextClass); + } + } + } + + + private void preprocessScriptNode(SimpleNode node, boolean staticContext) throws CompilerException { + // identify static methods and initializers -- we can't fire events statically + if (node.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION) { + if (node.getParent().getChild(0).getText().indexOf("static") != -1) { + staticContext = true; + } + } else if (node.getId() == JavaParserTreeConstants.JJTINITIALIZER) + if (node.getText().trim().startsWith("static")) { + staticContext = true; + } + + int count = node.jjtGetNumChildren(); + for (int i = 0; i < count; i++) { + preprocessScriptNode(node.getChild(i), staticContext); + } + + int id = node.getId(); + if (id == JavaParserTreeConstants.JJTNAME || id == JavaParserTreeConstants.JJTCLASSORINTERFACETYPE) { + scanCompoundSymbol(node.getText()); + } + if (!staticContext) { + String lhs = null; + if (id == JavaParserTreeConstants.JJTASSIGNMENTEXPRESSION || (id == JavaParserTreeConstants.JJTPOSTFIXEXPRESSION && node.jjtGetNumChildren() == 2)) { + lhs = ((SimpleNode) node.jjtGetChild(0)).getText().trim(); + } + else + if (id == JavaParserTreeConstants.JJTPREINCREMENTEXPRESSION || id == JavaParserTreeConstants.JJTPREDECREMENTEXPRESSION) { + lhs = ((SimpleNode) node.jjtGetChild(0)).getText().trim(); + } + if (lhs != null) { + FieldDescriptor[] fields = compiler.getScriptFields(); + for (FieldDescriptor field : fields) { + if (field.getName().equals(lhs)) { + //lhs.substring(lhs.lastIndexOf(".") + 1); + node.firstToken.image = "jaxx.runtime.Util.assignment(" + node.firstToken.image; + String outputClassName = compiler.getOutputClassName(); + node.lastToken.image = node.lastToken.image + ", \"" + lhs + "\", " + outputClassName + ".this)"; + } + } + } + } + } + + + /** + * Examines a Line to determine its real type. As all tokens returned by the parser are Lines, and + * they are just a tiny wrapper around the real node, this method strips off the wrapper layers to identify + * the real type of a node. + * + * @param line line to scan + * @return the line type + */ + private int getLineType(SimpleNode line) { + if (line.jjtGetNumChildren() == 1) { + SimpleNode node = line.getChild(0); + if (node.getId() == JavaParserTreeConstants.JJTBLOCKSTATEMENT) { + if (node.jjtGetNumChildren() == 1) { + return node.getChild(0).getId(); + } + } else + if (node.getId() == JavaParserTreeConstants.JJTCLASSORINTERFACEBODYDECLARATION) { + int id = node.getChild(0).getId(); + if (id == JavaParserTreeConstants.JJTMODIFIERS) { + return node.getChild(1).getId(); + } + if (id == JavaParserTreeConstants.JJTINITIALIZER) { + return id; + } + } + return node.getId(); + } + return JavaParserTreeConstants.JJTLINE; // generic value implying that it's okay to put into the initializer block + } + + + private SimpleNode findExplicitConstructorInvocation(SimpleNode parent) { + if (parent.getId() == JavaParserTreeConstants.JJTEXPLICITCONSTRUCTORINVOCATION) { + return parent; + } + + int count = parent.jjtGetNumChildren(); + for (int i = 0; i < count; i++) { + SimpleNode result = findExplicitConstructorInvocation(parent.getChild(i)); + if (result != null) { + return result; + } + } + return null; + } + + private void processConstructor(String modifiers, SimpleNode node) { assert node.getId() == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION : "expected node to be ConstructorDeclaration, found " + JavaParserTreeConstants.jjtNodeName[node.getId()] + " instead"; assert node.getChild(0).getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS : "expected node 0 to be FormalParameters, found " + JavaParserTreeConstants.jjtNodeName[node.getChild(1).getId()] + " instead"; @@ -196,141 +196,141 @@ } } - compiler.bodyCode.append(modifiers).append(" ").append(node.getText().substring(0,node.getText().length()-1) + code + "}"); + compiler.appendBodyCode(modifiers + " "+ node.getText().substring(0,node.getText().length()-1) + code + "}"); //compiler.bodyCode.append(";\n"); - } - - - private void scanScriptNode(SimpleNode node) throws CompilerException { - int nodeType = getLineType(node); - if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { // have to handle imports early so the preprocessing takes them into account - String text = node.getChild(0).getText().trim(); - if (text.startsWith("import")) { - text = text.substring("import".length()).trim(); - } - if (text.endsWith(";")) { - text = text.substring(0, text.length() - 1); - } - compiler.addImport(text); - } - - preprocessScriptNode(node, false); - - if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { - // do nothing, already handled above - } else if (nodeType == JavaParserTreeConstants.JJTMETHODDECLARATION) { - String returnType = null; - String name = null; - List<String> parameterTypes = new ArrayList<String>(); - //List<String> parameterNames = new ArrayList<String>(); - SimpleNode methodDeclaration = node.getChild(0).getChild(1); - assert methodDeclaration.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION; - for (int i = 0; i < methodDeclaration.jjtGetNumChildren(); i++) { - SimpleNode child = methodDeclaration.getChild(i); - int type = child.getId(); - if (type == JavaParserTreeConstants.JJTRESULTTYPE) { - String rawReturnType = child.getText().trim(); - returnType = TagManager.resolveClassName(rawReturnType, compiler); - // FIXME: this check fails for inner classes defined in this file - //if (returnType == null) - // throw new CompilerException("could not find class '" + rawReturnType + "'"); - } else - if (type == JavaParserTreeConstants.JJTMETHODDECLARATOR) { - name = child.firstToken.image.trim(); - SimpleNode formalParameters = child.getChild(0); - assert formalParameters.getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS; - for (int j = 0; j < formalParameters.jjtGetNumChildren(); j++) - { - SimpleNode parameter = formalParameters.getChild(j); - String rawParameterType = parameter.getChild(1).getText().trim().replaceAll("\\.\\.\\.", "[]"); - String parameterType = TagManager.resolveClassName(rawParameterType, compiler); - // FIXME: this check fails for inner classes defined in this file - //if (parameterType == null) - // throw new CompilerException("could not find class '" + rawParameterType + "'"); - parameterTypes.add(parameterType); - //parameterNames.add(parameter.getChild(2).getText().trim()); - } - } - } - compiler.bodyCode.append(node.getText()); - //compiler.bodyCode.append(";\n"); - compiler.addScriptMethod(new MethodDescriptor(name, Modifier.PUBLIC, returnType, parameterTypes.toArray(new String[parameterTypes.size()]), compiler.getClassLoader())); - } else - if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION || - nodeType == JavaParserTreeConstants.JJTINITIALIZER) { - String str = node.getText().trim(); - if (str.endsWith(";")) { - str+=";"; - } - compiler.bodyCode.append(str); - //compiler.bodyCode.append(";\n"); - } else - if (nodeType == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION) { - processConstructor(node.getChild(0).getChild(0).getText(), node.getChild(0).getChild(1)); - } else - if (nodeType == JavaParserTreeConstants.JJTLOCALVARIABLEDECLARATION || nodeType == JavaParserTreeConstants.JJTFIELDDECLARATION) { - // the "local" variable declarations in this expression aren't actually local -- they are flagged local - // just because there isn't an enclosing class scope visible to the parser. "Real" local variable - // declarations won't show up here, because they will be buried inside of methods. - String text = node.getText().trim(); - if (!text.endsWith(";")) { - text+=";"; - } - String declaration = text; - int equals = text.indexOf("="); - if (equals != -1) { - declaration = declaration.substring(0, equals); - } - declaration = declaration.trim(); - String[] declarationTokens = declaration.split("\\s"); - boolean isFinal = Arrays.asList(declarationTokens).contains("final"); - boolean isStatic = Arrays.asList(declarationTokens).contains("static"); - String name = declarationTokens[declarationTokens.length - 1]; - if (name.endsWith(";")) { - name = name.substring(0, name.length() - 1).trim(); - } - String className = declarationTokens[declarationTokens.length - 2]; - String type = TagManager.resolveClassName(className, compiler); - compiler.addScriptField(new FieldDescriptor(name, Modifier.PUBLIC, type, compiler.getClassLoader())); // TODO: determine the actual modifiers - if (equals != -1 && !isFinal && !isStatic) { // declare the field in the class body, but wait to actually initialize it - //compiler.bodyCode.append(text.substring(0, equals).trim()); - compiler.bodyCode.append(text.substring(0, equals).trim()).append(";"); - String initializer = text.substring(equals + 1).trim(); - if (type.endsWith("[]")) { - initializer = "new " + type + " " + initializer; - } - final String finalInitializer = name + " = " + initializer; - compiler.registerInitializer(new Runnable() { - public void run() { - compiler.registerCompiledObject(new ScriptInitializer(finalInitializer, compiler)); - } - }); - } else { - compiler.bodyCode.append(text); - } - compiler.bodyCode.append("\n"); - //compiler.bodyCode.append(";\n"); - } else { - String text = node.getText().trim(); - if (text.length() > 0) { - if (!text.endsWith(";")) { - text += ";"; - } - compiler.initializer.append(text); - //compiler.initializer.append(";\n"); - } - } - } - - - public void registerScript(String script) throws CompilerException { - JavaParser p = new JavaParser(new StringReader(script)); - //JavaParser p = new JavaParser(new StringReader(script + ";")); - while (!p.Line()) { - SimpleNode node = p.popNode(); - if (node != null) { - scanScriptNode(node); - } - } - } + } + + + private void scanScriptNode(SimpleNode node) throws CompilerException { + int nodeType = getLineType(node); + if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { // have to handle imports early so the preprocessing takes them into account + String text = node.getChild(0).getText().trim(); + if (text.startsWith("import")) { + text = text.substring("import".length()).trim(); + } + if (text.endsWith(";")) { + text = text.substring(0, text.length() - 1); + } + compiler.addImport(text); + } + + preprocessScriptNode(node, false); + + if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { + // do nothing, already handled above + } else if (nodeType == JavaParserTreeConstants.JJTMETHODDECLARATION) { + String returnType = null; + String name = null; + List<String> parameterTypes = new ArrayList<String>(); + //List<String> parameterNames = new ArrayList<String>(); + SimpleNode methodDeclaration = node.getChild(0).getChild(1); + assert methodDeclaration.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION; + for (int i = 0; i < methodDeclaration.jjtGetNumChildren(); i++) { + SimpleNode child = methodDeclaration.getChild(i); + int type = child.getId(); + if (type == JavaParserTreeConstants.JJTRESULTTYPE) { + String rawReturnType = child.getText().trim(); + returnType = TagManager.resolveClassName(rawReturnType, compiler); + // FIXME: this check fails for inner classes defined in this file + //if (returnType == null) + // throw new CompilerException("could not find class '" + rawReturnType + "'"); + } else + if (type == JavaParserTreeConstants.JJTMETHODDECLARATOR) { + name = child.firstToken.image.trim(); + SimpleNode formalParameters = child.getChild(0); + assert formalParameters.getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS; + for (int j = 0; j < formalParameters.jjtGetNumChildren(); j++) + { + SimpleNode parameter = formalParameters.getChild(j); + String rawParameterType = parameter.getChild(1).getText().trim().replaceAll("\\.\\.\\.", "[]"); + String parameterType = TagManager.resolveClassName(rawParameterType, compiler); + // FIXME: this check fails for inner classes defined in this file + //if (parameterType == null) + // throw new CompilerException("could not find class '" + rawParameterType + "'"); + parameterTypes.add(parameterType); + //parameterNames.add(parameter.getChild(2).getText().trim()); + } + } + } + compiler.appendBodyCode(node.getText()); + //compiler.bodyCode.append(";\n"); + compiler.addScriptMethod(new MethodDescriptor(name, Modifier.PUBLIC, returnType, parameterTypes.toArray(new String[parameterTypes.size()]), compiler.getClassLoader())); + } else + if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION || + nodeType == JavaParserTreeConstants.JJTINITIALIZER) { + String str = node.getText().trim(); + if (str.endsWith(";")) { + str+=";"; + } + compiler.appendBodyCode(str); + //compiler.bodyCode.append(";\n"); + } else + if (nodeType == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION) { + processConstructor(node.getChild(0).getChild(0).getText(), node.getChild(0).getChild(1)); + } else + if (nodeType == JavaParserTreeConstants.JJTLOCALVARIABLEDECLARATION || nodeType == JavaParserTreeConstants.JJTFIELDDECLARATION) { + // the "local" variable declarations in this expression aren't actually local -- they are flagged local + // just because there isn't an enclosing class scope visible to the parser. "Real" local variable + // declarations won't show up here, because they will be buried inside of methods. + String text = node.getText().trim(); + if (!text.endsWith(";")) { + text+=";"; + } + String declaration = text; + int equals = text.indexOf("="); + if (equals != -1) { + declaration = declaration.substring(0, equals); + } + declaration = declaration.trim(); + String[] declarationTokens = declaration.split("\\s"); + boolean isFinal = Arrays.asList(declarationTokens).contains("final"); + boolean isStatic = Arrays.asList(declarationTokens).contains("static"); + String name = declarationTokens[declarationTokens.length - 1]; + if (name.endsWith(";")) { + name = name.substring(0, name.length() - 1).trim(); + } + String className = declarationTokens[declarationTokens.length - 2]; + String type = TagManager.resolveClassName(className, compiler); + compiler.addScriptField(new FieldDescriptor(name, Modifier.PUBLIC, type, compiler.getClassLoader())); // TODO: determine the actual modifiers + if (equals != -1 && !isFinal && !isStatic) { // declare the field in the class body, but wait to actually initialize it + //compiler.bodyCode.append(text.substring(0, equals).trim()); + compiler.appendBodyCode(text.substring(0, equals).trim() + ";"); + String initializer = text.substring(equals + 1).trim(); + if (type.endsWith("[]")) { + initializer = "new " + type + " " + initializer; + } + final String finalInitializer = name + " = " + initializer; + compiler.registerInitializer(new Runnable() { + public void run() { + compiler.registerCompiledObject(new ScriptInitializer(finalInitializer, compiler)); + } + }); + } else { + compiler.appendBodyCode(text); + } + compiler.appendBodyCode("\n"); + //compiler.bodyCode.append(";\n"); + } else { + String text = node.getText().trim(); + if (text.length() > 0) { + if (!text.endsWith(";")) { + text += ";"; + } + compiler.appendInitializerCode(text); + //compiler.initializer.append(";\n"); + } + } + } + + + public void registerScript(String script) throws CompilerException { + JavaParser p = new JavaParser(new StringReader(script)); + //JavaParser p = new JavaParser(new StringReader(script + ";")); + while (!p.Line()) { + SimpleNode node = p.popNode(); + if (node != null) { + scanScriptNode(node); + } + } + } } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/css/Stylesheet.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/css/Stylesheet.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/css/Stylesheet.java 2008-10-19 19:44:06 UTC (rev 977) @@ -168,7 +168,7 @@ ClassDescriptorLoader.getClassDescriptor(MouseListener.class).getMethodDescriptor("mouseExited", new ClassDescriptor[]{ClassDescriptorLoader.getClassDescriptor(MouseEvent.class)}), propertyCode, compiler); - compiler.initDataBindings.append("{").append(propertyCode).append("}"); + compiler.appendInitDataBindings("{" + propertyCode + "}"); } catch (NoSuchMethodException e) { compiler.reportError("mouseout pseudoclass cannot be applied to object " + object.getObjectClass().getName() + " (no addMouseListener method)"); @@ -193,7 +193,7 @@ ClassDescriptorLoader.getClassDescriptor(MouseListener.class).getMethodDescriptor("mouseReleased", new ClassDescriptor[]{ClassDescriptorLoader.getClassDescriptor(MouseEvent.class)}), propertyCode, compiler); - compiler.initDataBindings.append("{").append(propertyCode).append("}"); + compiler.appendInitDataBindings("{" + propertyCode + "}"); } catch (NoSuchMethodException e) { compiler.reportError("mouseup pseudoclass cannot be applied to object " + object.getObjectClass().getName() + " (no addMouseListener method)"); @@ -203,11 +203,12 @@ pseudoClass = replaceObjectReferences(pseudoClass, object.getJavaCode()); String dest = object.getId() + ".style." + pseudoClass + ".add"; String destCode = TypeManager.getJavaCode(dest); - if (compiler.processDataBinding.length() > 0) - compiler.processDataBinding.append("else "); - compiler.processDataBinding.append("if ($dest.equals(").append(destCode).append(")) { if (").append(pseudoClass).append(") { ").append(propertyCode).append("} }"); + if (compiler.haveProcessDataBinding()) { + compiler.appendProcessDataBinding("else "); + } + compiler.appendProcessDataBinding("if ($dest.equals(" + destCode + ")) { if (" + pseudoClass + ") { "+ propertyCode + "} }"); new DataSource(dest, pseudoClass, compiler).compile("new jaxx.runtime.DataBindingListener(" + compiler.getRootObject().getJavaCode() + ", " + destCode + ")"); - compiler.initDataBindings.append("applyDataBinding(").append(destCode).append(");"); + compiler.appendInitDataBindings("applyDataBinding("+ destCode + ");"); } else throw new IllegalArgumentException("unrecognized pseudoclass: " + pseudoClass); } @@ -267,11 +268,12 @@ pseudoClass = replaceObjectReferences(pseudoClass, object.getJavaCode()); String dest = object.getId() + ".style." + pseudoClass + ".remove"; String destCode = TypeManager.getJavaCode(dest); - if (compiler.processDataBinding.length() > 0) - compiler.processDataBinding.append("else "); - compiler.processDataBinding.append("if ($dest.equals(").append(destCode).append(")) { if (").append(invert(pseudoClass)).append(") { ").append(propertyCode).append("} }"); + if (compiler.haveProcessDataBinding()) { + compiler.appendProcessDataBinding("else "); + } + compiler.appendProcessDataBinding("if ($dest.equals(" + destCode + ")) { if (" + invert(pseudoClass) + ") { " + propertyCode + "} }"); new DataSource(dest, pseudoClass, compiler).compile("new jaxx.runtime.DataBindingListener(" + compiler.getRootObject().getJavaCode() + ", " + destCode + ")"); - compiler.initDataBindings.append("applyDataBinding(").append(destCode).append(");"); + compiler.appendInitDataBindings("applyDataBinding(" + destCode + ");"); } else throw new IllegalArgumentException("unrecognized pseudoclass: " + pseudoClass); } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java 2008-10-19 19:44:06 UTC (rev 977) @@ -295,17 +295,10 @@ if (eventInfo != null) { // a "proxied" event is one that doesn't fire PropertyChangeEvent, so we need to convert its native event type into PropertyChangeEvents StringBuffer result = new StringBuffer(); String methodName = "$pr" + compiler.getUniqueId(propertyChangeListenerCode); - boolean methodExists = false; - JavaMethod[] methods = compiler.getJavaFile().getMethods(); - for (JavaMethod method : methods) { - if (method.getName().equals(methodName)) { - methodExists = true; - break; - } - } + boolean methodExists = compiler.hasMethod(methodName); ClassDescriptor eventClass = getEventClass(eventInfo.listenerClass); if (!methodExists) { - compiler.getJavaFile().addMethod(new JavaMethod(Modifier.PUBLIC, "void", methodName, + compiler.addMethodToJavaFile(new JavaMethod(Modifier.PUBLIC, "void", methodName, new JavaArgument[]{new JavaArgument(JAXXCompiler.getCanonicalName(eventClass), "event")}, null, propertyChangeListenerCode + ".propertyChange(null);")); } @@ -358,19 +351,12 @@ if (eventInfo != null) { // a "proxied" event is one that doesn't fire PropertyChangeEvent, so we need to convert its native event type into PropertyChangeEvents StringBuffer result = new StringBuffer(); String methodName = "$pr" + compiler.getUniqueId(propertyChangeListenerCode); - boolean methodExists = false; - JavaMethod[] methods = compiler.getJavaFile().getMethods(); - for (JavaMethod method : methods) { - if (method.getName().equals(methodName)) { - methodExists = true; - break; - } - } - ClassDescriptor eventClass = getEventClass(eventInfo.listenerClass); + boolean methodExists = compiler.hasMethod(methodName); if (!methodExists) { - compiler.getJavaFile().addMethod(new JavaMethod(Modifier.PUBLIC, "void", methodName, + ClassDescriptor eventClass = getEventClass(eventInfo.listenerClass); + compiler.addMethodToJavaFile(new JavaMethod(Modifier.PUBLIC, "void", methodName, new JavaArgument[]{new JavaArgument(JAXXCompiler.getCanonicalName(eventClass), "event")}, null, - propertyChangeListenerCode + ".propertyChange(null);")); + propertyChangeListenerCode + ".propertyChange(null);")); } try { String modelMemberName = eventInfo.modelName != null ? "get" + JAXXCompiler.capitalize(eventInfo.modelName) : null; Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/ApplicationHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/ApplicationHandler.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/ApplicationHandler.java 2008-10-19 19:44:06 UTC (rev 977) @@ -26,7 +26,7 @@ @Override public void setAttribute(CompiledObject object, String propertyName, String stringValue, boolean inline, JAXXCompiler compiler) throws CompilerException { if (propertyName.equals("lookAndFeel") && stringValue != null && !stringValue.trim().startsWith("{")) { - compiler.bodyCode.append("{ ").append(object.getJavaCode()).append(".setLookAndFeel(").append(TypeManager.getJavaCode(stringValue)).append("); }").append(JAXXCompiler.getLineSeparator()); + compiler.appendBodyCode("{ " + object.getJavaCode() + ".setLookAndFeel(" + TypeManager.getJavaCode(stringValue) + "); }" + JAXXCompiler.getLineSeparator()); } else { super.setAttribute(object, propertyName, stringValue, inline, compiler); } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JInternalFrameHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JInternalFrameHandler.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JInternalFrameHandler.java 2008-10-19 19:44:06 UTC (rev 977) @@ -48,6 +48,6 @@ @Override public void setAttributes(CompiledObject object, Element tag, JAXXCompiler compiler) throws CompilerException { super.setAttributes(object, tag, compiler); - compiler.initializer.append(object.getId()).append(".pack();\n"); + compiler.appendInitializerCode(object.getId() + ".pack();\n"); } } \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JWindowHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JWindowHandler.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/JWindowHandler.java 2008-10-19 19:44:06 UTC (rev 977) @@ -50,9 +50,10 @@ @Override public void compileSecondPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { super.compileSecondPass(tag, compiler); - CompiledObject object = (CompiledObject) objectMap.get(tag); + CompiledObject object = objectMap.get(tag); Map properties = object.getProperties(); - if (!properties.containsKey("width") && !properties.containsKey("height")) - compiler.lateInitializer.append(object.getId()).append(".pack();\n"); + if (!properties.containsKey("width") && !properties.containsKey("height")) { + compiler.appendLateInitializer(object.getId() + ".pack();\n"); + } } } \ No newline at end of file Modified: lutinjaxx/trunk/maven-jaxx-plugin/src/test/java/org/codelutin/jaxx/CompilerTest.java =================================================================== --- lutinjaxx/trunk/maven-jaxx-plugin/src/test/java/org/codelutin/jaxx/CompilerTest.java 2008-10-19 18:05:04 UTC (rev 976) +++ lutinjaxx/trunk/maven-jaxx-plugin/src/test/java/org/codelutin/jaxx/CompilerTest.java 2008-10-19 19:44:06 UTC (rev 977) @@ -1,6 +1,7 @@ package org.codelutin.jaxx; import jaxx.compiler.JAXXCompiler; +import jaxx.compiler.JAXXCompilerHelper; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.logging.SystemStreamLog; @@ -89,8 +90,8 @@ //do nothing } }); - Field fieldCompilers = JAXXCompiler.class.getDeclaredField("compilers"); - Field fieldErrorCount = JAXXCompiler.class.getDeclaredField("errorCount"); + Field fieldCompilers = JAXXCompilerHelper.class.getDeclaredField("compilers"); + Field fieldErrorCount = JAXXCompilerHelper.class.getDeclaredField("errorCount"); fieldCompilers.setAccessible(true); fieldErrorCount.setAccessible(true); @@ -207,8 +208,8 @@ //do nothing } }); - Field fieldCompilers = JAXXCompiler.class.getDeclaredField("compilers"); - Field fieldErrorCount = JAXXCompiler.class.getDeclaredField("errorCount"); + Field fieldCompilers = JAXXCompilerHelper.class.getDeclaredField("compilers"); + Field fieldErrorCount = JAXXCompilerHelper.class.getDeclaredField("errorCount"); fieldCompilers.setAccessible(true); fieldErrorCount.setAccessible(true);