Marius Renn | 65953da | 2012-03-27 10:44:45 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | # |
| 4 | # Copyright (C) 2011 The Android Open Source Project |
| 5 | # |
| 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | # you may not use this file except in compliance with the License. |
| 8 | # You may obtain a copy of the License at |
| 9 | # |
| 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | # |
| 12 | # Unless required by applicable law or agreed to in writing, software |
| 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | # See the License for the specific language governing permissions and |
| 16 | # limitations under the License. |
| 17 | # |
| 18 | |
| 19 | import os |
| 20 | import sys |
| 21 | |
| 22 | hFileTemplate = """/** |
| 23 | * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify! |
| 24 | **/ |
| 25 | |
| 26 | #ifndef %s |
| 27 | #define %s |
| 28 | |
| 29 | %s |
| 30 | |
| 31 | #endif // %s |
| 32 | """ |
| 33 | |
| 34 | jniFileTemplate = """/** |
| 35 | * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify! |
| 36 | **/ |
| 37 | |
| 38 | #include <stdint.h> |
| 39 | #include "native/%s.h" |
| 40 | |
| 41 | #ifdef __cplusplus |
| 42 | extern "C" { |
| 43 | #endif |
| 44 | |
| 45 | #include "jni.h" |
| 46 | |
| 47 | // Helper functions //////////////////////////////////////////////////////////////////////////////// |
| 48 | %s* Get%sAtIndex(JNIEnv* env, jobject buffer, int index) { |
| 49 | jclass base_class = (*env)->FindClass(env, "android/filterfw/core/NativeBuffer"); |
| 50 | jfieldID ptr_field = (*env)->GetFieldID(env, base_class, "mDataPointer", "J"); |
| 51 | uintptr_t data_ptr = (*env)->GetLongField(env, buffer, ptr_field); |
| 52 | %s* array = (%s*)data_ptr; |
| 53 | (*env)->DeleteLocalRef(env, base_class); |
| 54 | return &array[index]; |
| 55 | } |
| 56 | |
| 57 | // Declarations //////////////////////////////////////////////////////////////////////////////////// |
| 58 | JNIEXPORT jint JNICALL |
| 59 | Java_%s_getElementSize(JNIEnv* env, jobject thiz); |
| 60 | |
| 61 | %s |
| 62 | |
| 63 | #ifdef __cplusplus |
| 64 | } |
| 65 | #endif |
| 66 | |
| 67 | // Implementation ////////////////////////////////////////////////////////////////////////////////// |
| 68 | jint Java_%s_getElementSize(JNIEnv* env, jobject thiz) { |
| 69 | return sizeof(%s); |
| 70 | } |
| 71 | |
| 72 | %s |
| 73 | """ |
| 74 | |
| 75 | javaFileTemplate = """/** |
| 76 | * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify! |
| 77 | **/ |
| 78 | |
| 79 | package %s; |
| 80 | |
| 81 | import android.filterfw.core.NativeBuffer; |
| 82 | |
| 83 | %s |
| 84 | """ |
| 85 | |
| 86 | |
| 87 | def ToJavaName(cname, start_upper_at = 1): |
| 88 | lower = cname.split("_") |
| 89 | upper = [c.title() for c in lower] |
| 90 | return "".join(lower[:start_upper_at] + upper[start_upper_at:]) |
| 91 | |
| 92 | def ToJNIPackage(package, jclassname): |
| 93 | return "%s_%s" % (package.replace(".", "_"), jclassname) |
| 94 | |
| 95 | def ToMacroDefName(cname, pname): |
| 96 | return "%s_%s" % (pname.replace(".", "_").upper(), cname.upper()) |
| 97 | |
| 98 | class ParseError: |
| 99 | def __init__(self, lineno, message): |
| 100 | self.lineno = lineno |
| 101 | self.message = message |
| 102 | |
| 103 | def __str__(self): |
| 104 | return "On line %d: %s" % (self.lineno, self.message) |
| 105 | |
| 106 | class FieldType_BasePOD: |
| 107 | def __init__(self, name, structname, jclassname, package, javatype, ctype, jtype, defval): |
| 108 | self.name = name |
| 109 | self.structname = structname |
| 110 | self.jclassname = jclassname |
| 111 | self.package = package |
| 112 | self.javatype = javatype |
| 113 | self.ctype = ctype |
| 114 | self.jtype = jtype |
| 115 | self.defval = defval |
| 116 | |
| 117 | def cString(self): |
| 118 | return " %s %s;" % (self.ctype, self.name) |
| 119 | |
| 120 | def javaGetter(self): |
| 121 | return " public %s get%s(int index) {\n"\ |
| 122 | " assertReadable();\n"\ |
| 123 | " return nativeGet%s(index);\n"\ |
| 124 | " }" % (self.javatype, ToJavaName(self.name, 0), ToJavaName(self.name, 0)) |
| 125 | |
| 126 | def javaSetter(self): |
| 127 | return " public void set%s(int index, %s value) {\n"\ |
| 128 | " assertWritable();\n"\ |
| 129 | " nativeSet%s(index, value);\n"\ |
| 130 | " }" % (ToJavaName(self.name, 0), self.javatype, ToJavaName(self.name, 0)) |
| 131 | |
| 132 | def javaNativeGetter(self): |
| 133 | return " private native %s nativeGet%s(int index);"\ |
| 134 | % (self.javatype, ToJavaName(self.name, 0)) |
| 135 | |
| 136 | def javaNativeSetter(self): |
| 137 | return " private native boolean nativeSet%s(int index, %s value);"\ |
| 138 | % (ToJavaName(self.name, 0), self.javatype) |
| 139 | |
| 140 | def jniGetterDefString(self): |
| 141 | return "JNIEXPORT %s JNICALL\n" \ |
| 142 | "Java_%s_nativeGet%s(JNIEnv* env, jobject thiz, jint index);" \ |
| 143 | % (self.jtype, ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0)) |
| 144 | |
| 145 | def jniGetterImplString(self): |
| 146 | return \ |
| 147 | "%s Java_%s_nativeGet%s(JNIEnv* env, jobject thiz, jint index) {\n"\ |
| 148 | " %s* instance = Get%sAtIndex(env, thiz, index);\n"\ |
| 149 | " return instance ? instance->%s : %s;\n"\ |
| 150 | "}\n" % (self.jtype, ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0),\ |
| 151 | self.structname, self.structname, self.name, self.defval) |
| 152 | |
| 153 | def jniSetterDefString(self): |
| 154 | return "JNIEXPORT jboolean JNICALL\n" \ |
| 155 | "Java_%s_nativeSet%s(JNIEnv* env, jobject thiz, jint index, %s value);" \ |
| 156 | % (ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0), self.jtype) |
| 157 | |
| 158 | def jniSetterImplString(self): |
| 159 | return \ |
| 160 | "jboolean Java_%s_nativeSet%s(JNIEnv* env, jobject thiz, jint index, %s value) {\n"\ |
| 161 | " %s* instance = Get%sAtIndex(env, thiz, index);\n"\ |
| 162 | " if (instance) {\n"\ |
| 163 | " instance->%s = value;\n"\ |
| 164 | " return JNI_TRUE;\n"\ |
| 165 | " }\n"\ |
| 166 | " return JNI_FALSE;\n"\ |
| 167 | "}\n" % (ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0),\ |
| 168 | self.jtype, self.structname, self.structname, self.name) |
| 169 | |
| 170 | class FieldType_Float(FieldType_BasePOD): |
| 171 | def __init__(self, name, structname, jclassname, package): |
| 172 | FieldType_BasePOD.__init__(self, name, structname, jclassname, package, "float", "float", "jfloat", "0.0") |
| 173 | |
| 174 | class FieldType_Int(FieldType_BasePOD): |
| 175 | def __init__(self, name, structname, jclassname, package): |
| 176 | FieldType_BasePOD.__init__(self, name, structname, jclassname, package, "int", "int", "jint", "0") |
| 177 | |
| 178 | class FieldType_Long(FieldType_BasePOD): |
| 179 | def __init__(self, name, structname, jclassname, package): |
| 180 | FieldType_BasePOD.__init__(self, name, structname, jclassname, package, "long", "long long", "jlong", "0") |
| 181 | |
| 182 | class StructSpec: |
| 183 | |
| 184 | def parseTextFile(self, filepath): |
| 185 | # Init |
| 186 | self.name = None |
| 187 | self.package = None |
| 188 | self.fields = [] |
| 189 | self.structname = None |
| 190 | self.jclassname = None |
| 191 | self.libname = None |
| 192 | |
| 193 | # Open the file |
| 194 | txtfile = open(filepath) |
| 195 | |
| 196 | # Parse it line by line |
| 197 | lineno = 0 |
| 198 | for line in txtfile: |
| 199 | # Split line into components |
| 200 | linecomps = line.split() |
| 201 | if len(linecomps) == 0: |
| 202 | continue |
| 203 | |
| 204 | # Execute command |
| 205 | cmd = linecomps[0] |
| 206 | if cmd == "@name": |
| 207 | self.commandArgAssert(linecomps, 1, lineno) |
| 208 | self.name = linecomps[1] |
| 209 | if not self.structname: |
| 210 | self.structname = self.name |
| 211 | if not self.jclassname: |
| 212 | self.jclassname = self.name |
| 213 | elif cmd == "@package": |
| 214 | self.commandArgAssert(linecomps, 1, lineno) |
| 215 | self.package = linecomps[1] |
| 216 | elif cmd == "@libname": |
| 217 | self.commandArgAssert(linecomps, 1, lineno) |
| 218 | self.libname = linecomps[1] |
| 219 | elif cmd == "@structname": |
| 220 | self.commandArgAssert(linecomps, 1, lineno) |
| 221 | self.structname = linecomps[1] |
| 222 | elif cmd == "@javaclassname": |
| 223 | self.commandArgAssert(linecomps, 1, lineno) |
| 224 | self.jclassname = linecomps[1] |
| 225 | elif cmd == "@field": |
| 226 | self.commandArgAssert(linecomps, 2, lineno) |
| 227 | typestr = linecomps[1] |
| 228 | if typestr == "int": |
| 229 | fieldtype = FieldType_Int(linecomps[2], self.structname, self.jclassname, self.package) |
| 230 | elif typestr == "long": |
| 231 | fieldtype = FieldType_Long(linecomps[2], self.structname, self.jclassname, self.package) |
| 232 | elif typestr == "float": |
| 233 | fieldtype = FieldType_Float(linecomps[2], self.structname, self.jclassname, self.package) |
| 234 | else: |
| 235 | raise ParseError(lineno, "Unknown field type '%s'!" % typestr) |
| 236 | self.fields.append(fieldtype) |
| 237 | else: |
| 238 | raise ParseError(lineno, "Unknown command: '%s'!" % cmd) |
| 239 | |
| 240 | lineno = lineno + 1 |
| 241 | |
| 242 | # Make sure we have all required info |
| 243 | if not self.name: |
| 244 | raise ParseError(lineno, "Required field '@name' missing!") |
| 245 | elif not self.package: |
| 246 | raise ParseError(lineno, "Required field '@package' missing!") |
| 247 | elif not self.libname: |
| 248 | raise ParseError(lineno, "Required field '@libname' missing!") |
| 249 | |
| 250 | # Normalize values |
| 251 | if self.libname[:3] == "lib": |
| 252 | self.libname = self.libname[3:] |
| 253 | |
| 254 | def commandArgAssert(self, linecomps, expectedcount, lineno): |
| 255 | foundcount = len(linecomps) - 1 |
| 256 | if foundcount < expectedcount: |
| 257 | raise ParseError(lineno, "Not enough arguments specifed for command '%s'! Expected %d, " \ |
| 258 | "but got only %d!" % (linecomps[0], expectedcount, foundcount)) |
| 259 | elif foundcount > expectedcount + 1: |
| 260 | raise ParseError(lineno, "Too many arguments specifed for command '%s'! Expected %d, " \ |
| 261 | "but got %d!" % (linecomps[0], expectedcount, foundcount)) |
| 262 | |
| 263 | |
| 264 | def cStructString(self): |
| 265 | cfields = [f.cString() for f in self.fields] |
| 266 | return "typedef struct Struct%s {\n%s\n} %s;\n" % (self.structname,\ |
| 267 | "\n".join(cfields),\ |
| 268 | self.structname) |
| 269 | |
| 270 | def javaClassString(self): |
| 271 | jgetters = [f.javaGetter() for f in self.fields] |
| 272 | jsetters = [f.javaSetter() for f in self.fields] |
| 273 | jnativesetters = [f.javaNativeSetter() for f in self.fields] |
| 274 | jnativegetters = [f.javaNativeGetter() for f in self.fields] |
| 275 | return "public class %s extends NativeBuffer {\n\n"\ |
| 276 | " public %s() {\n"\ |
| 277 | " super();\n"\ |
| 278 | " }\n"\ |
| 279 | "\n"\ |
| 280 | " public %s(int count) {\n"\ |
| 281 | " super(count);\n"\ |
| 282 | " }\n"\ |
| 283 | "\n"\ |
| 284 | " public native int getElementSize();\n"\ |
| 285 | "\n"\ |
| 286 | "%s\n\n"\ |
| 287 | "%s\n\n"\ |
| 288 | "%s\n\n"\ |
| 289 | "%s\n\n"\ |
| 290 | " static {\n"\ |
| 291 | " System.loadLibrary(\"%s\");\n"\ |
| 292 | " }\n"\ |
| 293 | "\n"\ |
| 294 | "};\n" % (self.jclassname,\ |
| 295 | self.jclassname,\ |
| 296 | self.jclassname,\ |
| 297 | "\n\n".join(jgetters),\ |
| 298 | "\n\n".join(jsetters),\ |
| 299 | "\n\n".join(jnativegetters),\ |
| 300 | "\n\n".join(jnativesetters),\ |
| 301 | self.libname) |
| 302 | |
| 303 | def jniDeclString(self): |
| 304 | jnigetters = [f.jniGetterDefString() for f in self.fields] |
| 305 | jnisetters = [f.jniSetterDefString() for f in self.fields] |
| 306 | return "\n\n".join(jnigetters + jnisetters) |
| 307 | |
| 308 | def jniImplString(self): |
| 309 | jnigetters = [f.jniGetterImplString() for f in self.fields] |
| 310 | jnisetters = [f.jniSetterImplString() for f in self.fields] |
| 311 | return "\n\n".join(jnigetters + jnisetters) |
| 312 | |
| 313 | def hFileString(self): |
| 314 | defname = ToMacroDefName(self.structname, self.package) |
| 315 | return hFileTemplate % (defname, defname, self.cStructString(), defname) |
| 316 | |
| 317 | def javaFileString(self): |
| 318 | return javaFileTemplate % (self.package, self.javaClassString()) |
| 319 | |
| 320 | def jniFileString(self): |
| 321 | return jniFileTemplate % (self.structname.lower(),\ |
| 322 | self.structname,\ |
| 323 | self.structname,\ |
| 324 | self.structname,\ |
| 325 | self.structname,\ |
| 326 | ToJNIPackage(self.package, self.jclassname),\ |
| 327 | self.jniDeclString(),\ |
| 328 | ToJNIPackage(self.package, self.jclassname),\ |
| 329 | self.structname, |
| 330 | self.jniImplString()) |
| 331 | |
| 332 | def main(argv): |
| 333 | if len(argv) != 2: |
| 334 | print("Usage: %s <file.struct>" % argv[0]) |
| 335 | return -1 |
| 336 | |
| 337 | filepath = argv[1] |
| 338 | |
| 339 | structspec = StructSpec() |
| 340 | structspec.parseTextFile(filepath) |
| 341 | |
| 342 | hfilename = "%s.h" % structspec.structname.lower() |
| 343 | javafilename = "%s.java" % structspec.jclassname |
| 344 | jnifilename = "jni_%s.c" % structspec.structname.lower() |
| 345 | |
| 346 | javapackagepath = structspec.package.replace('.','/') |
| 347 | |
| 348 | rootdir = os.path.dirname(filepath) |
| 349 | hfilepath = "%s/../native/%s" % (rootdir, hfilename) |
| 350 | javafilepath = "%s/../java/%s/%s" % (rootdir, javapackagepath, javafilename) |
| 351 | jnifilepath = "%s/../jni/%s" % (rootdir, jnifilename) |
| 352 | |
| 353 | hfile = open(hfilepath, 'w') |
| 354 | hfile.write(structspec.hFileString()) |
| 355 | hfile.close() |
| 356 | |
| 357 | javafile = open(javafilepath, 'w') |
| 358 | javafile.write(structspec.javaFileString()) |
| 359 | javafile.close() |
| 360 | |
| 361 | jnifile = open(jnifilepath, 'w') |
| 362 | jnifile.write(structspec.jniFileString()) |
| 363 | jnifile.close() |
| 364 | |
| 365 | |
| 366 | if __name__ == "__main__": |
| 367 | sys.exit(main(sys.argv)) |