blob: 0e4c1bb2d66e0b3581b2435feadd5733ccbcfa59 [file] [log] [blame]
/*
* Copyright 2003-2004 The Apache Software Foundation.
// (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis.tools.cbindings;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.*;
import org.apache.axis.tools.common.CParsingTool;
import org.apache.axis.tools.common.Configuration;
import org.apache.axis.tools.common.DirectoryTree;
import org.apache.axis.tools.common.FileActor;
import org.apache.axis.tools.common.FilePart;
import org.apache.axis.tools.common.InputCppSourceCode;
import org.apache.axis.tools.common.MethodPart;
import org.apache.axis.tools.common.Parameter;
import org.apache.axis.tools.common.ParsingException;
import org.apache.axis.tools.common.PrototypePart;
import org.apache.axis.tools.common.Signature;
import org.apache.axis.tools.common.Utils;
/**
* This class is a tool that generates C bindings (header files) from C++
* header files. It contains a main program:-
*
* usage: Java CBindingGenerator -config <file> -source <dir> -target <dir>
*/
public class CBindingGenerator extends CParsingTool implements FileActor
{
private static Hashtable classMapping = new Hashtable();
static{
classMapping.put("AxisCPPConfigDefaults", "axiscConfigDefaults");
classMapping.put("AxisException", "axiscException");
classMapping.put("BasicHandler", "axiscBasicHandler");
classMapping.put("BasicNode", "axiscBasicNode");
classMapping.put("IAttribute", "axiscAttribute");
classMapping.put("IHandlerSoapDeSerializer","axiscHandlerSoapDeSerializer");
classMapping.put("IHandlerSoapSerializer", "axiscHandlerSoapSerializer");
classMapping.put("IHeaderBlock", "axiscHeaderBlock");
classMapping.put("IMessageData", "axiscMessageData");
classMapping.put("INamespace", "axiscNamespace");
classMapping.put("ISoapAttachment", "axiscSoapAttachment");
classMapping.put("ISoapFault", "axiscSoapFault");
classMapping.put("IWrapperSoapDeSerializer","axiscSoapDeSerializer");
classMapping.put("IWrapperSoapSerializer", "axiscSoapSerializer");
classMapping.put("OtherFaultException", "axiscOtherFaultException");
classMapping.put("SoapFaultException", "axiscSoapFaultException");
classMapping.put("TypeMapping", "axiscTypeMapping");
}
private CBindingGenerator(String[] args) throws Exception
{
super(args);
}
/**
* This method is used to determine the prefix to use for the class
* methods. All class methods should be prefixed with "axisc + classname".
* We use mapping table to try to shorten long names or to remove the 'I'
* from the class name. See mapping table above.
*/
public static String getClassMapping(String theClass)
{
String val = (String)classMapping.get(theClass);
if (val == null)
val = "axisc" + theClass;
return val;
}
/**
* This method is called by the DirectoryTree with two files: the
* input (source) file and the output (target) file. This method parses
* the source (header) file and writes out the C bindings to the target
* file. The depth is how deep in the source directory tree we are.
*/
public void actOnFile(File source, File target, int depth) throws Exception
{
if (Configuration.fileExcluded(source.getName()))
return;
Utils.outputDebugString("parsing " + source + "...");
String targetName = DirectoryTree.maybeAppendSeparator(target.toString());
targetName += source.getName().substring(0, source.getName().lastIndexOf(".")) + ".h";
FileWriter fw = new FileWriter(new File(targetName), false);
BufferedWriter bw = new BufferedWriter(fw);
FileReader fr = new FileReader(source);
BufferedReader inputFile = new BufferedReader(fr);
try
{
InputCppSourceCode sourceCode = new InputCppSourceCode(inputFile, source.getName());
generateHeader(sourceCode, bw);
}
catch (ParsingException pe)
{
failed = true;
}
bw.flush();
bw.close();
inputFile.close();
}
/**
* Generates a C header file from a C++ header file.
*/
private void generateHeader(InputCppSourceCode inputFile, BufferedWriter outputFile) throws Exception
{
boolean foundCopyright = false;
String define = inputFile.getName();
define = define.toUpperCase();
define = define.substring(0, define.indexOf("."));
define += "_INCLUDED";
String syncComment =
"/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n" +
"/* NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE */\n" +
"/* ---------------------------------------------------------------- */\n" +
"/* THIS HEADER FILE PATTERNED AFTER CORRESPONDING hpp HEADER FILE. */\n" +
"/* CHANGES TO hpp HEADER FILE MAY NEED TO BE PROPAGATED HERE AND IN */\n" +
"/* THE IMPLEMEMTATION OF THE C APIS. */\n" +
"/* */\n" +
"/* THIS FILE GENERATED BY CBINDINGGENERATOR TOOL. */\n" +
"/* ---------------------------------------------------------------- */\n" +
"/* NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE */\n" +
"/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */\n";
String cplusplusStart = "#ifdef __cplusplus\n extern \"C\" {\n#endif\n";
String cplusplusEnd = "#ifdef __cplusplus\n }\n#endif\n";
int prevPart = FilePart.COMMENT; // Suppresse newline before copyright
String text;
Iterator it = inputFile.getPartIterator();
while (it.hasNext())
{
FilePart fp = (FilePart) (it.next());
if (!foundCopyright
&& (FilePart.DIRECTIVE == fp.getType()
|| FilePart.ENUM == fp.getType()
|| FilePart.PROTOTYPE == fp.getType()))
{
outputFile.write("#ifndef " + define);
outputFile.newLine();
outputFile.write("#define " + define);
outputFile.newLine();
outputFile.newLine();
outputFile.write(syncComment);
outputFile.newLine();
outputFile.write(cplusplusStart);
outputFile.newLine();
foundCopyright = true;
}
switch (fp.getType())
{
case FilePart.COMMENT :
if (FilePart.COMMENT != prevPart)
outputFile.newLine();
prevPart = fp.getType();
text = fp.toString().trim();
StringTokenizer tkzr = new StringTokenizer(text, "\n\r");
while (tkzr.hasMoreTokens())
{
String line = tkzr.nextToken().trim();
if (-1 == line.indexOf("@author"))
{
if (line.startsWith("*"))
outputFile.write(" ");
outputFile.write(line);
outputFile.newLine();
}
}
break;
case FilePart.DIRECTIVE :
if (FilePart.DIRECTIVE != prevPart)
outputFile.newLine();
prevPart = fp.getType();
generateDirective(fp, outputFile, inputFile.getName());
break;
case FilePart.TYPEDEF :
prevPart = fp.getType();
generateTypedef(fp, outputFile);
break;
case FilePart.METHOD :
case FilePart.PROTOTYPE :
if (FilePart.COMMENT != prevPart
&& FilePart.METHOD != prevPart
&& FilePart.PROTOTYPE != prevPart)
outputFile.newLine();
prevPart = fp.getType();
generateFunctionPrototype(fp, outputFile);
break;
case FilePart.ENUM :
Utils.rude(
"Enums should be wrappered with a typedef so "
+ "they appear the same in C and C++",
inputFile.getName(), 0, fp.toString());
break;
// do nothing for other file parts
}
}
outputFile.newLine();
outputFile.write(cplusplusEnd);
outputFile.newLine();
outputFile.write("#endif /* " + define + " */");
outputFile.newLine();
}
private boolean keepIfdef = false;
private void generateDirective(FilePart fp, BufferedWriter outputFile,String filename) throws Exception
{
String text = fp.toString().trim();
if (-1 != text.indexOf("include"))
{
if (-1 != text.indexOf("axis/"))
{
// We currently do not support handlers for C bindings
if (-1 == text.indexOf("BasicHandler") &&
-1 == text.indexOf("IHandlerSoap") &&
-1 == text.indexOf("AxisUserAPIArrays")) // no equivalent
{
// Change .hpp files to .h files and dump out include
text = text.replaceAll(".hpp",".h");
outputFile.write(text);
outputFile.newLine();
}
}
else if (-1 != text.indexOf(".h>"))
{
// Just dump out .h include statements
outputFile.write(text);
outputFile.newLine();
}
}
// DEAD CODE? In AxisUserAPI.h we must keep a #ifdef WIN32/#else/#endif
else if (keepIfdef || ("AxisUserAPI.hpp".equals(filename) && -1 != text.indexOf("WIN32")))
{
outputFile.write(text);
outputFile.newLine();
if (!keepIfdef)
keepIfdef = true;
else if (-1 != text.indexOf("endif"))
keepIfdef = false;
}
}
private void generateTypedef(FilePart fp, BufferedWriter outputFile) throws Exception
{
// Look to see if this is a typedef of a function pointer
// or a typedef of a datatype. Function pointer typedefs
// end with a ) before the last semicolon and do not have
// their typedef name at the end so don't attempt to prefix
// the last token with AXISC_
String text = fp.toString().trim();
String lasttok = null;
for (int i=0; i<text.length(); i++)
{
String s = "" + text.charAt(i);
if (!";".equals(s) && -1 == Utils.whitespace.indexOf(s))
lasttok = s;
}
text = changeAxisToAxisc(text);
text = replaceInString(text,"bool","AxiscBool",null);
if (!")".equals(lasttok))
{
// Put AXISC_ on to the front of the typedef name which is always at the end.
StringTokenizer st = new StringTokenizer(text);
String tok = null;
while (st.hasMoreTokens()) tok = st.nextToken();
text = replaceInString(text,tok,"AXISC_"+tok,null);
}
outputFile.write(text);
outputFile.newLine();
}
private void generateFunctionPrototype(FilePart fp, BufferedWriter outputFile) throws Exception
{
Signature sign = null;
if (FilePart.PROTOTYPE == fp.getType())
{
PrototypePart pp = (PrototypePart) fp;
sign = pp.getSignature();
}
else
{
MethodPart mp = (MethodPart) fp;
sign = mp.getSignature();
}
// Ignore private methods.
if (!sign.getScope().equals("public") &&
!sign.getScope().equals("protected"))
return;
String classname = sign.getClassName();
if (null != classname && classname.endsWith("::"))
classname = classname.substring(0, classname.length() - 2);
String method = sign.getMethodName();
if (Configuration.methodExcluded(classname, method))
return;
Parameter[] parms = sign.getParameters();
String text = new String();
// Look to see if this method is overloaded by another method
// in the same class. If methods are overloaded in the same class then
// convert the longest method prototype to C. We should really
// superset the parameters in the two prototypes instead of
// picking the longest one.
List overloads = headers.getMethods(method);
boolean sameClass = true;
if (overloads.size() > 1)
{
Iterator it = overloads.iterator();
while (it.hasNext())
{
Signature s = (Signature) it.next();
if (s.getTrimClassName().equals(classname))
{
int n1 = 0;
int n2 = 0;
if (null != s.getParameters())
n1 = s.getParameters().length;
if (null != sign.getParameters())
n2 = sign.getParameters().length;
if (n1 > n2)
return;
}
}
}
String classMapping = getClassMapping(classname);
text += "AXISC_STORAGE_CLASS_INFO\n";
if (sign.isConstructor())
text += "AXISCHANDLE " + classMapping + "Create(";
else if (sign.isDestructor())
{
text += "void " + classMapping + "Destroy(AXISCHANDLE ";
String prettyClass = classNamePretty(classname);
text += Character.toLowerCase(prettyClass.charAt(0));
text += prettyClass.substring(1);
if (null != parms)
text += ", ";
}
else
{
String retType = toCType(sign.getReturnType());
// OS/400 C compiler has a problem with "const xsdc__string" as return type,
// so just replace that with "const char *"
if (System.getProperty("os.name").equals("OS/400"))
retType = retType.replaceAll("const xsdc__string","const char *");
text += retType + " ";
text += getClassMapping(classname);
text += Character.toUpperCase(method.charAt(0));
text += method.substring(1);
String retClass = getClassFromRetType(sign);
if ("AXISCHANDLE".equals(retType) && -1 == method.indexOf(retClass))
text += retClass;
text += "(";
if (null != classname && -1 == sign.getAttributes().indexOf("static"))
{
text += "AXISCHANDLE ";
String prettyClass = classNamePretty(classname);
text += Character.toLowerCase(prettyClass.charAt(0));
text += prettyClass.substring(1);
if (null != parms)
text += ", ";
}
}
// For now, ignore operator-type methods
if (-1 != text.indexOf("Operator="))
return;
if (parms != null)
{
for (int i = 0; i < parms.length; i++)
{
// Do not include prototypes with va_list...since these functions have
// probably been added in support of C binding.
if (parms[i].isVaListArg())
return;
if (0 != i)
{
text += ", ";
// wrap long lines at 50 chars
if (text.length() > 50)
text += "\n\t";
}
text += toCType(parms[i]) + " ";
String name = parms[i].getName();
if (null != name) // "..." has no parm name
text += name;
}
}
text += ");";
outputFile.write(text);
outputFile.newLine();
}
private static String classNamePretty(String className)
{
if (className == null)
return null;
// namespace is a reserved word so make sure we never return namespace
if (className.equals("INamespace"))
return className;
if (className.startsWith("I"))
return className.substring(1);
return className;
}
/**
* Converts a C++ datatype to a C-style datatype.
* References are converted to pointers.
* STL strings are converted to char* C-style strings
* String& is converted to char* (not char**)
* AXIS, Axis and axis are converted to AXISC, Axisc and axisc
*/
private String toCType(Parameter p) throws Exception
{
if (p.isVoid())
return "void";
String type = new String();
Iterator it = p.iterator();
String prev = new String();
while (it.hasNext())
{
String tok = (String) it.next();
if ("&".equals(tok))
{
if (!"string".equals(prev))
type += "*";
}
else if ("string".equals(tok))
{
type += "char*";
prev = tok;
}
else if (headers.isClassName(tok))
return "AXISCHANDLE";
else if ("AnyType".equals(tok))
type += "AxiscAnyType";
else if ("bool".equals(tok))
type += "AxiscBool";
else if ("XML_String".equals(tok))
type += "AXISC_XML_String";
else if (tok.startsWith("xsd__"))
type += replaceInString(tok,"xsd__","xsdc__",null);
else if (-1 != tok.toLowerCase().indexOf("axis"))
type += changeAxisToAxisc(tok);
else if (!Utils.cTypeQualifiers.contains(tok) &&
!Utils.cPrimitives.contains(tok) &&
!tok.equals("tm") && // Leave struct tm alone
!tok.equals("...") && // Leave ... alone
!tok.equals("va_list") && // Leave ... alone
!tok.startsWith("xsd"))
type += "AXISC_"+tok;
else
type += tok;
if (it.hasNext())
type += " ";
}
return type;
}
private String getClassFromRetType(Signature sign)
{
Parameter p = sign.getReturnType();
if (p.isVoid())
return null;
Iterator it = p.iterator();
while (it.hasNext())
{
String tok = (String) it.next();
if (headers.isClassName(tok))
return classNamePretty(tok);
}
return null;
}
/**
* Converts AXIS, axis and Axis to AXISC, axisc and Axisc.
* Does not convert Axisc, axisc or AXISC_
*/
private static String changeAxisToAxisc(String text) throws Exception
{
text = replaceInString(text,"axis","axisc",null);
text = replaceInString(text,"Axis","Axisc","Axisc");
text = replaceInString(text,"AXIS","AXISC","AXISC_");
return text;
}
/**
* Replaces from with to in text, excluding strings that start with exclude.
*/
private static String replaceInString(String text, String from, String to, String exclude) throws Exception
{
int idx = 0;
while (-1 != text.indexOf(from, idx))
{
int start = text.indexOf(from, idx);
if (null == exclude || text.indexOf(exclude, idx) != start)
{
text = text.substring(0, start) + to + text.substring(start + from.length());
idx = start + to.length();
}
else
idx = start + exclude.length();
}
return text;
}
public static void main(String[] args)
{
boolean failed = false;
try
{
CBindingGenerator gen = new CBindingGenerator(args);
File source = gen.checkFile("-source");
File target = gen.maybeCreateDirectory("-target");
gen.headers = gen.preparseHeaders("-source");
DirectoryTree tree = new DirectoryTree(gen, new HashSet(Arrays.asList(new Object[] { "hpp" })));
tree.walkTree(source, target, 0);
failed = gen.failed;
}
catch (Exception exception)
{
exception.printStackTrace();
failed = true;
}
if (failed)
{
Utils.outputDebugString("Finished! (but encountered problems)");
System.exit(-2);
}
Utils.outputDebugString("Finished!");
}
protected void printUsage()
{
System.out.println("usage: Java CBindingGenerator -config <file> -source <dir> -target <dir>");
}
}