blob: ff3cb8eb94fab90dae5a1fa8153ab5cd6c3e9563 [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
"""This module providesfunctionality for systems to generate
Elemental interfaces from the IDL database."""
import pdb
import os
import json
import systembaseelemental
from generator_java import *
class ElementalJsoSystem(systembaseelemental.SystemElemental):
def __init__(self, templates, database, emitters, output_dir):
super(ElementalJsoSystem, self).__init__(
templates, database, emitters, output_dir)
self._dart_interface_file_paths = []
def InterfaceGenerator(self,
interface,
common_prefix,
super_interface_name,
source_filter):
"""."""
module = getModule(interface.annotations)
if super_interface_name is not None:
interface_name = super_interface_name
else:
interface_name = interface.id
template_file = 'jso_impl_%s.darttemplate' % interface_name
template = self._templates.TryLoad(template_file)
if not template:
template = self._templates.Load('jso_impl.darttemplate')
if interface_name in self._mixins or interface_name.endswith("Callback") or interface_name.endswith('Handler'):
return NullInterfaceGenerator(module, self._database,
interface, None,
template,
common_prefix, super_interface_name,
source_filter)
dart_interface_file_path = self._FilePathForElementalInterface(module, interface_name)
self._dart_interface_file_paths.append(dart_interface_file_path)
dart_interface_code = self._emitters.FileEmitter(dart_interface_file_path)
return ElementalInterfaceGenerator(module, self._database,
interface, dart_interface_code,
template,
common_prefix, super_interface_name,
source_filter, self._mixins)
def ProcessCallback(self, interface, info):
pass
def _FilePathForElementalInterface(self, module, interface_name):
"""Returns the file path of the Dart interface definition."""
return os.path.join(self._output_dir, 'src', 'elemental', "js", module,
'Js%s.java' % interface_name)
# ------------------------------------------------------------------------------
# Used to suppress generation of JSO classes that are not needed
class NullInterfaceGenerator(systembaseelemental.ElementalBase):
def __init__(self, module, database, interface, emitter, template,
common_prefix, super_interface, source_filter):
pass
def StartInterface(self):
pass
def AddOperation(self, x, inherited):
pass
def AddIndexer(self, x):
pass
def AddConstant(self, x):
pass
def AddAttribute(self, x, y, inheritedGetter, inheritedSetter):
pass
def FinishInterface(self):
pass
class ElementalInterfaceGenerator(systembaseelemental.ElementalBase):
"""Generates Elemental Interface definition for one DOM IDL interface."""
def __init__(self, module, database, interface, emitter, template,
common_prefix, super_interface, source_filter, mixins):
"""Generates Dart code for the given interface.
Args:
interface -- an IDLInterface instance. It is assumed that all types have
been converted to Dart types (e.g. int, String), unless they are in the
same package as the interface.
common_prefix -- the prefix for the common library, if any.
super_interface -- the name of the common interface that this interface
implements, if any.
source_filter -- if specified, rewrites the names of any superinterfaces
that are not from these sources to use the common prefix.
"""
super(self.__class__, self).__init__()
self._module = module
self._database = database
self._interface = interface
self._emitter = emitter
self._template = template
self._common_prefix = common_prefix
self._super_interface = super_interface
self._source_filter = source_filter
self._mixins = mixins
current_dir = os.path.dirname(__file__)
self._docdatabase = json.load(open(os.path.join(current_dir, '..', 'docs/database.json')))
def addImport(self, imports, typeid):
# skip primitive types that are all lowercase first letter
etype = DartType(typeid)
if '<' not in typeid and etype[0].isupper():
rawtype = etype.split('<')[0]
if rawtype in ['Indexable', 'Settable', 'Mappable']:
pmodule = 'util'
imports['import elemental.js.%s.%s;\n' % (pmodule, 'Js' + rawtype)]=1
imports['import elemental.%s.%s;\n' % (pmodule, rawtype)]=1
elif etype not in java_lang and self._database.HasInterface(typeid):
pinterface = self._database.GetInterface(typeid)
pmodule = getModule(pinterface.annotations)
if pmodule != self._module and not rawtype.endswith("Callback") and not rawtype.endswith("Handler") and not rawtype == 'EventTarget':
imports['import elemental.js.%s.%s;\n' % (pmodule, 'Js' + rawtype)]=1
imports['import elemental.%s.%s;\n' % (pmodule, rawtype)]=1
def StartInterface(self):
if self._super_interface:
typename = self._super_interface
else:
typename = self._interface.id
implements = []
implements_raw = {}
extends = ''
suppressed_extends = []
imports = {}
alreadyImplemented = {}
for p in self._interface.parents:
self.getImplements(alreadyImplemented, p)
for attr in self._interface.attributes:
self.addImport(imports, attr.type.id)
for oper in self._interface.operations:
self.addImport(imports, oper.type.id)
for arg in oper.arguments:
self.addImport(imports, arg.type.id)
for parent in self._interface.parents:
if not parent.type.id in alreadyImplemented or parent.type.id == 'ElementalMixinBase':
self.addImport(imports, parent.type.id)
# TODO(vsm): Remove source_filter.
rawtype = DartType(parent.type.id).split('<')[0]
if rawtype == 'Object':
continue
if MatchSourceFilter(self._source_filter, parent) and DartType(parent.type.id) != 'Object' and rawtype not in implements_raw:
# Parent is a DOM type.
implements.append(DartType(parent.type.id))
implements_raw[rawtype]=1
#TODO(cromwellian) add in Indexable/IndexableInt/IndexableNumber
# elif '<' in parent.type.id:
# Parent is a Dart collection type.
# TODO(vsm): Make this check more robust.
# extends.append(parent.type.id)
# else:
# suppressed_extends.append('%s.%s' %
# (self._common_prefix, parent.type.id))
comment = ' extends'
implements_str = ''
# the Mixin base class is special, it extends JSO
if self._interface.id == 'ElementalMixinBase':
extends = ' extends JsElementalBase'
filtered_mixins = []
for mixin in self._mixins:
filtered_mixins.append(DartType(mixin))
implements.extend(filtered_mixins)
else:
# default, every type extends the Mixin base
extends = ' extends JsElementalMixinBase'
rawtype = DartType(self._interface.id).split('<')[0]
if rawtype not in implements_raw and rawtype != 'Object':
implements.insert(0, DartType(self._interface.id))
self.addImport(imports, self._interface.id)
# parents[0] is the implementing superclass, if exists, but not a mixin, reference its JSO impl
if len(self._interface.parents) > 0 and not self._interface.parents[0].type.id in self._mixins:
extends = ' extends Js' + DartType(self._interface.parents[0].type.id)
self.addImport(imports, self._interface.parents[0].type.id)
if implements:
implements_str += ' implements ' + ', '.join(implements)
comment = ','
factory_provider = None
constructor_info = AnalyzeConstructor(self._interface)
# TODO(vsm): Add appropriate package / namespace syntax.
(self._members_emitter,
self._top_level_emitter) = self._emitter.Emit(
self._template + '$!TOP_LEVEL',
ID='Js' + typename,
IMPLEMENTS=implements_str,
EXTENDS=extends, PACKAGE='elemental.js.' + self._module,
IMPORTS = ''.join(imports.keys()))
# TODO(cromwellian) auto-generate factory classes?
# if constructor_info:
# self._members_emitter.Emit(
# '\n'
# ' $CTOR($PARAMS);\n',
# CTOR=typename,
# PARAMS=constructor_info.ParametersInterfaceDeclaration());
# element_type = MaybeTypedArrayElementType(self._interface)
# if element_type:
# self._members_emitter.Emit(
# '\n'
# ' $CTOR(int length);\n'
# '\n'
# ' $CTOR.fromList(List<$TYPE> list);\n'
# '\n'
# ' $CTOR.fromBuffer(ArrayBuffer buffer,'
# ' [int byteOffset, int length]);\n',
# CTOR=self._interface.id,
# TYPE=DartType(element_type))
def FinishInterface(self):
# add all create* method implementations to Document
if self._interface.id == 'Document':
for iface in self._database.GetInterfaces():
if iface.id.endswith("Element"):
# try to determine tag name from interface name
elementName = iface.id;
elementName = elementName[0:len(elementName) - len("Element")].lower()
# tablecaption -> <caption>
if elementName == 'tablecaption':
elementName = 'caption'
# SVG is special, it needs createElementNS, strip off prefix
if elementName.startswith("svg"):
elementName = elementName[3:]
# special case, SVGElement is a root class, not a createable element
if elementName != '':
callName = DartType(iface.id)
# SVGSVGElement -> createSVGElement with tag as <svg>
if elementName == 'svg':
callName = 'SVGElement'
self._members_emitter.Emit('\n public final Js$TYPE create$CALL() {\n return createSvgElement("$ELEMENT").cast();\n }\n',
TYPE=DartType(iface.id),
CALL = callName,
ELEMENT=elementName)
elif elementName != '':
self._members_emitter.Emit('\n public final Js$TYPE create$TYPE() {\n return createElement("$ELEMENT").cast();\n }\n',
TYPE=DartType(iface.id),
ELEMENT=elementName)
if self._interface.id == 'Window':
for iface in self._database.GetInterfaces():
element_type = MaybeTypedArrayElementType(iface)
if element_type:
self._members_emitter.Emit(
'\n'
' public final native Js$TYPE new$CTOR(int length) /*-{ return new $TYPE(length); }-*/;\n'
'\n'
' public final native Js$TYPE new$CTOR(IndexableNumber list) /*-{ return new $TYPE(list); }-*/;\n'
'\n'
' public final native Js$TYPE new$CTOR(ArrayBuffer buffer,'
' int byteOffset, int length) /*-{ return new $TYPE(buffer, byteOffset, length); }-*/;\n',
CTOR=iface.id,
TYPE=iface.id)
constructor_info = AnalyzeConstructor(iface)
if constructor_info:
self._members_emitter.Emit(
'\n'
' public final native Js$TYPE new$CTOR($PARAMS) /*-{ return new $JSTYPE($ARGS); }-*/;\n',
TYPE=iface.id,
CTOR=iface.id,
JSTYPE=JsType(iface.id),
PARAMS=constructor_info.ParametersInterfaceDeclaration(),
ARGS=constructor_info.ParametersAsArgumentList(self._database));
def AddConstant(self, constant):
pass
def AddAttribute(self, getter, setter, inheritedGetter, inheritedSetter):
# you can't override methods in a JSO superclass
if getter and not inheritedGetter:
if getter.type.id == 'EventListener':
self._members_emitter.Emit('\n public final native $TYPE $NAME() /*-{\n return @elemental.js.dom.JsElementalMixinBase::getListenerFor(Lcom/google/gwt/core/client/JavaScriptObject;)(this.$FIELD);\n }-*/;\n',
NAME=getterName(getter),
TYPE=TypeOrVar(DartType(getter.type.id),
getter.type.id),
FIELD=getter.id)
else:
field = 'this.$FIELD'
if getter.id in self.reserved_keywords:
field_template = "this['$FIELD']"
else:
field_template = "this.$FIELD"
self._members_emitter.Emit('\n public final native $TYPE $NAME() /*-{\n return %s;\n }-*/;\n' % field_template ,
NAME=getterName(getter),
TYPE=JsoTypeOrVar(DartType(getter.type.id), self._mixins),
FIELD=getter.id)
if setter and not inheritedSetter:
if setter.type.id == 'EventListener':
self._members_emitter.Emit('\n public final native void $NAME($TYPE listener) /*-{\n this.$FIELD = @elemental.js.dom.JsElementalMixinBase::getHandlerFor(Lelemental/events/EventListener;)(listener);\n }-*/;',
NAME=setterName(setter),
TYPE=TypeOrVar(DartType(setter.type.id),
setter.type.id),
FIELD=setter.id)
else:
if setter.id in self.reserved_keywords:
field_template = "this['$FIELD']"
else:
field_template = "this.$FIELD"
self._members_emitter.Emit('\n public final native void $NAME($TYPE param_$FIELD) /*-{\n %s = param_$FIELD;\n }-*/;\n' % field_template,
NAME=setterName(setter),
TYPE=TypeOrVar(DartType(setter.type.id)),
FIELD=setter.id)
def AddIndexer(self, element_type):
# Interface inherits all operations from List<element_type>.
pass
def AddOperation(self, info, inherited):
"""
Arguments:
operations - contains the overloads, one or more operations with the same
name.
"""
# you can't override methods on a JSO superclass
if inherited:
return
# implemented on mixin base template (hack)
if info.name == 'addEventListener' or info.name == 'removeEventListener':
return
get_attrs = []
set_attrs = []
for attr in self._interface.attributes:
get_attrs.append(getterName(attr))
set_attrs.append('set%s' % ucfirst(attr.id))
if info.name in get_attrs or info.name in set_attrs:
return
body = ''
if info.type_name != 'void':
body += 'return '
# if op.is_fc_deleter:
# w('delete ')
# if info.id is None:
# w('this[')
# else:
args = info.ParametersAsArgumentList(self._database)
if info.name in self.reserved_keywords:
body += "this['%s'](%s" % (info.name, args)
else:
body += 'this.%s(%s' % (info.name, args)
#if op.id is None:
# w('];\n')
# else:
body += ');'
self._members_emitter.Emit('\n public final native $TYPE $NAME($PARAMS) /*-{\n $BODY\n }-*/;\n',
TYPE=JsoTypeOrVar(info.type_name, self._mixins),
NAME=self.fixReservedKeyWords(info.name),
PARAMS=info.ParametersInterfaceDeclaration(),
BODY=body)
def AddStaticOperation(self, info, inherited):
pass
# Interfaces get secondary members directly via the superinterfaces.
def AddSecondaryAttribute(self, interface, getter, setter):
pass
def AddSecondaryOperation(self, interface, attr):
pass
def ucfirst(string):
"""Upper cases the 1st character in a string"""
if len(string):
return '%s%s' % (string[0].upper(), string[1:])
return string
def getterName(attr):
name = DartDomNameOfAttribute(attr)
if attr.type.id == 'boolean':
if name.startswith('is'):
name = name[2:]
return 'is%s' % ucfirst(name)
return 'get%s' % ucfirst(name)
def setterName(attr):
name = DartDomNameOfAttribute(attr)
return 'set%s' % ucfirst(name)
def getModule(annotations):
htmlaliases = ['audio', 'webaudio', 'inspector', 'offline', 'p2p', 'window', 'websockets', 'threads', 'view', 'storage', 'fileapi']
module = "dom"
if 'WebKit' in annotations and 'module' in annotations['WebKit']:
module = annotations['WebKit']['module']
if module in htmlaliases:
return "html"
if module == 'core':
return 'dom'
return module
java_lang = ['Object', 'String', 'Exception', 'DOMTimeStamp', 'DOMString']