/*******************************************************************************
* Copyright (c) 2003, 2006 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.*;
/* Produces the java classes mapping to XPCOM Mozilla objects */
public class MozillaGenerator {
FileReader r = null;
FileWriter w = null;
int maxLines = 1000;
int cntLines = 0;
int n = 0;
String[] b = null;
String body = null;
int nMethods = 0;
String uuidName;
String uuidValue;
String className;
String parentName;
String[] constantNames;
String[] constantValues;
String[] methodNames;
String[][] argTypes;
String[][] argNames;
String bodyOrder;
Hashtable<Integer, Vector<String>> vtbls;
static boolean DEBUG = false;
// Contains the characters found before a method name
// Useful to extract the method name. e.g.
// NS_IMETHOD QueryInterface(const nsIID & uuid, void * *result) = 0;
// NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
// method name follows: QueryInterface, AddRef etc.
static String[] BEFORE_METHOD_NAME = {
" NS_IMETHOD ",
" NS_IMETHOD_(nsrefcnt) ",
" NS_IMETHOD_(void *) ",
" NS_IMETHOD_(void) ",
" NS_IMETHOD_(nsresult) ",
" NS_SCRIPTABLE NS_IMETHOD ",
" NS_SCRIPTABLE NS_IMETHOD_(nsrefcnt) ",
" NS_SCRIPTABLE NS_IMETHOD_(void *) ",
" NS_SCRIPTABLE NS_IMETHOD_(void) ",
" NS_SCRIPTABLE NS_IMETHOD_(nsresult) ",
};
static String NO_SUPER_CLASS = "SWT_NO_SUPER_CLASS";
static String[][] TYPES_C2JAVA = {
{ "PRBool *", "int[]" },
{ "nsIID &", "nsID" },
{ "nsCID &", "nsID" },
{ "nsCID * *", "int /*long*/" }, // nsID[] not supported by jnigen
{ "* *", "int /*long*/[]" },
{ "**", "int /*long*/[]" },
{ "* &", "int /*long*/[]" },
{ "PRUint32 *", "int[]" },
{ "PRInt32 *", "int[]" },
{ "PRInt64 *", "long[]" },
{ "PRUnichar *", "char[]" },
{ "char *", "byte[]" },
{ "float *", "float[]" },
{ "PRUint16 *", "short[]" },
{ "nativeWindow *", "int /*long*/[]" },
{ "nsWriteSegmentFun", "int /*long*/" },
{ "nativeWindow", "int /*long*/" },
{ "*", "int /*long*/" }, // c type containing one or more * (and any other character, and did not match previous patterns) is a simple pointer
{ "&", "int /*long*/" },
{ "PRUint32", "int" },
{ "PRInt32", "int" },
{ "PRInt64", "long" },
{ "nsresult", "int" },
{ "PRBool", "int" },
{ "float", "float" },
{ "PRUint16", "short" },
{ "size_t", "int" },
};
static String GECKO = "/bluebird/teamswt/swt-builddir/mozilla/1.4/linux_gtk2/mozilla/dist/include/";
static String TARGET_FOLDER = "/bluebird/teamswt/chrisx/amd64/workspace/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/";
static String[] XPCOM_HEADERS = {
"profile/nsIProfile.h",
"widget/nsIAppShell.h",
"widget/nsIBaseWindow.h",
"xpcom/nsIComponentManager.h",
"xpcom/nsIComponentRegistrar.h",
"webbrwsr/nsIContextMenuListener.h",
"docshell/nsIDocShell.h",
"dom/nsIDOMEvent.h",
"dom/nsIDOMMouseEvent.h",
"dom/nsIDOMUIEvent.h",
"dom/nsIDOMWindow.h",
"uriloader/nsIDownload.h",
"webbrwsr/nsIEmbeddingSiteWindow.h",
"xpcom/nsIFactory.h",
"xpcom/nsIFile.h",
"helperAppDlg/nsIHelperAppLauncherDialog.h",
"exthandler/nsIExternalHelperAppService.h", // contains nsIHelperAppLauncher
"xpcom/nsIInputStream.h",
"xpcom/nsIInterfaceRequestor.h",
"necko/nsIIOService.h",
"xpcom/nsILocalFile.h",
"xpcom/nsIMemory.h",
"progressDlg/nsIProgressDialog.h",
"windowwatcher/nsIPromptService.h",
"xpcom/nsIServiceManager.h",
"xpcom/nsISupports.h",
"webbrwsr/nsITooltipListener.h",
"necko/nsIURI.h",
"uriloader/nsIURIContentListener.h",
"xpcom/nsIWeakReference.h",
"webbrwsr/nsIWebBrowser.h",
"webbrwsr/nsIWebBrowserChrome.h",
"webbrwsr/nsIWebBrowserChromeFocus.h",
"webbrwsr/nsIWebBrowserFocus.h",
"docshell/nsIWebNavigation.h",
"uriloader/nsIWebProgress.h",
"uriloader/nsIWebProgressListener.h",
"embed_base/nsIWindowCreator.h",
"windowwatcher/nsIWindowWatcher.h" };
public static void main(String[] args) {
MozillaGenerator x = new MozillaGenerator();
for (int i = 0; i < XPCOM_HEADERS.length; i++)
x.parse(GECKO + XPCOM_HEADERS[i], TARGET_FOLDER);
x.outputVtblCall();
System.out.println("done");
}
public MozillaGenerator() {
vtbls = new Hashtable<Integer, Vector<String>>();
}
/** Write callbacks */
public void write(String data) {
if (DEBUG) {
System.out.print(data);
return;
}
try {
w.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
public void writeLine() {
if (DEBUG) {
System.out.println();
return;
}
write("\r\n");
}
public void writeLine(String data) {
if (DEBUG) {
System.out.println(data);
return;
}
write(data + "\r\n");
}
public void writeCopyrights() {
writeLine(COPYRIGHTS);
}
public void writePackageDeclaration() {
writeLine(PACKAGE_DECLARATION);
}
public void writeClassDeclaration(String className, String parentName) {
String line = "public class " + className;
if (!parentName.equals(NO_SUPER_CLASS)) line += " extends " + parentName;
line += " {";
writeLine(line);
}
public void writeLastMethodId(String parentName, int nMethods) {
String line = "\tstatic final int LAST_METHOD_ID = ";
if (!parentName.equals(NO_SUPER_CLASS)) line += parentName + ".LAST_METHOD_ID + " + nMethods + ";";
else line += "" + (nMethods - 1)+ ";"; // zero indexed
writeLine(line);
}
public void writeIID(String uuidName, String uuidValue) {
writeLine("\tpublic static final String " + uuidName + " =");
writeLine("\t\t\"" + uuidValue + "\";");
writeLine();
String iid = uuidName.substring(0, uuidName.indexOf("_STR"));
writeLine("\tpublic static final nsID " + iid + " =");
writeLine("\t\tnew nsID(" + uuidName + ");");
}
public void writeAddressField() {
writeLine("\tint /*long*/ address;");
}
public void writeConstructor(String className, String parentName) {
writeLine("\tpublic " + className + "(int /*long*/ address) {");
if (!parentName.equals(NO_SUPER_CLASS)) {
writeLine("\t\tsuper(address);");
} else {
writeLine("\t\tthis.address = address;");
}
writeLine("\t}");
}
public void writeAddressGetter() {
writeLine("\tpublic int /*long*/ getAddress() {");
writeLine("\t\treturn this.address;");
writeLine("\t}");
}
public void writeConstant(String name, String value) {
writeLine("\tpublic static final int " + name + " = " + value + ";");
}
public void writeMethod(String name, String parentName, int methodIndex, String[] argTypes, String[] argNames) {
write("\tpublic int " + name + "(");
for (int i = 0; i < argTypes.length; i++) {
write(argTypes[i] + " " + argNames[i]);
if (i < argTypes.length - 1)
write(", ");
}
write(") {");
writeLine();
String line = "\t\treturn XPCOM.VtblCall(";
if (!parentName.equals(NO_SUPER_CLASS)) line += parentName + ".LAST_METHOD_ID + " + (methodIndex + 1) + ", getAddress()";
else line += methodIndex + ", getAddress()"; // zero indexed
write(line);
if (argTypes.length > 0)
write(", ");
for (int i = 0; i < argTypes.length; i++) {
write(argNames[i]);
if (i < argTypes.length - 1)
write(", ");
}
writeLine(");");
writeLine("\t}");
}
public void writeClassEnd() {
write("}");
}
public void logVtblCall(String[] argTypes) {
String vtbl = "static final native int VtblCall(int fnNumber, int /*long*/ ppVtbl";
if (argTypes.length > 0)
vtbl += ", ";
for (int i = 0; i < argTypes.length; i++) {
vtbl += argTypes[i] + " arg" + i;
if (i < argTypes.length - 1)
vtbl += ", ";
}
vtbl += ");";
Integer key = new Integer(argTypes.length);
Vector<String> list = vtbls.get(key);
if (list == null) {
list = new Vector<String>();
vtbls.put(key, list);
}
boolean duplicate = false;
Enumeration<String> e = list.elements();
while (e.hasMoreElements()) {
String s = e.nextElement();
if (vtbl.equals(s)) {
duplicate = true;
break;
}
}
if (!duplicate)
list.add(vtbl);
}
public void outputVtblCall() {
Enumeration<Integer> e = vtbls.keys();
int n = 0;
while (e.hasMoreElements()) {
e.nextElement();
n++;
}
Integer[] keys = new Integer[n];
e = vtbls.keys();
n = 0;
while (e.hasMoreElements()) {
keys[n] = e.nextElement();
n++;
}
Arrays.sort(keys);
for (int i = 0; i < keys.length; i++) {
Vector<?> list = vtbls.get(keys[i]);
Object[] elts = list.toArray();
Arrays.sort(elts);
for (int j = 0; j < elts.length; j++) {
System.out.println(elts[j]);
}
}
}
/** Parsing invoking write callbacks */
/*
* Convert a C header file into a Java source file matching SWT Mozilla
binding.
*/
public void parse(String src, String destPath) {
if (DEBUG) writeLine("*** PARSING <"+src+"> to folder "+destPath);
b = new String[maxLines];
cntLines = 0;
try {
r = new FileReader(src);
BufferedReader br = new BufferedReader(r);
while ((b[cntLines] = br.readLine()) != null) {
cntLines++;
}
br.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
n = 0;
boolean lookForClasses = true;
while (lookForClasses) {
/* parsing */
lookForClasses = parse();
String destFile = destPath + className + ".java";
try {
w = new FileWriter(destFile);
if (DEBUG) writeLine("** CREATED JAVA FILE <"+destFile+">");
} catch (IOException e) {
e.printStackTrace();
return;
}
/* writing */
writeCopyrights();
writePackageDeclaration();
writeLine();
writeClassDeclaration(className, parentName);
writeLine();
writeLastMethodId(parentName, nMethods);
writeLine();
writeIID(uuidName, uuidValue);
writeLine();
if (parentName.equals(NO_SUPER_CLASS)) {
writeAddressField();
writeLine();
}
writeConstructor(className, parentName);
writeLine();
if (parentName.equals(NO_SUPER_CLASS)) {
writeAddressGetter();
writeLine();
}
int constantIndex = 0, methodIndex = 0;
for (int i = 0; i < bodyOrder.length(); i++) {
if (bodyOrder.charAt(i) == 'C') {
writeConstant(constantNames[constantIndex],
constantValues[constantIndex]);
if (i < bodyOrder.length() - 1) writeLine();
constantIndex++;
} else if (bodyOrder.charAt(i) == 'M') {
writeMethod(methodNames[methodIndex], parentName, methodIndex,
argTypes[methodIndex], argNames[methodIndex]);
if (i < bodyOrder.length() - 1) writeLine();
methodIndex++;
}
}
writeClassEnd();
try {
w.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String getPackages() {
return "package org.eclipse.swt.internal.mozilla;";
}
public boolean parse() {
if (!jumpToUuidDeclaration()) return false;
uuidName = getUuidName(b[n]);
if (DEBUG)
System.out.println("UUID name: <" + uuidName + ">");
uuidValue = getUuidValue(b[n]);
if (DEBUG)
System.out.println("UUID value: <" + uuidValue + ">");
jumpToInterfaceDeclaration();
className = getClassName(b[n]);
if (DEBUG)
System.out.println("Interface name: <" + className + ">");
parentName = getParentName(b[n]);
if (DEBUG)
System.out.println("parentName: <" + parentName + ">");
parseBody();
return true;
}
boolean jumpToUuidDeclaration() {
// jump to line matching: "#define NS_IWEBBROWSERCHROME_IID_STR "ba434c60-9d52-11d3-afb0-00a024ffc08c""
while (!(b[n].startsWith("#define ") && b[n].indexOf("_IID_STR \"") != -1)) {
n++;
if (n >= cntLines) return false;
}
return true;
}
//assume a declaration matching: "#define NS_IWEBBROWSERCHROME_IID_STR "ba434c60-9d52-11d3-afb0-00a024ffc08c""
//returns NS_IWEBBROWSERCHROME_IID_STR
String getUuidName(String declaration) {
return declaration.substring(declaration.indexOf("#define ")
+ "#define ".length(), declaration.indexOf(" \""));
}
//assume a declaration matching: "#define NS_IWEBBROWSERCHROME_IID_STR "ba434c60-9d52-11d3-afb0-00a024ffc08c""
//returns ba434c60-9d52-11d3-afb0-00a024ffc08c
String getUuidValue(String declaration) {
return declaration.substring(declaration.indexOf("_IID_STR \"")
+ "_IID_STR \"".length(), declaration.lastIndexOf('"'));
}
void jumpToInterfaceDeclaration() {
// jump to line matching: "class NS_NO_VTABLE nsIWebBrowserChrome : public nsISupports {"
while (!(b[n].startsWith("class NS_NO_VTABLE "))) {
n++;
}
}
// Assume a declaration matching: "class NS_NO_VTABLE nsIWebBrowserChrome : public nsISupports {"
// or "class NS_NO_VTABLE NS_SCRIPTABLE nsIWebBrowserChrome : public nsISupports {" returns nsIWebBrowserChrome.
// Special case for nsISupports that has no super class: class NS_NO_VTABLE nsISupports {
String getClassName(String declaration) {
int endIndex = declaration.indexOf(" :");
// nsISupports special case (no super class)
if (endIndex == -1) endIndex = declaration.indexOf(" {");
String searchString = "class NS_NO_VTABLE NS_SCRIPTABLE";
int startIndex = declaration.indexOf(searchString);
if (startIndex == -1) {
searchString = "class NS_NO_VTABLE ";
startIndex = declaration.indexOf(searchString);
}
return declaration.substring(startIndex + searchString.length(), endIndex);
}
// assume a declaration matching: "class NS_NO_VTABLE nsIWebBrowserChrome : public nsISupports {"
// returns nsISupports
// special case for nsISupports that has no super class: class NS_NO_VTABLE nsISupports {
String getParentName(String declaration) {
if (declaration.indexOf(" :") == -1) return NO_SUPER_CLASS;
return declaration.substring(declaration.indexOf(": public ")
+ ": public ".length(), declaration.indexOf(" {"));
}
// parse methods and constants declarations starting at the current index
// out:
// .String body - contains the corresponding java content
// .n - set to the end of the interface body declaration ( line with the enclosing "};" )
// .nMethods - set to the number of methods parsed
void parseBody() {
body = "";
bodyOrder = "";
int nConstants = 0;
nMethods = 0;
int tmp_n = n;
while (true) {
int type = jumpToNextConstantOrMethod();
if (type == CONSTANT)
nConstants++;
if (type == METHOD)
nMethods++;
if (type == END_BODY)
break;
n++;
}
n = tmp_n;
constantNames = new String[nConstants];
constantValues = new String[nConstants];
methodNames = new String[nMethods];
argTypes = new String[nMethods][];
argNames = new String[nMethods][];
int constantIndex = 0, methodIndex = 0;
while (true) {
int type = jumpToNextConstantOrMethod();
if (type == CONSTANT) {
parseConstant(b[n], constantIndex);
bodyOrder += "C";
constantIndex++;
}
if (type == METHOD) {
parseMethod(b[n], methodIndex);
logVtblCall(argTypes[methodIndex]);
bodyOrder += "M";
methodIndex++;
}
if (type == END_BODY)
return;
n++;
}
}
static int CONSTANT = 0;
static int METHOD = 1;
static int END_BODY = 2;
boolean isEndOfInterfaceBody() {
return b[n].startsWith("};");
}
int jumpToNextConstantOrMethod() {
while (!isEndOfInterfaceBody()) {
if (b[n].startsWith(" enum { ")) {
return CONSTANT;
}
if (methodNameStartIndexOf(b[n]) != -1) {
return METHOD;
}
n++;
}
return END_BODY;
}
void parseConstant(String constant, int constantIndex) {
String constantName = constant.substring(constant.indexOf(" enum { ")
+ " enum { ".length(), constant.indexOf(" ="));
if (DEBUG)
writeLine("constantName <" + constantName + ">");
constantNames[constantIndex] = constantName;
// most constants values have a trailing U
// enum { APP_TYPE_UNKNOWN = 0U };
int endIndex = constant.indexOf("U };");
// a few others don't
// enum { ENUMERATE_FORWARDS = 0 };
if (endIndex == -1) endIndex = constant.indexOf(" };");
String constantValue = constant.substring(constant.indexOf(" = ")
+ " = ".length(), endIndex);
if (DEBUG)
writeLine("constantValue <" + constantValue + ">");
constantValues[constantIndex] = constantValue;
}
// NS_IMETHOD SetStatus(PRUint32 statusType, const PRUnichar *status) = 0;
// identify:
// method name: <SetStatus>
// Nbr of arguments: 2
// Type of argument 0: PRUint32
// Name of argument 0: statusType
// Type of argument 1: const PRUnichar *
// Name of argument 1: status
void parseMethod(String line, int methodIndex) {
int start = methodNameStartIndexOf(line);
int end = methodNameEndIndexOf(line);
String methodName = line.substring(start, end);
if (DEBUG)
writeLine("method name: <" + methodName + ">");
methodNames[methodIndex] = methodName;
int argStart = end+"(".length();
int argEnd = line.indexOf(")", argStart);
parseArgs(line.substring(argStart, argEnd), methodIndex);
}
// Given a line, returns the start of the method name or -1
// if the line does not contain a method declaration.
int methodNameStartIndexOf(String line) {
for (int i = 0; i < BEFORE_METHOD_NAME.length; i++) {
int index = line.indexOf(BEFORE_METHOD_NAME[i]);
if (index != -1) return index + BEFORE_METHOD_NAME[i].length();
}
return -1;
}
int methodNameEndIndexOf(String line) {
int startIndex = methodNameStartIndexOf(line);
return line.indexOf("(", startIndex);
}
void parseArgs(String args, int methodIndex) {
int nArgs = -1;
// methods with no args look like: () or (void)
String[] noArgs = new String[] { "", "void" };
for (int i = 0; i < noArgs.length; i++) {
if (args.equals(noArgs[i])) {
nArgs = 0;
break;
}
}
if (nArgs == -1)
nArgs = count(args, ", ") + 1;
String[] argTypes = new String[nArgs];
this.argTypes[methodIndex] = argTypes;
String[] argNames = new String[nArgs];
this.argNames[methodIndex] = argNames;
int typeStart = 0;
// name is separated from its type by either of the following (sorted by decreasing size to find the most complete pattern */
String[] typeNameSep = new String[] { " * *", " **", " * & ", " * ", " *",
" & ", " " };
for (int i = 0; i < nArgs; i++) {
/* get the type */
int nextTypeStart = i < nArgs - 1 ? args.indexOf(", ", typeStart)
+ ", ".length() : args.length();
int typeNameSepIndex = 0;
int separatorIndex = 0;
for (; typeNameSepIndex < typeNameSep.length; typeNameSepIndex++) {
separatorIndex = args.indexOf(typeNameSep[typeNameSepIndex],
typeStart);
if (separatorIndex != -1 && separatorIndex < nextTypeStart)
break;
}
String separator = typeNameSep[typeNameSepIndex];
argTypes[i] = getC2JavaType(args.substring(typeStart,
separatorIndex + separator.length()));
if (DEBUG)
writeLine("arg type" + i + ": <" + argTypes[i] + ">");
/* get the name */
int nameStart = separatorIndex + separator.length();
int nameEnd = i < nArgs - 1 ? args.indexOf(", ", nameStart) : args
.length();
argNames[i] = args.substring(nameStart, nameEnd);
if (DEBUG)
writeLine("arg name" + i + ": <" + argNames[i] + ">");
typeStart = nextTypeStart;
}
}
String getC2JavaType(String cType) {
for (int i = 0; i < TYPES_C2JAVA.length; i++) {
if (cType.indexOf(TYPES_C2JAVA[i][0]) != -1)
return TYPES_C2JAVA[i][1];
}
return "!ERROR UNKNOWN C TYPE <" + cType + ">!";
}
// how many times part can be found in s
static int count(String s, String part) {
int index = -1, cnt = 0;
while ((index = s.indexOf(part, index + 1)) != -1)
cnt++;
return cnt;
}
static String COPYRIGHTS =
"/* ***** BEGIN LICENSE BLOCK *****\r\n"
+ " * Version: MPL 1.1\r\n"
+ " *\r\n"
+ " * The contents of this file are subject to the Mozilla Public License Version\r\n"
+ " * 1.1 (the \"License\"); you may not use this file except in compliance with\r\n"
+ " * the License. You may obtain a copy of the License at\r\n"
+ " * http://www.mozilla.org/MPL/\r\n"
+ " *\r\n"
+ " * Software distributed under the License is distributed on an \"AS IS\" basis,\r\n"
+ " * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\r\n"
+ " * for the specific language governing rights and limitations under the\r\n"
+ " * License.\r\n"
+ " *\r\n"
+ " * The Original Code is Mozilla Communicator client code, released March 31, 1998.\r\n"
+ " *\r\n"
+ " * The Initial Developer of the Original Code is\r\n"
+ " * Netscape Communications Corporation.\r\n"
+ " * Portions created by Netscape are Copyright (C) 1998-1999\r\n"
+ " * Netscape Communications Corporation. All Rights Reserved.\r\n"
+ " *\r\n"
+ " * Contributor(s):\r\n"
+ " *\r\n"
+ " * IBM\r\n"
+ " * - Binding to permit interfacing between Mozilla and SWT\r\n"
+ " * - Copyright (C) 2003, 2009 IBM Corp. All Rights Reserved.\r\n"
+ " *\r\n" + " * ***** END LICENSE BLOCK ***** */";
static String PACKAGE_DECLARATION = "package org.eclipse.swt.internal.mozilla;";
}