| /******************************************************************************* |
| * Copyright (c) 2008, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.tools.internal; |
| |
| import java.io.*; |
| import java.util.*; |
| import java.util.stream.*; |
| |
| import javax.xml.parsers.*; |
| |
| import org.w3c.dom.*; |
| import org.w3c.dom.Node; |
| import org.xml.sax.*; |
| @SuppressWarnings("unchecked") |
| public class MacGenerator { |
| String[] xmls; |
| Document[] documents; |
| String outputDir, outputLibDir, extrasDir, mainClassName; |
| String delimiter = System.getProperty("line.separator"); |
| boolean generate64Code; |
| PrintWriter out; |
| |
| public static boolean BUILD_C_SOURCE = true; |
| public static boolean GENERATE_ALLOC = true; |
| public static boolean GENERATE_STRUCTS = false; |
| public static boolean USE_SYSTEM_BRIDGE_FILES = false; |
| |
| static final char[] INT_LONG = "int /*long*/".toCharArray(); |
| static final char[] INT_LONG_ARRAY = "int[] /*long[]*/".toCharArray(); |
| static final char[] FLOAT_DOUBLE = "float /*double*/".toCharArray(); |
| static final char[] FLOAT_DOUBLE_ARRAY = "float[] /*double[]*/".toCharArray(); |
| static final char[] LONG_INT = "long /*int*/".toCharArray(); |
| static final char[] LONG_INT_ARRAY = "long[] /*int[]*/".toCharArray(); |
| static final char[] DOUBLE_FLOAT = "double /*float*/".toCharArray(); |
| static final char[] DOUBLE_FLOAT_ARRAY = "double[] /*float[]*/".toCharArray(); |
| |
| public MacGenerator() { |
| } |
| |
| static void list(File path, ArrayList<String> list) { |
| if (path == null) return; |
| File[] frameworks = path.listFiles(); |
| if (frameworks == null) return; |
| for (int i = 0; i < frameworks.length; i++) { |
| File file = frameworks[i]; |
| String name = file.getName(); |
| int index = name.lastIndexOf("."); |
| if (index != -1) { |
| String xml = file.getAbsolutePath() + "/Resources/BridgeSupport/" + name.substring(0, index) + "Full.bridgesupport"; |
| if (new File(xml).exists()) { |
| list.add(xml); |
| } |
| } |
| } |
| } |
| |
| static int indexOf(final char[] toBeFound, final char[] array, final int start) { |
| final int arrayLength = array.length; |
| final int toBeFoundLength = toBeFound.length; |
| if (toBeFoundLength > arrayLength || start < 0) return -1; |
| if (toBeFoundLength == 0) return 0; |
| if (toBeFoundLength == arrayLength) { |
| for (int i = start; i < arrayLength; i++) { |
| if (array[i] != toBeFound[i]) return -1; |
| } |
| return 0; |
| } |
| arrayLoop: for (int i = start, max = arrayLength - toBeFoundLength + 1; i < max; i++) { |
| if (array[i] == toBeFound[0]) { |
| for (int j = 1; j < toBeFoundLength; j++) { |
| if (array[i + j] != toBeFound[j]) continue arrayLoop; |
| } |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static boolean replace(char[] source, char[] src, char[] dest) { |
| boolean changed = false; |
| int start = 0; |
| while (start < source.length) { |
| int index = indexOf(src, source, start); |
| if (index == -1) break; |
| changed |= true; |
| System.arraycopy(dest, 0, source, index, dest.length); |
| start = index + 1; |
| } |
| return changed; |
| } |
| |
| void output(String fileName, char[] source) { |
| try { |
| if (source.length > 0) { |
| if (generate64Code) { |
| replace(source, INT_LONG, LONG_INT); |
| replace(source, INT_LONG_ARRAY, LONG_INT_ARRAY); |
| replace(source, FLOAT_DOUBLE, DOUBLE_FLOAT); |
| replace(source, FLOAT_DOUBLE_ARRAY, DOUBLE_FLOAT_ARRAY); |
| } else { |
| replace(source, LONG_INT, INT_LONG); |
| replace(source, LONG_INT_ARRAY, INT_LONG_ARRAY); |
| replace(source, DOUBLE_FLOAT, FLOAT_DOUBLE); |
| replace(source, DOUBLE_FLOAT_ARRAY, FLOAT_DOUBLE_ARRAY); |
| } |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| PrintStream stream = new PrintStream(out); |
| stream.print(source); |
| stream.flush(); |
| JNIGenerator.output(out.toByteArray(), fileName); |
| } |
| } catch (Exception e) { |
| System.out.println("Problem"); |
| e.printStackTrace(System.out); |
| } |
| } |
| |
| int getLevel(Node node) { |
| int level = 0; |
| while (node != null) { |
| level++; |
| node = node.getParentNode(); |
| } |
| return level; |
| } |
| |
| void merge(Document document, Document extraDocument) { |
| if (extraDocument == null) return; |
| |
| /* Build a lookup table for extraDocument */ |
| HashMap<String, Node> extras = new HashMap<>(); |
| buildLookup(extraDocument, extras); |
| |
| /* Merge attributes on existing elements building a lookup table for document */ |
| HashMap<String, Node> lookup = new HashMap<>(); |
| merge(document, extras, lookup); |
| |
| /* |
| * Merge new elements. Extras at this point contains only elements that were |
| * not found in the document. |
| */ |
| ArrayList<Node> sortedNodes = Collections.list(Collections.enumeration(extras.values())); |
| Collections.sort(sortedNodes, (arg0, arg1) -> { |
| int compare = getLevel(arg0) - getLevel(arg1); |
| if (compare == 0) { |
| return arg0.getNodeName().compareTo(arg1.getNodeName()); |
| } |
| return compare; |
| }); |
| String delimiter = System.getProperty("line.separator"); |
| for (Iterator<Node> iterator = sortedNodes.iterator(); iterator.hasNext();) { |
| Node node = iterator.next(); |
| String name = node.getNodeName(); |
| if ("arg".equals(name) || "retval".equals(name)) { |
| if (!sortedNodes.contains(node.getParentNode())) continue; |
| } |
| Node parent = lookup.get(getKey(node.getParentNode())); |
| Element element = document.createElement(node.getNodeName()); |
| String text = parent.getChildNodes().getLength() == 0 ? delimiter : ""; |
| for (int i = 0, level = getLevel(parent) - 1; i < level; i++) { |
| text += " "; |
| } |
| parent.appendChild(document.createTextNode(text)); |
| parent.appendChild(element); |
| parent.appendChild(document.createTextNode(delimiter)); |
| NamedNodeMap attributes = node.getAttributes(); |
| for (int j = 0, length = attributes.getLength(); j < length; j++) { |
| Node attr = attributes.item(j); |
| element.setAttribute(attr.getNodeName(), attr.getNodeValue()); |
| } |
| lookup.put(getKey(element), element); |
| } |
| } |
| |
| public void generate(ProgressMonitor progress) { |
| if (progress != null) { |
| progress.setTotal(BUILD_C_SOURCE ? 4 : 3); |
| progress.setMessage("extra attributes..."); |
| } |
| generateExtraAttributes(); |
| if (progress != null) { |
| progress.step(); |
| progress.setMessage(mainClassName); |
| } |
| generateMainClass(); |
| if (progress != null) { |
| progress.step(); |
| progress.setMessage("classes..."); |
| } |
| generateClasses(); |
| if (GENERATE_STRUCTS) { |
| if (progress != null) { |
| progress.step(); |
| progress.setMessage("structs..."); |
| } |
| generateStructs(); |
| } |
| if (BUILD_C_SOURCE) { |
| if (progress != null) { |
| progress.step(); |
| progress.setMessage("C source..."); |
| } |
| generateCSource(); |
| } |
| if (progress != null) { |
| progress.step(); |
| progress.setMessage("Done."); |
| } |
| } |
| |
| void generateCSource() { |
| JNIGeneratorApp app = new JNIGeneratorApp(); |
| String outputLibDir = this.outputLibDir != null ? this.outputLibDir : outputDir + "/library"; |
| app.setMainClassName(mainClassName, outputLibDir, outputDir); |
| app.generate(); |
| } |
| |
| String fixDelimiter(String str) { |
| if (delimiter.equals("\n")) return str; |
| int index = 0, length = str.length(); |
| StringBuffer buffer = new StringBuffer(); |
| while (index != -1) { |
| int start = index; |
| index = str.indexOf('\n', start); |
| if (index == -1) { |
| buffer.append(str.substring(start, length)); |
| } else { |
| buffer.append(str.substring(start, index)); |
| buffer.append(delimiter); |
| index++; |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| String getParamName(Node param, int i) { |
| NamedNodeMap paramAttributes = param.getAttributes(); |
| Node swtName = paramAttributes.getNamedItem("swt_param_name"); |
| String paramName = ""; |
| if (swtName != null) { |
| paramName = swtName.getNodeValue(); |
| } else { |
| Node node = paramAttributes.getNamedItem("name"); |
| if (node != null) paramName = node.getNodeValue(); |
| } |
| if (paramName.length() == 0) { |
| Node node = paramAttributes.getNamedItem("index"); |
| String index = "0"; |
| if (node != null) { |
| index = node.getNodeValue(); |
| } else { |
| index = String.valueOf(i); |
| } |
| paramName = "arg" + index; |
| } |
| if (paramName.equals("boolean")) paramName = "b"; |
| return paramName; |
| } |
| |
| void generateFields(String structName, ArrayList<?> fields) { |
| for (Iterator<?> iterator = fields.iterator(); iterator.hasNext();) { |
| Node field = (Node)iterator.next(); |
| NamedNodeMap fieldAttributes = field.getAttributes(); |
| String fieldName = fieldAttributes.getNamedItem("name").getNodeValue(); |
| String type = getJavaType(field), type64 = getJavaType64(field); |
| out("\t"); |
| out("public "); |
| out(type); |
| if (!type.equals(type64)) { |
| out(" /*"); |
| out(type64); |
| out("*/"); |
| } |
| out(" "); |
| out(fieldName); |
| if (isStruct(field)) { |
| out(" = new "); |
| String clazz = getDeclaredType(fieldAttributes, field); |
| out (clazz); |
| out ("()"); |
| } |
| out(";"); |
| outln(); |
| } |
| } |
| |
| private String getDeclaredType(NamedNodeMap map, Node location) { |
| Node declaredType = map.getNamedItem("declared_type"); |
| String value = declaredType != null ? declaredType.getNodeValue() : null; |
| if(value == null) { |
| System.err.printf("Unable to detect declared_type. Check bridge file! It might have been removed, inheritance changed, etc. It could also be an issue with gen_bridge_metadata. Location: %s %n", toDebugLocation(location)); |
| return "nodeclaredtype"; |
| } |
| |
| // strip any _Nullable and _Nonnull annotations |
| value = value.replace("_Nullable", "").replace("_Nonnull", "").replace("_Null_unspecified", ""); |
| |
| // also remove any white spaces |
| value = value.chars().filter((c)->!Character.isWhitespace(c)).mapToObj(c -> String.valueOf((char)c)).collect(Collectors.joining()); |
| |
| return value; |
| } |
| |
| void generateMethods(String className, ArrayList<?> methods) { |
| for (Iterator<?> iterator = methods.iterator(); iterator.hasNext();) { |
| Node method = (Node)iterator.next(); |
| NamedNodeMap mthAttributes = method.getAttributes(); |
| String sel = mthAttributes.getNamedItem("selector").getNodeValue(); |
| if ("NSObject".equals(className)) { |
| if ("alloc".equals(sel) || "dealloc".equals(sel)) continue; |
| } |
| out("public "); |
| boolean isStatic = isStatic(method); |
| if (isStatic) out("static "); |
| Node returnNode = getReturnNode(method.getChildNodes()); |
| if (getType(returnNode).equals("void")) returnNode = null; |
| String returnType = "", returnType64 = ""; |
| if (returnNode != null) { |
| String type = returnType = getJavaType(returnNode), type64 = returnType64 = getJavaType64(returnNode); |
| |
| // convert "instancetype" to class name |
| if(type.equals("instancetype")) { |
| type = returnType = className; |
| type64 = returnType64 = className; |
| } |
| |
| out(type); |
| if (!type.equals(type64)) { |
| out(" /*"); |
| out(type64); |
| out("*/"); |
| } |
| out(" "); |
| } else { |
| out("void "); |
| } |
| String methodName = sel; |
| if (isUnique(method, methods)) { |
| int index = methodName.indexOf(":"); |
| if (index != -1) methodName = methodName.substring(0, index); |
| } else { |
| //TODO improve this selector |
| methodName = methodName.replaceAll(":", "_"); |
| if (isStatic) methodName = "static_" + methodName; |
| } |
| out(methodName); |
| out("("); |
| NodeList params = method.getChildNodes(); |
| boolean first = true; |
| int argIndex = 0; |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| if (!first) out(", "); |
| String type = getJavaType(param), type64 = getJavaType64(param); |
| out( type); |
| if (!type.equals(type64)) { |
| out(" /*"); |
| out(type64); |
| out("*/"); |
| } |
| first = false; |
| out(" "); |
| out(getParamName(param, argIndex++)); |
| } |
| } |
| out(") {"); |
| outln(); |
| if (returnNode != null && isStruct(returnNode)) { |
| out("\t"); |
| out(returnType); |
| out(" result = new "); |
| out(returnType); |
| out("();"); |
| outln(); |
| out("\tOS.objc_msgSend_stret(result, "); |
| } else if (returnNode != null && isBoolean(returnNode)) { |
| out("\treturn "); |
| out("OS.objc_msgSend_bool("); |
| } else if (returnNode != null && isFloatingPoint(returnNode)) { |
| out("\treturn "); |
| String type = getType(returnNode), type64 = getType64(returnNode); |
| if (type.equals(type64) && type.equals("float")) { |
| out("OS.objc_msgSend_floatret("); |
| } else { |
| if (returnType.equals("float")) out("(float /*double*/)"); |
| out("OS.objc_msgSend_fpret("); |
| } |
| } else if (returnNode != null && isObject(returnNode)) { |
| out("\tint /*long*/ result = OS.objc_msgSend("); |
| } else { |
| if (returnNode != null) { |
| out("\treturn "); |
| if ((returnType.equals("int") && returnType64.equals("int")) || !returnType.equals("int")) { |
| out("("); |
| out(returnType); |
| out(")"); |
| } |
| if (returnType.equals("int") && returnType64.equals("int")) { |
| out("/*64*/"); |
| } |
| } else { |
| out("\t"); |
| } |
| out("OS.objc_msgSend("); |
| } |
| if (isStatic) { |
| out("OS.class_"); |
| out(className); |
| } else { |
| out("this.id"); |
| } |
| out(", OS."); |
| out(getSelConst(sel)); |
| first = false; |
| argIndex = 0; |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| if (!first) out(", "); |
| first = false; |
| String paramName = getParamName(param, argIndex++); |
| if (isObject(param)) { |
| out(paramName); |
| out(" != null ? "); |
| out(paramName); |
| out(".id : 0"); |
| } else { |
| out(paramName); |
| } |
| } |
| } |
| out(")"); |
| out(";"); |
| outln(); |
| if (returnNode != null && isObject(returnNode)) { |
| if (!isStatic && returnType.equals(className)) { |
| out("\treturn result == this.id ? this : (result != 0 ? new "); |
| out(returnType); |
| out("(result) : null);"); |
| } else { |
| out("\treturn result != 0 ? new "); |
| NamedNodeMap attributes = returnNode.getAttributes(); |
| Node swt_alloc = attributes.getNamedItem("swt_alloc"); |
| if (swt_alloc != null && swt_alloc.getNodeValue().equals("true")) { |
| out(className); |
| } else { |
| out(returnType); |
| } |
| out("(result) : null;"); |
| } |
| outln(); |
| } else if (returnNode != null && isStruct(returnNode)) { |
| out("\treturn result;"); |
| outln(); |
| } |
| out("}"); |
| outln(); |
| outln(); |
| } |
| } |
| |
| void generateExtraFields(String className) { |
| /* sizeof field */ |
| out("\t"); |
| out("public static int sizeof = OS." + className + "_sizeof();"); |
| outln(); |
| if ("CGSize".equals(className)) { |
| outln(); |
| out("\tpublic String toString () {"); |
| outln(); |
| out("\t\treturn \"CGSize {\" + width + \" \" + height + \"}\";"); |
| outln(); |
| out("\t}"); |
| outln(); |
| } |
| if ("CGRect".equals(className)) { |
| outln(); |
| out("\tpublic String toString () {"); |
| outln(); |
| out("\t\treturn \"CGRect {\" + origin.x + \" \" + origin.y + \" \" + size.width + \" \" + size.height + \"}\";"); |
| outln(); |
| out("\t}"); |
| outln(); |
| } |
| if ("CGPoint".equals(className)) { |
| outln(); |
| out("\tpublic String toString () {"); |
| outln(); |
| out("\t\treturn \"CGPoint {\" + x + \" \" + y + \"}\";"); |
| outln(); |
| out("\t}"); |
| outln(); |
| } |
| } |
| |
| void generateExtraMethods(String className) { |
| /* Empty constructor */ |
| out("public "); |
| out(className); |
| out("() {"); |
| outln(); |
| out("\tsuper();"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| /* pointer constructor */ |
| out("public "); |
| out(className); |
| out("(int /*long*/ id) {"); |
| outln(); |
| out("\tsuper(id);"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| /* object constructor */ |
| out("public "); |
| out(className); |
| out("(id id) {"); |
| outln(); |
| out("\tsuper(id);"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| /* NSObject helpers */ |
| if (className.equals("NSObject")) { |
| if (GENERATE_ALLOC) { |
| out("public NSObject alloc() {"); |
| outln(); |
| out("\tthis.id = OS.objc_msgSend(objc_getClass(), OS.sel_alloc);"); |
| outln(); |
| out("\treturn this;"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| } |
| } |
| /* NSString helpers */ |
| if (className.equals("NSString")) { |
| /* Get java string */ |
| out("public String getString() {"); |
| outln(); |
| out("\tchar[] buffer = new char[(int)/*64*/length()];"); |
| outln(); |
| out("\tgetCharacters(buffer);"); |
| outln(); |
| out("\treturn new String(buffer);"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| /* create NSString */ |
| out("public NSString initWithString(String str) {"); |
| outln(); |
| out("\tchar[] buffer = new char[str.length()];"); |
| outln(); |
| out("\tstr.getChars(0, buffer.length, buffer, 0);"); |
| outln(); |
| out("\treturn initWithCharacters(buffer, buffer.length);"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| out("public static NSString stringWith(String str) {"); |
| outln(); |
| out("\tchar[] buffer = new char[str.length()];"); |
| outln(); |
| out("\tstr.getChars(0, buffer.length, buffer, 0);"); |
| outln(); |
| out("\treturn stringWithCharacters(buffer, buffer.length);"); |
| outln(); |
| out("}"); |
| outln(); |
| outln(); |
| } |
| } |
| |
| TreeMap<String, Object[]> getGeneratedClasses() { |
| TreeMap<String, Object[]> classes = new TreeMap<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("class".equals(node.getNodeName()) && getGen(node)) { |
| ArrayList<Node> methods; |
| String name = node.getAttributes().getNamedItem("name").getNodeValue(); |
| Object[] clazz = classes.get(name); |
| if (clazz == null) { |
| methods = new ArrayList<>(); |
| classes.put(name, new Object[]{node, methods}); |
| } else { |
| methods = (ArrayList<Node>)clazz[1]; |
| } |
| NodeList methodList = node.getChildNodes(); |
| for (int j = 0; j < methodList.getLength(); j++) { |
| Node method = methodList.item(j); |
| if ("method".equals(method.getNodeName()) && getGen(method)) { |
| methods.add(method); |
| } |
| } |
| } |
| } |
| } |
| return classes; |
| } |
| |
| TreeMap<String, Object[]> getGeneratedStructs() { |
| TreeMap<String, Object[]> structs = new TreeMap<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("struct".equals(node.getNodeName()) && getGen(node)) { |
| ArrayList<Node> fields; |
| String name = node.getAttributes().getNamedItem("name").getNodeValue(); |
| Object[] clazz = structs.get(name); |
| if (clazz == null) { |
| fields = new ArrayList<>(); |
| structs.put(name, new Object[]{node, fields}); |
| } else { |
| fields = (ArrayList<Node>)clazz[1]; |
| } |
| NodeList fieldList = node.getChildNodes(); |
| for (int j = 0; j < fieldList.getLength(); j++) { |
| Node field = fieldList.item(j); |
| if ("field".equals(field.getNodeName()) && getGen(field)) { |
| fields.add(field); |
| } |
| } |
| } |
| } |
| } |
| return structs; |
| } |
| |
| void copyClassMethodsDown(final Map<String, Object[]> classes) { |
| ArrayList<Object[]> sortedClasses = Collections.list(Collections.enumeration(classes.values())); |
| Collections.sort(sortedClasses, new Comparator<Object>() { |
| int getHierarchyLevel(Node node) { |
| String superclass = getSuperclassName(node); |
| int level = 0; |
| while (!superclass.equals("id") && !superclass.equals("NSObject")) { |
| level++; |
| superclass = getSuperclassName((Node)classes.get(superclass)[0]); |
| } |
| return level; |
| } |
| @Override |
| public int compare(Object arg0, Object arg1) { |
| return getHierarchyLevel((Node)((Object[])arg0)[0]) - getHierarchyLevel((Node)((Object[])arg1)[0]); |
| } |
| }); |
| for (Iterator<Object[]> iterator = sortedClasses.iterator(); iterator.hasNext();) { |
| Object[] clazz = iterator.next(); |
| Node node = (Node)clazz[0]; |
| ArrayList<Node> methods = (ArrayList<Node>)clazz[1]; |
| Object[] superclass = classes.get(getSuperclassName(node)); |
| if (superclass != null) { |
| for (Iterator<?> iterator2 = ((ArrayList<?>)superclass[1]).iterator(); iterator2.hasNext();) { |
| Node method = (Node) iterator2.next(); |
| if (isStatic(method)) { |
| methods.add(method); |
| } |
| } |
| } |
| } |
| } |
| |
| String getSuperclassName (Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node superclass = attributes.getNamedItem("swt_superclass"); |
| if (superclass != null) { |
| return superclass.getNodeValue(); |
| } else { |
| Node name = attributes.getNamedItem("name"); |
| if (name.getNodeValue().equals("NSObject")) { |
| return "id"; |
| } else { |
| return "NSObject"; |
| } |
| } |
| } |
| |
| void generateClasses() { |
| MetaData metaData = new MetaData(mainClassName); |
| TreeMap<String, Object[]> classes = getGeneratedClasses(); |
| copyClassMethodsDown(classes); |
| |
| Set<String> classNames = classes.keySet(); |
| for (Iterator<String> iterator = classNames.iterator(); iterator.hasNext();) { |
| CharArrayWriter out = new CharArrayWriter(); |
| this.out = new PrintWriter(out); |
| |
| out(fixDelimiter(metaData.getCopyright())); |
| |
| String className = iterator.next(); |
| Object[] clazz = classes.get(className); |
| Node node = (Node)clazz[0]; |
| ArrayList<?> methods = (ArrayList<?>)clazz[1]; |
| out("package "); |
| String packageName = getPackageName(mainClassName); |
| out(packageName); |
| out(";"); |
| outln(); |
| outln(); |
| out("public class "); |
| out(className); |
| out(" extends "); |
| out(getSuperclassName(node)); |
| out(" {"); |
| outln(); |
| outln(); |
| generateExtraMethods(className); |
| generateMethods(className, methods); |
| out("}"); |
| outln(); |
| |
| String fileName = outputDir + packageName.replace('.', '/') + "/" + className + ".java"; |
| this.out.flush(); |
| output(fileName, out.toCharArray()); |
| this.out = null; |
| } |
| } |
| |
| void generateStructs() { |
| MetaData metaData = new MetaData(mainClassName); |
| TreeMap<String, Object[]> structs = getGeneratedStructs(); |
| |
| Set<String> structNames = structs.keySet(); |
| for (Iterator<String> iterator = structNames.iterator(); iterator.hasNext();) { |
| CharArrayWriter out = new CharArrayWriter(); |
| this.out = new PrintWriter(out); |
| |
| out(fixDelimiter(metaData.getCopyright())); |
| |
| String className = iterator.next(); |
| Object[] clazz = structs.get(className); |
| ArrayList<?> methods = (ArrayList<?>)clazz[1]; |
| out("package "); |
| String packageName = getPackageName(mainClassName); |
| out(packageName); |
| out(";"); |
| outln(); |
| outln(); |
| out("public class "); |
| out(className); |
| out(" {"); |
| outln(); |
| generateFields(className, methods); |
| generateExtraFields(className); |
| out("}"); |
| outln(); |
| |
| String fileName = outputDir + packageName.replace('.', '/') + "/" + className + ".java"; |
| this.out.flush(); |
| output(fileName, out.toCharArray()); |
| this.out = null; |
| } |
| } |
| |
| void generateExtraAttributes() { |
| Document[] documents = getDocuments(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null || !getGen(document.getDocumentElement())) continue; |
| saveExtraAttributes(xmls[x], document); |
| } |
| } |
| |
| void generateMainClass() { |
| CharArrayWriter out = new CharArrayWriter(); |
| this.out = new PrintWriter(out); |
| |
| String header = "", footer = ""; |
| String fileName = outputDir + mainClassName.replace('.', '/') + ".java"; |
| try (FileInputStream is = new FileInputStream(fileName); |
| InputStreamReader input = new InputStreamReader(new BufferedInputStream(is))){ |
| StringBuffer str = new StringBuffer(); |
| char[] buffer = new char[4096]; |
| int read; |
| while ((read = input.read(buffer)) != -1) { |
| str.append(buffer, 0, read); |
| } |
| String section = "/** This section is auto generated */"; |
| int start = str.indexOf(section) + section.length(); |
| int end = str.indexOf(section, start); |
| header = str.substring(0, start); |
| footer = end == -1 ? "\n}" : str.substring(end); |
| generate64Code = str.indexOf("long /*int*/") != -1; |
| input.close(); |
| } catch (IOException e) { |
| } |
| |
| out(header); |
| outln(); |
| outln(); |
| |
| out("/** Custom callbacks */"); |
| outln(); |
| generateCustomCallbacks(); |
| outln(); |
| out("/** Classes */"); |
| outln(); |
| generateClassesConst(); |
| outln(); |
| out("/** Protocols */"); |
| outln(); |
| generateProtocolsConst(); |
| outln(); |
| out("/** Selectors */"); |
| outln(); |
| generateSelectorsConst(); |
| outln(); |
| out("/** Constants */"); |
| outln(); |
| generateEnums(); |
| outln(); |
| out("/** Globals */"); |
| outln(); |
| generateConstants(); |
| outln(); |
| out("/** Functions */"); |
| outln(); |
| outln(); |
| generateFunctions(); |
| outln(); |
| out("/** Super Sends */"); |
| outln(); |
| generateSends(true); |
| outln(); |
| out("/** Sends */"); |
| outln(); |
| generateSends(false); |
| outln(); |
| generateStructNatives(); |
| |
| outln(); |
| out(footer); |
| this.out.flush(); |
| output(fileName, out.toCharArray()); |
| this.out = null; |
| } |
| |
| public Document[] getDocuments() { |
| if (documents == null) { |
| String[] xmls = getXmls(); |
| documents = new Document[xmls.length]; |
| for (int i = 0; i < xmls.length; i++) { |
| String xmlPath = xmls[i]; |
| Document document = documents[i] = getDocument(xmlPath); |
| if (document == null) continue; |
| if (mainClassName != null && outputDir != null) { |
| String packageName = getPackageName(mainClassName); |
| String folder = extrasDir != null ? extrasDir : outputDir + packageName.replace('.', '/'); |
| String extrasPath = folder + "/" + getFileName(xmlPath) + ".extras"; |
| merge(document, getDocument(extrasPath)); |
| } |
| } |
| } |
| return documents; |
| } |
| |
| public String[] getXmls() { |
| if (xmls == null || xmls.length == 0) { |
| ArrayList<String> array = new ArrayList<>(); |
| if (USE_SYSTEM_BRIDGE_FILES) { |
| list(new File("/System/Library/Frameworks"), array); |
| list(new File("/System/Library/Frameworks/CoreServices.framework/Frameworks"), array); |
| list(new File("/System/Library/Frameworks/ApplicationServices.framework/Frameworks"), array); |
| } else { |
| String packageName = getPackageName(mainClassName); |
| File folder = new File(extrasDir != null ? extrasDir : outputDir + packageName.replace('.', '/')); |
| File[] files = folder.listFiles((FilenameFilter) (dir, name) -> name.endsWith("Full.bridgesupport")); |
| for (int i = 0; i < files.length; i++) { |
| array.add(files[i].getAbsolutePath()); |
| } |
| } |
| Collections.sort(array, (o1, o2) -> new File(o1).getName().compareTo(new File(o2).getName())); |
| xmls = array.toArray(new String[array.size()]); |
| } |
| return xmls; |
| } |
| |
| void saveExtraAttributes(String xmlPath, Document document) { |
| try { |
| String packageName = getPackageName(mainClassName); |
| String folder = extrasDir != null ? extrasDir : outputDir + packageName.replace('.', '/'); |
| String fileName = folder + "/" + getFileName(xmlPath) + ".extras"; |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| DOMWriter writer = new DOMWriter(new PrintStream(out)); |
| String[] names = getIDAttributeNames(); |
| String[] filter = new String[names.length + 2]; |
| filter[0] = "class_method"; |
| filter[1] = "swt_.*"; |
| System.arraycopy(names, 0, filter, 2, names.length); |
| writer.setIDAttributes(names); |
| writer.setAttributeFilter(filter); |
| writer.setNodeFilter("swt_"); |
| writer.print(document); |
| if (out.size() > 0) JNIGenerator.output(out.toByteArray(), fileName); |
| } catch (Exception e) { |
| System.out.println("Problem"); |
| e.printStackTrace(System.out); |
| } |
| } |
| |
| public String getOutputDir() { |
| return outputDir; |
| } |
| |
| public void setOutputDir(String dir) { |
| if (dir != null) { |
| if (!dir.endsWith("\\") && !dir.endsWith("/") ) { |
| dir += "/"; |
| } |
| } |
| this.outputDir = dir; |
| } |
| |
| public void setOutputLibDir(String dir) { |
| if (dir != null) { |
| if (!dir.endsWith("\\") && !dir.endsWith("/") ) { |
| dir += "/"; |
| } |
| } |
| this.outputLibDir = dir; |
| } |
| |
| public void setExtrasDir(String dir) { |
| if (dir != null) { |
| if (!dir.endsWith("\\") && !dir.endsWith("/") ) { |
| dir += "/"; |
| } |
| } |
| this.extrasDir = dir; |
| } |
| |
| public void setXmls(String[] xmls) { |
| this.xmls = xmls; |
| this.documents = null; |
| } |
| |
| public void setMainClass(String mainClassName) { |
| this.mainClassName = mainClassName; |
| } |
| |
| Document getDocument(String xmlPath) { |
| try { |
| InputStream is = null; |
| if (xmlPath.indexOf(File.separatorChar) == -1) is = getClass().getResourceAsStream(xmlPath); |
| if (is == null) is = new BufferedInputStream(new FileInputStream(xmlPath)); |
| if (is != null) return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(is)); |
| } catch (Exception e) { |
| // e.printStackTrace(); |
| } |
| return null; |
| } |
| |
| public String[] getExtraAttributeNames(Node node) { |
| String name = node.getNodeName(); |
| if (name.equals("method")) { |
| NamedNodeMap attribs = node.getAttributes(); |
| if (attribs != null && attribs.getNamedItem("variadic") != null) { |
| return new String[]{"swt_gen_super_msgSend", "swt_gen_custom_callback", "swt_variadic_count","swt_variadic_java_types"}; |
| } |
| return new String[]{"swt_gen_super_msgSend", "swt_gen_custom_callback"}; |
| } else if (name.equals("function")) { |
| NamedNodeMap attribs = node.getAttributes(); |
| if (attribs != null && attribs.getNamedItem("variadic") != null) { |
| return new String[]{"swt_variadic_count","swt_variadic_java_types"}; |
| } |
| } else if (name.equals("class")) { |
| return new String[]{"swt_superclass"}; |
| } else if (name.equals("retval")) { |
| return new String[]{"swt_java_type", "swt_java_type64", "swt_alloc"}; |
| } else if (name.equals("arg")) { |
| return new String[]{"swt_java_type", "swt_java_type64", "swt_param_name", "swt_param_cast"}; |
| } |
| return new String[0]; |
| } |
| |
| public String getFileName(String xmlPath) { |
| File file = new File(xmlPath); |
| return file.getName(); |
| } |
| |
| int indexOfNode(Node node) { |
| Node parent = node.getParentNode(); |
| int count = 0; |
| Node temp = parent.getFirstChild(); |
| while (temp != node) { |
| count++; |
| temp = temp.getNextSibling(); |
| } |
| return count; |
| } |
| |
| String getKey (Node node) { |
| StringBuffer buffer = new StringBuffer(); |
| while (node != null) { |
| if (buffer.length() > 0) buffer.append("_"); |
| String name = node.getNodeName(); |
| StringBuffer key = new StringBuffer(name); |
| if ("arg".equals(name)) { |
| key.append("-"); |
| key.append(indexOfNode(node)); |
| } else { |
| Node nameAttrib = getIDAttribute(node); |
| if (nameAttrib != null) { |
| key.append("-"); |
| key.append(nameAttrib.getNodeValue()); |
| } |
| } |
| NamedNodeMap attributes = node.getAttributes(); |
| if (attributes != null) { |
| boolean isStatic = attributes.getNamedItem("class_method") != null; |
| if (isStatic) key.append("-static"); |
| } |
| buffer.append(key.reverse()); |
| node = node.getParentNode(); |
| } |
| buffer.reverse(); |
| return buffer.toString(); |
| } |
| |
| public Node getIDAttribute(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| if (attributes == null) return null; |
| String[] names = getIDAttributeNames(); |
| for (int i = 0; i < names.length; i++) { |
| Node nameAttrib = attributes.getNamedItem(names[i]); |
| if (nameAttrib != null) return nameAttrib; |
| } |
| return null; |
| } |
| |
| public String[] getIDAttributeNames() { |
| return new String[]{ |
| "name", |
| "selector", |
| "path", |
| }; |
| } |
| |
| void merge(Node node, HashMap<String, Node> extras, HashMap<String, Node> docLookup) { |
| NodeList list = node.getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node childNode = list.item(i); |
| if (childNode.getNodeType() == Node.ELEMENT_NODE) { |
| String key = getKey(childNode); |
| if (docLookup != null && docLookup.get(key) == null) { |
| docLookup.put(key, childNode); |
| } |
| Node extra = extras.remove(key); |
| if (extra != null) { |
| NamedNodeMap attributes = extra.getAttributes(); |
| for (int j = 0, length = attributes.getLength(); j < length; j++) { |
| Node attr = attributes.item(j); |
| String name = attr.getNodeName(); |
| if (name.startsWith("swt_")) { |
| ((Element)childNode).setAttribute(name, attr.getNodeValue()); |
| } |
| } |
| } |
| } |
| merge(childNode, extras, docLookup); |
| } |
| } |
| |
| |
| void out(String str) { |
| PrintWriter out = this.out; |
| out.print(str); |
| } |
| |
| void outln() { |
| PrintWriter out = this.out; |
| out.println(); |
| } |
| |
| void generateConstants() { |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("constant".equals(node.getNodeName())) { |
| if (getGen(node)) { |
| NamedNodeMap attributes = node.getAttributes(); |
| String constName = attributes.getNamedItem("name").getNodeValue(); |
| out("/** @method flags=const */"); |
| outln(); |
| out("public static final native "); |
| String type = getType(node), type64 = getType64(node); |
| out(type); |
| if (!type.equals(type64)) { |
| out(" /*"); |
| out(type64); |
| out("*/"); |
| } |
| out(" "); |
| out(constName); |
| out("();"); |
| outln(); |
| if (getDeclaredType(attributes, node).equals("NSString*")) { |
| out("public static final NSString "); |
| out(constName); |
| out(" = new NSString("); |
| out(constName); |
| out("());"); |
| outln(); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void generateEnums() { |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("enum".equals(node.getNodeName())) { |
| if (getGen(node)) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node valueNode = attributes.getNamedItem("value"); |
| if (valueNode != null) { |
| String value = valueNode.getNodeValue(); |
| out("public static final "); |
| boolean isLong = false; |
| if (value.indexOf('.') != -1) { |
| out("double "); |
| } else { |
| if (value.equals("4294967295")) { |
| out("int "); |
| value = "-1"; |
| } else { |
| try { |
| Integer.parseInt(value); |
| out("int "); |
| } catch (NumberFormatException e) { |
| isLong = true; |
| out("long "); |
| } |
| } |
| } |
| out(attributes.getNamedItem("name").getNodeValue()); |
| out(" = "); |
| out(value); |
| if (isLong && !value.endsWith("L")) out("L"); |
| out(";"); |
| outln(); |
| } else { |
| System.err.printf("No value for enum. Check bridge file! It might have been removed, renamed, etc. Location: %s %n", toDebugLocation(node)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| boolean getGen(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| if (attributes == null) return false; |
| Node gen = attributes.getNamedItem("swt_gen"); |
| return gen != null && !gen.getNodeValue().equals("false"); |
| } |
| |
| boolean getGenSuper(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| if (attributes == null) return false; |
| Node gen = attributes.getNamedItem("swt_gen_super_msgSend"); |
| return gen != null && !gen.getNodeValue().equals("false"); |
| } |
| |
| boolean getGenCallback(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| if (attributes == null) return false; |
| Node gen = attributes.getNamedItem("swt_gen_custom_callback"); |
| return gen != null && !gen.getNodeValue().equals("false"); |
| } |
| |
| boolean isStatic(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node isStatic = attributes.getNamedItem("class_method"); |
| return isStatic != null && isStatic.getNodeValue().equals("true"); |
| } |
| |
| boolean isStruct(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node type = attributes.getNamedItem("type"); |
| if (type == null) return false; |
| String code = type.getNodeValue(); |
| return code.startsWith("{"); |
| } |
| |
| boolean isFloatingPoint(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node type = attributes.getNamedItem("type"); |
| if (type == null) return false; |
| String code = type.getNodeValue(); |
| return code.equals("f") || code.equals("d"); |
| } |
| |
| boolean isObject(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node type = attributes.getNamedItem("type"); |
| if (type == null) return false; |
| String code = type.getNodeValue(); |
| return code.equals("@"); |
| } |
| |
| boolean isBoolean(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node type = attributes.getNamedItem("type"); |
| if (type == null) return false; |
| String code = type.getNodeValue(); |
| return code.equals("B"); |
| } |
| |
| void buildLookup(Node node, HashMap<String, Node> table) { |
| NodeList list = node.getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node childNode = list.item(i); |
| if (childNode.getNodeType() == Node.ELEMENT_NODE) { |
| String key = getKey(childNode); |
| if (table.get(key) == null) table.put(key, childNode); |
| buildLookup(childNode, table); |
| } |
| } |
| } |
| |
| boolean isUnique(Node method, ArrayList<?> methods) { |
| String methodName = method.getAttributes().getNamedItem("selector").getNodeValue(); |
| String signature = ""; |
| NodeList params = method.getChildNodes(); |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| signature += getJavaType(param); |
| } |
| } |
| int index = methodName.indexOf(":"); |
| if (index != -1) methodName = methodName.substring(0, index); |
| for (Iterator<?> iterator = methods.iterator(); iterator.hasNext();) { |
| Node other = (Node) iterator.next(); |
| NamedNodeMap attributes = other.getAttributes(); |
| Node otherSel = null; |
| if (attributes != null) otherSel = attributes.getNamedItem("selector"); |
| if (other != method && otherSel != null) { |
| String otherName = otherSel.getNodeValue(); |
| index = otherName.indexOf(":"); |
| if (index != -1) otherName = otherName.substring(0, index); |
| if (methodName.equals(otherName)) { |
| NodeList otherParams = other.getChildNodes(); |
| String otherSignature = ""; |
| for (int k = 0; k < otherParams.getLength(); k++) { |
| Node param = otherParams.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| otherSignature += getJavaType(param); |
| } |
| } |
| if (signature.equals(otherSignature)) { |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| void generateSelectorsConst() { |
| TreeSet<String> set = new TreeSet<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("class".equals(node.getNodeName()) || "informal_protocol".equals(node.getNodeName())) { |
| if (getGen(node)) { |
| NodeList methods = node.getChildNodes(); |
| for (int j = 0; j < methods.getLength(); j++) { |
| Node method = methods.item(j); |
| if (getGen(method)) { |
| NamedNodeMap mthAttributes = method.getAttributes(); |
| String sel = mthAttributes.getNamedItem("selector").getNodeValue(); |
| set.add(sel); |
| } |
| } |
| } |
| } |
| } |
| } |
| if (set.size() > 0) { |
| set.add("alloc"); |
| set.add("dealloc"); |
| } |
| for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) { |
| String sel = iterator.next(); |
| String selConst = getSelConst(sel); |
| out("public static final int /*long*/ "); |
| out(selConst); |
| out(" = "); |
| out("sel_registerName(\""); |
| out(sel); |
| out("\");"); |
| outln(); |
| } |
| } |
| |
| void generateStructNatives() { |
| TreeSet<String> set = new TreeSet<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("struct".equals(node.getNodeName()) && getGen(node)) { |
| set.add(getIDAttribute(node).getNodeValue()); |
| } |
| } |
| } |
| out("/** Sizeof natives */"); |
| outln(); |
| for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) { |
| String struct = iterator.next(); |
| out("public static final native int "); |
| out(struct); |
| out("_sizeof();"); |
| outln(); |
| } |
| outln(); |
| out("/** Memmove natives */"); |
| outln(); |
| outln(); |
| for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) { |
| String struct = iterator.next(); |
| out("/**"); |
| outln(); |
| out(" * @param dest cast=(void *),flags=no_in critical"); |
| outln(); |
| out(" * @param src cast=(void *),flags=critical"); |
| // out(" * @param src cast=(void *),flags=no_out critical"); |
| outln(); |
| out(" */"); |
| outln(); |
| out("public static final native void memmove("); |
| out("int /*long*/ dest, "); |
| out(struct); |
| out(" src, int /*long*/ size);"); |
| outln(); |
| out("/**"); |
| outln(); |
| out(" * @param dest cast=(void *),flags=no_in critical"); |
| outln(); |
| out(" * @param src cast=(void *),flags=critical"); |
| // out(" * @param src cast=(void *),flags=no_out critical"); |
| outln(); |
| out(" */"); |
| outln(); |
| out("public static final native void memmove("); |
| out(struct); |
| out(" dest, int /*long*/ src, int /*long*/ size);"); |
| outln(); |
| } |
| } |
| |
| String buildSend(Node method, boolean tags, boolean only64, boolean superCall) { |
| Node returnNode = getReturnNode(method.getChildNodes()); |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("public static final native "); |
| if (returnNode != null && isStruct(returnNode)) { |
| buffer.append("void "); |
| buffer.append(superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"); |
| buffer.append("("); |
| buffer.append(getJavaType(returnNode)); |
| buffer.append(" result, "); |
| } else if (returnNode != null && isFloatingPoint(returnNode)) { |
| String type = getType(returnNode), type64 = getType64(returnNode); |
| if (type.equals(type64) && type.equals("float")) { |
| buffer.append("float "); |
| buffer.append(superCall ? "objc_msgSendSuper_floatret" : "objc_msgSend_floatret"); |
| } else { |
| buffer.append("double "); |
| buffer.append(superCall ? "objc_msgSendSuper_fpret" : "objc_msgSend_fpret"); |
| } |
| buffer.append("("); |
| } else if (returnNode != null && isBoolean(returnNode)) { |
| buffer.append("boolean "); |
| buffer.append(superCall ? "objc_msgSendSuper_bool" : "objc_msgSend_bool"); |
| buffer.append("("); |
| } else { |
| if (only64) { |
| buffer.append("long"); |
| } else { |
| if (tags) { |
| buffer.append("int /*long*/"); |
| } else { |
| buffer.append("int"); |
| } |
| } |
| buffer.append(" "); |
| buffer.append(superCall ? "objc_msgSendSuper" : "objc_msgSend"); |
| buffer.append("("); |
| } |
| if (superCall) { |
| if (only64) { |
| buffer.append("objc_super superId, long sel"); |
| } else { |
| if (tags) { |
| buffer.append("objc_super superId, int /*long*/ sel"); |
| } else { |
| buffer.append("objc_super superId, int sel"); |
| } |
| } |
| } else { |
| if (only64) { |
| buffer.append("long id, long sel"); |
| } else { |
| if (tags) { |
| buffer.append("int /*long*/ id, int /*long*/ sel"); |
| } else { |
| buffer.append("int id, int sel"); |
| } |
| } |
| } |
| NodeList params = method.getChildNodes(); |
| boolean first = false; |
| int count = 0; |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| if (!first) buffer.append(", "); |
| if (isStruct(param)) { |
| buffer.append(getJavaType(param)); |
| } else { |
| String type = getType(param), type64 = getType64(param); |
| buffer.append(only64 ? type64 : type); |
| if (!only64 && tags && !type.equals(type64)) { |
| buffer.append(" /*"); |
| buffer.append(type64); |
| buffer.append("*/"); |
| } |
| } |
| first = false; |
| buffer.append(" arg"); |
| buffer.append(String.valueOf(count++)); |
| } |
| } |
| buffer.append(");"); |
| return buffer.toString(); |
| } |
| |
| String getCType (Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| return getDeclaredType(attributes, node); |
| } |
| |
| Node findNSObjectMethod(Node method) { |
| NamedNodeMap methodAttributes = method.getAttributes(); |
| String selector = methodAttributes.getNamedItem("selector").getNodeValue(); |
| NodeList list = method.getParentNode().getParentNode().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node cls = list.item(i); |
| if ("class".equals(cls.getNodeName())) { |
| NamedNodeMap classAttributes = cls.getAttributes(); |
| if ("NSObject".equals(classAttributes.getNamedItem("name").getNodeValue())) { |
| NodeList methods = cls.getChildNodes(); |
| for (int j = 0; j < methods.getLength(); j++) { |
| Node mth = methods.item(j); |
| if ("method".equals(mth.getNodeName())) { |
| NamedNodeMap mthAttributes = mth.getAttributes(); |
| if (selector.equals(mthAttributes.getNamedItem("selector").getNodeValue())) { |
| return mth; |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| void generateCustomCallbacks() { |
| TreeMap<String, Node> set = new TreeMap<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if (("class".equals(node.getNodeName()) || "informal_protocol".equals(node.getNodeName())) && getGen(node)) { |
| NodeList methods = node.getChildNodes(); |
| for (int j = 0; j < methods.getLength(); j++) { |
| Node method = methods.item(j); |
| if ("method".equals(method.getNodeName()) && getGen(method) && getGenCallback(method)) { |
| NamedNodeMap mthAttributes = method.getAttributes(); |
| String sel = mthAttributes.getNamedItem("selector").getNodeValue(); |
| set.put(sel, method); |
| } |
| } |
| } |
| } |
| } |
| for (Iterator<String> iterator = set.keySet().iterator(); iterator.hasNext();) { |
| String key = iterator.next(); |
| Node method = set.get(key); |
| if ("informal_protocol".equals(method.getParentNode().getNodeName())) { |
| method = findNSObjectMethod(method); |
| if (method == null) continue; |
| } |
| String nativeMth = key.replaceAll(":", "_"); |
| out("/** @method callback_types="); |
| Node returnNode = getReturnNode(method.getChildNodes()); |
| out(returnNode == null ? "void" : getCType(returnNode)); |
| out(";id;SEL;"); |
| NodeList params = method.getChildNodes(); |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| out(getCType(param)); |
| out(";"); |
| } |
| } |
| out(",callback_flags="); |
| out(returnNode != null && isStruct(returnNode) ? "struct" : "none"); |
| out(";none;none;"); |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| out (isStruct(param) ? "struct" : "none"); |
| out(";"); |
| } |
| } |
| out(" */"); |
| outln(); |
| out("public static final native int /*long*/ CALLBACK_"); |
| out(nativeMth); |
| out("(int /*long*/ func);"); |
| outln(); |
| } |
| } |
| |
| void generateSends(boolean superCall) { |
| TreeMap<String, Node> set = new TreeMap<>(); |
| TreeMap<String, Node> set64 = new TreeMap<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("class".equals(node.getNodeName()) && getGen(node)) { |
| NodeList methods = node.getChildNodes(); |
| for (int j = 0; j < methods.getLength(); j++) { |
| Node method = methods.item(j); |
| if ("method".equals(method.getNodeName()) && getGen(method) && (!superCall || getGenSuper(method))) { |
| String code = buildSend(method, false, false, superCall); |
| String code64 = buildSend(method, false, true, superCall); |
| if (set.get(code) == null) { |
| set.put(code, method); |
| } |
| if (set64.get(code64) == null) { |
| set64.put(code64, method); |
| } |
| } |
| } |
| } |
| } |
| } |
| outln(); |
| TreeMap<String, Node> tagsSet = new TreeMap<>(); |
| for (Iterator<String> iterator = set.keySet().iterator(); iterator.hasNext();) { |
| String key = iterator.next(); |
| Node method = set.get(key); |
| String tagCode = buildSend(method, false, true, superCall); |
| if (set64.get(tagCode) != null) { |
| tagsSet.put(key, method); |
| iterator.remove(); |
| set64.remove(tagCode); |
| } |
| } |
| TreeMap<String, Node> all = new TreeMap<>(); |
| for (Iterator<String> iterator = tagsSet.keySet().iterator(); iterator.hasNext();) { |
| String key = iterator.next(); |
| Node method = tagsSet.get(key); |
| all.put(buildSend(method, true, false, superCall), method); |
| } |
| for (Iterator<String> iterator = set.keySet().iterator(); iterator.hasNext();) { |
| String key = iterator.next(); |
| all.put(key, set.get(key)); |
| } |
| for (Iterator<String> iterator = set64.keySet().iterator(); iterator.hasNext();) { |
| String key = iterator.next(); |
| all.put(key, set64.get(key)); |
| } |
| for (Iterator<String> iterator = all.keySet().iterator(); iterator.hasNext();) { |
| String key = iterator.next(); |
| Node method = all.get(key); |
| NodeList params = method.getChildNodes(); |
| ArrayList<String> tags = new ArrayList<>(); |
| int count = 0; |
| for (int k = 0; k < params.getLength(); k++) { |
| Node param = params.item(k); |
| if ("arg".equals(param.getNodeName())) { |
| if (isStruct(param)) { |
| tags.add(" * @param arg" + count + " flags=struct"); |
| } |
| count++; |
| } |
| } |
| out("/**"); |
| if (tags.size() > 0) { |
| outln(); |
| out(" *"); |
| } |
| out(" @method flags=cast"); |
| if (tags.size() > 0) outln(); |
| for (Iterator<String> iterator2 = tags.iterator(); iterator2.hasNext();) { |
| String tag = iterator2.next(); |
| out(tag); |
| outln(); |
| } |
| out(" */"); |
| outln(); |
| out(key.toString()); |
| outln(); |
| } |
| } |
| |
| String getSelConst(String sel) { |
| return "sel_" + sel.replaceAll(":", "_"); |
| } |
| |
| void generateClassesConst() { |
| TreeSet<String> set = new TreeSet<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("class".equals(node.getNodeName())) { |
| if (getGen(node)) { |
| NamedNodeMap attributes = node.getAttributes(); |
| String name = attributes.getNamedItem("name").getNodeValue(); |
| set.add(name); |
| } |
| } |
| } |
| } |
| for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) { |
| String cls = iterator.next(); |
| String clsConst = "class_" + cls; |
| out("public static final int /*long*/ "); |
| out(clsConst); |
| out(" = "); |
| out("objc_getClass(\""); |
| out(cls); |
| out("\");"); |
| outln(); |
| } |
| } |
| |
| void generateProtocolsConst() { |
| TreeSet<String> set = new TreeSet<>(); |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("informal_protocol".equals(node.getNodeName())) { |
| if (getGen(node)) { |
| NamedNodeMap attributes = node.getAttributes(); |
| String name = attributes.getNamedItem("name").getNodeValue(); |
| set.add(name); |
| } |
| } |
| } |
| } |
| for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) { |
| String cls = iterator.next(); |
| String clsConst = "protocol_" + cls; |
| out("public static final int /*long*/ "); |
| out(clsConst); |
| out(" = "); |
| out("objc_getProtocol(\""); |
| out(cls); |
| out("\");"); |
| outln(); |
| } |
| } |
| |
| String getPackageName(String className) { |
| int dot = mainClassName.lastIndexOf('.'); |
| if (dot == -1) return ""; |
| return mainClassName.substring(0, dot); |
| } |
| |
| String getClassName(String className) { |
| int dot = mainClassName.lastIndexOf('.'); |
| if (dot == -1) return mainClassName; |
| return mainClassName.substring(dot + 1); |
| } |
| |
| Node getReturnNode(NodeList list) { |
| for (int j = 0; j < list.getLength(); j++) { |
| Node node = list.item(j); |
| if ("retval".equals(node.getNodeName())) { |
| return node; |
| } |
| } |
| return null; |
| } |
| |
| String getType(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node javaType = attributes.getNamedItem("swt_java_type"); |
| if (javaType != null) return javaType.getNodeValue(); |
| Node type = attributes.getNamedItem("type"); |
| if (type == null) { |
| System.err.printf("Unable to detect type. Check bridge file! It might have been removed, inheritance changed, etc. Location: %s %n", toDebugLocation(node)); |
| return "notype"; |
| } |
| String code = type.getNodeValue(); |
| return getType(code, attributes, false, node); |
| } |
| |
| private String toDebugLocation(Node location) { |
| StringBuilder result = new StringBuilder(); |
| while(location != null) { |
| if(result.length() > 0) { |
| result.insert(0, " > "); |
| } |
| result.insert(0, getNodeInfo(location)); |
| location = location.getParentNode(); |
| } |
| return result.toString(); |
| } |
| |
| private String getNodeInfo(Node location) { |
| String name = location.getNodeName(); |
| if (name != null) { |
| NamedNodeMap attributes = location.getAttributes(); |
| if (attributes != null) { |
| StringBuilder info = new StringBuilder(); |
| info.append(name).append("["); |
| for (int i = 0; i < attributes.getLength(); i++) { |
| Node attribute = attributes.item(i); |
| if (i > 0) { |
| info.append(", "); |
| } |
| info.append(attribute.getNodeName()).append("=").append(attribute.getNodeValue()); |
| } |
| return info.append("]").toString(); |
| } |
| return name; |
| } |
| return location.toString(); |
| } |
| |
| String getType64(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node javaType = attributes.getNamedItem("swt_java_type"); |
| if (javaType != null) { |
| Node javaType64 = attributes.getNamedItem("swt_java_type64"); |
| return javaType64 != null ? javaType64.getNodeValue() : javaType.getNodeValue(); |
| } |
| Node attrib = attributes.getNamedItem("type"); |
| if (attrib == null) return "notype"; |
| String code = attrib.getNodeValue(); |
| Node attrib64 = attributes.getNamedItem("type64"); |
| if (attrib64 != null) code = attrib64.getNodeValue(); |
| return getType(code, attributes, true, node); |
| } |
| |
| String getType(String code, NamedNodeMap attributes, boolean is64, Node location) { |
| if (code.equals("c")) return "byte"; |
| if (code.equals("i")) return "int"; |
| if (code.equals("s")) return "short"; |
| if (code.equals("l")) return "int"; |
| if (code.equals("q")) return "long"; |
| if (code.equals("C")) return "byte"; |
| if (code.equals("I")) return "int"; |
| if (code.equals("S")) return "short"; |
| if (code.equals("L")) return "int"; |
| if (code.equals("Q")) return "long"; |
| if (code.equals("f")) return "float"; |
| if (code.equals("d")) return "double"; |
| if (code.equals("B")) return "boolean"; |
| if (code.equals("v")) return "void"; |
| if (code.equals("*")) return is64 ? "long" : "int"; |
| if (code.equals("@")) return is64 ? "long" : "int"; |
| if (code.equals("#")) return is64 ? "long" : "int"; |
| if (code.equals(":")) return is64 ? "long" : "int"; |
| if (code.startsWith("^")) return is64 ? "long" : "int"; |
| if (code.startsWith("{")) { |
| return getDeclaredType(attributes, location); |
| } |
| if (code.startsWith("@?")) { |
| return is64 ? "long" : "int"; |
| } |
| return "BAD " + code; |
| } |
| |
| String getJNIType(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| String code = attributes.getNamedItem("type").getNodeValue(); |
| if (code.equals("c")) return "B"; |
| if (code.equals("i")) return "I"; |
| if (code.equals("s")) return "S"; |
| if (code.equals("l")) return "I"; |
| if (code.equals("q")) return "J"; |
| if (code.equals("C")) return "B"; |
| if (code.equals("I")) return "I"; |
| if (code.equals("S")) return "S"; |
| if (code.equals("L")) return "I"; |
| if (code.equals("Q")) return "J"; |
| if (code.equals("f")) return "F"; |
| if (code.equals("d")) return "D"; |
| if (code.equals("B")) return "Z"; |
| if (code.equals("v")) return "V"; |
| if (code.equals("*")) return "I"; |
| if (code.equals("@")) return "I"; |
| if (code.equals("#")) return "I"; |
| if (code.equals(":")) return "I"; |
| if (code.startsWith("^")) return "I"; |
| if (code.startsWith("@?")) return "I"; |
| if (code.startsWith("[")) return "BAD " + code; |
| if (code.startsWith("{")) { |
| return "BAD " + code; |
| } |
| if (code.startsWith("(")) return "BAD " + code; |
| return "BAD " + code; |
| } |
| |
| String getJavaType(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node javaType = attributes.getNamedItem("swt_java_type"); |
| if (javaType != null) return javaType.getNodeValue().trim(); |
| Node type = attributes.getNamedItem("type"); |
| if (type == null) return "notype"; |
| String code = type.getNodeValue(); |
| return getJavaType(code, attributes, false, node); |
| } |
| |
| String getJavaType64(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node javaType = attributes.getNamedItem("swt_java_type"); |
| if (javaType != null) { |
| Node javaType64 = attributes.getNamedItem("swt_java_type64"); |
| return javaType64 != null ? javaType64.getNodeValue() : javaType.getNodeValue(); |
| } |
| Node attrib = attributes.getNamedItem("type"); |
| if (attrib == null) return "notype"; |
| String code = attrib.getNodeValue(); |
| Node attrib64 = attributes.getNamedItem("type64"); |
| if (attrib64 != null) code = attrib64.getNodeValue(); |
| return getJavaType(code, attributes, true, node); |
| } |
| |
| String getJavaType(String code, NamedNodeMap attributes, boolean is64, Node location) { |
| if (code.equals("c")) return "byte"; |
| if (code.equals("i")) return "int"; |
| if (code.equals("s")) return "short"; |
| if (code.equals("l")) return "int"; |
| if (code.equals("q")) return "long"; |
| if (code.equals("C")) return "byte"; |
| if (code.equals("I")) return "int"; |
| if (code.equals("S")) return "short"; |
| if (code.equals("L")) return "int"; |
| if (code.equals("Q")) return "long"; |
| if (code.equals("f")) return "float"; |
| if (code.equals("d")) return "double"; |
| if (code.equals("B")) return "boolean"; |
| if (code.equals("v")) return "void"; |
| if (code.equals("*")) return is64 ? "long" : "int"; |
| if (code.equals("#")) return is64 ? "long" : "int"; |
| if (code.equals(":")) return is64 ? "long" : "int"; |
| if (code.startsWith("^")) return is64 ? "long" : "int"; |
| if (code.equals("@")) { |
| String type = getDeclaredType(attributes, location); |
| int index = type.indexOf('*'); |
| if (index != -1) type = type.substring(0, index); |
| index = type.indexOf('<'); |
| if (index != -1) type = type.substring(0, index); |
| return type.trim(); |
| } |
| if (code.startsWith("{")) { |
| return getDeclaredType(attributes, location).trim(); |
| } |
| if (code.startsWith("@?")) return is64 ? "long" : "int"; |
| return "BAD " + code; |
| } |
| |
| static String[] split(String str, String separator) { |
| StringTokenizer tk = new StringTokenizer(str, separator); |
| ArrayList<String> result = new ArrayList<>(); |
| while (tk.hasMoreTokens()) { |
| result.add(tk.nextToken()); |
| } |
| return result.toArray(new String[result.size()]); |
| } |
| |
| void generateFunctions() { |
| for (int x = 0; x < xmls.length; x++) { |
| Document document = documents[x]; |
| if (document == null) continue; |
| NodeList list = document.getDocumentElement().getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node node = list.item(i); |
| if ("function".equals(node.getNodeName())) { |
| if (getGen(node)) { |
| NamedNodeMap attributes = node.getAttributes(); |
| String name = attributes.getNamedItem("name").getNodeValue(); |
| NodeList params = node.getChildNodes(); |
| int count = 0; |
| for (int j = 0; j < params.getLength(); j++) { |
| Node param = params.item(j); |
| if ("arg".equals(param.getNodeName())) { |
| count++; |
| } |
| } |
| if (count > 0) { |
| out("/**"); |
| outln(); |
| } |
| int argIndex = 0; |
| for (int j = 0; j < params.getLength(); j++) { |
| Node param = params.item(j); |
| if ("arg".equals(param.getNodeName())) { |
| out(" * @param "); |
| out(getParamName(param, argIndex++)); |
| if (isStruct(param)) { |
| out(" flags=struct"); |
| } else { |
| out(" cast="); |
| NamedNodeMap paramAttributes = param.getAttributes(); |
| Node swtCast = paramAttributes.getNamedItem("swt_param_cast"); |
| String cast = swtCast != null ? swtCast.getNodeValue(): getDeclaredType(paramAttributes, param); |
| if (!cast.startsWith("(")) out("("); |
| out(cast); |
| if (!cast.endsWith(")")) out(")"); |
| } |
| outln(); |
| } |
| } |
| if (count > 0) { |
| out(" */"); |
| outln(); |
| } |
| out("public static final native "); |
| Node returnNode = getReturnNode(node.getChildNodes()); |
| if (returnNode != null) { |
| String type = getType(returnNode), type64 = getType64(returnNode); |
| out(type); |
| if (!type.equals(type64)) { |
| out(" /*"); |
| out(type64); |
| out("*/"); |
| } |
| out(" "); |
| } else { |
| out("void "); |
| } |
| out(name); |
| out("("); |
| params = node.getChildNodes(); |
| boolean first = true; |
| argIndex = 0; |
| for (int j = 0; j < params.getLength(); j++) { |
| Node param = params.item(j); |
| if ("arg".equals(param.getNodeName())) { |
| if (!first) out(", "); |
| first = false; |
| String type = getType(param), type64 = getType64(param); |
| out(type); |
| if (!type.equals(type64)) { |
| out(" /*"); |
| out(type64); |
| out("*/"); |
| } |
| out(" "); |
| out(getParamName(param, argIndex++)); |
| } |
| } |
| generateVariadics(node); |
| out(");"); |
| outln(); |
| } |
| } |
| } |
| } |
| } |
| |
| void generateVariadics(Node node) { |
| NamedNodeMap attributes = node.getAttributes(); |
| Node variadicCount = attributes.getNamedItem("swt_variadic_count"); |
| if (variadicCount != null) { |
| Node variadicTypes = attributes.getNamedItem("swt_variadic_java_types"); |
| String[] types = null; |
| if (variadicTypes != null) { |
| types = split(variadicTypes.getNodeValue(), ","); |
| } |
| int varCount = 0; |
| try { |
| varCount = Integer.parseInt(variadicCount.getNodeValue()); |
| } catch (NumberFormatException e) {} |
| for (int j = 0; j < varCount; j++) { |
| out(", "); |
| if (types != null && types.length > j && !types[j].equals("*")) { |
| out(types[j]); |
| } else if (types != null && types[types.length - 1].equals("*")) { |
| out(types[types.length - 2]); |
| } else { |
| out("int /*long*/"); |
| } |
| out(" varArg"); |
| out("" + j); |
| } |
| } |
| } |
| |
| public static void main(String[] args) { |
| try { |
| MacGenerator gen = new MacGenerator(); |
| gen.setXmls(args); |
| gen.setOutputDir("../org.eclipse.swt/Eclipse SWT PI/cocoa/"); |
| gen.setMainClass("org.eclipse.swt.internal.cocoa.OS"); |
| gen.generate(null); |
| } catch (Throwable e) { |
| e.printStackTrace(); |
| } |
| } |
| } |