// Copyright 2004-2005, FreeHEP.
package org.freehep.aid;
import java.io.*;
import java.util.*;
import org.freehep.rtti.*;
import org.freehep.util.*;
import org.freehep.util.io.*;
/**
* @author Mark Donszelmann
* @version $Id: PythonClassGenerator.java 8584 2006-08-10 23:06:37Z duns $
*/
public class PythonClassGenerator extends AbstractGenerator {
protected static final String language = "py";
protected static final Set builtinTypes = new HashSet();
static {
builtinTypes.add("NoneType");
builtinTypes.add("TypeType");
builtinTypes.add("BooleanType");
builtinTypes.add("IntType");
builtinTypes.add("LongType");
builtinTypes.add("FloatType");
builtinTypes.add("ComplexType");
builtinTypes.add("StringType");
builtinTypes.add("UnicodeType");
builtinTypes.add("TupleType");
builtinTypes.add("ListType");
builtinTypes.add("DictType");
builtinTypes.add("DictionaryType");
builtinTypes.add("FunctionType");
builtinTypes.add("LambdaType");
builtinTypes.add("GeneratorType");
builtinTypes.add("CodeType");
builtinTypes.add("ClassType");
builtinTypes.add("InstanceType");
builtinTypes.add("MethodType");
builtinTypes.add("UnboundMethodType");
builtinTypes.add("BuiltinFunctionType");
builtinTypes.add("BuiltinMethodType");
builtinTypes.add("ModuleType");
builtinTypes.add("FileType");
builtinTypes.add("XRangeType");
builtinTypes.add("SliceType");
builtinTypes.add("EllipsisType");
builtinTypes.add("TracebackType");
builtinTypes.add("FrameType");
builtinTypes.add("BufferType");
builtinTypes.add("StringTypes");
}
protected UserProperties importProperties = new UserProperties();
protected UserProperties typeProperties = new UserProperties();
protected UserProperties namesProperties = new UserProperties();
protected UserProperties valueProperties = new UserProperties();
protected UserProperties commentProperties = new UserProperties();
public PythonClassGenerator(String propDir) {
super();
AidUtil.loadProperties(properties, getClass(), propDir, "aid.py.properties");;
AidUtil.loadProperties(importProperties, getClass(), propDir, "aid.imports."+language+".properties");
AidUtil.loadProperties(typeProperties, getClass(), propDir, "aid.types."+language+".properties");
AidUtil.loadProperties(namesProperties, getClass(), propDir, "aid.names."+language+".properties");
AidUtil.loadProperties(valueProperties, getClass(), propDir, "aid.values."+language+".properties");
AidUtil.loadProperties(commentProperties, getClass(), propDir, "aid.comments."+language+".properties");
properties.setProperty("py.interface", "true");
}
public String directory(IClass clazz) {
String directory = clazz.getPackageName();
directory = typeProperties.getProperty(directory, directory);
return directory.replaceAll("\\.","/");
}
public String filename(IClass clazz) {
return clazz.getName()+".py";
}
protected boolean isClass(IClass clazz) {
return true;
}
public boolean print(File file, IClass clazz) throws IOException {
IndentPrintWriter out = new IndentPrintWriter(new PrintWriter(new BufferedWriter(new FileWriter(file))));
out.setIndentString(" ");
// these go in separate files
printEnumFields(file, clazz);
// Prepare...
IMethod[] methods = clazz.getMethods();
Set/*<String>*/ allNames = new HashSet();
Map/*<String, IMethod>*/ single = new HashMap();
Map/*<String, List<IMethod> >*/ overloaded = new HashMap();
// FIXME, we should also check the superclass if the same methodname is defined.
if (methods.length > 0) {
// loop over all methods to find overloaded ones, store the methods and the names.
for (int i=0; i<methods.length; i++) {
String name = methods[i].getName();
if (allNames.contains(name)) {
List methodList = (List)overloaded.get(name);
if (methodList == null) {
methodList = new ArrayList();
overloaded.put(name, methodList);
}
methodList.add(methods[i]);
// remove the entry from single, and add to methodList if exist
IMethod first = (IMethod)single.remove(name);
if (first != null) methodList.add(first);
} else {
allNames.add(name);
single.put(name, methods[i]);
}
}
}
printHeader(out, clazz);
SortedSet sysImports = new TreeSet();
SortedSet imports = new TreeSet();
// print import statements
importFromSingle(clazz, single, sysImports, imports);
importFromOverloaded(clazz, overloaded, sysImports, imports);
if (!sysImports.isEmpty()) {
out.println();
for (Iterator i = sysImports.iterator(); i.hasNext(); ) {
out.println(i.next());
}
}
if (!imports.isEmpty()) {
out.println();
for (Iterator i = imports.iterator(); i.hasNext(); ) {
out.println(i.next());
}
}
out.println();
printClassHeader(out, clazz);
// fields first for enums
IField[] fields = clazz.getFields();
if (fields.length > 0) out.println();
for (int i=0; i<fields.length; i++) {
if (i!= 0) out.println();
printField(out, fields[i], true);
}
if (methods.length > 0) {
// output the single methods
for (Iterator i=single.keySet().iterator(); i.hasNext(); ) {
String name = (String)i.next();
IMethod method = (IMethod)single.get(name);
printMethod(out, clazz, method);
}
// output the overloaded methods, filter out duplicates, and print their dispatch method
for (Iterator i=overloaded.keySet().iterator(); i.hasNext(); ) {
String name = (String)i.next();
List methodList = (List)overloaded.get(name);
int maxNumberOfArguments = 0;
Set overloadedNames = new HashSet();
for (Iterator j=methodList.iterator(); j.hasNext(); ) {
IMethod method = (IMethod)j.next();
String overloadedName = overloadedMethodName(method);
if (!overloadedNames.contains(overloadedName)) {
overloadedNames.add(overloadedName);
printOverloadedMethod(out, clazz, method, overloadedName);
maxNumberOfArguments = Math.max(maxNumberOfArguments, method.getParameterTypes().length);
}
}
printDispatchMethod(out, clazz, methods, name, maxNumberOfArguments);
}
}
printEOCComments(out, clazz);
printEOFComments(out, clazz);
out.println("# end of class or interface");
printEOFComments(out, clazz);
out.println();
out.close();
return false;
}
protected void printEnumFields(File file, IClass clazz) throws IOException {
IField[] enums = clazz.getEnumFields();
for (int i=0; i<enums.length; i++) {
String name = enums[i].getNamedType().getType().getName();
IndentPrintWriter eout = new IndentPrintWriter(new BufferedWriter(new FileWriter(
new File(file.getParentFile(), name+".py"))));
printHeader(eout, clazz);
eout.println("class "+name+":");
printField(eout, enums[i], false);
eout.close();
}
}
protected void printHeader(IndentPrintWriter out, IClass clazz) {
warning(out);
out.println();
String[] packageComments = clazz.getPackageComments(language);
if (packageComments.length > 0) {
out.println("\"\"\"");
for (int i=0; i<packageComments.length; i++) {
out.println(packageComments[i]);
}
out.println("\"\"\"");
}
// String packageName = clazz.getPackageName();
// if (!packageName.equals("")) packageName = typeProperties.getProperty(packageName, packageName);
// if (!packageName.equals("")) {
// out.println("package "+packageName+";");
// }
}
protected void warning(IndentPrintWriter out) {
// first line to signal our files!
out.println("\"\"\" AID-GENERATED");
out.println("=========================================================================");
out.println("This class was generated by AID - Abstract Interface Definition ");
out.println("DO NOT MODIFY, but use the org.freehep.aid.Aid utility to regenerate it. ");
out.println("=========================================================================");
out.println("\"\"\"");
}
protected void importFromOverloaded(IClass clazz, Map/*<String, List<IMethod> >*/ overloaded, SortedSet sysImports, SortedSet imports) {
String[] interfaces = clazz.getInterfaces();
for (int i=0; i<interfaces.length; i++) {
importFrom(interfaces[i], clazz, sysImports, imports, true);
}
// we only care about overloaded methods, since the others have no types.
for (Iterator i=overloaded.keySet().iterator(); i.hasNext(); ) {
String name = (String)i.next();
List methodList = (List)overloaded.get(name);
for (Iterator j=methodList.iterator(); j.hasNext(); ) {
IMethod method = (IMethod)j.next();
INamedType[] parameterTypes = method.getParameterTypes();
for (int p=0; p<parameterTypes.length; p++) {
importFrom(parameterTypes[p].getType(), clazz, sysImports, imports, true);
String init = parameterTypes[p].getInit();
if (init != null) {
init = valueProperties.getProperty(init, init);
importFrom(init, clazz, sysImports, imports, false);
}
}
}
}
IField[] fields = clazz.getFields();
for (int i=0; i<fields.length; i++) {
IType type = fields[i].getNamedType().getType();
importFrom(type, clazz, sysImports, imports, true);
}
}
// imports only definitions for init values, since py has no types.
protected void importFromSingle(IClass clazz, Map/*<String, IMethod>*/ single, SortedSet sysImports, SortedSet imports) {
for (Iterator i=single.keySet().iterator(); i.hasNext(); ) {
String name = (String)i.next();
IMethod method = (IMethod)single.get(name);
INamedType[] parameterTypes = method.getParameterTypes();
for (int p=0; p<parameterTypes.length; p++) {
String init = parameterTypes[p].getInit();
if (init != null) {
init = valueProperties.getProperty(init, init);
importFrom(init, clazz, sysImports, imports, false);
}
}
}
}
protected void importFrom(IType type, IClass clazz, SortedSet sysImports, SortedSet imports, boolean report) {
if (type.isEnumeration()) return;
importFrom(type.getName(), clazz, sysImports, imports, report);
}
protected void importFrom(String name, IClass clazz, SortedSet sysImports, SortedSet imports, boolean report) {
if (name == null) return;
// escape the array name.
if (name.equals("[]")) name = "\\[\\]";
name = typeProperties.getProperty(name, name).trim();
if (name.equals("")) return;
String importName = importProperties.getProperty(name, null);
if (importName != null) {
importName = importName.trim();
if (!importName.equals(clazz.getPackageName()+"."+clazz.getName())) {
int dot = importName.lastIndexOf(".");
if (dot >= 0) {
importName = importName.substring(0, dot);
importName = typeProperties.getProperty(importName, importName).trim() + "." + name;
imports.add("from "+importName+" import *");
} else {
sysImports.add("from "+importName+" import "+name);
}
}
} else {
if (report) System.err.println("Do not know how to import '"+name+"'");
}
}
protected void printClassHeader(IndentPrintWriter out, IClass clazz) {
out.print("class ");
out.print(clazz.getName());
String[] classes = clazz.getInterfaces();
if (classes.length > 0) {
int k = 0;
for (int i=0; i<classes.length; i++) {
String superclass = typeProperties.getProperty(classes[i], classes[i]);
if (!superclass.equals("")) {
if (k == 0) {
out.print("(");
} else {
out.print(", ");
}
out.print(superclass);
k++;
}
}
if (k > 0) out.print(")");
}
out.println(": ");
String[] comments = clazz.getComments(language);
if (comments.length > 0) {
out.println(" \"\"\"");
for (int i=0; i<comments.length; i++) {
out.print(" ");
out.println(comments[i]);
}
out.println(" \"\"\"");
out.println();
}
}
protected void printMethod(IndentPrintWriter out, IClass clazz, IMethod method) {
printMethodHeader(out, clazz, method);
printMethodComments(out, method);
printMethodBody(out, clazz, method);
}
protected void printDispatchMethod(IndentPrintWriter out, IClass clazz, IMethod[] methods, String name, int maxNumberOfParameters) {
// method header
out.print(" def ");
out.print(name);
out.print("(self");
for (int i=1; i<=maxNumberOfParameters ; i++) {
out.print(", ");
out.print("arg"+i+" = None");
}
out.println("):");
// special comment
out.println(" \"\"\"Dispatch method for the '"+name+"' routine.");
out.println(" This method takes a maximum number of arguments = "+maxNumberOfParameters);
out.println(" Look at the individual methods with name '"+name+"_...' for documentation.");
out.println(" @throws TypeError if number of parameters incorrect or types incompatible.");
out.println(" \"\"\"");
out.println();
// find all overloaded methods and put the expressions to select them into a map, mapping to the calls.
// this will filter all duplicates.
Map expressions = new HashMap();
for (int m=0; m<methods.length; m++) {
IMethod method = methods[m];
if (method.getName().equals(name)) {
INamedType[] parameterTypes = method.getParameterTypes();
// the expression
StringBuffer expression = new StringBuffer();
// FIXME, since we map parameters of M types to N types where M >= N, some if clauses are never
// reached and could be taken out...
for (int i=0; i<maxNumberOfParameters; i++) {
if (i != 0) expression.append(" and ");
if (i < parameterTypes.length) {
String type = type(parameterTypes[i].getType());
String listSubType = null;
if (!builtinTypes.contains(type)) {
// special case for listType, we check on subtype later...
String listType = "ListType";
if (type.endsWith(listType)) {
listSubType = type.substring(0, type.length()-listType.length());
type = listType;
}
}
expression.append("isinstance(arg"+(i+1)+", "+type+")");
// NOTE: does not handle Lists of Lists
// check on subtype.
if (listSubType != null) {
expression.append(" and (((len(arg"+(i+1)+") > 0) and isinstance(arg"+(i+1)+"[0], "+listSubType+")) or (len(arg"+(i+1)+") == 0))");
}
} else {
expression.append("(arg"+(i+1)+" == None)");
}
}
if (!expressions.containsKey(expression.toString())) {
// the call
StringBuffer call = new StringBuffer();
call.append(" self.");
call.append(overloadedMethodName(method));
call.append("(");
for (int i=0; i<parameterTypes.length; i++) {
if (i != 0) call.append(", ");
call.append("arg"+(i+1));
}
call.append(")");
expressions.put(expression.toString(), call.toString());
}
}
}
// now print all left over expressions and their calls.
String expression = null;
for (Iterator i = expressions.keySet().iterator(); i.hasNext(); ) {
// the if
out.print(" ");
out.print(expression == null ? "if" : "elif");
out.print(" ");
expression = (String)i.next();
out.print(expression);
out.print(":");
out.println();
out.println(expressions.get(expression));
}
out.println(" else:");
out.println(" raise TypeError");
out.println();
}
protected void printOverloadedMethod(IndentPrintWriter out, IClass clazz, IMethod method, String overloadedName) {
INamedType[] parameterTypes = method.getParameterTypes();
out.print(" def ");
out.print(overloadedName);
out.print("(self");
for (int i=0; i<parameterTypes.length; i++) {
out.print(", ");
String name = parameterTypes[i].getName();
out.print(namesProperties.getProperty(name, name));
}
out.println("):");
printMethodComments(out, method);
out.println(" raise NotImplementedError");
out.println();
}
protected void printMethodComments(IndentPrintWriter out, IMethod method) {
String[] comments = method.getComments(language);
if (comments.length > 0) {
out.println(" \"\"\"");
for (int i=0; i<comments.length; i++) {
// type translation
comments[i] = translate(comments[i], typeProperties);
// value translation
comments[i] = translate(comments[i], valueProperties);
// comment translation
comments[i] = translate(comments[i], commentProperties);
out.print(" ");
out.println(comments[i]);
}
out.println(" \"\"\"");
out.println();
}
}
private String translate(String s, Properties p) {
for (Enumeration e=p.propertyNames(); e.hasMoreElements(); ) {
String name = (String)e.nextElement();
// FIXME: name should be escaped...
s = s.replaceAll("(^|\\s)"+name+"(\\s|$)", "$1"+p.getProperty(name)+"$2");
}
return s;
}
protected String overloadedMethodName(IMethod method) {
StringBuffer s = new StringBuffer();
s.append(method.getName());
INamedType[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0) {
for (int i=0; i<parameterTypes.length; i++) {
// if (typeName.length() > 0) typeName.setCharAt(0, Character.toUpperCase(typeName.charAt(0)));
s.append("_");
s.append(type(parameterTypes[i].getType()));
}
} else {
s.append("_None");
}
return s.toString();
}
protected void printMethodHeader(IndentPrintWriter out, IClass clazz, IMethod method) {
out.print(" def ");
out.print(method.getName());
out.print("(self");
INamedType[] parameterTypes = method.getParameterTypes();
int noOfParameters = parameterTypes.length;
for (int i=0; i<noOfParameters; i++) {
out.print(", ");
String name = parameterTypes[i].getName();
out.print(namesProperties.getProperty(name, name));
String init = parameterTypes[i].getInit();
if (init != null) init = valueProperties.getProperty(init, init);
if ((init != null) && !init.equals("")) {
out.print(" = ");
out.print(init);
}
}
out.println("):");
}
protected void printMethodBody(IndentPrintWriter out, IClass clazz, IMethod method) {
out.println(" raise NotImplementedError");
out.println();
}
protected void printField(IndentPrintWriter out, IField field, boolean prefix) {
IType type = field.getNamedType().getType();
String[] comments = field.getComments(language);
int enumInit = 0;
boolean enumSet = false;
StringBuffer initStringBuffer = new StringBuffer();
out.print(" ");
while (field != null) {
INamedType namedType = field.getNamedType();
String name = namedType.getName();
name = namesProperties.getProperty(name, name);
if (type.isEnumeration() && prefix && !type.getName().equals("")) out.print(type.getName()+"_");
out.print(name);
String init = namedType.getInit();
if (init != null) init = valueProperties.getProperty(init, init);
if ((init != null) && !init.equals("")) {
if (type.isEnumeration()) {
int value = Integer.decode(init).intValue();
initStringBuffer.append(enumInit == 0 ? "[" : ", ");
initStringBuffer.append(value);
enumInit = value+1;
enumSet = true;
} else {
out.print(" = ");
out.print(init);
}
} else if (type.isEnumeration()) {
initStringBuffer.append(enumInit == 0 ? "[" : ", ");
initStringBuffer.append(enumInit);
enumInit++;
}
field = field.getNext();
if (field != null) {
out.print(", ");
}
}
if (type.isEnumeration()) {
if (enumSet) {
out.println(" = "+initStringBuffer.toString()+"]");
} else {
out.println(" = range("+enumInit+")");
}
}
if (comments.length > 0) {
out.println(" \"\"\"");
for (int i=0; i<comments.length; i++) {
out.print(" ");
out.println(comments[i]);
}
out.println();
out.println(" \"\"\"");
}
out.println();
out.println();
}
protected String type(IType type) {
return type(type, 0);
}
private String type(IType type, int nesting) {
IType[] types = type.getTypes();
StringBuffer s = new StringBuffer();
String typeName = type.getName();
if (typeName.equals("[]")) {
s.append(type(types[0]));
} else {
typeName = typeProperties.getProperty(typeName, typeName);
s.append(typeName);
if (types.length > 0) {
// print subtypes as "of", except if the template type was empty
if (!typeName.equals("")) {
if (nesting == 0) s.append("of");
}
s.append(type(types[0], nesting+1));
for (int i=1; i<types.length; i++) {
s.append("and");
s.append(type(types[i], nesting+1));
}
}
}
for (int i=0; i<type.getDimension(); i++) s.append("ListType");
return s.toString();
}
protected void printEOCComments(IndentPrintWriter out, IClass clazz) {
String[] eocComments = clazz.getEOCComments(language);
if (eocComments.length > 0) {
out.println();
out.println(" \"\"\"");
for (int i=0; i<eocComments.length; i++) {
out.print(" ");
out.println(eocComments[i]);
}
out.println(" \"\"\"");
}
}
protected void printEOPComments(IndentPrintWriter out, IClass clazz) {
String[] eopComments = clazz.getEOPComments(language);
if (eopComments.length > 0) {
out.println();
out.println(" \"\"\"");
for (int i=0; i<eopComments.length; i++) {
out.print(" ");
out.println(eopComments[i]);
}
out.println(" \"\"\"");
}
}
protected void printEOFComments(IndentPrintWriter out, IClass clazz) {
String[] eofComments = clazz.getEOFComments(language);
if (eofComments.length > 0) {
out.println();
out.println("\"\"\"");
for (int i=0; i<eofComments.length; i++) {
out.println(eofComments[i]);
}
out.println("\"\"\"");
}
}
}