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
19import os
20import sys
21
22hFileTemplate = """/**
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
34jniFileTemplate = """/**
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
42extern "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 ////////////////////////////////////////////////////////////////////////////////////
58JNIEXPORT jint JNICALL
59Java_%s_getElementSize(JNIEnv* env, jobject thiz);
60
61%s
62
63#ifdef __cplusplus
64}
65#endif
66
67// Implementation //////////////////////////////////////////////////////////////////////////////////
68jint Java_%s_getElementSize(JNIEnv* env, jobject thiz) {
69  return sizeof(%s);
70}
71
72%s
73"""
74
75javaFileTemplate = """/**
76 * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify!
77 **/
78
79package %s;
80
81import android.filterfw.core.NativeBuffer;
82
83%s
84"""
85
86
87def 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
92def ToJNIPackage(package, jclassname):
93  return "%s_%s" % (package.replace(".", "_"), jclassname)
94
95def ToMacroDefName(cname, pname):
96  return "%s_%s" % (pname.replace(".", "_").upper(), cname.upper())
97
98class 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
106class 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
170class 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
174class 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
178class 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
182class 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
332def 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
366if __name__ == "__main__":
367  sys.exit(main(sys.argv))
368