Source Code Cross Referenced for JAXXCompiler.java in  » XML-UI » JAXX » jaxx » compiler » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » XML UI » JAXX » jaxx.compiler 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2006 Ethan Nicholas. All rights reserved.
0003:         * Use is subject to license terms.
0004:         */
0005:        package jaxx.compiler;
0006:
0007:        import java.io.*;
0008:        import java.lang.reflect.*;
0009:        import java.net.*;
0010:        import java.util.*;
0011:        import java.util.List;
0012:        import java.util.regex.*;
0013:        import java.util.zip.*;
0014:        import javax.xml.parsers.*;
0015:        import javax.xml.transform.*;
0016:        import javax.xml.transform.dom.*;
0017:        import javax.xml.transform.sax.*;
0018:        import org.w3c.dom.*;
0019:        import org.xml.sax.*;
0020:        import org.xml.sax.helpers.*;
0021:
0022:        import jaxx.*;
0023:        import jaxx.css.*;
0024:        import jaxx.parser.ParseException;
0025:        import jaxx.reflect.*;
0026:        import jaxx.runtime.*;
0027:        import jaxx.runtime.swing.*;
0028:        import jaxx.spi.*;
0029:        import jaxx.tags.*;
0030:        import jaxx.types.*;
0031:
0032:        /** Compiles JAXX files into Java classes. */
0033:        public class JAXXCompiler {
0034:            /** True to throw exceptions when we encounter unresolvable classes, false to ignore.
0035:             * This is currently set to false until JAXX has full support for inner classes
0036:             * (including enumerations), because currently they don't always resolve (but will
0037:             * generally compile without error anyway).
0038:             */
0039:            public static final boolean STRICT_CHECKS = false;
0040:
0041:            public static final String JAXX_NAMESPACE = "http://www.jaxxframework.org/";
0042:            public static final String JAXX_INTERNAL_NAMESPACE = "http://www.jaxxframework.org/internal";
0043:
0044:            /** Maximum length of an inlinable creation method. */
0045:            private static final int INLINE_THRESHOLD = 300;
0046:
0047:            /** Contains import declarations (of the form "javax.swing.") which are always imported in all compiler instances. */
0048:            private static List/*<String>*/staticImports = new ArrayList/*<String>*/();
0049:
0050:            static {
0051:                staticImports.add("java.awt.*");
0052:                staticImports.add("java.awt.event.*");
0053:                staticImports.add("java.beans.*");
0054:                staticImports.add("java.io.*");
0055:                staticImports.add("java.lang.*");
0056:                staticImports.add("java.util.*");
0057:                staticImports.add("javax.swing.*");
0058:                staticImports.add("javax.swing.border.*");
0059:                staticImports.add("javax.swing.event.*");
0060:                staticImports.add("jaxx.runtime.swing.JAXXButtonGroup");
0061:                staticImports.add("jaxx.runtime.swing.HBox");
0062:                staticImports.add("jaxx.runtime.swing.VBox");
0063:                staticImports.add("jaxx.runtime.swing.Table");
0064:            }
0065:
0066:            private static DefaultObjectHandler firstPassClassTagHandler = new DefaultObjectHandler(
0067:                    ClassDescriptorLoader.getClassDescriptor(Object.class));
0068:
0069:            /** A list of Runnables which will be run after the first compilation pass.  This is primarily used
0070:             * to trigger the creation of CompiledObjects, which cannot be created during the first pass and must be
0071:             * created in document order.
0072:             */
0073:            private List/*<Runnable>*/initializers = new ArrayList/*<Runnable>*/();
0074:
0075:            /** Files being compiled during the compilation session, may be modified as compilation progresses and additional dependencies are found. */
0076:            private static List/*<File>*/jaxxFiles = new ArrayList/*<File>*/();
0077:
0078:            /** Class names corresponding to the files in the jaxxFiles list. */
0079:            private static List/*<String>*/jaxxFileClassNames = new ArrayList/*<String>*/();
0080:
0081:            /** Maps the names of classes being compiled to the compiler instance handling the compilation. */
0082:            private static Map/*<String, JAXXCompiler>*/compilers = new HashMap/*<String, JAXXCompiler>*/();
0083:
0084:            /** Maps the names of classes being compiled to their symbol tables (created after the first compiler pass). */
0085:            private static Map/*<String, SymbolTable>*/symbolTables = new HashMap/*<String, SymbolTable>*/();
0086:
0087:            private CompilerOptions options;
0088:
0089:            /** Used for error reporting purposes, so we can report the right line number. */
0090:            private Stack/*<Element>*/tagsBeingCompiled = new Stack/*<Element>*/();
0091:
0092:            /** Used for error reporting purposes, so we can report the right source file. */
0093:            private Stack/*<File>*/sourceFiles = new Stack/*<File>*/();
0094:
0095:            /** Maps object ID strings to the objects themselves.  These are created during the second compilation pass. */
0096:            private Map/*<String, CompiledObject>*/objects = new LinkedHashMap/*<String, CompiledObject>*/();
0097:
0098:            /** Maps objects to their ID strings.  These are created during the second compilation pass. */
0099:            private Map/*<CompiledObject, String>*/ids = new LinkedHashMap/*<CompiledObject, String>*/();
0100:
0101:            private static int errorCount;
0102:            private static int warningCount;
0103:
0104:            private boolean failed;
0105:
0106:            /** Object corresponding to the root tag in the document. */
0107:            private CompiledObject root;
0108:
0109:            /** Contains strings of the form "javax.swing." */
0110:            private Set/*<String>*/importedPackages = new HashSet/*<String>*/();
0111:
0112:            /** Contains strings of the form "javax.swing.Timer" */
0113:            private Set/*<String>*/importedClasses = new HashSet/*<String>*/();
0114:
0115:            /** Keeps track of open components (components still having children added). */
0116:            private Stack/*<CompiledObject>*/openComponents = new Stack/*<CompiledObject>*/();
0117:
0118:            /** Sequence number used to create automatic variable names. */
0119:            private int autogenID = 0;
0120:
0121:            private List/*<DataBinding>*/dataBindings = new ArrayList/*<DataBinding>*/();
0122:
0123:            private JavaFile javaFile = new JavaFile();
0124:
0125:            // true if a main() method has been declared in a script
0126:            boolean mainDeclared;
0127:
0128:            private SymbolTable symbolTable = new SymbolTable();
0129:
0130:            // TODO: replace these public StringBuffers with something a little less stupid
0131:
0132:            /** Extra code to be added to the instance initializer. */
0133:            public StringBuffer initializer = new StringBuffer();
0134:
0135:            /** Extra code to be added at the end of the instance initializer. */
0136:            public StringBuffer lateInitializer = new StringBuffer();
0137:
0138:            /** Extra code to be added to the class body. */
0139:            public StringBuffer bodyCode = new StringBuffer();
0140:
0141:            /** Code to initialize data bindings. */
0142:            public StringBuffer initDataBindings = new StringBuffer();
0143:
0144:            /** Body of the applyDataBinding method. */
0145:            public StringBuffer applyDataBinding = new StringBuffer();
0146:
0147:            /** Body of the removeDataBinding method. */
0148:            public StringBuffer removeDataBinding = new StringBuffer();
0149:
0150:            /** Body of the processDataBinding method. */
0151:            public StringBuffer processDataBinding = new StringBuffer();
0152:
0153:            /** Base directory used for path resolution (normally the directory in which the .jaxx file resides). */
0154:            private File baseDir;
0155:
0156:            /** .jaxx file being compiled. */
0157:            private File src;
0158:
0159:            /** Generated .java file. */
0160:            private File dest;
0161:
0162:            /** Parsed XML of src file. */
0163:            private Document document;
0164:
0165:            /** Name of class being compiled. */
0166:            private String outputClassName;
0167:
0168:            private ScriptManager scriptManager = new ScriptManager(this );
0169:
0170:            /** Combination of all stylesheets registered using {@link #registerStylesheet}. */
0171:            private Stylesheet stylesheet;
0172:
0173:            /** Contains all attributes defined inline on class tags. */
0174:            private List/*<Rule>*/inlineStyles = new ArrayList/*<Rule>*/();
0175:
0176:            /** Maps objects (expressed in Java code) to event listener classes (e.g. MouseListener) to Lists of EventHandlers.  The final list 
0177:             * contains all event  handlers of a particular type attached to a particular object (again, as represented by a Java expression). */
0178:            private Map/*<String, Map<ClassDescriptor, List<EventHandler>>>*/eventHandlers = new HashMap/*<CString, Map<ClassDescriptor, List<EventHandler>>>*/();
0179:
0180:            private Map/*<Object, String>*/uniqueIds = new HashMap/*<Object, String>*/();
0181:
0182:            private Map/*<EventHandler, String>*/eventHandlerMethodNames = new HashMap/*<EventHandler, String>*/();
0183:
0184:            /** ClassLoader which searches the user-specified class path in addition to the normal class path */
0185:            private ClassLoader classLoader;
0186:
0187:            private static final int PASS_1 = 0;
0188:            private static final int PASS_2 = 1;
0189:            private static int currentPass;
0190:
0191:            static {
0192:                try {
0193:                    loadLibraries();
0194:                } catch (Exception e) {
0195:                    throw new RuntimeException(e);
0196:                }
0197:            }
0198:
0199:            public static void init() {
0200:                // forces static initializer to run if it hasn't yet
0201:            }
0202:
0203:            public static void loadLibraries() throws IOException,
0204:                    ClassNotFoundException, InstantiationException,
0205:                    IllegalAccessException {
0206:                Enumeration/*<URL>*/e = JAXXCompiler.class.getClassLoader()
0207:                        .getResources("jaxx.properties");
0208:                while (e.hasMoreElements()) {
0209:                    Properties p = new Properties();
0210:                    InputStream in = ((URL) e.nextElement()).openConnection()
0211:                            .getInputStream();
0212:                    p.load(in);
0213:                    in.close();
0214:
0215:                    String initializer = p.getProperty("jaxx.initializer");
0216:                    if (initializer != null)
0217:                        ((Initializer) Class.forName(initializer).newInstance())
0218:                                .initialize();
0219:                }
0220:            }
0221:
0222:            private JAXXCompiler(ClassLoader classLoader) {
0223:                this .options = new CompilerOptions();
0224:                this .classLoader = classLoader;
0225:                addImport("java.lang.*");
0226:            }
0227:
0228:            /** Creates a new JAXXCompiler.
0229:             */
0230:            protected JAXXCompiler(File baseDir, File src,
0231:                    String outputClassName, CompilerOptions options) {
0232:                this .baseDir = baseDir;
0233:                this .src = src;
0234:                sourceFiles.push(src);
0235:                this .outputClassName = outputClassName;
0236:                this .options = options;
0237:                addImport(outputClassName.substring(0, outputClassName
0238:                        .lastIndexOf(".") + 1)
0239:                        + "*");
0240:                Iterator/*<String>*/i = staticImports.iterator();
0241:                while (i.hasNext())
0242:                    addImport((String) i.next());
0243:            }
0244:
0245:            /** Creates a dummy JAXXCompiler for use in unit testing. */
0246:            public static JAXXCompiler createDummyCompiler() {
0247:                return createDummyCompiler(JAXXCompiler.class.getClassLoader());
0248:            }
0249:
0250:            /** Creates a dummy JAXXCompiler for use in unit testing. */
0251:            public static JAXXCompiler createDummyCompiler(
0252:                    ClassLoader classLoader) {
0253:                return new JAXXCompiler(classLoader);
0254:            }
0255:
0256:            public CompilerOptions getOptions() {
0257:                return options;
0258:            }
0259:
0260:            public JavaFile getJavaFile() {
0261:                return javaFile;
0262:            }
0263:
0264:            private void compileFirstPass() throws IOException {
0265:                try {
0266:                    InputStream in = new FileInputStream(src);
0267:                    document = parseDocument(in);
0268:                    in.close();
0269:                    compileFirstPass(document.getDocumentElement());
0270:                } catch (SAXParseException e) {
0271:                    reportError(e.getLineNumber(), "Invalid XML: "
0272:                            + e.getMessage());
0273:                } catch (SAXException e) {
0274:                    reportError(null, "Error parsing XML document: " + e);
0275:                }
0276:            }
0277:
0278:            private void runInitializers() {
0279:                Iterator/*<Runnable>*/i = initializers.iterator();
0280:                while (i.hasNext()) {
0281:                    ((Runnable) i.next()).run();
0282:                    i.remove();
0283:                }
0284:            }
0285:
0286:            /** Registers a <code>Runnable</code> which will be executed after the first
0287:             * compilation pass is complete.
0288:             */
0289:            public void registerInitializer(Runnable r) {
0290:                initializers.add(r);
0291:            }
0292:
0293:            private void compileSecondPass() throws IOException {
0294:                if (!tagsBeingCompiled.isEmpty())
0295:                    throw new RuntimeException(
0296:                            "Internal error: starting pass two, but tagsBeingCompiled is not empty: "
0297:                                    + tagsBeingCompiled);
0298:
0299:                compileSecondPass(document.getDocumentElement());
0300:            }
0301:
0302:            private void applyStylesheets() {
0303:                Iterator/*<CompiledObject>*/i = new ArrayList/*<CompiledObject>*/(
0304:                        objects.values()).iterator();
0305:                while (i.hasNext()) {
0306:                    CompiledObject object = (CompiledObject) i.next();
0307:                    TagManager.getTagHandler(object.getObjectClass())
0308:                            .applyStylesheets(object, this );
0309:                }
0310:            }
0311:
0312:            private void generateCode() throws IOException {
0313:                if (options.getTargetDirectory() != null)
0314:                    dest = new File(options.getTargetDirectory(),
0315:                            outputClassName.replace('.', File.separatorChar)
0316:                                    + ".java");
0317:                else
0318:                    dest = new File(baseDir, outputClassName
0319:                            .substring(outputClassName.lastIndexOf(".") + 1)
0320:                            + ".java");
0321:                PrintWriter out = new PrintWriter(new FileWriter(dest));
0322:                createJavaSource(out);
0323:                out.close();
0324:            }
0325:
0326:            private void runJavac() {
0327:                try {
0328:                    URL jaxxURL = getClass().getResource(
0329:                            "/jaxx/compiler/JAXXCompiler.class");
0330:                    if (jaxxURL == null)
0331:                        throw new InternalError(
0332:                                "Can't-happen error: could not find /jaxx/compiler/JAXXCompiler.class on class path");
0333:                    String classpath = jaxxURL.toString();
0334:                    if (classpath.startsWith("jar:")) {
0335:                        classpath = classpath.substring("jar:".length());
0336:                        classpath = classpath.substring(0, classpath
0337:                                .indexOf("!"));
0338:                        classpath = URLtoFile(classpath).getPath();
0339:                    }
0340:                    URL runtimeURL = getClass().getResource(
0341:                            "/jaxx/runtime/JAXXObject.class");
0342:                    if (runtimeURL == null)
0343:                        throw new InternalError(
0344:                                "Can't-happen error: could not find /jaxx/runtime/JAXXObject.class on class path");
0345:                    String runtime = runtimeURL.toString();
0346:                    if (runtime.startsWith("jar:")) {
0347:                        runtime = runtime.substring("jar:".length());
0348:                        runtime = runtime.substring(0, runtime.indexOf("!"));
0349:                        runtime = URLtoFile(runtime).getPath();
0350:                    }
0351:                    classpath += File.pathSeparator + runtime
0352:                            + File.pathSeparator + options.getClassPath()
0353:                            + File.pathSeparator + ".";
0354:                    Class javac = Class.forName("com.sun.tools.javac.Main");
0355:                    Method main = javac.getMethod("compile",
0356:                            new Class[] { String[].class });
0357:                    final PrintStream oldErr = System.err;
0358:                    System.setErr(new PrintStream(
0359:                            new FilterOutputStream(oldErr) {
0360:                                public void write(byte[] b, int off, int len)
0361:                                        throws IOException {
0362:                                    String stringValue = new String(b, off, len)
0363:                                            .trim();
0364:                                    if (stringValue.startsWith("Error:")
0365:                                            || stringValue.startsWith("Usage:")
0366:                                            || stringValue.endsWith("error")
0367:                                            || stringValue.endsWith("errors"))
0368:                                        failed = true;
0369:                                    if (stringValue
0370:                                            .endsWith("uses unchecked or unsafe operations.")
0371:                                            || stringValue
0372:                                                    .endsWith("with -Xlint:unchecked for details."))
0373:                                        return;
0374:                                    super .write(b, off, len);
0375:                                }
0376:                            }));
0377:                    List/*<String>*/javacOpts = new ArrayList();
0378:                    if (options.getJavacOpts() != null)
0379:                        javacOpts.addAll(Arrays.asList(options.getJavacOpts()
0380:                                .split("\\s+")));
0381:                    if (options.getTargetDirectory() != null) {
0382:                        String destRoot = options.getTargetDirectory()
0383:                                .getPath();
0384:                        javacOpts.add("-d");
0385:                        javacOpts.add(destRoot);
0386:                        classpath += File.pathSeparator + destRoot;
0387:                    }
0388:                    javacOpts.add("-classpath");
0389:                    javacOpts.add(classpath);
0390:                    javacOpts.add(dest.getPath());
0391:                    main.invoke(null, new Object[] { javacOpts
0392:                            .toArray(new String[javacOpts.size()]) });
0393:                    System.setErr(oldErr);
0394:                } catch (ClassNotFoundException e) {
0395:                    System.err
0396:                            .println("Unable to find javac (com.sun.tools.javac.Main) on class path.");
0397:                    System.err
0398:                            .println("jaxxc launch script is responsible for adding javac (typically");
0399:                    System.err
0400:                            .println("located in tools.jar) to the class path;  it either added the");
0401:                    System.err
0402:                            .println("wrong path or tools.jar does not exist.");
0403:                    System.err.println();
0404:                    System.err
0405:                            .println("Check to make sure that JAVA_HOME points to a valid JDK");
0406:                    System.err.println("installation.");
0407:                    failed = true;
0408:                } catch (Exception e) {
0409:                    System.err
0410:                            .println("An error occurred while invoking javac:");
0411:                    e.printStackTrace();
0412:                    failed = true;
0413:                }
0414:
0415:                if (!options.getKeepJavaFiles())
0416:                    dest.delete();
0417:            }
0418:
0419:            private void createJavaSource(PrintWriter out) throws IOException {
0420:                int dotPos = outputClassName.lastIndexOf(".");
0421:                String packageName = dotPos != -1 ? outputClassName.substring(
0422:                        0, dotPos) : null;
0423:                String simpleClassName = outputClassName.substring(dotPos + 1);
0424:                outputClass(packageName, simpleClassName, out);
0425:            }
0426:
0427:            public String getOutputClassName() {
0428:                return outputClassName;
0429:            }
0430:
0431:            public static SAXParser getSAXParser() {
0432:                try {
0433:                    SAXParserFactory factory = SAXParserFactory.newInstance();
0434:                    factory.setNamespaceAware(true);
0435:                    SAXParser parser = factory.newSAXParser();
0436:                    return parser;
0437:                } catch (SAXException e) {
0438:                    throw new RuntimeException(e);
0439:                } catch (ParserConfigurationException e) {
0440:                    throw new RuntimeException(e);
0441:                }
0442:            }
0443:
0444:            public static Document parseDocument(InputStream in)
0445:                    throws IOException, SAXException {
0446:                try {
0447:                    TransformerFactory factory = TransformerFactory
0448:                            .newInstance();
0449:                    Transformer transformer = factory.newTransformer();
0450:                    transformer.setErrorListener(new ErrorListener() {
0451:                        public void warning(TransformerException ex)
0452:                                throws TransformerException {
0453:                            throw ex;
0454:                        }
0455:
0456:                        public void error(TransformerException ex)
0457:                                throws TransformerException {
0458:                            throw ex;
0459:                        }
0460:
0461:                        public void fatalError(TransformerException ex)
0462:                                throws TransformerException {
0463:                            throw ex;
0464:                        }
0465:                    });
0466:
0467:                    DOMResult result = new DOMResult();
0468:                    transformer.transform(new SAXSource(new XMLFilterImpl(
0469:                            getSAXParser().getXMLReader()) {
0470:                        Locator locator;
0471:
0472:                        public void setDocumentLocator(Locator locator) {
0473:                            this .locator = locator;
0474:                        }
0475:
0476:                        public void startElement(String uri, String localName,
0477:                                String qName, Attributes atts)
0478:                                throws SAXException {
0479:                            AttributesImpl resultAtts = new AttributesImpl(atts);
0480:                            resultAtts.addAttribute(JAXX_INTERNAL_NAMESPACE,
0481:                                    "line", "internal:line", "CDATA", String
0482:                                            .valueOf(locator.getLineNumber()));
0483:                            getContentHandler().startElement(uri, localName,
0484:                                    qName, resultAtts);
0485:                        }
0486:                    }, new InputSource(in)), result);
0487:                    return (Document) result.getNode();
0488:                } catch (TransformerConfigurationException e) {
0489:                    throw new RuntimeException(e);
0490:                } catch (TransformerException e) {
0491:                    Throwable ex = e;
0492:                    while (ex.getCause() != null)
0493:                        ex = ex.getCause();
0494:                    if (ex instanceof  IOException)
0495:                        throw (IOException) ex;
0496:                    if (ex instanceof  SAXException)
0497:                        throw (SAXException) ex;
0498:                    if (ex instanceof  RuntimeException)
0499:                        throw (RuntimeException) ex;
0500:                    throw new RuntimeException(ex);
0501:                }
0502:            }
0503:
0504:            public File getBaseDir() {
0505:                return baseDir;
0506:            }
0507:
0508:            public Set/*<String>*/getImportedClasses() {
0509:                return importedClasses;
0510:            }
0511:
0512:            public Set/*<String>*/getImportedPackages() {
0513:                return importedPackages;
0514:            }
0515:
0516:            private boolean inlineCreation(CompiledObject object) {
0517:                return object.getId().startsWith("$")
0518:                        && object.getInitializationCode(this ).length() < INLINE_THRESHOLD;
0519:            }
0520:
0521:            public void checkOverride(CompiledObject object)
0522:                    throws CompilerException {
0523:                if (object.getId().startsWith("$"))
0524:                    return;
0525:                ClassDescriptor ancestor = root.getObjectClass();
0526:                if (ancestor == object.getObjectClass())
0527:                    return;
0528:                while (ancestor != null) {
0529:                    try {
0530:                        FieldDescriptor f = ancestor
0531:                                .getDeclaredFieldDescriptor(object.getId());
0532:                        if (!f.getType().isAssignableFrom(
0533:                                object.getObjectClass()))
0534:                            reportError("attempting to redefine superclass member '"
0535:                                    + object.getId()
0536:                                    + "' as incompatible type (was "
0537:                                    + f.getType()
0538:                                    + ", redefined as "
0539:                                    + object.getObjectClass() + ")");
0540:                        object.setOverride(true);
0541:                        break;
0542:                    } catch (NoSuchFieldException e) {
0543:                        ancestor = ancestor.getSuperclass();
0544:                    }
0545:                }
0546:            }
0547:
0548:            private Iterator/*<CompiledObject>*/getObjectCreationOrder() {
0549:                return objects.values().iterator();
0550:            }
0551:
0552:            protected JavaMethod createConstructor(String className)
0553:                    throws CompilerException {
0554:                StringBuffer code = new StringBuffer();
0555:                String constructorParams = root.getConstructorParams();
0556:                if (constructorParams != null) {
0557:                    code.append("        super(" + constructorParams + ");");
0558:                    code.append(getLineSeparator());
0559:                }
0560:                code.append("$initialize();");
0561:                code.append(getLineSeparator());
0562:                return new JavaMethod(Modifier.PUBLIC, null, className, null,
0563:                        null, code.toString());
0564:            }
0565:
0566:            protected JavaMethod createInitializer(String className)
0567:                    throws CompilerException {
0568:                StringBuffer code = new StringBuffer();
0569:                code.append("$objectMap.put("
0570:                        + TypeManager.getJavaCode(root.getId()) + ", this);");
0571:                code.append(getLineSeparator());
0572:
0573:                Iterator/*<CompiledObject>*/i = getObjectCreationOrder();
0574:                boolean lastWasMethodCall = false;
0575:                while (i.hasNext()) {
0576:                    CompiledObject object = (CompiledObject) i.next();
0577:                    if (object != root && !object.isOverride()) {
0578:                        if (inlineCreation(object)) {
0579:                            if (lastWasMethodCall) {
0580:                                lastWasMethodCall = false;
0581:                                code.append(getLineSeparator());
0582:                            }
0583:                            code.append(getCreationCode(object));
0584:                            code.append(getLineSeparator());
0585:                        } else {
0586:                            code.append(object.getCreationMethodName() + "();");
0587:                            code.append(getLineSeparator());
0588:                            lastWasMethodCall = true;
0589:                        }
0590:                    }
0591:                }
0592:                String rootCode = root.getInitializationCode(this );
0593:                if (rootCode != null && rootCode.length() > 0) {
0594:                    code.append(rootCode);
0595:                    code.append(getLineSeparator());
0596:                }
0597:                code.append(getLineSeparator());
0598:                if (initializer.length() > 0) {
0599:                    code.append(initializer);
0600:                    code.append(getLineSeparator());
0601:                }
0602:                code.append("$completeSetup();");
0603:                code.append(getLineSeparator());
0604:                return new JavaMethod(Modifier.PRIVATE, "void", "$initialize",
0605:                        null, null, code.toString());
0606:            }
0607:
0608:            protected JavaMethod createCompleteSetupMethod() {
0609:                StringBuffer code = new StringBuffer();
0610:                code.append("allComponentsCreated = true;");
0611:                code.append(getLineSeparator());
0612:                Iterator/*<CompiledObject>*/i = objects.values().iterator();
0613:                while (i.hasNext()) {
0614:                    CompiledObject object = (CompiledObject) i.next();
0615:                    if (object.getId().startsWith("$"))
0616:                        code.append(object.getAdditionCode());
0617:                    else {
0618:                        code.append(object.getAdditionMethodName() + "();"
0619:                                + getLineSeparator());
0620:                        String additionCode = object.getAdditionCode();
0621:                        if (additionCode.length() > 0)
0622:                            additionCode = "if (allComponentsCreated) {"
0623:                                    + getLineSeparator() + additionCode + "}";
0624:                        javaFile.addMethod(new JavaMethod(Modifier.PROTECTED,
0625:                                "void", object.getAdditionMethodName(), null,
0626:                                null, additionCode));
0627:                    }
0628:                    code.append(getLineSeparator());
0629:                }
0630:
0631:                code.append(initDataBindings);
0632:
0633:                if (lateInitializer.length() > 0) {
0634:                    code.append(lateInitializer);
0635:                    code.append(getLineSeparator());
0636:                }
0637:                return new JavaMethod(Modifier.PRIVATE, "void",
0638:                        "$completeSetup", null, null, code.toString());
0639:            }
0640:
0641:            protected JavaMethod createProcessDataBindingMethod() {
0642:                StringBuffer code = new StringBuffer();
0643:                boolean super classIsJAXXObject = ClassDescriptorLoader
0644:                        .getClassDescriptor(JAXXObject.class).isAssignableFrom(
0645:                                root.getObjectClass());
0646:                // the force parameter forces the update to happen even if it is already in activeBindings.  This
0647:                // is used on superclass invocations b/c by the time the call gets to the superclass, it is already
0648:                // marked active and would otherwise be skipped
0649:                if (processDataBinding.length() > 0) {
0650:                    code
0651:                            .append("        if (!$force && $activeBindings.contains($dest)) return;");
0652:                    code.append(getLineSeparator());
0653:                    code.append("        $activeBindings.add($dest);");
0654:                    code.append(getLineSeparator());
0655:                    code.append("        try {");
0656:                    code.append(getLineSeparator());
0657:                    if (processDataBinding.length() > 0) {
0658:                        code.append(processDataBinding);
0659:                        code.append(getLineSeparator());
0660:                    }
0661:                    if (super classIsJAXXObject) {
0662:                        code.append("        else");
0663:                        code.append(getLineSeparator());
0664:                        code
0665:                                .append("            super.processDataBinding($dest, true);");
0666:                        code.append(getLineSeparator());
0667:                    }
0668:                    code.append("        }");
0669:                    code.append(getLineSeparator());
0670:                    code.append("        finally {");
0671:                    code.append(getLineSeparator());
0672:                    code.append("            $activeBindings.remove($dest);");
0673:                    code.append(getLineSeparator());
0674:                    code.append("        }");
0675:                    code.append(getLineSeparator());
0676:                } else if (super classIsJAXXObject) {
0677:                    code
0678:                            .append("        super.processDataBinding($dest, true);");
0679:                    code.append(getLineSeparator());
0680:                }
0681:                return new JavaMethod(Modifier.PUBLIC, "void",
0682:                        "processDataBinding", new JavaArgument[] {
0683:                                new JavaArgument("String", "$dest"),
0684:                                new JavaArgument("boolean", "$force") }, null,
0685:                        code.toString());
0686:            }
0687:
0688:            protected void createJavaFile(String packageName, String className)
0689:                    throws CompilerException {
0690:                String fullClassName = packageName != null ? packageName + "."
0691:                        + className : className;
0692:                if (root == null)
0693:                    throw new CompilerException("root tag must be a class tag");
0694:                ClassDescriptor super class = root.getObjectClass();
0695:                boolean super classIsJAXXObject = ClassDescriptorLoader
0696:                        .getClassDescriptor(JAXXObject.class).isAssignableFrom(
0697:                                super class);
0698:                javaFile.setModifiers(Modifier.PUBLIC);
0699:                javaFile.setClassName(fullClassName);
0700:                javaFile.setSuperClass(getCanonicalName(super class));
0701:                javaFile
0702:                        .setInterfaces(new String[] { getCanonicalName(JAXXObject.class) });
0703:
0704:                Iterator/*<CompiledObject>*/i = objects.values().iterator();
0705:                while (i.hasNext()) {
0706:                    CompiledObject object = (CompiledObject) i.next();
0707:                    if (!object.isOverride()
0708:                            && !(object instanceof  ScriptInitializer)) {
0709:                        int access = object.getId().startsWith("$") ? Modifier.PRIVATE
0710:                                : Modifier.PROTECTED;
0711:                        if (object == root)
0712:                            javaFile.addField(new JavaField(access,
0713:                                    fullClassName, object.getId(), "this"));
0714:                        else
0715:                            javaFile.addField(new JavaField(access,
0716:                                    getCanonicalName(object.getObjectClass()),
0717:                                    object.getId()));
0718:                    }
0719:                }
0720:
0721:                if (!super classIsJAXXObject) {
0722:                    javaFile.addField(new JavaField(Modifier.PROTECTED,
0723:                            "java.util.List", "$activeBindings",
0724:                            "new ArrayList()"));
0725:                    javaFile
0726:                            .addField(new JavaField(Modifier.PROTECTED,
0727:                                    "java.util.Map", "$bindingSources",
0728:                                    "new HashMap()"));
0729:                }
0730:
0731:                if (stylesheet != null)
0732:                    javaFile.addField(new JavaField(0, "java.util.Map",
0733:                            "$previousValues", "new java.util.HashMap()"));
0734:
0735:                javaFile.addMethod(createConstructor(className));
0736:                javaFile.addMethod(createInitializer(className));
0737:
0738:                for (int j = 0; j < dataBindings.size(); j++) {
0739:                    DataBinding dataBinding = (DataBinding) dataBindings.get(j);
0740:                    if (dataBinding.compile(true))
0741:                        initDataBindings.append("applyDataBinding("
0742:                                + TypeManager.getJavaCode(dataBinding.getId())
0743:                                + ");" + JAXXCompiler.getLineSeparator());
0744:                }
0745:
0746:                javaFile.addBodyCode(bodyCode.toString());
0747:
0748:                i = objects.values().iterator();
0749:                while (i.hasNext()) {
0750:                    CompiledObject object = (CompiledObject) i.next();
0751:                    if (!inlineCreation(object)) {
0752:                        if (object != root)
0753:                            javaFile.addMethod(new JavaMethod(
0754:                                    Modifier.PROTECTED, "void", object
0755:                                            .getCreationMethodName(), null,
0756:                                    null, getCreationCode(object)));
0757:                    }
0758:                }
0759:
0760:                javaFile.addField(new JavaField(Modifier.PRIVATE, "boolean",
0761:                        "allComponentsCreated"));
0762:
0763:                javaFile.addMethod(createCompleteSetupMethod());
0764:
0765:                javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void",
0766:                        "applyDataBinding",
0767:                        new JavaArgument[] { new JavaArgument("String",
0768:                                "$binding") }, null, applyDataBinding
0769:                                .toString()
0770:                                + getLineSeparator()
0771:                                + "    processDataBinding($binding);"));
0772:
0773:                javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void",
0774:                        "removeDataBinding",
0775:                        new JavaArgument[] { new JavaArgument("String",
0776:                                "$binding") }, null, removeDataBinding
0777:                                .toString()));
0778:
0779:                javaFile
0780:                        .addMethod(new JavaMethod(Modifier.PUBLIC, "void",
0781:                                "processDataBinding",
0782:                                new JavaArgument[] { new JavaArgument("String",
0783:                                        "dest") }, null,
0784:                                "processDataBinding(dest, false);"));
0785:
0786:                javaFile.addMethod(createProcessDataBindingMethod());
0787:
0788:                if (!super classIsJAXXObject) {
0789:                    javaFile.addField(createObjectMap());
0790:                    javaFile.addMethod(createGetObjectByIdMethod());
0791:                }
0792:
0793:                javaFile.addField(createJAXXObjectDescriptorField());
0794:                javaFile.addMethod(createGetJAXXObjectDescriptorMethod());
0795:
0796:                ClassDescriptor currentClass = root.getObjectClass();
0797:                MethodDescriptor firePropertyChange = null;
0798:                while (firePropertyChange == null && currentClass != null) {
0799:                    try {
0800:                        firePropertyChange = currentClass
0801:                                .getDeclaredMethodDescriptor(
0802:                                        "firePropertyChange",
0803:                                        new ClassDescriptor[] {
0804:                                                ClassDescriptorLoader
0805:                                                        .getClassDescriptor(String.class),
0806:                                                ClassDescriptorLoader
0807:                                                        .getClassDescriptor(Object.class),
0808:                                                ClassDescriptorLoader
0809:                                                        .getClassDescriptor(Object.class) });
0810:
0811:                    } catch (NoSuchMethodException e) {
0812:                        currentClass = currentClass.getSuperclass();
0813:                    }
0814:                }
0815:
0816:                int modifiers = firePropertyChange != null ? firePropertyChange
0817:                        .getModifiers() : 0;
0818:                if (Modifier.isPublic(modifiers)) {
0819:                    // we have all the support we need
0820:                }
0821:                if (Modifier.isProtected(modifiers)) {
0822:                    // there is property change support but the firePropertyChange method is protected
0823:                    javaFile
0824:                            .addMethod(new JavaMethod(Modifier.PUBLIC, "void",
0825:                                    "firePropertyChange", new JavaArgument[] {
0826:                                            new JavaArgument(
0827:                                                    "java.lang.String",
0828:                                                    "propertyName"),
0829:                                            new JavaArgument(
0830:                                                    "java.lang.Object",
0831:                                                    "oldValue"),
0832:                                            new JavaArgument(
0833:                                                    "java.lang.Object",
0834:                                                    "newValue") }, null,
0835:                                    "super.firePropertyChange(propertyName, oldValue, newValue);"));
0836:                } else {
0837:                    // either no support at all or firePropertyChange isn't accessible
0838:                    addPropertyChangeSupport(javaFile);
0839:                }
0840:
0841:                addEventHandlers(javaFile);
0842:
0843:                if (ClassDescriptorLoader.getClassDescriptor(Application.class)
0844:                        .isAssignableFrom(root.getObjectClass())
0845:                        && !mainDeclared) {
0846:                    // TODO: check for existing main method first
0847:                    javaFile
0848:                            .addMethod(new JavaMethod(Modifier.PUBLIC
0849:                                    | Modifier.STATIC, "void", "main",
0850:                                    new JavaArgument[] { new JavaArgument(
0851:                                            "String[]", "arg") }, null,
0852:                                    "SwingUtilities.invokeLater(new Runnable() { public void run() { new "
0853:                                            + className
0854:                                            + "().setVisible(true); } });"));
0855:                }
0856:            }
0857:
0858:            protected void outputClass(String packageName, String className,
0859:                    PrintWriter out) throws CompilerException {
0860:                createJavaFile(packageName, className);
0861:                out.println(javaFile.toString());
0862:            }
0863:
0864:            public void addImport(String text) {
0865:                if (text.endsWith("*"))
0866:                    importedPackages.add(text.substring(0, text.length() - 1));
0867:                else
0868:                    importedClasses.add(text);
0869:
0870:                if (!text.equals("*"))
0871:                    getJavaFile().addImport(text);
0872:            }
0873:
0874:            private JavaField createObjectMap() {
0875:                return new JavaField(Modifier.PROTECTED, "Map", "$objectMap",
0876:                        "new HashMap()");
0877:            }
0878:
0879:            protected JavaMethod createGetObjectByIdMethod() {
0880:                return new JavaMethod(Modifier.PUBLIC, "java.lang.Object",
0881:                        "getObjectById", new JavaArgument[] { new JavaArgument(
0882:                                "String", "id") }, null,
0883:                        "return $objectMap.get(id);");
0884:            }
0885:
0886:            public JAXXObjectDescriptor getJAXXObjectDescriptor() {
0887:                runInitializers();
0888:                CompiledObject[] components = (CompiledObject[]) new ArrayList/*<CompiledObject>*/(
0889:                        objects.values()).toArray(new CompiledObject[objects
0890:                        .size()]);
0891:                assert initializers.isEmpty() : "there are pending initializers remaining";
0892:                assert root != null : "root object has not been defined";
0893:                assert Arrays.asList(components).contains(root) : "root object is not registered";
0894:                ComponentDescriptor[] descriptors = new ComponentDescriptor[components.length];
0895:                // as we print, sort the array so that component's parents are always before the components themselves
0896:                for (int i = 0; i < components.length; i++) {
0897:                    CompiledObject parent = components[i].getParent();
0898:                    while (parent != null) {
0899:                        boolean found = false;
0900:                        for (int j = i + 1; j < components.length; j++) { // found parent after component, swap them
0901:                            if (components[j] == parent) {
0902:                                components[j] = components[i];
0903:                                components[i] = parent;
0904:                                found = true;
0905:                                break;
0906:                            }
0907:                        }
0908:                        if (!found)
0909:                            break;
0910:                        parent = components[i].getParent();
0911:                    }
0912:                    int parentIndex = -1;
0913:                    if (parent != null) {
0914:                        for (int j = 0; j < i; j++) {
0915:                            if (components[j] == parent) {
0916:                                parentIndex = j;
0917:                                break;
0918:                            }
0919:                        }
0920:                    }
0921:                    descriptors[i] = new ComponentDescriptor(components[i]
0922:                            .getId(), components[i] == root ? outputClassName
0923:                            : components[i].getObjectClass().getName(),
0924:                            components[i].getStyleClass(),
0925:                            parentIndex != -1 ? descriptors[parentIndex] : null);
0926:                }
0927:
0928:                Stylesheet stylesheet = getStylesheet();
0929:                if (stylesheet == null)
0930:                    stylesheet = new Stylesheet();
0931:
0932:                return new JAXXObjectDescriptor(descriptors, stylesheet);
0933:            }
0934:
0935:            protected JavaField createJAXXObjectDescriptorField() {
0936:                try {
0937:                    JAXXObjectDescriptor descriptor = getJAXXObjectDescriptor();
0938:                    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
0939:                    ObjectOutputStream out = new ObjectOutputStream(
0940:                            new GZIPOutputStream(buffer));
0941:                    out.writeObject(descriptor);
0942:                    out.close();
0943:                    // the use of the weird deprecated constructor is deliberate -- we need to store the data as a String
0944:                    // in the compiled class file, since byte array initialization is horribly inefficient compared to
0945:                    // String initialization.  So we store the bytes in the String, and we quite explicitly want a 1:1
0946:                    // mapping between bytes and chars, with the high byte of the char set to zero.  We can then safely
0947:                    // reconstitute the original byte[] at a later date.  This is unquestionably an abuse of the String
0948:                    // type, but if we could efficiently store a byte[] we wouldn't have to do this.
0949:                    String data = new String(buffer.toByteArray(), 0);
0950:
0951:                    int sizeLimit = 65000; // constant strings are limited to 64K, and I'm not brave enough to push right up to the limit
0952:                    if (data.length() < sizeLimit)
0953:                        return new JavaField(
0954:                                Modifier.PRIVATE | Modifier.STATIC,
0955:                                "java.lang.String", "$jaxxObjectDescriptor",
0956:                                TypeManager.getJavaCode(data));
0957:                    else {
0958:                        StringBuffer initializer = new StringBuffer();
0959:                        for (int i = 0; i < data.length(); i += sizeLimit) {
0960:                            String name = "$jaxxObjectDescriptor" + i;
0961:                            javaFile.addField(new JavaField(Modifier.PRIVATE
0962:                                    | Modifier.STATIC, "java.lang.String",
0963:                                    name, TypeManager
0964:                                            .getJavaCode(data.substring(i, Math
0965:                                                    .min(i + sizeLimit, data
0966:                                                            .length())))));
0967:                            if (initializer.length() > 0)
0968:                                initializer.append(" + ");
0969:                            initializer.append("String.valueOf(" + name + ")");
0970:                        }
0971:                        return new JavaField(
0972:                                Modifier.PRIVATE | Modifier.STATIC,
0973:                                "java.lang.String", "$jaxxObjectDescriptor",
0974:                                initializer.toString());
0975:                    }
0976:                } catch (IOException e) {
0977:                    throw new RuntimeException(
0978:                            "Internal error: can't-happen error", e);
0979:                }
0980:            }
0981:
0982:            protected JavaMethod createGetJAXXObjectDescriptorMethod() {
0983:                return new JavaMethod(
0984:                        Modifier.PUBLIC | Modifier.STATIC,
0985:                        "jaxx.runtime.JAXXObjectDescriptor",
0986:                        "$getJAXXObjectDescriptor",
0987:                        null,
0988:                        null,
0989:                        "return jaxx.runtime.Util.decodeCompressedJAXXObjectDescriptor($jaxxObjectDescriptor);");
0990:            }
0991:
0992:            public String getEventHandlerMethodName(EventHandler handler) {
0993:                String result = (String) eventHandlerMethodNames.get(handler);
0994:                if (result == null) {
0995:                    result = "$ev" + eventHandlerMethodNames.size();
0996:                    eventHandlerMethodNames.put(handler, result);
0997:                }
0998:                return result;
0999:            }
1000:
1001:            protected void addEventHandlers(JavaFile javaFile) {
1002:                Iterator/*Map.Entry<String, Map<ClassDescriptor, List<EventHandler>>>*/i = eventHandlers
1003:                        .entrySet().iterator();
1004:                while (i.hasNext()) { // outer loop is iterating over different objects (well, technically, different Java expressions)
1005:                    Map.Entry/*<String, Map<ClassDescriptor, List<EventHandler>>*/e1 = (Map.Entry) i
1006:                            .next();
1007:                    String expression = (String) e1.getKey();
1008:                    Iterator/*Map.Entry<ClassDescriptor, List<EventHandler>>*/j = ((Map) e1
1009:                            .getValue()).entrySet().iterator();
1010:                    while (j.hasNext()) { // iterate over different types of listeners for this particular object (MouseListener, ComponentListener, etc.)
1011:                        Map.Entry/*<ClassDescriptor, List<EventHandler>>*/e2 = (Map.Entry) j
1012:                                .next();
1013:                        ClassDescriptor listenerClass = (ClassDescriptor) e2
1014:                                .getKey();
1015:                        Iterator/*<EventHandler>*/k = ((List) e2.getValue())
1016:                                .iterator();
1017:                        while (k.hasNext()) { // iterate over individual event handlers of a single type
1018:                            EventHandler handler = (EventHandler) k.next();
1019:                            String methodName = getEventHandlerMethodName(handler);
1020:                            MethodDescriptor listenerMethod = handler
1021:                                    .getListenerMethod();
1022:                            if (listenerMethod.getParameterTypes().length != 1)
1023:                                throw new CompilerException(
1024:                                        "Expected event handler "
1025:                                                + listenerMethod.getName()
1026:                                                + " of class "
1027:                                                + handler.getListenerClass()
1028:                                                + " to have exactly one argument");
1029:                            javaFile.addMethod(new JavaMethod(Modifier.PUBLIC,
1030:                                    "void", methodName,
1031:                                    new JavaArgument[] { new JavaArgument(
1032:                                            getCanonicalName(listenerMethod
1033:                                                    .getParameterTypes()[0]),
1034:                                            "event") }, null, handler
1035:                                            .getJavaCode()));
1036:
1037:                        }
1038:                    }
1039:                }
1040:            }
1041:
1042:            protected String getCreationCode(CompiledObject object)
1043:                    throws CompilerException {
1044:                if (object instanceof  ScriptInitializer)
1045:                    return object.getInitializationCode(this );
1046:                else {
1047:                    StringBuffer result = new StringBuffer();
1048:                    result.append(object.getId());
1049:                    result.append(" = ");
1050:                    String constructorParams = object.getConstructorParams();
1051:                    if (constructorParams != null)
1052:                        result.append("("
1053:                                + getCanonicalName(object.getObjectClass())
1054:                                + ") new "
1055:                                + getCanonicalName(object.getObjectClass())
1056:                                + "(" + constructorParams + ");");
1057:                    else
1058:                        result.append("new "
1059:                                + getCanonicalName(object.getObjectClass())
1060:                                + "();");
1061:                    result.append(getLineSeparator());
1062:                    String initCode = object.getInitializationCode(this );
1063:                    if (initCode != null && initCode.length() > 0)
1064:                        result.append(initCode);
1065:                    result.append("$objectMap.put("
1066:                            + TypeManager.getJavaCode(object.getId()) + ", "
1067:                            + object.getId() + ");");
1068:
1069:                    return result.toString();
1070:                }
1071:            }
1072:
1073:            protected void addPropertyChangeSupport(JavaFile javaFile)
1074:                    throws CompilerException {
1075:                javaFile.addField(new JavaField(0,
1076:                        "java.beans.PropertyChangeSupport",
1077:                        "$propertyChangeSupport"));
1078:
1079:                javaFile
1080:                        .addMethod(new JavaMethod(
1081:                                0,
1082:                                "java.beans.PropertyChangeSupport",
1083:                                "$getPropertyChangeSupport",
1084:                                null,
1085:                                null,
1086:                                "if ($propertyChangeSupport == null)\n"
1087:                                        + "    $propertyChangeSupport = new PropertyChangeSupport(this);\n"
1088:                                        + "return $propertyChangeSupport;"));
1089:
1090:                javaFile
1091:                        .addMethod(new JavaMethod(Modifier.PUBLIC, "void",
1092:                                "addPropertyChangeListener",
1093:                                new JavaArgument[] { new JavaArgument(
1094:                                        "java.beans.PropertyChangeListener",
1095:                                        "listener") }, null,
1096:                                "$getPropertyChangeSupport().addPropertyChangeListener(listener);"));
1097:
1098:                javaFile
1099:                        .addMethod(new JavaMethod(
1100:                                Modifier.PUBLIC,
1101:                                "void",
1102:                                "addPropertyChangeListener",
1103:                                new JavaArgument[] {
1104:                                        new JavaArgument("java.lang.String",
1105:                                                "property"),
1106:                                        new JavaArgument(
1107:                                                "java.beans.PropertyChangeListener",
1108:                                                "listener") }, null,
1109:                                "$getPropertyChangeSupport().addPropertyChangeListener(property, listener);"));
1110:
1111:                javaFile
1112:                        .addMethod(new JavaMethod(Modifier.PUBLIC, "void",
1113:                                "removePropertyChangeListener",
1114:                                new JavaArgument[] { new JavaArgument(
1115:                                        "java.beans.PropertyChangeListener",
1116:                                        "listener") }, null,
1117:                                "$getPropertyChangeSupport().removePropertyChangeListener(listener);"));
1118:
1119:                javaFile
1120:                        .addMethod(new JavaMethod(
1121:                                Modifier.PUBLIC,
1122:                                "void",
1123:                                "removePropertyChangeListener",
1124:                                new JavaArgument[] {
1125:                                        new JavaArgument("java.lang.String",
1126:                                                "property"),
1127:                                        new JavaArgument(
1128:                                                "java.beans.PropertyChangeListener",
1129:                                                "listener") }, null,
1130:                                "$getPropertyChangeSupport().removePropertyChangeListener(property, listener);"));
1131:
1132:                javaFile
1133:                        .addMethod(new JavaMethod(
1134:                                Modifier.PUBLIC,
1135:                                "void",
1136:                                "firePropertyChange",
1137:                                new JavaArgument[] {
1138:                                        new JavaArgument("java.lang.String",
1139:                                                "propertyName"),
1140:                                        new JavaArgument("java.lang.Object",
1141:                                                "oldValue"),
1142:                                        new JavaArgument("java.lang.Object",
1143:                                                "newValue") },
1144:                                null,
1145:                                "$getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue);"));
1146:            }
1147:
1148:            public void compileFirstPass(final Element tag) throws IOException {
1149:                tagsBeingCompiled.push(tag);
1150:
1151:                String namespace = tag.getNamespaceURI();
1152:                String fullClassName = null;
1153:                String localName = tag.getLocalName();
1154:                boolean namespacePrefix = tag.getPrefix() != null;
1155:                // resolve class tags into fully-qualified class name
1156:                if (namespace != null && namespace.endsWith("*")) {
1157:                    String packageName = namespace.substring(0, namespace
1158:                            .length() - 1);
1159:                    if (localName.startsWith(packageName)) // class name is fully-qualified already
1160:                        fullClassName = TagManager.resolveClassName(localName,
1161:                                this );
1162:                    else { // namespace not included in class name, probably need the namespace to resolve
1163:                        fullClassName = TagManager.resolveClassName(packageName
1164:                                + localName, this );
1165:                        if (fullClassName == null && !namespacePrefix) // it was just a default namespace, try again without using the namespace
1166:                            fullClassName = TagManager.resolveClassName(
1167:                                    localName, this );
1168:                    }
1169:                } else
1170:                    fullClassName = TagManager
1171:                            .resolveClassName(localName, this );
1172:
1173:                if (fullClassName != null) { // we are definitely dealing with a class tag
1174:                    addDependencyClass(fullClassName);
1175:                    namespace = fullClassName.substring(0, fullClassName
1176:                            .lastIndexOf(".") + 1)
1177:                            + "*";
1178:                    if (symbolTable.getSuperclassName() == null)
1179:                        symbolTable.setSuperclassName(fullClassName);
1180:                    String id = tag.getAttribute("id");
1181:                    if (id.length() > 0)
1182:                        symbolTable.getClassTagIds().put(id, fullClassName);
1183:                }
1184:                // during the first pass, we can't create ClassDescriptors for JAXX files because they may not have been processed yet
1185:                // (and we can't wait until they have been processed because of circular dependencies).  So we don't do any processing
1186:                // during the first pass which requires having a ClassDescriptor;  here we determine whether we have a class tag or not
1187:                // (class tag namespaces end in "*") and use a generic handler if so.  The real handler is used during the second pass.
1188:                TagHandler handler = (namespace != null && namespace
1189:                        .endsWith("*")) ? firstPassClassTagHandler : TagManager
1190:                        .getTagHandler(tag.getNamespaceURI(), localName,
1191:                                namespacePrefix, this );
1192:                if (handler != firstPassClassTagHandler
1193:                        && handler instanceof  DefaultObjectHandler) {
1194:                    fullClassName = ((DefaultObjectHandler) handler)
1195:                            .getBeanClass().getName();
1196:                    namespace = fullClassName.substring(0, fullClassName
1197:                            .lastIndexOf(".") + 1)
1198:                            + "*";
1199:                    handler = firstPassClassTagHandler;
1200:                }
1201:                if (handler == firstPassClassTagHandler) {
1202:                    final String finalClassName = fullClassName;
1203:                    registerInitializer(new Runnable() { // register an initializer which will create the CompiledObject after pass 1
1204:                        public void run() {
1205:                            DefaultObjectHandler handler = (DefaultObjectHandler) TagManager
1206:                                    .getTagHandler(null, finalClassName,
1207:                                            JAXXCompiler.this );
1208:                            if (handler == null)
1209:                                throw new CompilerException(
1210:                                        "Internal error: missing TagHandler for '"
1211:                                                + finalClassName + "'");
1212:                            handler.registerCompiledObject(tag,
1213:                                    JAXXCompiler.this );
1214:                        }
1215:                    });
1216:                }
1217:                if (handler != null) {
1218:                    try {
1219:                        handler.compileFirstPass(tag, this );
1220:                    } catch (CompilerException e) {
1221:                        reportError(e);
1222:                    }
1223:                } else {
1224:                    reportError("Could not find a Java class corresponding to: <"
1225:                            + tag.getTagName() + ">");
1226:                    failed = true;
1227:                }
1228:
1229:                Element finished = (Element) tagsBeingCompiled.pop();
1230:                if (finished != tag)
1231:                    throw new RuntimeException(
1232:                            "internal error: just finished compiling "
1233:                                    + tag
1234:                                    + ", but top of tagsBeingCompiled stack is "
1235:                                    + finished);
1236:            }
1237:
1238:            public void compileSecondPass(Element tag) throws IOException {
1239:                tagsBeingCompiled.push(tag);
1240:
1241:                TagHandler handler = TagManager.getTagHandler(tag
1242:                        .getNamespaceURI(), tag.getLocalName(),
1243:                        tag.getPrefix() != null, this );
1244:                if (handler != null)
1245:                    handler.compileSecondPass(tag, this );
1246:                else {
1247:                    reportError("Could not find a Java class corresponding to: <"
1248:                            + tag.getTagName() + ">");
1249:                    assert false : "can't-happen error: error should have been reported during the fast pass and caused an abort";
1250:                    failed = true;
1251:                }
1252:
1253:                Element finished = (Element) tagsBeingCompiled.pop();
1254:                if (finished != tag)
1255:                    throw new RuntimeException(
1256:                            "internal error: just finished compiling "
1257:                                    + tag
1258:                                    + ", but top of tagsBeingCompiled stack is "
1259:                                    + finished);
1260:
1261:            }
1262:
1263:            // 1.5 adds getCanonicalName; unfortunately we can't depend on 1.5 features yet
1264:            public static String getCanonicalName(Class clazz) {
1265:                if (clazz.isArray()) {
1266:                    String canonicalName = getCanonicalName(clazz
1267:                            .getComponentType());
1268:                    if (canonicalName != null)
1269:                        return canonicalName + "[]";
1270:                    else
1271:                        return null;
1272:                } else
1273:                    return clazz.getName().replace('$', '.');
1274:            }
1275:
1276:            public static String getCanonicalName(ClassDescriptor clazz) {
1277:                if (clazz.isArray()) {
1278:                    String canonicalName = getCanonicalName(clazz
1279:                            .getComponentType());
1280:                    if (canonicalName != null)
1281:                        return canonicalName + "[]";
1282:                    else
1283:                        return null;
1284:                } else
1285:                    return clazz.getName().replace('$', '.');
1286:            }
1287:
1288:            public static String capitalize(String s) {
1289:                if (s.length() == 0)
1290:                    return s;
1291:                return Character.toUpperCase(s.charAt(0)) + s.substring(1);
1292:            }
1293:
1294:            public String[] parseParameterList(String parameters)
1295:                    throws CompilerException {
1296:                List/*<String>*/result = new ArrayList/*<String>*/();
1297:                StringBuffer current = new StringBuffer();
1298:                int state = 0; // normal
1299:                for (int i = 0; i < parameters.length(); i++) {
1300:                    char c = parameters.charAt(i);
1301:                    switch (state) {
1302:                    case 0: // normal
1303:                        switch (c) {
1304:                        case '"':
1305:                            current.append(c);
1306:                            state = 1;
1307:                            break; // in quoted string
1308:                        case '\\':
1309:                            current.append(c);
1310:                            state = 2;
1311:                            break; // immediately after backslash
1312:                        case ',':
1313:                            if (current.length() > 0) {
1314:                                result.add(current.toString());
1315:                                current.setLength(0);
1316:                                break;
1317:                            } else
1318:                                reportError("error parsing parameter list: "
1319:                                        + parameters);
1320:                        default:
1321:                            current.append(c);
1322:                        }
1323:                        break;
1324:                    case 1: // in quoted string
1325:                        switch (c) {
1326:                        case '"':
1327:                            current.append(c);
1328:                            state = 0;
1329:                            break; // normal
1330:                        case '\\':
1331:                            current.append(c);
1332:                            state = 3;
1333:                            break; // immediate after backslash in quoted string
1334:                        default:
1335:                            current.append(c);
1336:                        }
1337:                        break;
1338:                    case 2: // immediately after backslash
1339:                        current.append(c);
1340:                        state = 0; // normal
1341:                        break;
1342:                    case 3: // immediately after backslash in quoted string
1343:                        current.append(c);
1344:                        state = 1; // in quoted string
1345:                        break;
1346:                    }
1347:                }
1348:                if (current.length() > 0)
1349:                    result.add(current.toString());
1350:                return (String[]) result.toArray(new String[result.size()]);
1351:            }
1352:
1353:            public void openComponent(CompiledObject component)
1354:                    throws CompilerException {
1355:                openComponent(component, null);
1356:            }
1357:
1358:            public void openComponent(CompiledObject component,
1359:                    String constraints) throws CompilerException {
1360:                CompiledObject parent = getOpenComponent();
1361:                openInvisibleComponent(component);
1362:                if (parent != null && !component.isOverride())
1363:                    parent.addChild(component, constraints, this );
1364:            }
1365:
1366:            public void openInvisibleComponent(CompiledObject component) {
1367:                if (!ids.containsKey(component))
1368:                    registerCompiledObject(component);
1369:                openComponents.push(component);
1370:            }
1371:
1372:            public CompiledObject getOpenComponent() {
1373:                if (openComponents.isEmpty())
1374:                    return null;
1375:                else
1376:                    return (CompiledObject) openComponents.peek();
1377:            }
1378:
1379:            public void closeComponent(CompiledObject component) {
1380:                if (openComponents.pop() != component)
1381:                    throw new IllegalArgumentException(
1382:                            "can only close the topmost open object");
1383:            }
1384:
1385:            public CompiledObject getRootObject() {
1386:                return root;
1387:            }
1388:
1389:            public void registerCompiledObject(CompiledObject object) {
1390:                assert symbolTables.values().contains(symbolTable) : "attempting to register CompiledObject before pass 1 is complete";
1391:                if (root == null)
1392:                    root = object;
1393:
1394:                String id = object.getId();
1395:                if (ids.containsKey(object))
1396:                    reportError("object '" + object
1397:                            + "' is already registered with id '"
1398:                            + ids.get(object) + "', cannot re-register as '"
1399:                            + id + "'");
1400:                if (objects.containsKey(id)
1401:                        && !(objects.get(id) instanceof  Element))
1402:                    reportError("id '" + id
1403:                            + "' is already registered to component "
1404:                            + objects.get(id));
1405:                objects.put(id, object);
1406:                ids.put(object, id);
1407:            }
1408:
1409:            public String getAutoId(ClassDescriptor objectClass) {
1410:                if (options.getOptimize()) {
1411:                    return "$" + Integer.toString(autogenID++, 36);
1412:                } else {
1413:                    String name = objectClass.getName();
1414:                    name = name.substring(name.lastIndexOf(".") + 1);
1415:                    return "$" + name + autogenID++;
1416:                }
1417:            }
1418:
1419:            public String getUniqueId(Object object) {
1420:                String result = (String) uniqueIds.get(object);
1421:                if (result == null) {
1422:                    result = "$u" + uniqueIds.size();
1423:                    uniqueIds.put(object, result);
1424:                }
1425:                return result;
1426:            }
1427:
1428:            public SymbolTable getSymbolTable() {
1429:                return symbolTable;
1430:            }
1431:
1432:            public CompiledObject getCompiledObject(String id) {
1433:                runInitializers();
1434:                assert symbolTables.values().contains(symbolTable) : "attempting to retrieve CompiledObject before pass 1 is complete";
1435:                return (CompiledObject) objects.get(id);
1436:            }
1437:
1438:            private Matcher leftBraceMatcher = Pattern.compile(
1439:                    "^(\\{)|[^\\\\](\\{)").matcher("");
1440:
1441:            private int getNextLeftBrace(String string, int pos) {
1442:                leftBraceMatcher.reset(string);
1443:                return leftBraceMatcher.find(pos) ? Math.max(leftBraceMatcher
1444:                        .start(1), leftBraceMatcher.start(2)) : -1;
1445:            }
1446:
1447:            private Matcher rightBraceMatcher = Pattern.compile(
1448:                    "^(\\})|[^\\\\](\\})").matcher("");
1449:
1450:            private int getNextRightBrace(String string, int pos) {
1451:                leftBraceMatcher.reset(string);
1452:                rightBraceMatcher.reset(string);
1453:                int openCount = 1;
1454:                int rightPos = -1;
1455:                while (openCount > 0) {
1456:                    pos++;
1457:                    int leftPos = leftBraceMatcher.find(pos) ? Math.max(
1458:                            leftBraceMatcher.start(1), leftBraceMatcher
1459:                                    .start(2)) : -1;
1460:                    rightPos = rightBraceMatcher.find(pos) ? Math.max(
1461:                            rightBraceMatcher.start(1), rightBraceMatcher
1462:                                    .start(2)) : -1;
1463:                    assert leftPos == -1 || leftPos >= pos;
1464:                    assert rightPos == -1 || rightPos >= pos;
1465:                    if (leftPos != -1 && leftPos < rightPos) {
1466:                        pos = leftPos;
1467:                        openCount++;
1468:                    } else if (rightPos != -1) {
1469:                        pos = rightPos;
1470:                        openCount--;
1471:                    } else
1472:                        openCount = 0;
1473:                }
1474:                return pos;
1475:            }
1476:
1477:            /** Examine an attribute value for data binding expressions.  Returns a 'cooked' expression which
1478:             * can be used to determine the resulting value.  It is expected that this expression will be used 
1479:             * as the source expression in a call to {@link #registerDataBinding}.
1480:             * If the attribute value does not invoke data binding, this method returns <code>null</code>
1481:             *
1482:             *@param stringValue the string value of the property from the XML
1483:             *@param type the type of the property, from the <code>JAXXPropertyDescriptor</code>
1484:             *@return a processed version of the expression
1485:             */
1486:            public String processDataBindings(String stringValue,
1487:                    ClassDescriptor type) throws CompilerException {
1488:                int pos = getNextLeftBrace(stringValue, 0);
1489:                if (pos != -1) {
1490:                    StringBuffer expression = new StringBuffer();
1491:                    int lastPos = 0;
1492:                    while (pos != -1 && pos < stringValue.length()) {
1493:                        if (pos > lastPos) {
1494:                            if (expression.length() > 0)
1495:                                expression.append(" + ");
1496:                            expression.append('"');
1497:                            expression.append(JAXXCompiler
1498:                                    .escapeJavaString(stringValue.substring(
1499:                                            lastPos, pos)));
1500:                            expression.append('"');
1501:                        }
1502:
1503:                        if (expression.length() > 0)
1504:                            expression.append(" + ");
1505:                        expression.append('(');
1506:                        int pos2 = getNextRightBrace(stringValue, pos + 1);
1507:                        if (pos2 == -1) {
1508:                            reportError("unmatched '{' in expression: "
1509:                                    + stringValue);
1510:                            return "";
1511:                        }
1512:                        expression.append(stringValue.substring(pos + 1, pos2));
1513:                        expression.append(')');
1514:                        pos2++;
1515:                        if (pos2 < stringValue.length()) {
1516:                            pos = getNextLeftBrace(stringValue, pos2);
1517:                            lastPos = pos2;
1518:                        } else {
1519:                            pos = stringValue.length();
1520:                            lastPos = pos;
1521:                        }
1522:                    }
1523:                    if (lastPos < stringValue.length()) {
1524:                        if (expression.length() > 0)
1525:                            expression.append(" + ");
1526:                        expression.append('"');
1527:                        expression.append(JAXXCompiler
1528:                                .escapeJavaString(stringValue
1529:                                        .substring(lastPos)));
1530:                        expression.append('"');
1531:                    }
1532:                    return type == ClassDescriptorLoader
1533:                            .getClassDescriptor(String.class) ? "String.valueOf("
1534:                            + expression + ")"
1535:                            : expression.toString();
1536:                }
1537:                return null;
1538:            }
1539:
1540:            public void registerDataBinding(String src, String dest,
1541:                    String assignment) {
1542:                try {
1543:                    src = checkJavaCode(src);
1544:                    dataBindings.add(new DataBinding(src, dest, assignment,
1545:                            this ));
1546:                } catch (CompilerException e) {
1547:                    reportError("While parsing data binding for '"
1548:                            + dest.substring(dest.lastIndexOf(".") + 1) + "': "
1549:                            + e.getMessage());
1550:                }
1551:            }
1552:
1553:            public ScriptManager getScriptManager() {
1554:                return scriptManager;
1555:            }
1556:
1557:            /** Verifies that a snippet of Java code parses correctly.  A warning is generated if the string has enclosing
1558:             * curly braces.  Returns a "cooked" version of the string which has enclosing curly braces removed.
1559:             *
1560:             *@param javaCode the Java code snippet to test
1561:             *@throws CompilerException if the code cannot be parsed
1562:             */
1563:            public String checkJavaCode(String javaCode) {
1564:                javaCode = scriptManager.trimScript(javaCode);
1565:                scriptManager.checkParse(javaCode);
1566:                return javaCode;
1567:            }
1568:
1569:            public void registerEventHandler(EventHandler handler) {
1570:                String objectCode = handler.getObjectCode();
1571:                Map/*<ClassDescriptor, List<EventHandler>>*/listeners = (Map) eventHandlers
1572:                        .get(objectCode);
1573:                if (listeners == null) {
1574:                    listeners = new HashMap/*<ClassDescriptor, List<EventHandler>>*/();
1575:                    eventHandlers.put(objectCode, listeners);
1576:                }
1577:                ClassDescriptor listenerClass = handler.getListenerClass();
1578:                List/*<EventHandler>*/handlerList = (List) listeners
1579:                        .get(listenerClass);
1580:                if (handlerList == null) {
1581:                    handlerList = new ArrayList/*<EventHandler>*/();
1582:                    listeners.put(listenerClass, handlerList);
1583:                }
1584:                handlerList.add(handler);
1585:            }
1586:
1587:            public FieldDescriptor[] getScriptFields() {
1588:                List/*<FieldDescriptor>*/scriptFields = symbolTable
1589:                        .getScriptFields();
1590:                return (FieldDescriptor[]) scriptFields
1591:                        .toArray(new FieldDescriptor[scriptFields.size()]);
1592:            }
1593:
1594:            public void addScriptField(FieldDescriptor field) {
1595:                symbolTable.getScriptFields().add(field);
1596:            }
1597:
1598:            public MethodDescriptor[] getScriptMethods() {
1599:                List/*<MethodDescriptor>*/scriptMethods = symbolTable
1600:                        .getScriptMethods();
1601:                return (MethodDescriptor[]) scriptMethods
1602:                        .toArray(new MethodDescriptor[scriptMethods.size()]);
1603:            }
1604:
1605:            public void addScriptMethod(MethodDescriptor method) {
1606:                if (method.getName().equals("main")
1607:                        && method.getParameterTypes().length == 1
1608:                        && method.getParameterTypes()[0].getName().equals(
1609:                                "[Ljava.lang.String;"))
1610:                    mainDeclared = true;
1611:                symbolTable.getScriptMethods().add(method);
1612:            }
1613:
1614:            public void registerScript(String script) throws CompilerException {
1615:                registerScript(script, null);
1616:            }
1617:
1618:            public void registerScript(String script, File sourceFile)
1619:                    throws CompilerException {
1620:                if (sourceFile != null)
1621:                    sourceFiles.push(sourceFile);
1622:                scriptManager.registerScript(script);
1623:
1624:                if (sourceFile != null) {
1625:                    File pop = (File) sourceFiles.pop();
1626:                    if (pop != sourceFile)
1627:                        throw new RuntimeException(
1628:                                "leaving registerScript(), but "
1629:                                        + sourceFile
1630:                                        + " was not the top entry on the stack (found "
1631:                                        + pop + " instead)");
1632:                }
1633:            }
1634:
1635:            public String preprocessScript(String script)
1636:                    throws CompilerException {
1637:                return scriptManager.preprocessScript(script);
1638:            }
1639:
1640:            public void registerStylesheet(Stylesheet stylesheet) {
1641:                if (this .stylesheet == null)
1642:                    this .stylesheet = stylesheet;
1643:                else
1644:                    this .stylesheet.add(stylesheet.getRules());
1645:            }
1646:
1647:            public Stylesheet getStylesheet() {
1648:                Stylesheet merged = new Stylesheet();
1649:                if (stylesheet != null)
1650:                    merged.add(stylesheet.getRules());
1651:                merged.add((Rule[]) inlineStyles.toArray(new Rule[inlineStyles
1652:                        .size()]));
1653:                return merged;
1654:            }
1655:
1656:            public Stack/*<File>*/getSourceFiles() {
1657:                return sourceFiles;
1658:            }
1659:
1660:            public void addInlineStyle(CompiledObject object,
1661:                    String propertyName, boolean dataBinding) {
1662:                inlineStyles.add(Rule.inlineAttribute(object, propertyName,
1663:                        dataBinding));
1664:            }
1665:
1666:            public void reportWarning(String warning) {
1667:                Element currentTag = null;
1668:                if (!tagsBeingCompiled.isEmpty())
1669:                    currentTag = (Element) tagsBeingCompiled.peek();
1670:                reportWarning(currentTag, warning, 0);
1671:            }
1672:
1673:            public void reportWarning(Element tag, String warning,
1674:                    int lineOffset) {
1675:                String lineNumber = null;
1676:                if (tag != null) {
1677:                    String lineAttr = tag.getAttributeNS(
1678:                            JAXX_INTERNAL_NAMESPACE, "line");
1679:                    if (lineAttr.length() > 0)
1680:                        lineNumber = lineAttr;
1681:                }
1682:                File src = (File) sourceFiles.peek();
1683:                try {
1684:                    src = src.getCanonicalFile();
1685:                } catch (IOException e) {
1686:                }
1687:
1688:                System.err.print(src);
1689:                if (lineNumber != null)
1690:                    System.err.print(":"
1691:                            + ((sourceFiles.size() == 1) ? Integer
1692:                                    .parseInt(lineNumber)
1693:                                    + lineOffset : lineOffset + 1));
1694:                System.err.println(": Warning: " + warning);
1695:                warningCount++;
1696:            }
1697:
1698:            public void reportError(String error) {
1699:                Element currentTag = null;
1700:                if (!tagsBeingCompiled.isEmpty())
1701:                    currentTag = (Element) tagsBeingCompiled.peek();
1702:                reportError(currentTag, error);
1703:            }
1704:
1705:            public void reportError(CompilerException ex) {
1706:                reportError(null, ex);
1707:            }
1708:
1709:            public void reportError(String extraMessage, CompilerException ex) {
1710:                String message = ex.getMessage();
1711:                if (ex.getClass() == UnsupportedAttributeException.class
1712:                        || ex.getClass() == UnsupportedTagException.class)
1713:                    message = ex.getClass().getName().substring(
1714:                            ex.getClass().getName().lastIndexOf(".") + 1)
1715:                            + ": " + message;
1716:                int lineOffset;
1717:                if (ex instanceof  ParseException)
1718:                    lineOffset = Math.max(0,
1719:                            ((ParseException) ex).getLine() - 1);
1720:                else
1721:                    lineOffset = 0;
1722:                Element currentTag = null;
1723:                if (!tagsBeingCompiled.isEmpty())
1724:                    currentTag = (Element) tagsBeingCompiled.peek();
1725:                reportError(currentTag, extraMessage != null ? extraMessage
1726:                        + message : message, lineOffset);
1727:            }
1728:
1729:            public void reportError(Element tag, String error) {
1730:                reportError(tag, error, 0);
1731:            }
1732:
1733:            public void reportError(Element tag, String error, int lineOffset) {
1734:                int lineNumber = 0;
1735:                if (tag != null) {
1736:                    String lineAttr = tag.getAttributeNS(
1737:                            JAXX_INTERNAL_NAMESPACE, "line");
1738:                    if (lineAttr.length() > 0)
1739:                        lineNumber = Integer.parseInt(lineAttr);
1740:                }
1741:                lineNumber = Math.max(lineNumber, 1) + lineOffset;
1742:                reportError(lineNumber, error);
1743:            }
1744:
1745:            public void reportError(int lineNumber, String error) {
1746:                File src = sourceFiles.isEmpty() ? null : (File) sourceFiles
1747:                        .peek();
1748:                try {
1749:                    if (src != null)
1750:                        src = src.getCanonicalFile();
1751:                } catch (IOException e) {
1752:                }
1753:
1754:                System.err.print(src != null ? src.getPath()
1755:                        : "<unknown source>");
1756:                if (lineNumber > 0)
1757:                    System.err.print(":" + lineNumber);
1758:                System.err.println(": " + error);
1759:                errorCount++;
1760:                failed = true;
1761:            }
1762:
1763:            /** Escapes a string using standard Java escape sequences, generally in preparation to including it in a string literal
1764:             * in a compiled Java file.
1765:             *
1766:             *@param raw the raw string to be escape
1767:             *@return a string in which all 'dangerous' characters have been replaced by equivalent Java escape sequences
1768:             **/
1769:            public static String escapeJavaString(String raw) {
1770:                StringBuffer out = new StringBuffer(raw);
1771:                for (int i = 0; i < out.length(); i++) {
1772:                    char c = out.charAt(i);
1773:                    if (c == '\\' || c == '"') {
1774:                        out.insert(i, '\\');
1775:                        i++;
1776:                    } else if (c == '\n') {
1777:                        out.replace(i, i + 1, "\\n");
1778:                        i++;
1779:                    } else if (c == '\r') {
1780:                        out.replace(i, i + 1, "\\r");
1781:                        i++;
1782:                    } else if (c < 32 || c > 127) {
1783:                        String value = Integer.toString((int) c, 16);
1784:                        while (value.length() < 4)
1785:                            value = "0" + value;
1786:                        out.replace(i, i + 1, "\\u" + value);
1787:                        i += 5;
1788:                    }
1789:                }
1790:                return out.toString();
1791:            }
1792:
1793:            /** Returns the system line separator string.
1794:             *
1795:             *@return the string used to separate lines
1796:             */
1797:            public static String getLineSeparator() {
1798:                return System.getProperty("line.separator", "\n");
1799:            }
1800:
1801:            /** Returns a <code>ClassLoader</code> which searches the user-specified class path in addition
1802:             * to the normal system class path.
1803:             *
1804:             *@return <code>ClassLoader</code> to use while resolving class references
1805:             */
1806:            public ClassLoader getClassLoader() {
1807:                if (classLoader == null) {
1808:                    String classPath = options.getClassPath();
1809:                    if (classPath == null)
1810:                        classPath = ".";
1811:                    String[] paths = classPath.split(File.pathSeparator);
1812:                    URL[] urls = new URL[paths.length];
1813:                    for (int i = 0; i < paths.length; i++) {
1814:                        try {
1815:                            urls[i] = new File(paths[i]).toURL();
1816:                        } catch (MalformedURLException e) {
1817:                            throw new RuntimeException(e);
1818:                        }
1819:                    }
1820:                    classLoader = new URLClassLoader(urls, getClass()
1821:                            .getClassLoader());
1822:                }
1823:
1824:                return classLoader;
1825:            }
1826:
1827:            /** Returns the compiler instance which is processing the specified JAXX class.  Each class is compiled by a
1828:             * different compiler instance.
1829:             */
1830:            public static JAXXCompiler getJAXXCompiler(String className) {
1831:                return compilers != null ? (JAXXCompiler) compilers
1832:                        .get(className) : null;
1833:            }
1834:
1835:            /** Returns the symbol table for the specified JAXX class.  Must be called during the second compiler pass. 
1836:             * Returns <code>null</code> if no such symbol table could be found.
1837:             */
1838:            public static SymbolTable getSymbolTable(String className) {
1839:                JAXXCompiler compiler = getJAXXCompiler(className);
1840:                if (compiler == null)
1841:                    return null;
1842:                return compiler.getSymbolTable();
1843:            }
1844:
1845:            public static File URLtoFile(URL url) {
1846:                return URLtoFile(url.toString());
1847:            }
1848:
1849:            public static File URLtoFile(String urlString) {
1850:                if (!urlString.startsWith("file:"))
1851:                    throw new IllegalArgumentException(
1852:                            "url must start with 'file:'");
1853:                urlString = urlString.substring("file:".length());
1854:                if (urlString.startsWith("/")
1855:                        && System.getProperty("os.name").startsWith("Windows"))
1856:                    urlString = urlString.substring(1);
1857:                try {
1858:                    return new File(URLDecoder.decode(urlString.replace('/',
1859:                            File.separatorChar), "utf-8"));
1860:                } catch (UnsupportedEncodingException e) {
1861:                    throw new RuntimeException(e);
1862:                }
1863:            }
1864:
1865:            public void addDependencyClass(String className) {
1866:                if (!jaxxFileClassNames.contains(className)) {
1867:                    URL jaxxURL = getClassLoader().getResource(
1868:                            className.replace('.', '/') + ".jaxx");
1869:                    URL classURL = getClassLoader().getResource(
1870:                            className.replace('.', '/') + ".class");
1871:                    if (jaxxURL != null && classURL != null) {
1872:                        try {
1873:                            File jaxxFile = URLtoFile(jaxxURL);
1874:                            File classFile = URLtoFile(classURL);
1875:                            if (classFile.lastModified() > jaxxFile
1876:                                    .lastModified())
1877:                                return; // class file is newer, no need to recompile
1878:                        } catch (Exception e) {
1879:                            // do nothing
1880:                        }
1881:                    }
1882:
1883:                    if (jaxxURL != null
1884:                            && jaxxURL.toString().startsWith("file:")) {
1885:                        File jaxxFile = URLtoFile(jaxxURL);
1886:                        try {
1887:                            jaxxFile = jaxxFile.getCanonicalFile();
1888:                        } catch (IOException ex) {
1889:                        }
1890:                        assert jaxxFile.getName()
1891:                                .equalsIgnoreCase(
1892:                                        className.substring(className
1893:                                                .lastIndexOf(".") + 1)
1894:                                                + ".jaxx") : "expecting file name to match "
1895:                                + className
1896:                                + ", but found "
1897:                                + jaxxFile.getName();
1898:                        if (jaxxFile.getName()
1899:                                .equals(
1900:                                        className.substring(className
1901:                                                .lastIndexOf(".") + 1)
1902:                                                + ".jaxx")) { // check case match
1903:                            if (currentPass == PASS_2)
1904:                                throw new AssertionError(
1905:                                        "Internal error: adding dependency class "
1906:                                                + className
1907:                                                + " during second compilation pass");
1908:                            jaxxFileClassNames.add(className);
1909:                            jaxxFiles.add(jaxxFile);
1910:                        } else
1911:                            return; // case mismatch, ignore
1912:                    }
1913:                }
1914:            }
1915:
1916:            /** Compiled a set of files, expressed as paths relative to a base directory.  The class names of the compiled files are derived 
1917:             * from the relative path strings (e.g. "example/Foo.jaxx" compiles into a class named "example.Foo").  Returns <code>true</code>
1918:             * if compilation succeeds, <code>false</code> if it fails.  Warning and error messages are sent to <code>System.err</code>.
1919:             *
1920:             *@param base the directory against which to resolve relative paths
1921:             *@param relativePaths a list of relative paths to .jaxx files being compiled
1922:             *@param options the compiler options to use
1923:             *@return <code>true</code> if compilation succeeds, <code>false</code> otherwise
1924:             */
1925:            public static synchronized boolean compile(File base,
1926:                    String[] relativePaths, CompilerOptions options) {
1927:                File[] files = new File[relativePaths.length];
1928:                String[] classNames = new String[relativePaths.length];
1929:                for (int i = 0; i < files.length; i++) {
1930:                    files[i] = new File(base, relativePaths[i]);
1931:                    classNames[i] = relativePaths[i].substring(0,
1932:                            relativePaths[i].lastIndexOf("."));
1933:                    classNames[i] = classNames[i].replace(File.separatorChar,
1934:                            '.');
1935:                    classNames[i] = classNames[i].replace('/', '.');
1936:                    classNames[i] = classNames[i].replace('\\', '.');
1937:                    classNames[i] = classNames[i].replace(':', '.');
1938:                }
1939:                return compile(files, classNames, options);
1940:            }
1941:
1942:            /** Resets all state in preparation for a new compilation session. */
1943:            private static void reset() {
1944:                errorCount = 0;
1945:                warningCount = 0;
1946:                jaxxFiles.clear();
1947:                jaxxFileClassNames.clear();
1948:                symbolTables.clear();
1949:                compilers.clear();
1950:            }
1951:
1952:            /** Compiled a set of files, with the class names specified explicitly.  The class compiled from files[i] will be named classNames[i].
1953:             * Returns <code>true</code> if compilation succeeds, <code>false</code> if it fails.  Warning and error messages are sent to 
1954:             * <code>System.err</code>.
1955:             *
1956:             *@param files the .jaxx files to compile
1957:             *@param classNames the names of the classes being compiled
1958:             *@param options the compiler options to use
1959:             *@return <code>true</code> if compilation succeeds, <code>false</code> otherwise
1960:             */
1961:            public static synchronized boolean compile(File[] files,
1962:                    String[] classNames, CompilerOptions options) {
1963:                reset(); // just to be safe...
1964:                jaxxFiles.addAll(Arrays.asList(files));
1965:                jaxxFileClassNames.addAll(Arrays.asList(classNames));
1966:                try {
1967:                    boolean success = true;
1968:
1969:                    // pass 1
1970:                    currentPass = PASS_1;
1971:                    boolean compiled;
1972:                    do {
1973:                        compiled = false;
1974:                        assert jaxxFiles.size() == jaxxFileClassNames.size();
1975:                        Iterator/*<String>*/filesIterator = new ArrayList/*<File>*/(
1976:                                jaxxFiles).iterator(); // clone it so it can safely be modified while we're iterating
1977:                        Iterator/*<String>*/classNamesIterator = new ArrayList/*<String>*/(
1978:                                jaxxFileClassNames).iterator();
1979:                        while (filesIterator.hasNext()) {
1980:                            File file = (File) filesIterator.next();
1981:                            String className = (String) classNamesIterator
1982:                                    .next();
1983:                            if (symbolTables.get(file) == null) {
1984:                                compiled = true;
1985:                                if (compilers.containsKey(className))
1986:                                    throw new CompilerException(
1987:                                            "Internal error: "
1988:                                                    + className
1989:                                                    + " is already being compiled, attempting to compile it again");
1990:
1991:                                File destDir = options.getTargetDirectory();
1992:                                if (destDir != null) {
1993:                                    int dotPos = className.lastIndexOf(".");
1994:                                    if (dotPos != -1)
1995:                                        destDir = new File(destDir, className
1996:                                                .substring(0, dotPos)
1997:                                                .replace('.',
1998:                                                        File.separatorChar));
1999:                                    destDir.mkdirs();
2000:                                } else
2001:                                    destDir = file.getParentFile();
2002:                                JAXXCompiler compiler = new JAXXCompiler(file
2003:                                        .getParentFile(), file, className,
2004:                                        options);
2005:                                compilers.put(className, compiler);
2006:                                compiler.compileFirstPass();
2007:                                assert !symbolTables.values().contains(
2008:                                        compiler.getSymbolTable()) : "symbolTable is already registered";
2009:                                symbolTables.put(file, compiler
2010:                                        .getSymbolTable());
2011:                                if (compiler.failed)
2012:                                    success = false;
2013:                            }
2014:                        }
2015:
2016:                    } while (compiled);
2017:
2018:                    // pass 2
2019:                    currentPass = PASS_2;
2020:                    if (success) {
2021:                        assert jaxxFiles.size() == jaxxFileClassNames.size();
2022:                        List/*<String>*/jaxxFilesClone = new ArrayList(
2023:                                jaxxFiles);
2024:                        Iterator/*<String>*/filesIterator = jaxxFilesClone
2025:                                .iterator();
2026:                        Iterator/*<String>*/classNamesIterator = jaxxFileClassNames
2027:                                .iterator();
2028:                        while (filesIterator.hasNext()) {
2029:                            File file = (File) filesIterator.next();
2030:                            String className = (String) classNamesIterator
2031:                                    .next();
2032:                            JAXXCompiler compiler = (JAXXCompiler) compilers
2033:                                    .get(className);
2034:                            if (compiler == null)
2035:                                throw new CompilerException(
2036:                                        "Internal error: could not find compiler for "
2037:                                                + className
2038:                                                + " during second pass");
2039:                            if (!compiler.failed)
2040:                                compiler.runInitializers();
2041:                            compiler.compileSecondPass();
2042:                            if (compiler.failed)
2043:                                success = false;
2044:                        }
2045:                        if (!jaxxFilesClone.equals(jaxxFiles))
2046:                            throw new AssertionError(
2047:                                    "Internal error: compilation set altered during pass 2 (was "
2048:                                            + jaxxFilesClone + ", modified to "
2049:                                            + jaxxFiles + ")");
2050:                    }
2051:
2052:                    // stylesheet application
2053:                    if (success) {
2054:                        assert jaxxFiles.size() == jaxxFileClassNames.size();
2055:                        Iterator/*<String>*/filesIterator = jaxxFiles
2056:                                .iterator();
2057:                        Iterator/*<String>*/classNamesIterator = jaxxFileClassNames
2058:                                .iterator();
2059:                        while (filesIterator.hasNext()) {
2060:                            File file = (File) filesIterator.next();
2061:                            String className = (String) classNamesIterator
2062:                                    .next();
2063:                            JAXXCompiler compiler = (JAXXCompiler) compilers
2064:                                    .get(className);
2065:                            if (compiler == null)
2066:                                throw new CompilerException(
2067:                                        "Internal error: could not find compiler for "
2068:                                                + className
2069:                                                + " during stylesheet application");
2070:                            compiler.applyStylesheets();
2071:                            if (compiler.failed)
2072:                                success = false;
2073:                        }
2074:                    }
2075:
2076:                    // code generation
2077:                    if (success) {
2078:                        assert jaxxFiles.size() == jaxxFileClassNames.size();
2079:                        Iterator/*<String>*/filesIterator = jaxxFiles
2080:                                .iterator();
2081:                        Iterator/*<String>*/classNamesIterator = jaxxFileClassNames
2082:                                .iterator();
2083:                        while (filesIterator.hasNext()) {
2084:                            File file = (File) filesIterator.next();
2085:                            String className = (String) classNamesIterator
2086:                                    .next();
2087:                            JAXXCompiler compiler = (JAXXCompiler) compilers
2088:                                    .get(className);
2089:                            if (compiler == null)
2090:                                throw new CompilerException(
2091:                                        "Internal error: could not find compiler for "
2092:                                                + className
2093:                                                + " during code generation");
2094:                            compiler.generateCode();
2095:                            if (compiler.failed)
2096:                                success = false;
2097:                        }
2098:                    }
2099:
2100:                    // javac
2101:                    if (success && options.getRunJavac()) {
2102:                        assert jaxxFiles.size() == jaxxFileClassNames.size();
2103:                        Iterator/*<String>*/filesIterator = jaxxFiles
2104:                                .iterator();
2105:                        Iterator/*<String>*/classNamesIterator = jaxxFileClassNames
2106:                                .iterator();
2107:                        while (filesIterator.hasNext()) {
2108:                            File file = (File) filesIterator.next();
2109:                            String className = (String) classNamesIterator
2110:                                    .next();
2111:                            JAXXCompiler compiler = (JAXXCompiler) compilers
2112:                                    .get(className);
2113:                            if (compiler == null)
2114:                                throw new CompilerException(
2115:                                        "Internal error: could not find compiler for "
2116:                                                + className
2117:                                                + " during compilation");
2118:                            compiler.runJavac();
2119:                            if (compiler.failed)
2120:                                success = false;
2121:                        }
2122:                    }
2123:
2124:                    if (warningCount == 1)
2125:                        System.err.println("1 warning");
2126:                    else if (warningCount > 0)
2127:                        System.err.println(warningCount + " warnings");
2128:
2129:                    if (errorCount == 1)
2130:                        System.err.println("1 error");
2131:                    else if (errorCount > 0)
2132:                        System.err.println(errorCount + " errors");
2133:
2134:                    return success;
2135:                } catch (CompilerException e) {
2136:                    System.err.println(e.getMessage());
2137:                    e.printStackTrace();
2138:                    return false;
2139:                } catch (Throwable e) {
2140:                    e.printStackTrace();
2141:                    return false;
2142:                } finally {
2143:                    reset();
2144:                }
2145:            }
2146:
2147:            private static void showUsage() {
2148:                System.out.println("Usage: jaxxc <options> <source files>");
2149:                System.out.println();
2150:                System.out.println("Source files must end in extension .jaxx");
2151:                System.out
2152:                        .println("Use JAXX_OPTS environment variable to pass arguments to Java runtime");
2153:                System.out.println();
2154:                System.out.println("Supported options include:");
2155:                System.out
2156:                        .println("  -classpath <paths>  paths to search for user classes");
2157:                System.out.println("  -cp <paths>         same as -classpath");
2158:                System.out
2159:                        .println("  -d <directory>      target directory for generated class files");
2160:                System.out
2161:                        .println("  -javac_opts <opts>  options to pass to javac");
2162:                System.out
2163:                        .println("  -java or -j         produce .java files, but do not compile them");
2164:                System.out
2165:                        .println("  -keep or -k         preserve generated .java files after compilation");
2166:                System.out
2167:                        .println("  -optimize or -o     optimize during compilation");
2168:                System.out
2169:                        .println("  -version            display version information");
2170:                System.out.println();
2171:                System.out
2172:                        .println("See http://www.jaxxframework.org/ for full documentation.");
2173:            }
2174:
2175:            public static String getVersion() {
2176:                return "1.0.3-beta2";
2177:            }
2178:
2179:            public static void main(String[] arg) throws Exception {
2180:                boolean success = true;
2181:
2182:                CompilerOptions options = new CompilerOptions();
2183:                List/*<String>*/files = new ArrayList/*<String>*/();
2184:                for (int i = 0; i < arg.length; i++) {
2185:                    if (arg[i].endsWith(".jaxx")) {
2186:                        files.add(arg[i]);
2187:                    } else if (arg[i].equals("-d")) {
2188:                        if (++i < arg.length) {
2189:                            File targetDirectory = new File(arg[i]);
2190:                            if (!targetDirectory.exists()) {
2191:                                System.err
2192:                                        .println("Error: could not find target directory: "
2193:                                                + targetDirectory);
2194:                                errorCount++;
2195:                                success = false;
2196:                            }
2197:                            options.setTargetDirectory(targetDirectory);
2198:                        } else
2199:                            success = false;
2200:                    } else if (arg[i].equals("-cp")
2201:                            || arg[i].equals("-classpath")) {
2202:                        if (++i < arg.length)
2203:                            options.setClassPath(arg[i]);
2204:                        else
2205:                            success = false;
2206:                    } else if (arg[i].equals("-javac_opts")) {
2207:                        if (++i < arg.length)
2208:                            options.setJavacOpts(arg[i]);
2209:                        else
2210:                            success = false;
2211:                    } else if (arg[i].equals("-k") || arg[i].equals("-keep"))
2212:                        options.setKeepJavaFiles(true);
2213:                    else if (arg[i].equals("-j") || arg[i].equals("-java")) {
2214:                        options.setKeepJavaFiles(true);
2215:                        options.setRunJavac(false);
2216:                    } else if (arg[i].equals("-o")
2217:                            || arg[i].equals("-optimize"))
2218:                        options.setOptimize(true);
2219:                    else if (arg[i].equals("-version")) {
2220:                        System.err.println("jaxxc version " + getVersion()
2221:                                + " by Ethan Nicholas");
2222:                        System.err.println("http://www.jaxxframework.org/");
2223:                        System.exit(0);
2224:                    } else if (arg[i].equals("-internalDumpVersion")) { // used by ant to extract the version info
2225:                        System.out.println("jaxx.version=" + getVersion());
2226:                        return;
2227:                    } else {
2228:                        success = false;
2229:                    }
2230:                }
2231:
2232:                success &= (errorCount == 0 && files.size() > 0);
2233:
2234:                if (success)
2235:                    success = compile(new File("."), (String[]) files
2236:                            .toArray(new String[files.size()]), options);
2237:                else {
2238:                    showUsage();
2239:                    System.exit(1);
2240:                }
2241:
2242:                System.exit(success ? 0 : 1);
2243:            }
2244:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.