001/*******************************************************************************
002 * Copyright (C) 2009-2011 FuseSource Corp.
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 *******************************************************************************/
011package org.fusesource.hawtjni.generator;
012
013import java.io.BufferedInputStream;
014import java.io.ByteArrayOutputStream;
015import java.io.File;
016import java.io.FileInputStream;
017import java.io.IOException;
018import java.io.InputStream;
019import java.io.InputStreamReader;
020import java.io.PrintStream;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.Map;
027import java.util.Set;
028import java.util.StringTokenizer;
029import java.util.TreeMap;
030import java.util.TreeSet;
031
032import javax.xml.parsers.DocumentBuilderFactory;
033
034import org.fusesource.hawtjni.generator.HawtJNI.UsageException;
035import org.fusesource.hawtjni.generator.util.FileSupport;
036import org.w3c.dom.Document;
037import org.w3c.dom.Element;
038import org.w3c.dom.NamedNodeMap;
039import org.w3c.dom.Node;
040import org.w3c.dom.NodeList;
041import org.xml.sax.InputSource;
042
043/**
044 * 
045 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
046 */
047public 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}