001    /*******************************************************************************
002     * Copyright (c) 2009 Progress Software, Inc.
003     * Copyright (c) 2008 IBM Corporation and others.
004     *
005     * All rights reserved. This program and the accompanying materials
006     * are made available under the terms of the Eclipse Public License v1.0
007     * which accompanies this distribution, and is available at
008     * http://www.eclipse.org/legal/epl-v10.html
009     *
010     *******************************************************************************/
011    package org.fusesource.hawtjni.generator;
012    
013    import java.io.BufferedInputStream;
014    import java.io.ByteArrayOutputStream;
015    import java.io.File;
016    import java.io.FileInputStream;
017    import java.io.IOException;
018    import java.io.InputStream;
019    import java.io.InputStreamReader;
020    import java.io.PrintStream;
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.Comparator;
024    import java.util.HashMap;
025    import java.util.Iterator;
026    import java.util.Map;
027    import java.util.Set;
028    import java.util.StringTokenizer;
029    import java.util.TreeMap;
030    import java.util.TreeSet;
031    
032    import javax.xml.parsers.DocumentBuilderFactory;
033    
034    import org.fusesource.hawtjni.generator.HawtJNI.UsageException;
035    import org.fusesource.hawtjni.generator.util.FileSupport;
036    import org.w3c.dom.Document;
037    import org.w3c.dom.Element;
038    import org.w3c.dom.NamedNodeMap;
039    import org.w3c.dom.Node;
040    import org.w3c.dom.NodeList;
041    import org.xml.sax.InputSource;
042    
043    /**
044     * 
045     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
046     */
047    public class MacGenerator {
048        String[] xmls;
049        Document[] documents;
050        String outputDir, mainClassName;
051        String delimiter = System.getProperty("line.separator");
052        PrintStream out;
053    
054        public MacGenerator() {
055        }
056    
057        static void list(File path, ArrayList<String> list) {
058            if (path == null)
059                return;
060            File[] frameworks = path.listFiles();
061            if (frameworks == null)
062                return;
063            for (int i = 0; i < frameworks.length; i++) {
064                File file = frameworks[i];
065                String name = file.getName();
066                int index = name.lastIndexOf(".");
067                if (index != -1) {
068                    String xml = file.getAbsolutePath() + "/Resources/BridgeSupport/" + name.substring(0, index) + "Full.bridgesupport";
069                    if (new File(xml).exists()) {
070                        list.add(xml);
071                    }
072                }
073            }
074        }
075    
076        int getLevel(Node node) {
077            int level = 0;
078            while (node != null) {
079                level++;
080                node = node.getParentNode();
081            }
082            return level;
083        }
084    
085        void merge(Document document, Document extraDocument) {
086            if (extraDocument == null)
087                return;
088    
089            /* Build a lookup table for extraDocument */
090            HashMap<String, Node> extras = new HashMap<String, Node>();
091            buildLookup(extraDocument, extras);
092    
093            /*
094             * Merge attributes on existing elements building a lookup table for
095             * document
096             */
097            HashMap<String, Node> lookup = new HashMap<String, Node>();
098            merge(document, extras, lookup);
099    
100            /*
101             * Merge new elements. Extras at this point contains only elements that
102             * were not found in the document.
103             */
104            ArrayList<Node> sortedNodes = Collections.list(Collections.enumeration(extras.values()));
105            Collections.sort(sortedNodes, new Comparator<Node>() {
106                public int compare(Node arg0, Node arg1) {
107                    int compare = getLevel(arg0) - getLevel(arg1);
108                    if (compare == 0) {
109                        return (arg0).getNodeName().compareTo((arg1).getNodeName());
110                    }
111                    return compare;
112                }
113            });
114            String delimiter = System.getProperty("line.separator");
115            for (Iterator<Node> iterator = sortedNodes.iterator(); iterator.hasNext();) {
116                Node node = iterator.next();
117                String name = node.getNodeName();
118                if ("arg".equals(name) || "retval".equals(name)) {
119                    if (!sortedNodes.contains(node.getParentNode()))
120                        continue;
121                }
122                Node parent = lookup.get(getKey(node.getParentNode()));
123                Element element = document.createElement(node.getNodeName());
124                String text = parent.getChildNodes().getLength() == 0 ? delimiter : "";
125                for (int i = 0, level = getLevel(parent) - 1; i < level; i++) {
126                    text += "  ";
127                }
128                parent.appendChild(document.createTextNode(text));
129                parent.appendChild(element);
130                parent.appendChild(document.createTextNode(delimiter));
131                NamedNodeMap attributes = node.getAttributes();
132                for (int j = 0, length = attributes.getLength(); j < length; j++) {
133                    Node attr = (Node) attributes.item(j);
134                    element.setAttribute(attr.getNodeName(), attr.getNodeValue());
135                }
136                lookup.put(getKey(element), element);
137            }
138        }
139    
140        public void generate(ProgressMonitor progress) throws UsageException {
141            if (progress != null) {
142                progress.setTotal(3);
143                progress.setMessage("extra attributes...");
144            }
145            generateExtraAttributes();
146            if (progress != null) {
147                progress.step();
148                progress.setMessage(mainClassName);
149            }
150            generateMainClass();
151            if (progress != null) {
152                progress.step();
153                progress.setMessage("classes...");
154            }
155            generateClasses();
156            if (progress != null) {
157                progress.step();
158                progress.setMessage("Done.");
159            }
160        }
161    
162    
163        String fixDelimiter(String str) {
164            if (delimiter.equals("\n"))
165                return str;
166            int index = 0, length = str.length();
167            StringBuffer buffer = new StringBuffer();
168            while (index != -1) {
169                int start = index;
170                index = str.indexOf('\n', start);
171                if (index == -1) {
172                    buffer.append(str.substring(start, length));
173                } else {
174                    buffer.append(str.substring(start, index));
175                    buffer.append(delimiter);
176                    index++;
177                }
178            }
179            return buffer.toString();
180        }
181    
182        void generateMethods(String className, ArrayList<Node> methods) {
183            for (Node method : methods) {
184                NamedNodeMap mthAttributes = method.getAttributes();
185                String sel = mthAttributes.getNamedItem("selector").getNodeValue();
186                out("public ");
187                boolean isStatic = isStatic(method);
188                if (isStatic)
189                    out("static ");
190                Node returnNode = getReturnNode(method.getChildNodes());
191                if (getType(returnNode).equals("void"))
192                    returnNode = null;
193                String returnType = "", returnType64 = "";
194                if (returnNode != null) {
195                    String type = returnType = getJavaType(returnNode), type64 = returnType64 = getJavaType64(returnNode);
196                    out(type);
197                    if (!type.equals(type64)) {
198                        out(" /*");
199                        out(type64);
200                        out("*/");
201                    }
202                    out(" ");
203                } else {
204                    out("void ");
205                }
206                String methodName = sel;
207                if (isUnique(method, methods)) {
208                    int index = methodName.indexOf(":");
209                    if (index != -1)
210                        methodName = methodName.substring(0, index);
211                } else {
212                    // TODO improve this selector
213                    methodName = methodName.replaceAll(":", "_");
214                    if (isStatic)
215                        methodName = "static_" + methodName;
216                }
217                out(methodName);
218                out("(");
219                NodeList params = method.getChildNodes();
220                boolean first = true;
221                for (int k = 0; k < params.getLength(); k++) {
222                    Node param = params.item(k);
223                    if ("arg".equals(param.getNodeName())) {
224                        NamedNodeMap paramAttributes = param.getAttributes();
225                        if (!first)
226                            out(", ");
227                        String type = getJavaType(param), type64 = getJavaType64(param);
228                        out(type);
229                        if (!type.equals(type64)) {
230                            out(" /*");
231                            out(type64);
232                            out("*/");
233                        }
234                        first = false;
235                        out(" ");
236                        String paramName = paramAttributes.getNamedItem("name").getNodeValue();
237                        if (paramName.length() == 0)
238                            paramName = "arg" + paramAttributes.getNamedItem("index").getNodeValue();
239                        if (paramName.equals("boolean"))
240                            paramName = "b";
241                        out(paramName);
242                    }
243                }
244                out(") {");
245                outln();
246                if (returnNode != null && isStruct(returnNode)) {
247                    out("\t");
248                    out(returnType);
249                    out(" result = new ");
250                    out(returnType);
251                    out("();");
252                    outln();
253                    out("\tOS.objc_msgSend_stret(result, ");
254                } else if (returnNode != null && isBoolean(returnNode)) {
255                    out("\treturn ");
256                    out("OS.objc_msgSend_bool(");
257                } else if (returnNode != null && isFloatingPoint(returnNode)) {
258                    out("\treturn ");
259                    if (returnType.equals("float"))
260                        out("(float)");
261                    out("OS.objc_msgSend_fpret(");
262                } else if (returnNode != null && isObject(returnNode)) {
263                    out("\tint /*long*/ result = OS.objc_msgSend(");
264                } else {
265                    if (returnNode != null) {
266                        out("\treturn ");
267                        if ((returnType.equals("int") && returnType64.equals("int")) || !returnType.equals("int")) {
268                            out("(");
269                            out(returnType);
270                            out(")");
271                        }
272                        if (returnType.equals("int") && returnType64.equals("int")) {
273                            out("/*64*/");
274                        }
275                    } else {
276                        out("\t");
277                    }
278                    out("OS.objc_msgSend(");
279                }
280                if (isStatic) {
281                    out("OS.class_");
282                    out(className);
283                } else {
284                    out("this.id");
285                }
286                out(", OS.");
287                out(getSelConst(sel));
288                first = false;
289                for (int k = 0; k < params.getLength(); k++) {
290                    Node param = params.item(k);
291                    if ("arg".equals(param.getNodeName())) {
292                        NamedNodeMap paramAttributes = param.getAttributes();
293                        if (!first)
294                            out(", ");
295                        first = false;
296                        String paramName = paramAttributes.getNamedItem("name").getNodeValue();
297                        if (paramName.length() == 0)
298                            paramName = "arg" + paramAttributes.getNamedItem("index").getNodeValue();
299                        if (paramName.equals("boolean"))
300                            paramName = "b";
301                        if (isObject(param)) {
302                            out(paramName);
303                            out(" != null ? ");
304                            out(paramName);
305                            out(".id : 0");
306                        } else {
307                            out(paramName);
308                        }
309                    }
310                }
311                out(")");
312                out(";");
313                outln();
314                if (returnNode != null && isObject(returnNode)) {
315                    if (!isStatic && returnType.equals(className)) {
316                        out("\treturn result == this.id ? this : (result != 0 ? new ");
317                        out(returnType);
318                        out("(result) : null);");
319                    } else {
320                        out("\treturn result != 0 ? new ");
321                        NamedNodeMap attributes = returnNode.getAttributes();
322                        Node hawtjni_alloc = attributes.getNamedItem("hawtjni_alloc");
323                        if (hawtjni_alloc != null && hawtjni_alloc.getNodeValue().equals("true")) {
324                            out(className);
325                        } else {
326                            out(returnType);
327                        }
328                        out("(result) : null;");
329                    }
330                    outln();
331                } else if (returnNode != null && isStruct(returnNode)) {
332                    out("\treturn result;");
333                    outln();
334                }
335                out("}");
336                outln();
337                outln();
338            }
339        }
340    
341        void generateExtraMethods(String className) {
342            /* Empty constructor */
343            out("public ");
344            out(className);
345            out("() {");
346            outln();
347            out("\tsuper();");
348            outln();
349            out("}");
350            outln();
351            outln();
352            /* pointer constructor */
353            out("public ");
354            out(className);
355            out("(int /*long*/ id) {");
356            outln();
357            out("\tsuper(id);");
358            outln();
359            out("}");
360            outln();
361            outln();
362            /* object constructor */
363            out("public ");
364            out(className);
365            out("(id id) {");
366            outln();
367            out("\tsuper(id);");
368            outln();
369            out("}");
370            outln();
371            outln();
372            /* NSObject helpers */
373            if (className.equals("NSObject")) {
374                out("public NSObject alloc() {");
375                outln();
376                out("\tthis.id = OS.objc_msgSend(objc_getClass(), OS.sel_alloc);");
377                outln();
378                out("\treturn this;");
379                outln();
380                out("}");
381                outln();
382                outln();
383            }
384            /* NSString helpers */
385            if (className.equals("NSString")) {
386                /* Get java string */
387                out("public String getString() {");
388                outln();
389                out("\tchar[] buffer = new char[(int)/*64*/length()];");
390                outln();
391                out("\tgetCharacters(buffer);");
392                outln();
393                out("\treturn new String(buffer);");
394                outln();
395                out("}");
396                outln();
397                outln();
398                /* create NSString */
399                out("public NSString initWithString(String str) {");
400                outln();
401                out("\tchar[] buffer = new char[str.length()];");
402                outln();
403                out("\tstr.getChars(0, buffer.length, buffer, 0);");
404                outln();
405                out("\treturn initWithCharacters(buffer, buffer.length);");
406                outln();
407                out("}");
408                outln();
409                outln();
410                out("public static NSString stringWith(String str) {");
411                outln();
412                out("\tchar[] buffer = new char[str.length()];");
413                outln();
414                out("\tstr.getChars(0, buffer.length, buffer, 0);");
415                outln();
416                out("\treturn stringWithCharacters(buffer, buffer.length);");
417                outln();
418                out("}");
419                outln();
420                outln();
421            }
422        }
423    
424        static class NodeEntry {
425            private final Node parent;
426            private final ArrayList<Node> children;
427    
428            public NodeEntry(Node parent, ArrayList<Node> children) {
429                this.parent = parent;
430                this.children = children;
431            }
432        }
433        
434        TreeMap<String, NodeEntry> getGeneratedClasses() {
435            TreeMap<String, NodeEntry> classes = new TreeMap<String, NodeEntry>();
436            for (int x = 0; x < xmls.length; x++) {
437                Document document = documents[x];
438                if (document == null)
439                    continue;
440                NodeList list = document.getDocumentElement().getChildNodes();
441                for (int i = 0; i < list.getLength(); i++) {
442                    Node node = list.item(i);
443                    if ("class".equals(node.getNodeName()) && getGen(node)) {
444                        ArrayList<Node> methods;
445                        String name = node.getAttributes().getNamedItem("name").getNodeValue();
446                        NodeEntry clazz = classes.get(name);
447                        if (clazz == null) {
448                            methods = new ArrayList<Node>();
449                            classes.put(name, new NodeEntry(node, methods));
450                        } else {
451                            methods = clazz.children;
452                        }
453                        NodeList methodList = node.getChildNodes();
454                        for (int j = 0; j < methodList.getLength(); j++) {
455                            Node method = methodList.item(j);
456                            if ("method".equals(method.getNodeName()) && getGen(method)) {
457                                methods.add(method);
458                            }
459                        }
460                    }
461                }
462            }
463            return classes;
464        }
465    
466        void copyClassMethodsDown(final Map<String, NodeEntry> classes) {
467            ArrayList<NodeEntry> sortedClasses = Collections.list(Collections.enumeration(classes.values()));
468            Collections.sort(sortedClasses, new Comparator<NodeEntry>() {
469                int getHierarchyLevel(Node node) {
470                    String superclass = getSuperclassName(node);
471                    int level = 0;
472                    while (!superclass.equals("id")) {
473                        level++;
474                        superclass = getSuperclassName(classes.get(superclass).parent);
475                    }
476                    return level;
477                }
478    
479                public int compare(NodeEntry arg0, NodeEntry arg1) {
480                    return getHierarchyLevel(arg0.parent) - getHierarchyLevel(arg1.parent);
481                }
482            });
483            for (NodeEntry clazz : sortedClasses) {
484                Node node = (Node) clazz.parent;
485                ArrayList<Node> methods = (ArrayList<Node>) clazz.children;
486                NodeEntry superclass = classes.get(getSuperclassName(node));
487                if (superclass != null) {
488                    for (Node method : superclass.children) {
489                        if (isStatic(method)) {
490                            methods.add(method);
491                        }
492                    }
493                }
494            }
495        }
496    
497        String getSuperclassName(Node node) {
498            NamedNodeMap attributes = node.getAttributes();
499            Node superclass = attributes.getNamedItem("hawtjni_superclass");
500            if (superclass != null) {
501                return superclass.getNodeValue();
502            } else {
503                Node name = attributes.getNamedItem("name");
504                if (name.getNodeValue().equals("NSObject")) {
505                    return "id";
506                } else {
507                    return "NSObject";
508                }
509            }
510        }
511    
512        void generateClasses() {
513            TreeMap<String, NodeEntry> classes = getGeneratedClasses();
514            copyClassMethodsDown(classes);
515    
516            Set<String> classNames = classes.keySet();
517            for (Iterator<String> iterator = classNames.iterator(); iterator.hasNext();) {
518                ByteArrayOutputStream out = new ByteArrayOutputStream();
519                this.out = new PrintStream(out);
520    
521    //            out(fixDelimiter(metaData.getCopyright()));
522    
523                String className = iterator.next();
524                NodeEntry clazz = classes.get(className);
525                Node node = clazz.parent;
526                ArrayList<Node> methods = clazz.children;
527                out("package ");
528                String packageName = getPackageName(mainClassName);
529                out(packageName);
530                out(";");
531                outln();
532                outln();
533                out("public class ");
534                out(className);
535                out(" extends ");
536                out(getSuperclassName(node));
537                out(" {");
538                outln();
539                outln();
540                generateExtraMethods(className);
541                generateMethods(className, methods);
542                out("}");
543                outln();
544    
545                String fileName = outputDir + packageName.replace('.', '/') + "/" + className + ".java";
546                try {
547                    out.flush();
548                    if (out.size() > 0) {
549                        FileSupport.write(out.toByteArray(), new File(fileName));
550                    }
551                } catch (Exception e) {
552                    System.out.println("Problem");
553                    e.printStackTrace(System.out);
554                }
555                out = null;
556            }
557        }
558    
559        void generateExtraAttributes() {
560            Document[] documents = getDocuments();
561            for (int x = 0; x < xmls.length; x++) {
562                Document document = documents[x];
563                if (document == null || !getGen(document.getDocumentElement()))
564                    continue;
565                saveExtraAttributes(xmls[x], document);
566            }
567        }
568    
569        void generateMainClass() {
570            ByteArrayOutputStream out = new ByteArrayOutputStream();
571            this.out = new PrintStream(out);
572    
573            String header = "", footer = "";
574            String fileName = outputDir + mainClassName.replace('.', '/') + ".java";
575            FileInputStream is = null;
576            try {
577                InputStreamReader input = new InputStreamReader(new BufferedInputStream(is = new FileInputStream(fileName)));
578                StringBuffer str = new StringBuffer();
579                char[] buffer = new char[4096];
580                int read;
581                while ((read = input.read(buffer)) != -1) {
582                    str.append(buffer, 0, read);
583                }
584                String section = "/** This section is auto generated */";
585                int start = str.indexOf(section) + section.length();
586                int end = str.indexOf(section, start);
587                header = str.substring(0, start);
588                footer = str.substring(end);
589            } catch (IOException e) {
590            } finally {
591                try {
592                    if (is != null)
593                        is.close();
594                } catch (IOException e) {
595                }
596            }
597    
598            out(header);
599            outln();
600            outln();
601    
602            out("/** Custom callbacks */");
603            outln();
604            generateCustomCallbacks();
605            outln();
606            out("/** Classes */");
607            outln();
608            generateClassesConst();
609            outln();
610            out("/** Protocols */");
611            outln();
612            generateProtocolsConst();
613            outln();
614            out("/** Selectors */");
615            outln();
616            generateSelectorsConst();
617            outln();
618            out("/** Constants */");
619            outln();
620            generateEnums();
621            outln();
622            out("/** Globals */");
623            outln();
624            generateConstants();
625            outln();
626            out("/** Functions */");
627            outln();
628            outln();
629            generateFunctions();
630            outln();
631            out("/** Super Sends */");
632            outln();
633            generateSends(true);
634            outln();
635            out("/** Sends */");
636            outln();
637            generateSends(false);
638            outln();
639            generateStructNatives();
640    
641            outln();
642            out(footer);
643            try {
644                out.flush();
645                if (out.size() > 0) {
646                    FileSupport.write(out.toByteArray(), new File(fileName));
647                    
648                }
649            } catch (Exception e) {
650                System.out.println("Problem");
651                e.printStackTrace(System.out);
652            }
653        }
654    
655        public Document[] getDocuments() {
656            if (documents == null) {
657                String[] xmls = getXmls();
658                documents = new Document[xmls.length];
659                for (int i = 0; i < xmls.length; i++) {
660                    String xmlPath = xmls[i];
661                    Document document = documents[i] = getDocument(xmlPath);
662                    if (document == null)
663                        continue;
664                    if (mainClassName != null && outputDir != null) {
665                        String packageName = getPackageName(mainClassName);
666                        String extrasPath = outputDir + packageName.replace('.', '/') + "/" + getFileName(xmlPath) + ".extras";
667                        merge(document, getDocument(extrasPath));
668                    }
669                }
670            }
671            return documents;
672        }
673    
674        public String[] getXmls() {
675            if (xmls == null || xmls.length == 0) {
676                ArrayList<String> array = new ArrayList<String>();
677                list(new File("/System/Library/Frameworks"), array);
678                list(new File("/System/Library/Frameworks/CoreServices.framework/Frameworks"), array);
679                list(new File("/System/Library/Frameworks/ApplicationServices.framework/Frameworks"), array);
680                Collections.sort(array, new Comparator<String>() {
681                    public int compare(String o1, String o2) {
682                        return new File(o1).getName().compareTo(new File(o2).getName());
683                    }
684                });
685                xmls = array.toArray(new String[array.size()]);
686            }
687            return xmls;
688        }
689    
690        void saveExtraAttributes(String xmlPath, Document document) {
691            try {
692                String packageName = getPackageName(mainClassName);
693                String fileName = outputDir + packageName.replace('.', '/') + "/" + getFileName(xmlPath) + ".extras";
694                ByteArrayOutputStream out = new ByteArrayOutputStream();
695                DOMWriter writer = new DOMWriter(new PrintStream(out));
696                String[] names = getIDAttributeNames();
697                String[] filter = new String[names.length + 2];
698                filter[0] = "class_method";
699                filter[1] = "hawtjni_.*";
700                System.arraycopy(names, 0, filter, 2, names.length);
701                writer.setAttributeFilter(filter);
702                writer.setNodeFilter("hawtjni_");
703                writer.print(document);
704                if (out.size() > 0) {
705                    FileSupport.write(out.toByteArray(), new File(fileName));
706                }
707            } catch (Exception e) {
708                System.out.println("Problem");
709                e.printStackTrace(System.out);
710            }
711        }
712    
713        public void setOutputDir(String dir) {
714            if (dir != null) {
715                if (!dir.endsWith("\\") && !dir.endsWith("/")) {
716                    dir += "/";
717                }
718            }
719            this.outputDir = dir;
720        }
721    
722        public void setXmls(String[] xmls) {
723            this.xmls = xmls;
724            this.documents = null;
725        }
726    
727        public void setMainClass(String mainClassName) {
728            this.mainClassName = mainClassName;
729        }
730    
731        Document getDocument(String xmlPath) {
732            try {
733                InputStream is = null;
734                if (xmlPath.indexOf(File.separatorChar) == -1)
735                    is = getClass().getResourceAsStream(xmlPath);
736                if (is == null)
737                    is = new BufferedInputStream(new FileInputStream(xmlPath));
738                if (is != null)
739                    return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(is));
740            } catch (Exception e) {
741                // e.printStackTrace();
742            }
743            return null;
744        }
745    
746        public String[] getExtraAttributeNames(Node node) {
747            String name = node.getNodeName();
748            if (name.equals("method")) {
749                return new String[] { "hawtjni_gen_super_msgSend", "hawtjni_gen_custom_callback" };
750            } else if (name.equals("function")) {
751                NamedNodeMap attribs = node.getAttributes();
752                if (attribs != null && attribs.getNamedItem("variadic") != null) {
753                    return new String[] { "hawtjni_variadic_count", "hawtjni_variadic_java_types" };
754                }
755            } else if (name.equals("class")) {
756                return new String[] { "hawtjni_superclass" };
757            } else if (name.equals("retval")) {
758                return new String[] { "hawtjni_java_type", "hawtjni_java_type64", "hawtjni_alloc" };
759            } else if (name.equals("arg")) {
760                return new String[] { "hawtjni_java_type", "hawtjni_java_type64" };
761            }
762            return new String[0];
763        }
764    
765        public String getFileName(String xmlPath) {
766            File file = new File(xmlPath);
767            return file.getName();
768        }
769    
770        String getKey(Node node) {
771            StringBuffer buffer = new StringBuffer();
772            while (node != null) {
773                if (buffer.length() > 0)
774                    buffer.append("_");
775                String name = node.getNodeName();
776                StringBuffer key = new StringBuffer(name);
777                Node nameAttrib = getIDAttribute(node);
778                if (nameAttrib != null) {
779                    key.append("-");
780                    key.append(nameAttrib.getNodeValue());
781                }
782                NamedNodeMap attributes = node.getAttributes();
783                if (attributes != null) {
784                    boolean isStatic = attributes.getNamedItem("class_method") != null;
785                    if (isStatic)
786                        key.append("-static");
787                }
788                buffer.append(key.reverse());
789                node = node.getParentNode();
790            }
791            buffer.reverse();
792            return buffer.toString();
793        }
794    
795        public Node getIDAttribute(Node node) {
796            NamedNodeMap attributes = node.getAttributes();
797            if (attributes == null)
798                return null;
799            String[] names = getIDAttributeNames();
800            for (int i = 0; i < names.length; i++) {
801                Node nameAttrib = attributes.getNamedItem(names[i]);
802                if (nameAttrib != null)
803                    return nameAttrib;
804            }
805            return null;
806        }
807    
808        public String[] getIDAttributeNames() {
809            return new String[] { "name", "selector", "path", };
810        }
811    
812        void merge(Node node, HashMap<String, Node> extras, HashMap<String, Node> docLookup) {
813            NodeList list = node.getChildNodes();
814            for (int i = 0; i < list.getLength(); i++) {
815                Node childNode = list.item(i);
816                if (childNode.getNodeType() == Node.ELEMENT_NODE) {
817                    String key = getKey(childNode);
818                    if (docLookup != null && docLookup.get(key) == null) {
819                        docLookup.put(key, childNode);
820                    }
821                    Node extra = extras.remove(key);
822                    if (extra != null) {
823                        NamedNodeMap attributes = extra.getAttributes();
824                        for (int j = 0, length = attributes.getLength(); j < length; j++) {
825                            Node attr = (Node) attributes.item(j);
826                            String name = attr.getNodeName();
827                            if (name.startsWith("hawtjni_")) {
828                                ((Element) childNode).setAttribute(name, attr.getNodeValue());
829                            }
830                        }
831                    }
832                }
833                merge(childNode, extras, docLookup);
834            }
835        }
836    
837        void out(String str) {
838            PrintStream out = this.out;
839            if (out == null)
840                out = System.out;
841            out.print(str);
842        }
843    
844        void outln() {
845            PrintStream out = this.out;
846            if (out == null)
847                out = System.out;
848            out.println();
849        }
850    
851        void generateConstants() {
852            for (int x = 0; x < xmls.length; x++) {
853                Document document = documents[x];
854                if (document == null)
855                    continue;
856                NodeList list = document.getDocumentElement().getChildNodes();
857                for (int i = 0; i < list.getLength(); i++) {
858                    Node node = list.item(i);
859                    if ("constant".equals(node.getNodeName())) {
860                        if (getGen(node)) {
861                            NamedNodeMap attributes = node.getAttributes();
862                            String constName = attributes.getNamedItem("name").getNodeValue();
863                            out("/** @method flags=const */");
864                            outln();
865                            out("public static final native ");
866                            String type = getType(node), type64 = getType64(node);
867                            out(type);
868                            if (!type.equals(type64)) {
869                                out(" /*");
870                                out(type64);
871                                out("*/");
872                            }
873                            out(" ");
874                            out(constName);
875                            out("();");
876                            outln();
877                            if (attributes.getNamedItem("declared_type").getNodeValue().equals("NSString*")) {
878                                out("public static final NSString ");
879                                out(constName);
880                                out(" = new NSString(");
881                                out(constName);
882                                out("());");
883                                outln();
884                            }
885                        }
886                    }
887                }
888            }
889        }
890    
891        void generateEnums() {
892            for (int x = 0; x < xmls.length; x++) {
893                Document document = documents[x];
894                if (document == null)
895                    continue;
896                NodeList list = document.getDocumentElement().getChildNodes();
897                for (int i = 0; i < list.getLength(); i++) {
898                    Node node = list.item(i);
899                    if ("enum".equals(node.getNodeName())) {
900                        if (getGen(node)) {
901                            NamedNodeMap attributes = node.getAttributes();
902                            Node valueNode = attributes.getNamedItem("value");
903                            if (valueNode != null) {
904                                String value = valueNode.getNodeValue();
905                                out("public static final ");
906                                boolean isLong = false;
907                                if (value.indexOf('.') != -1) {
908                                    out("double ");
909                                } else {
910                                    try {
911                                        Integer.parseInt(value);
912                                        out("int ");
913                                    } catch (NumberFormatException e) {
914                                        isLong = true;
915                                        out("long ");
916                                    }
917                                }
918                                out(attributes.getNamedItem("name").getNodeValue());
919                                out(" = ");
920                                out(value);
921                                if (isLong && !value.endsWith("L"))
922                                    out("L");
923                                out(";");
924                                outln();
925                            }
926                        }
927                    }
928                }
929            }
930        }
931    
932        boolean getGen(Node node) {
933            NamedNodeMap attributes = node.getAttributes();
934            if (attributes == null)
935                return false;
936            Node gen = attributes.getNamedItem("hawtjni_gen");
937            return gen != null && !gen.getNodeValue().equals("false");
938        }
939    
940        boolean getGenSuper(Node node) {
941            NamedNodeMap attributes = node.getAttributes();
942            if (attributes == null)
943                return false;
944            Node gen = attributes.getNamedItem("hawtjni_gen_super_msgSend");
945            return gen != null && !gen.getNodeValue().equals("false");
946        }
947    
948        boolean getGenCallback(Node node) {
949            NamedNodeMap attributes = node.getAttributes();
950            if (attributes == null)
951                return false;
952            Node gen = attributes.getNamedItem("hawtjni_gen_custom_callback");
953            return gen != null && !gen.getNodeValue().equals("false");
954        }
955    
956        boolean isStatic(Node node) {
957            NamedNodeMap attributes = node.getAttributes();
958            Node isStatic = attributes.getNamedItem("class_method");
959            return isStatic != null && isStatic.getNodeValue().equals("true");
960        }
961    
962        boolean isStruct(Node node) {
963            NamedNodeMap attributes = node.getAttributes();
964            String code = attributes.getNamedItem("type").getNodeValue();
965            return code.startsWith("{");
966        }
967    
968        boolean isFloatingPoint(Node node) {
969            NamedNodeMap attributes = node.getAttributes();
970            String code = attributes.getNamedItem("type").getNodeValue();
971            return code.equals("f") || code.equals("d");
972        }
973    
974        boolean isObject(Node node) {
975            NamedNodeMap attributes = node.getAttributes();
976            String code = attributes.getNamedItem("type").getNodeValue();
977            return code.equals("@");
978        }
979    
980        boolean isBoolean(Node node) {
981            NamedNodeMap attributes = node.getAttributes();
982            String code = attributes.getNamedItem("type").getNodeValue();
983            return code.equals("B");
984        }
985    
986        void buildLookup(Node node, HashMap<String, Node> table) {
987            NodeList list = node.getChildNodes();
988            for (int i = 0; i < list.getLength(); i++) {
989                Node childNode = list.item(i);
990                if (childNode.getNodeType() == Node.ELEMENT_NODE) {
991                    String key = getKey(childNode);
992                    if (table.get(key) == null)
993                        table.put(key, childNode);
994                    buildLookup(childNode, table);
995                }
996            }
997        }
998    
999        boolean isUnique(Node method, ArrayList<Node> methods) {
1000            String methodName = method.getAttributes().getNamedItem("selector").getNodeValue();
1001            String signature = "";
1002            NodeList params = method.getChildNodes();
1003            for (int k = 0; k < params.getLength(); k++) {
1004                Node param = params.item(k);
1005                if ("arg".equals(param.getNodeName())) {
1006                    signature += getJavaType(param);
1007                }
1008            }
1009            int index = methodName.indexOf(":");
1010            if (index != -1)
1011                methodName = methodName.substring(0, index);
1012            for (Node node : methods) {
1013                NamedNodeMap attributes = node.getAttributes();
1014                Node otherSel = null;
1015                if (attributes != null)
1016                    otherSel = attributes.getNamedItem("selector");
1017                if (node != method && otherSel != null) {
1018                    String otherName = otherSel.getNodeValue();
1019                    index = otherName.indexOf(":");
1020                    if (index != -1)
1021                        otherName = otherName.substring(0, index);
1022                    if (methodName.equals(otherName)) {
1023                        NodeList otherParams = node.getChildNodes();
1024                        String otherSignature = "";
1025                        for (int k = 0; k < otherParams.getLength(); k++) {
1026                            Node param = otherParams.item(k);
1027                            if ("arg".equals(param.getNodeName())) {
1028                                otherSignature += getJavaType(param);
1029                            }
1030                        }
1031                        if (signature.equals(otherSignature)) {
1032                            return false;
1033                        }
1034                    }
1035                }
1036            }
1037            return true;
1038        }
1039    
1040        void generateSelectorsConst() {
1041            TreeSet<String> set = new TreeSet<String>();
1042            for (int x = 0; x < xmls.length; x++) {
1043                Document document = documents[x];
1044                if (document == null)
1045                    continue;
1046                NodeList list = document.getDocumentElement().getChildNodes();
1047                for (int i = 0; i < list.getLength(); i++) {
1048                    Node node = list.item(i);
1049                    if ("class".equals(node.getNodeName()) || "informal_protocol".equals(node.getNodeName())) {
1050                        if (getGen(node)) {
1051                            NodeList methods = node.getChildNodes();
1052                            for (int j = 0; j < methods.getLength(); j++) {
1053                                Node method = methods.item(j);
1054                                if (getGen(method)) {
1055                                    NamedNodeMap mthAttributes = method.getAttributes();
1056                                    String sel = mthAttributes.getNamedItem("selector").getNodeValue();
1057                                    set.add(sel);
1058                                }
1059                            }
1060                        }
1061                    }
1062                }
1063            }
1064            set.add("alloc");
1065            for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
1066                String sel = iterator.next();
1067                String selConst = getSelConst(sel);
1068                out("public static final int /*long*/ ");
1069                out(selConst);
1070                out(" = ");
1071                out("sel_registerName(\"");
1072                out(sel);
1073                out("\");");
1074                outln();
1075            }
1076        }
1077    
1078        void generateStructNatives() {
1079            TreeSet<String> set = new TreeSet<String>();
1080            for (int x = 0; x < xmls.length; x++) {
1081                Document document = documents[x];
1082                if (document == null)
1083                    continue;
1084                NodeList list = document.getDocumentElement().getChildNodes();
1085                for (int i = 0; i < list.getLength(); i++) {
1086                    Node node = list.item(i);
1087                    if ("struct".equals(node.getNodeName()) && getGen(node)) {
1088                        set.add(getIDAttribute(node).getNodeValue());
1089                    }
1090                }
1091            }
1092            out("/** Sizeof natives */");
1093            outln();
1094            for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
1095                String struct = iterator.next();
1096                out("public static final native int ");
1097                out(struct);
1098                out("_sizeof();");
1099                outln();
1100            }
1101            outln();
1102            out("/** Memmove natives */");
1103            outln();
1104            outln();
1105            for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
1106                String struct = iterator.next();
1107                out("/**");
1108                outln();
1109                out(" * @param dest cast=(void *),flags=no_in critical");
1110                outln();
1111                out(" * @param src cast=(void *),flags=critical");
1112                // out(" * @param src cast=(void *),flags=no_out critical");
1113                outln();
1114                out(" */");
1115                outln();
1116                out("public static final native void memmove(");
1117                out("int /*long*/ dest, ");
1118                out(struct);
1119                out(" src, int /*long*/ size);");
1120                outln();
1121                out("/**");
1122                outln();
1123                out(" * @param dest cast=(void *),flags=no_in critical");
1124                outln();
1125                out(" * @param src cast=(void *),flags=critical");
1126                // out(" * @param src cast=(void *),flags=no_out critical");
1127                outln();
1128                out(" */");
1129                outln();
1130                out("public static final native void memmove(");
1131                out(struct);
1132                out(" dest, int /*long*/ src, int /*long*/ size);");
1133                outln();
1134            }
1135        }
1136    
1137        String buildSend(Node method, boolean tags, boolean only64, boolean superCall) {
1138            Node returnNode = getReturnNode(method.getChildNodes());
1139            StringBuffer buffer = new StringBuffer();
1140            buffer.append("public static final native ");
1141            if (returnNode != null && isStruct(returnNode)) {
1142                buffer.append("void ");
1143                buffer.append(superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret");
1144                buffer.append("(");
1145                buffer.append(getJavaType(returnNode));
1146                buffer.append(" result, ");
1147            } else if (returnNode != null && isFloatingPoint(returnNode)) {
1148                buffer.append("double ");
1149                buffer.append(superCall ? "objc_msgSendSuper_fpret" : "objc_msgSend_fpret");
1150                buffer.append("(");
1151            } else if (returnNode != null && isBoolean(returnNode)) {
1152                buffer.append("boolean ");
1153                buffer.append(superCall ? "objc_msgSendSuper_bool" : "objc_msgSend_bool");
1154                buffer.append("(");
1155            } else {
1156                if (only64) {
1157                    buffer.append("long");
1158                } else {
1159                    if (tags) {
1160                        buffer.append("int /*long*/");
1161                    } else {
1162                        buffer.append("int");
1163                    }
1164                }
1165                buffer.append(" ");
1166                buffer.append(superCall ? "objc_msgSendSuper" : "objc_msgSend");
1167                buffer.append("(");
1168            }
1169            if (superCall) {
1170                if (only64) {
1171                    buffer.append("objc_super superId, long sel");
1172                } else {
1173                    if (tags) {
1174                        buffer.append("objc_super superId, int /*long*/ sel");
1175                    } else {
1176                        buffer.append("objc_super superId, int sel");
1177                    }
1178                }
1179            } else {
1180                if (only64) {
1181                    buffer.append("long id, long sel");
1182                } else {
1183                    if (tags) {
1184                        buffer.append("int /*long*/ id, int /*long*/ sel");
1185                    } else {
1186                        buffer.append("int id, int sel");
1187                    }
1188                }
1189            }
1190            NodeList params = method.getChildNodes();
1191            boolean first = false;
1192            int count = 0;
1193            for (int k = 0; k < params.getLength(); k++) {
1194                Node param = params.item(k);
1195                if ("arg".equals(param.getNodeName())) {
1196                    if (!first)
1197                        buffer.append(", ");
1198                    if (isStruct(param)) {
1199                        buffer.append(getJavaType(param));
1200                    } else {
1201                        String type = getType(param), type64 = getType64(param);
1202                        buffer.append(only64 ? type64 : type);
1203                        if (!only64 && tags && !type.equals(type64)) {
1204                            buffer.append(" /*");
1205                            buffer.append(type64);
1206                            buffer.append("*/");
1207                        }
1208                    }
1209                    first = false;
1210                    buffer.append(" arg");
1211                    buffer.append(String.valueOf(count++));
1212                }
1213            }
1214            buffer.append(");");
1215            return buffer.toString();
1216        }
1217    
1218        String getCType(Node node) {
1219            NamedNodeMap attributes = node.getAttributes();
1220            return attributes.getNamedItem("declared_type").getNodeValue();
1221        }
1222    
1223        Node findNSObjectMethod(Node method) {
1224            NamedNodeMap methodAttributes = method.getAttributes();
1225            String selector = methodAttributes.getNamedItem("selector").getNodeValue();
1226            NodeList list = method.getParentNode().getParentNode().getChildNodes();
1227            for (int i = 0; i < list.getLength(); i++) {
1228                Node cls = list.item(i);
1229                if ("class".equals(cls.getNodeName())) {
1230                    NamedNodeMap classAttributes = cls.getAttributes();
1231                    if ("NSObject".equals(classAttributes.getNamedItem("name").getNodeValue())) {
1232                        NodeList methods = cls.getChildNodes();
1233                        for (int j = 0; j < methods.getLength(); j++) {
1234                            Node mth = methods.item(j);
1235                            if ("method".equals(mth.getNodeName())) {
1236                                NamedNodeMap mthAttributes = mth.getAttributes();
1237                                if (selector.equals(mthAttributes.getNamedItem("selector").getNodeValue())) {
1238                                    return mth;
1239                                }
1240                            }
1241                        }
1242                    }
1243                }
1244            }
1245            return null;
1246        }
1247    
1248        void generateCustomCallbacks() {
1249            TreeMap<String, Node> set = new TreeMap<String, Node>();
1250            for (int x = 0; x < xmls.length; x++) {
1251                Document document = documents[x];
1252                if (document == null)
1253                    continue;
1254                NodeList list = document.getDocumentElement().getChildNodes();
1255                for (int i = 0; i < list.getLength(); i++) {
1256                    Node node = list.item(i);
1257                    if (("class".equals(node.getNodeName()) || "informal_protocol".equals(node.getNodeName())) && getGen(node)) {
1258                        NodeList methods = node.getChildNodes();
1259                        for (int j = 0; j < methods.getLength(); j++) {
1260                            Node method = methods.item(j);
1261                            if ("method".equals(method.getNodeName()) && getGen(method) && getGenCallback(method)) {
1262                                NamedNodeMap mthAttributes = method.getAttributes();
1263                                String sel = mthAttributes.getNamedItem("selector").getNodeValue();
1264                                set.put(sel, method);
1265                            }
1266                        }
1267                    }
1268                }
1269            }
1270            for (Iterator<String> iterator = set.keySet().iterator(); iterator.hasNext();) {
1271                String key = iterator.next();
1272                Node method = set.get(key);
1273                if ("informal_protocol".equals(method.getParentNode().getNodeName())) {
1274                    method = findNSObjectMethod(method);
1275                    if (method == null)
1276                        continue;
1277                }
1278                String nativeMth = key.replaceAll(":", "_");
1279                out("/** @method callback_types=");
1280                Node returnNode = getReturnNode(method.getChildNodes());
1281                out(returnNode == null ? "void" : getCType(returnNode));
1282                out(";id;SEL;");
1283                NodeList params = method.getChildNodes();
1284                for (int k = 0; k < params.getLength(); k++) {
1285                    Node param = params.item(k);
1286                    if ("arg".equals(param.getNodeName())) {
1287                        out(getCType(param));
1288                        out(";");
1289                    }
1290                }
1291                out(",callback_flags=");
1292                out(returnNode != null && isStruct(returnNode) ? "struct" : "none");
1293                out(";none;none;");
1294                for (int k = 0; k < params.getLength(); k++) {
1295                    Node param = params.item(k);
1296                    if ("arg".equals(param.getNodeName())) {
1297                        out(isStruct(param) ? "struct" : "none");
1298                        out(";");
1299                    }
1300                }
1301                out(" */");
1302                outln();
1303                out("public static final native int /*long*/ CALLBACK_");
1304                out(nativeMth);
1305                out("(int /*long*/ func);");
1306                outln();
1307            }
1308        }
1309    
1310        void generateSends(boolean superCall) {
1311            TreeMap<String, Node> set = new TreeMap<String, Node>();
1312            TreeMap<String, Node> set64 = new TreeMap<String, Node>();
1313            for (int x = 0; x < xmls.length; x++) {
1314                Document document = documents[x];
1315                if (document == null)
1316                    continue;
1317                NodeList list = document.getDocumentElement().getChildNodes();
1318                for (int i = 0; i < list.getLength(); i++) {
1319                    Node node = list.item(i);
1320                    if ("class".equals(node.getNodeName()) && getGen(node)) {
1321                        NodeList methods = node.getChildNodes();
1322                        for (int j = 0; j < methods.getLength(); j++) {
1323                            Node method = methods.item(j);
1324                            if ("method".equals(method.getNodeName()) && getGen(method) && (!superCall || getGenSuper(method))) {
1325                                String code = buildSend(method, false, false, superCall);
1326                                String code64 = buildSend(method, false, true, superCall);
1327                                if (set.get(code) == null) {
1328                                    set.put(code, method);
1329                                }
1330                                if (set64.get(code64) == null) {
1331                                    set64.put(code64, method);
1332                                }
1333                            }
1334                        }
1335                    }
1336                }
1337            }
1338            outln();
1339            TreeMap<String, Node> tagsSet = new TreeMap<String, Node>();
1340            for (Iterator<String> iterator = set.keySet().iterator(); iterator.hasNext();) {
1341                String key = iterator.next();
1342                Node method = set.get(key);
1343                String tagCode = buildSend(method, false, true, superCall);
1344                if (set64.get(tagCode) != null) {
1345                    tagsSet.put(key, method);
1346                    iterator.remove();
1347                    set64.remove(tagCode);
1348                }
1349            }
1350            TreeMap<String, Node> all = new TreeMap<String, Node>();
1351            for (Iterator<String> iterator = tagsSet.keySet().iterator(); iterator.hasNext();) {
1352                String key = iterator.next();
1353                Node method = tagsSet.get(key);
1354                all.put(buildSend(method, true, false, superCall), method);
1355            }
1356            for (Iterator<String> iterator = set.keySet().iterator(); iterator.hasNext();) {
1357                String key = iterator.next();
1358                all.put(key, set.get(key));
1359            }
1360            for (Iterator<String> iterator = set64.keySet().iterator(); iterator.hasNext();) {
1361                String key = iterator.next();
1362                all.put(key, set64.get(key));
1363            }
1364            for (Iterator<String> iterator = all.keySet().iterator(); iterator.hasNext();) {
1365                String key = iterator.next();
1366                Node method = all.get(key);
1367                NodeList params = method.getChildNodes();
1368                ArrayList<String> tags = new ArrayList<String>();
1369                int count = 0;
1370                for (int k = 0; k < params.getLength(); k++) {
1371                    Node param = params.item(k);
1372                    if ("arg".equals(param.getNodeName())) {
1373                        if (isStruct(param)) {
1374                            tags.add(" * @param arg" + count + " flags=struct");
1375                        }
1376                        count++;
1377                    }
1378                }
1379                out("/**");
1380                if (tags.size() > 0) {
1381                    outln();
1382                    out(" *");
1383                }
1384                out(" @method flags=cast");
1385                if (tags.size() > 0)
1386                    outln();
1387                for (String tag : tags) {
1388                    out(tag);
1389                    outln();
1390                }
1391                out(" */");
1392                outln();
1393                out(key.toString());
1394                outln();
1395            }
1396        }
1397    
1398        String getSelConst(String sel) {
1399            return "sel_" + sel.replaceAll(":", "_");
1400        }
1401    
1402        void generateClassesConst() {
1403            TreeSet<String> set = new TreeSet<String>();
1404            for (int x = 0; x < xmls.length; x++) {
1405                Document document = documents[x];
1406                if (document == null)
1407                    continue;
1408                NodeList list = document.getDocumentElement().getChildNodes();
1409                for (int i = 0; i < list.getLength(); i++) {
1410                    Node node = list.item(i);
1411                    if ("class".equals(node.getNodeName())) {
1412                        if (getGen(node)) {
1413                            NamedNodeMap attributes = node.getAttributes();
1414                            String name = attributes.getNamedItem("name").getNodeValue();
1415                            set.add(name);
1416                        }
1417                    }
1418                }
1419            }
1420            for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
1421                String cls = iterator.next();
1422                String clsConst = "class_" + cls;
1423                out("public static final int /*long*/ ");
1424                out(clsConst);
1425                out(" = ");
1426                out("objc_getClass(\"");
1427                out(cls);
1428                out("\");");
1429                outln();
1430            }
1431        }
1432    
1433        void generateProtocolsConst() {
1434            TreeSet<String> set = new TreeSet<String>();
1435            for (int x = 0; x < xmls.length; x++) {
1436                Document document = documents[x];
1437                if (document == null)
1438                    continue;
1439                NodeList list = document.getDocumentElement().getChildNodes();
1440                for (int i = 0; i < list.getLength(); i++) {
1441                    Node node = list.item(i);
1442                    if ("informal_protocol".equals(node.getNodeName())) {
1443                        if (getGen(node)) {
1444                            NamedNodeMap attributes = node.getAttributes();
1445                            String name = attributes.getNamedItem("name").getNodeValue();
1446                            set.add(name);
1447                        }
1448                    }
1449                }
1450            }
1451            for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
1452                String cls = iterator.next();
1453                String clsConst = "protocol_" + cls;
1454                out("public static final int /*long*/ ");
1455                out(clsConst);
1456                out(" = ");
1457                out("objc_getProtocol(\"");
1458                out(cls);
1459                out("\");");
1460                outln();
1461            }
1462        }
1463    
1464        String getPackageName(String className) {
1465            int dot = mainClassName.lastIndexOf('.');
1466            if (dot == -1)
1467                return "";
1468            return mainClassName.substring(0, dot);
1469        }
1470    
1471        String getClassName(String className) {
1472            int dot = mainClassName.lastIndexOf('.');
1473            if (dot == -1)
1474                return mainClassName;
1475            return mainClassName.substring(dot + 1);
1476        }
1477    
1478        Node getReturnNode(NodeList list) {
1479            for (int j = 0; j < list.getLength(); j++) {
1480                Node node = list.item(j);
1481                if ("retval".equals(node.getNodeName())) {
1482                    return node;
1483                }
1484            }
1485            return null;
1486        }
1487    
1488        String getType(Node node) {
1489            NamedNodeMap attributes = node.getAttributes();
1490            Node javaType = attributes.getNamedItem("hawtjni_java_type");
1491            if (javaType != null)
1492                return javaType.getNodeValue();
1493            String code = attributes.getNamedItem("type").getNodeValue();
1494            return getType(code, attributes, false);
1495        }
1496    
1497        String getType64(Node node) {
1498            NamedNodeMap attributes = node.getAttributes();
1499            Node javaType = attributes.getNamedItem("hawtjni_java_type");
1500            if (javaType != null) {
1501                Node javaType64 = attributes.getNamedItem("hawtjni_java_type64");
1502                return javaType64 != null ? javaType64.getNodeValue() : javaType.getNodeValue();
1503            }
1504            Node attrib = attributes.getNamedItem("type");
1505            String code = attrib.getNodeValue();
1506            Node attrib64 = attributes.getNamedItem("type64");
1507            if (attrib64 != null)
1508                code = attrib64.getNodeValue();
1509            return getType(code, attributes, true);
1510        }
1511    
1512        String getType(String code, NamedNodeMap attributes, boolean is64) {
1513            if (code.equals("c"))
1514                return "byte";
1515            if (code.equals("i"))
1516                return "int";
1517            if (code.equals("s"))
1518                return "short";
1519            if (code.equals("l"))
1520                return "int";
1521            if (code.equals("q"))
1522                return "long";
1523            if (code.equals("C"))
1524                return "byte";
1525            if (code.equals("I"))
1526                return "int";
1527            if (code.equals("S"))
1528                return "short";
1529            if (code.equals("L"))
1530                return "int";
1531            if (code.equals("Q"))
1532                return "long";
1533            if (code.equals("f"))
1534                return "float";
1535            if (code.equals("d"))
1536                return "double";
1537            if (code.equals("B"))
1538                return "boolean";
1539            if (code.equals("v"))
1540                return "void";
1541            if (code.equals("*"))
1542                return is64 ? "long" : "int";
1543            if (code.equals("@"))
1544                return is64 ? "long" : "int";
1545            if (code.equals("#"))
1546                return is64 ? "long" : "int";
1547            if (code.equals(":"))
1548                return is64 ? "long" : "int";
1549            if (code.startsWith("^"))
1550                return is64 ? "long" : "int";
1551            if (code.startsWith("{")) {
1552                return attributes.getNamedItem("declared_type").getNodeValue();
1553            }
1554            return "BAD " + code;
1555        }
1556    
1557        String getJNIType(Node node) {
1558            NamedNodeMap attributes = node.getAttributes();
1559            String code = attributes.getNamedItem("type").getNodeValue();
1560            if (code.equals("c"))
1561                return "B";
1562            if (code.equals("i"))
1563                return "I";
1564            if (code.equals("s"))
1565                return "S";
1566            if (code.equals("l"))
1567                return "I";
1568            if (code.equals("q"))
1569                return "J";
1570            if (code.equals("C"))
1571                return "B";
1572            if (code.equals("I"))
1573                return "I";
1574            if (code.equals("S"))
1575                return "S";
1576            if (code.equals("L"))
1577                return "I";
1578            if (code.equals("Q"))
1579                return "J";
1580            if (code.equals("f"))
1581                return "F";
1582            if (code.equals("d"))
1583                return "D";
1584            if (code.equals("B"))
1585                return "Z";
1586            if (code.equals("v"))
1587                return "V";
1588            if (code.equals("*"))
1589                return "I";
1590            if (code.equals("@"))
1591                return "I";
1592            if (code.equals("#"))
1593                return "I";
1594            if (code.equals(":"))
1595                return "I";
1596            if (code.startsWith("^"))
1597                return "I";
1598            if (code.startsWith("["))
1599                return "BAD " + code;
1600            if (code.startsWith("{")) {
1601                return "BAD " + code;
1602            }
1603            if (code.startsWith("("))
1604                return "BAD " + code;
1605            return "BAD " + code;
1606        }
1607    
1608        String getJavaType(Node node) {
1609            NamedNodeMap attributes = node.getAttributes();
1610            Node javaType = attributes.getNamedItem("hawtjni_java_type");
1611            if (javaType != null)
1612                return javaType.getNodeValue().trim();
1613            String code = attributes.getNamedItem("type").getNodeValue();
1614            return getJavaType(code, attributes, false);
1615        }
1616    
1617        String getJavaType64(Node node) {
1618            NamedNodeMap attributes = node.getAttributes();
1619            Node javaType = attributes.getNamedItem("hawtjni_java_type");
1620            if (javaType != null) {
1621                Node javaType64 = attributes.getNamedItem("hawtjni_java_type64");
1622                return javaType64 != null ? javaType64.getNodeValue() : javaType.getNodeValue();
1623            }
1624            Node attrib = attributes.getNamedItem("type");
1625            String code = attrib.getNodeValue();
1626            Node attrib64 = attributes.getNamedItem("type64");
1627            if (attrib64 != null)
1628                code = attrib64.getNodeValue();
1629            return getJavaType(code, attributes, true);
1630        }
1631    
1632        String getJavaType(String code, NamedNodeMap attributes, boolean is64) {
1633            if (code.equals("c"))
1634                return "byte";
1635            if (code.equals("i"))
1636                return "int";
1637            if (code.equals("s"))
1638                return "short";
1639            if (code.equals("l"))
1640                return "int";
1641            if (code.equals("q"))
1642                return "long";
1643            if (code.equals("C"))
1644                return "byte";
1645            if (code.equals("I"))
1646                return "int";
1647            if (code.equals("S"))
1648                return "short";
1649            if (code.equals("L"))
1650                return "int";
1651            if (code.equals("Q"))
1652                return "long";
1653            if (code.equals("f"))
1654                return "float";
1655            if (code.equals("d"))
1656                return "double";
1657            if (code.equals("B"))
1658                return "boolean";
1659            if (code.equals("v"))
1660                return "void";
1661            if (code.equals("*"))
1662                return is64 ? "long" : "int";
1663            if (code.equals("#"))
1664                return is64 ? "long" : "int";
1665            if (code.equals(":"))
1666                return is64 ? "long" : "int";
1667            if (code.startsWith("^"))
1668                return is64 ? "long" : "int";
1669            if (code.equals("@")) {
1670                String type = attributes.getNamedItem("declared_type").getNodeValue();
1671                int index = type.indexOf('*');
1672                if (index != -1)
1673                    type = type.substring(0, index);
1674                index = type.indexOf('<');
1675                if (index != -1)
1676                    type = type.substring(0, index);
1677                return type.trim();
1678            }
1679            if (code.startsWith("{")) {
1680                return attributes.getNamedItem("declared_type").getNodeValue().trim();
1681            }
1682            return "BAD " + code;
1683        }
1684    
1685        static String[] split(String str, String separator) {
1686            StringTokenizer tk = new StringTokenizer(str, separator);
1687            ArrayList<Object> result = new ArrayList<Object>();
1688            while (tk.hasMoreElements()) {
1689                result.add(tk.nextElement());
1690            }
1691            return result.toArray(new String[result.size()]);
1692        }
1693    
1694        void generateFunctions() {
1695            for (int x = 0; x < xmls.length; x++) {
1696                Document document = documents[x];
1697                if (document == null)
1698                    continue;
1699                NodeList list = document.getDocumentElement().getChildNodes();
1700                for (int i = 0; i < list.getLength(); i++) {
1701                    Node node = list.item(i);
1702                    if ("function".equals(node.getNodeName())) {
1703                        if (getGen(node)) {
1704                            NamedNodeMap attributes = node.getAttributes();
1705                            String name = attributes.getNamedItem("name").getNodeValue();
1706                            NodeList params = node.getChildNodes();
1707                            int count = 0;
1708                            for (int j = 0; j < params.getLength(); j++) {
1709                                Node param = params.item(j);
1710                                if ("arg".equals(param.getNodeName())) {
1711                                    count++;
1712                                }
1713                            }
1714                            if (count > 0) {
1715                                out("/**");
1716                                outln();
1717                            }
1718                            for (int j = 0; j < params.getLength(); j++) {
1719                                Node param = params.item(j);
1720                                if ("arg".equals(param.getNodeName())) {
1721                                    NamedNodeMap paramAttributes = param.getAttributes();
1722                                    out(" * @param ");
1723                                    out(paramAttributes.getNamedItem("name").getNodeValue());
1724                                    if (isStruct(param)) {
1725                                        out(" flags=struct");
1726                                    } else {
1727                                        out(" cast=");
1728                                        Node declaredType = paramAttributes.getNamedItem("declared_type");
1729                                        String cast = declaredType.getNodeValue();
1730                                        if (!cast.startsWith("("))
1731                                            out("(");
1732                                        out(cast);
1733                                        if (!cast.endsWith(")"))
1734                                            out(")");
1735                                    }
1736                                    outln();
1737                                }
1738                            }
1739                            if (count > 0) {
1740                                out(" */");
1741                                outln();
1742                            }
1743                            out("public static final native ");
1744                            Node returnNode = getReturnNode(node.getChildNodes());
1745                            if (returnNode != null) {
1746                                String type = getType(returnNode), type64 = getType64(returnNode);
1747                                out(type);
1748                                if (!type.equals(type64)) {
1749                                    out(" /*");
1750                                    out(type64);
1751                                    out("*/");
1752                                }
1753                                out(" ");
1754                            } else {
1755                                out("void ");
1756                            }
1757                            out(name);
1758                            out("(");
1759                            params = node.getChildNodes();
1760                            boolean first = true;
1761                            for (int j = 0; j < params.getLength(); j++) {
1762                                Node param = params.item(j);
1763                                if ("arg".equals(param.getNodeName())) {
1764                                    NamedNodeMap paramAttributes = param.getAttributes();
1765                                    if (!first)
1766                                        out(", ");
1767                                    first = false;
1768                                    String type = getType(param), type64 = getType64(param);
1769                                    out(type);
1770                                    if (!type.equals(type64)) {
1771                                        out(" /*");
1772                                        out(type64);
1773                                        out("*/");
1774                                    }
1775                                    out(" ");
1776                                    out(paramAttributes.getNamedItem("name").getNodeValue());
1777                                }
1778                            }
1779                            generateVariadics(node);
1780                            out(");");
1781                            outln();
1782                        }
1783                    }
1784                }
1785            }
1786        }
1787    
1788        void generateVariadics(Node node) {
1789            NamedNodeMap attributes = node.getAttributes();
1790            Node variadicCount = attributes.getNamedItem("hawtjni_variadic_count");
1791            if (variadicCount != null) {
1792                Node variadicTypes = attributes.getNamedItem("hawtjni_variadic_java_types");
1793                String[] types = null;
1794                if (variadicTypes != null) {
1795                    types = split(variadicTypes.getNodeValue(), ",");
1796                }
1797                int varCount = 0;
1798                try {
1799                    varCount = Integer.parseInt(variadicCount.getNodeValue());
1800                } catch (NumberFormatException e) {
1801                }
1802                for (int j = 0; j < varCount; j++) {
1803                    out(", ");
1804                    if (types != null && types.length > j && !types[j].equals("*")) {
1805                        out(types[j]);
1806                    } else if (types != null && types[types.length - 1].equals("*")) {
1807                        out(types[types.length - 2]);
1808                    } else {
1809                        out("int /*long*/");
1810                    }
1811                    out(" varArg");
1812                    out("" + j);
1813                }
1814            }
1815        }
1816    
1817        public static void main(String[] args) {
1818            try {
1819                MacGenerator gen = new MacGenerator();
1820                gen.setXmls(args);
1821                gen.setOutputDir("../org.eclipse.hawtjni/Eclipse SWT PI/cocoa/");
1822                gen.setMainClass("org.eclipse.hawtjni.internal.cocoa.OS");
1823                gen.generate(null);
1824            } catch (Throwable e) {
1825                e.printStackTrace();
1826            }
1827        }
1828    }