3 # Copyright (c) 2014 Intel Corporation. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
10 from string import Template
12 from java_class_component import Enum, Field
13 from java_method import Method
15 class JavaClassLoader(object):
16 """Manager class maintains all loaded java classes."""
17 def __init__(self, src_path, class_list):
18 self._src_path = src_path
19 self._java_data_map = {}
20 self._class_list = class_list
22 def IsInternalClass(self, clazz):
23 return clazz in self._class_list
25 def LoadJavaClass(self, clazz):
26 java_data = self._java_data_map.get(clazz, None)
30 file_name = os.path.join(self._src_path, '%s.java' % clazz)
31 self._java_data_map[clazz] = self.LoadJavaFile(file_name)
33 return self._java_data_map[clazz]
35 def LoadJavaFile(self, file_name):
37 file_handle = open(file_name, 'r')
38 file_content = file_handle.read()
41 print 'Error reading input Java file, please check.'
44 java_data = InternalJavaFileData(self)
45 java_data.SetClassContent(file_content)
49 def MangleInternalNameToBridgeName(self, internal_name):
50 if not self.IsInternalClass(internal_name):
53 return internal_name.replace('Internal', 'Bridge')
55 def MangleInternalNameToWrapperName(self, internal_name):
56 if not self.IsInternalClass(internal_name):
59 return internal_name.replace('Internal', '')
61 def GenerateDoc(self, doc):
64 def ReplaceInternal(matchobj):
65 match = matchobj.group(0)
66 if self.IsInternalClass(match):
67 return self.LoadJavaClass(match).wrapper_name
70 return re.sub('XWalk[a-zA-Z_0-9]*Internal', ReplaceInternal, doc)
72 class InternalJavaFileData(object):
73 """Data class stores the generator information of internal class."""
74 ANNOTATION_CREATE_INTERNALLY = 'createInternally'
75 ANNOTATION_CREATE_EXTERNALLY = 'createExternally'
76 ANNOTATION_EXTEND_CLASS = 'extendClass'
77 ANNOTATION_NO_INSTANCE = 'noInstance'
78 ANNOTATION_INSTANCE = 'instance'
79 ANNOTATION_IMPL = 'impl'
81 def __init__(self, class_loader):
82 self._class_loader = class_loader
84 self._bridge_name = ''
85 self._wrapper_name = ''
86 self._class_type = '' # class or interface
88 self._class_annotations = {}
93 self._package_name = ''
95 def LoadJavaClass(self, clazz):
96 return self._class_loader.LoadJavaClass(clazz)
100 return self._class_name
103 def bridge_name(self):
104 return self._bridge_name
107 def wrapper_name(self):
108 return self._wrapper_name
111 def class_type(self):
112 return self._class_type
116 return self._class_doc
119 def class_annotations(self):
120 return self._class_annotations
139 def package_name(self):
140 return self._package_name
142 def SetClassContent(self, content):
143 self.ExtractPackageName(content)
144 self.ExtractImports(content)
145 self.ExtractClassProperties(content)
146 self.ExtractMethods(content)
147 self.ExtractFields(content)
148 self.ExtractEnums(content)
150 def ExtractPackageName(self, java_content):
151 package_re = re.compile('\s*package\s+(?P<package>[a-zA-Z0-9._]+)\s*;')
152 for match in re.finditer(package_re, java_content):
153 self._package_name = match.group('package')
155 def ExtractImports(self, java_content):
156 imports_re = re.compile('\s*import\s+(?P<imported>[a-zA-Z0-9._*]+)\s*;')
157 for match in re.finditer(imports_re, java_content):
158 imported = match.group('imported')
159 # Determine whether the import rule should be ignored for generated code.
160 # TODO: Currently we only use a blacklist to filter the import rule.
161 if imported.startswith('org.xwalk.core.internal') or \
162 imported.startswith('org.chromium'):
164 self._imports.append(imported)
166 def ExtractClassProperties(self, java_content):
167 class_re = re.compile(
168 '(?P<class_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
170 '(?P<annotation_content>[a-zA-Z0-9.,=\s]*)\)?'
171 '\s*public\s+([a-z]+\s+)*'
172 '(?P<type>(class|interface))\s+'
173 '(?P<class_name>[a-zA-Z0-9]*)')
174 for match in re.finditer(class_re, java_content):
175 annotation_content = match.group('annotation_content')
176 self._class_name = match.group('class_name')
177 self._bridge_name = \
178 self._class_loader.MangleInternalNameToBridgeName(self._class_name)
179 self._wrapper_name = \
180 self._class_loader.MangleInternalNameToWrapperName(self._class_name)
181 self._class_type = match.group('type')
182 self._class_doc = match.group('class_doc')
183 self.ParseClassAnnotations(annotation_content)
185 def ParseClassAnnotations(self, annotation):
186 """Class annotation contains the following optional attributes:
187 'extendClass' - The class have to extend
188 'createExternally' - boolean
189 'craeteInternally' - boolean
190 'noInstance' - boolean
192 'impl' - Class to impl
193 'instance - instance'"""
194 extend_class_re = re.compile('extendClass\s*=\s*'
195 '(?P<extend_class>[a-zA-Z0-9.]+)')
196 for match in re.finditer(extend_class_re, annotation):
197 extend_class = match.group('extend_class')
198 self._class_annotations['extendClass'] = extend_class
200 create_internally_re = re.compile('createInternally\s*=\s*'
201 '(?P<create_internally>(true|false))')
202 for match in re.finditer(create_internally_re, annotation):
203 create_internally = match.group('create_internally')
204 if create_internally == 'true':
205 self._class_annotations['createInternally'] = True
206 elif create_internally == 'false':
207 self._class_annotations['createInternally'] = False
209 create_externally_re = re.compile('createExternally\s*=\s*'
210 '(?P<create_externally>(true|false))')
211 for match in re.finditer(create_externally_re, annotation):
212 create_externally = match.group('create_externally')
213 if create_externally == 'true':
214 self._class_annotations['createExternally'] = True
215 elif create_externally == 'false':
216 self._class_annotations['createExternally'] = False
218 no_instance_re = re.compile('noInstance\s*=\s*'
219 '(?P<no_instance>(true|false))')
220 for match in re.finditer(no_instance_re, annotation):
221 no_instance = match.group('no_instance')
222 if no_instance == 'true':
223 self._class_annotations['noInstance'] = True
224 elif no_instance == 'false':
225 self._class_annotations['noInstance'] = False
227 is_const_re = re.compile('isConst\s*=\s*'
228 '(?P<is_const>(true|false))')
229 for match in re.finditer(is_const_re, annotation):
230 is_const = match.group('is_const')
231 if is_const == 'true':
232 self._class_annotations['isConst'] = True
233 elif is_const == 'false':
234 self._class_annotations['isConst'] = False
236 impl_re = re.compile('impl\s*=\s*'
237 '(?P<impl>[a-zA-Z0-9.]+)')
238 for match in re.finditer(impl_re, annotation):
239 impl = match.group('impl')
240 self._class_annotations['impl'] = impl
242 instance_re = re.compile('instance\s*=\s*'
243 '(?P<instance>[a-zA-Z0-9.]+)')
244 for match in re.finditer(instance_re, annotation):
245 instance = match.group('instance')
246 self._class_annotations['instance'] = instance
248 def ExtractMethods(self, java_content):
249 constructor_re = re.compile(
250 '(?P<method_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
252 '(?P<method_annotation>[a-zA-Z0-9\$%,\s\(\)\{\};._"=]*)\)?'
253 '\s*public\s(?P<method_name>[a-zA-Z0-9]+)\('
254 '(?P<method_params>[a-zA-Z0-9\s,\[\]\>\<]*)\)')
255 for match in re.finditer(constructor_re, java_content):
256 method_annotation = match.group('method_annotation')
257 method_name = match.group('method_name')
258 method_params = match.group('method_params')
259 method_doc = match.group('method_doc')
263 True, # is_constructor
267 method_params, method_annotation, method_doc)
268 self._methods.append(method)
270 method_re = re.compile(
271 '(?P<method_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
273 '(?P<method_annotation>[a-zA-Z0-9%,\s\(\)\{\};._"=]*)\)?'
274 '\s*public\s+(?P<method_return>[a-zA-Z0-9]+)\s+'
275 '(?P<method_name>[a-zA-Z0-9]+)\('
276 '(?P<method_params>[a-zA-Z0-9\s,\]\[\<\>]*)\)')
277 for match in re.finditer(method_re, java_content):
278 method_annotation = match.group('method_annotation')
279 method_name = match.group('method_name')
280 method_params = match.group('method_params')
281 method_return = match.group('method_return')
282 method_doc = match.group('method_doc')
286 False, # is_constructor
289 method_name, method_return, method_params,
290 method_annotation, method_doc)
291 self._methods.append(method)
293 method_re = re.compile(
294 '(?P<method_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
296 '(?P<method_annotation>[a-zA-Z0-9%,\s\(\)\{\};._"=]*)\)?'
297 '\s*public\s+static\s+(synchronized\s+)*'
298 '(?P<method_return>[a-zA-Z0-9]+)\s+'
299 '(?P<method_name>[a-zA-Z0-9]+)\('
300 '(?P<method_params>[a-zA-Z0-9\s,\[\]\<\>]*)\)')
301 for match in re.finditer(method_re, java_content):
302 method_annotation = match.group('method_annotation')
303 method_name = match.group('method_name')
304 method_params = match.group('method_params')
305 method_return = match.group('method_return')
306 method_doc = match.group('method_doc')
310 False, # is_constructor
313 method_name, method_return, method_params,
314 method_annotation, method_doc)
315 self._methods.append(method)
317 method_re = re.compile(
318 '(?P<method_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
320 '(?P<method_annotation>[a-zA-Z0-9%,\s\(\)\{\};._"=]*)\)?'
321 '\s*public\s+abstract\s+(synchronized\s+)*'
322 '(?P<method_return>[a-zA-Z0-9]+)\s+'
323 '(?P<method_name>[a-zA-Z0-9]+)\('
324 '(?P<method_params>[a-zA-Z0-9\s,\[\]\<\>]*)\)')
325 for match in re.finditer(method_re, java_content):
326 method_annotation = match.group('method_annotation')
327 method_name = match.group('method_name')
328 method_params = match.group('method_params')
329 method_return = match.group('method_return')
330 method_doc = match.group('method_doc')
334 False, # is_constructor
337 method_name, method_return, method_params,
338 method_annotation, method_doc)
339 self._methods.append(method)
341 def ExtractFields(self, java_content):
342 field_re = re.compile(
343 '(?P<field_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
344 '\s*@XWalkAPI\s*public\s+static\s+final\s+'
345 '(?P<field_type>[a-zA-Z0-9_]+)\s+'
346 '(?P<field_name>[a-zA-Z0-9_]+)\s*=\s*'
347 '(?P<field_value>[a-zA-Z0-9-_"]+)\s*;')
348 for match in re.finditer(field_re, java_content):
349 field_type = match.group('field_type')
350 field_name = match.group('field_name')
351 field_value = match.group('field_value')
352 field_doc = match.group('field_doc')
353 field_object = Field(field_type, field_name, field_value, field_doc)
354 self._fields.append(field_object)
356 def ExtractEnums(self, java_content):
357 enum_re = re.compile(
358 '(?P<enum_doc>(\n\s*/\*\*.*\n(\s+\*(.)*\n)+\s+\*/\s*)?)\n'
359 '\s*@XWalkAPI\s*public\s+enum\s+'
360 '(?P<enum_name>[a-zA-Z0-9_]+)\s+{'
361 '(?P<enum_content>(.|\n)*?)\s*}')
362 for match in re.finditer(enum_re, java_content):
363 enum_name = match.group('enum_name')
364 enum_content = match.group('enum_content')
365 enum_doc = match.group('enum_doc')
366 enum_object = Enum(enum_name, enum_content, enum_doc)
367 self._enums[enum_name] = enum_object
369 def HasCreateInternallyAnnotation(self):
370 return self._class_annotations.get(
371 InternalJavaFileData.ANNOTATION_CREATE_INTERNALLY, False)
373 def UseAsInstanceInBridgeCall(self, var):
374 return '%s.getWrapper()' % self.UseAsReturnInBridgeSuperCall(var)
376 def UseAsInstanceInBridgeOverrideCall(self, var):
377 clazz = self._class_annotations.get('instance', self._class_name)
378 clazz = clazz.replace('.class', '')
379 return '(%s) %s' % (self.LoadJavaClass(clazz).bridge_name, var)
381 def UseAsReturnInBridgeSuperCall(self, var):
382 clazz = self._class_annotations.get('instance', self._class_name)
383 clazz = clazz.replace('.class', '')
385 if self.LoadJavaClass(clazz).class_annotations.get(
386 'createInternally', False):
387 typed_var_template = Template('(${VAR} instanceof ${BRIDGE_TYPE} ?'\
388 ' ((${BRIDGE_TYPE}) ${VAR} ) : new ${BRIDGE_TYPE}(${INTERNAL_VAR}))')
390 'BRIDGE_TYPE': self.LoadJavaClass(clazz).bridge_name,
391 'INTERNAL_VAR': var if clazz == self._class_name else\
392 '(%s) %s' % (clazz, var)}
393 var = typed_var_template.substitute(value)
397 def UseAsTypeInBridgeAndBridgeSuperCall(self):
398 clazz = self._class_annotations.get('instance', self._class_name)
399 clazz = clazz.replace('.class', '')
401 return self.LoadJavaClass(clazz).bridge_name
403 def UseAsInstanceInBridgeSuperCall(self, var):
404 # pylint: disable=R0201
407 def UseAsInstanceInWrapperCall(self, var):
408 clazz = self._class_annotations.get('instance', self._class_name)
409 clazz = clazz.replace('.class', '')
411 if clazz != self._class_name:
412 var = '((%s) %s)' % (self.LoadJavaClass(clazz).wrapper_name, var)
413 return '%s.getBridge()' % var
415 def UseAsTypeInWrapperCall(self):
416 return self._wrapper_name
418 def GetFullBridgeName(self, subclass=None):
419 if not self._class_loader.IsInternalClass(self._class_name):
420 return self._class_name
422 clazz = self._class_annotations.get(
423 InternalJavaFileData.ANNOTATION_INSTANCE, self._class_name)
424 clazz = clazz.replace('.class', '')
425 package_string = 'org.xwalk.core.internal.%s'
427 return package_string % self.LoadJavaClass(clazz).bridge_name
429 return package_string % (clazz + '$' + subclass)
431 def GetFullWrapperName(self, subclass=None):
432 if not self._class_loader.IsInternalClass(self._class_name):
433 return self._class_name
436 return "org.xwalk.core.%s" % self._wrapper_name
438 return "org.xwalk.core.%s$%s" % (self._wrapper_name,
439 subclass.replace('Internal', ''))