microproduct/dem-sentiral/ISCEApp/site-packages/stone/backends/obj_c.py

284 lines
9.7 KiB
Python

from __future__ import absolute_import, division, print_function, unicode_literals
from contextlib import contextmanager
from stone.ir import (
is_list_type,
is_map_type,
is_struct_type,
is_union_type,
is_nullable_type,
is_user_defined_type,
is_void_type,
unwrap_nullable, )
from stone.backend import CodeBackend
from stone.backends.obj_c_helpers import (
fmt_camel_upper,
fmt_class,
fmt_class_prefix,
fmt_import, )
stone_warning = """\
///
/// Copyright (c) 2016 Dropbox, Inc. All rights reserved.
///
/// Auto-generated by Stone, do not modify.
///
"""
# This will be at the top of the generated file.
base_file_comment = """\
{}\
""".format(stone_warning)
undocumented = '(no description).'
comment_prefix = '/// '
class ObjCBaseBackend(CodeBackend):
"""Wrapper class over Stone generator for Obj C logic."""
# pylint: disable=abstract-method
@contextmanager
def block_m(self, class_name):
with self.block(
'@implementation {}'.format(class_name),
delim=('', '@end'),
dent=0):
self.emit()
yield
@contextmanager
def block_h_from_data_type(self, data_type, protocol=None):
assert is_user_defined_type(data_type), \
'Expected user-defined type, got %r' % type(data_type)
if not protocol:
extensions = []
if data_type.parent_type and is_struct_type(data_type):
extensions.append(fmt_class_prefix(data_type.parent_type))
else:
if is_union_type(data_type):
# Use a handwritten base class
extensions.append('NSObject')
else:
extensions.append('NSObject')
extend_suffix = ' : {}'.format(
', '.join(extensions)) if extensions else ''
else:
base = fmt_class_prefix(data_type.parent_type) if (
data_type.parent_type and
not is_union_type(data_type)) else 'NSObject'
extend_suffix = ' : {} <{}>'.format(base, ', '.join(protocol))
with self.block(
'@interface {}{}'.format(
fmt_class_prefix(data_type), extend_suffix),
delim=('', '@end'),
dent=0):
self.emit()
yield
@contextmanager
def block_h(self,
class_name,
protocol=None,
extensions=None,
protected=None):
if not extensions:
extensions = ['NSObject']
if not protocol:
extend_suffix = ' : {}'.format(', '.join(extensions))
else:
extend_suffix = ' : {} <{}>'.format(', '.join(extensions),
fmt_class(protocol))
base_interface_str = '@interface {}{} {{' if protected else '@interface {}{}'
with self.block(
base_interface_str.format(class_name, extend_suffix),
delim=('', '@end'),
dent=0):
if protected:
with self.block('', delim=('', '')):
self.emit('@protected')
for field_name, field_type in protected:
self.emit('{} _{};'.format(field_type, field_name))
self.emit('}')
self.emit()
yield
@contextmanager
def block_init(self):
with self.block('if (self)'):
yield
self.emit('return self;')
@contextmanager
def block_func(self, func, args=None, return_type='void',
class_func=False):
args = args if args is not None else []
modifier = '-' if not class_func else '+'
base_string = '{} ({}){}:{}' if args else '{} ({}){}'
signature = base_string.format(modifier, return_type, func, args)
with self.block(signature):
yield
def _get_imports_m(self, data_types, default_imports):
"""Emits all necessary implementation file imports for the given Stone data type."""
if not isinstance(data_types, list):
data_types = [data_types]
import_classes = default_imports
for data_type in data_types:
import_classes.append(fmt_class_prefix(data_type))
if data_type.parent_type:
import_classes.append(fmt_class_prefix(data_type.parent_type))
if is_struct_type(
data_type) and data_type.has_enumerated_subtypes():
for _, subtype in data_type.get_all_subtypes_with_tags():
import_classes.append(fmt_class_prefix(subtype))
for field in data_type.all_fields:
data_type, _ = unwrap_nullable(field.data_type)
# unpack list or map
while is_list_type(data_type) or is_map_type(data_type):
data_type = (data_type.value_data_type if
is_map_type(data_type) else data_type.data_type)
if is_user_defined_type(data_type):
import_classes.append(fmt_class_prefix(data_type))
if import_classes:
import_classes = list(set(import_classes))
import_classes.sort()
return import_classes
def _get_imports_h(self, data_types):
"""Emits all necessary header file imports for the given Stone data type."""
if not isinstance(data_types, list):
data_types = [data_types]
import_classes = []
for data_type in data_types:
if is_user_defined_type(data_type):
import_classes.append(fmt_class_prefix(data_type))
for field in data_type.all_fields:
data_type, _ = unwrap_nullable(field.data_type)
# unpack list or map
while is_list_type(data_type) or is_map_type(data_type):
data_type = (data_type.value_data_type if
is_map_type(data_type) else data_type.data_type)
if is_user_defined_type(data_type):
import_classes.append(fmt_class_prefix(data_type))
import_classes = list(set(import_classes))
import_classes.sort()
return import_classes
def _generate_imports_h(self, import_classes):
import_classes = list(set(import_classes))
import_classes.sort()
for import_class in import_classes:
self.emit('@class {};'.format(import_class))
if import_classes:
self.emit()
def _generate_imports_m(self, import_classes):
import_classes = list(set(import_classes))
import_classes.sort()
for import_class in import_classes:
self.emit(fmt_import(import_class))
self.emit()
def _generate_init_imports_h(self, data_type):
self.emit('#import <Foundation/Foundation.h>')
self.emit()
self.emit('#import "DBSerializableProtocol.h"')
if data_type.parent_type and not is_union_type(data_type):
self.emit(fmt_import(fmt_class_prefix(data_type.parent_type)))
self.emit()
def _get_namespace_route_imports(self,
namespace,
include_route_args=True,
include_route_deep_args=False):
result = []
def _unpack_and_store_data_type(data_type):
data_type, _ = unwrap_nullable(data_type)
if is_list_type(data_type):
while is_list_type(data_type):
data_type, _ = unwrap_nullable(data_type.data_type)
if not is_void_type(data_type) and is_user_defined_type(data_type):
result.append(data_type)
for route in namespace.routes:
if include_route_args:
data_type, _ = unwrap_nullable(route.arg_data_type)
_unpack_and_store_data_type(data_type)
elif include_route_deep_args:
data_type, _ = unwrap_nullable(route.arg_data_type)
if is_union_type(data_type) or is_list_type(data_type):
_unpack_and_store_data_type(data_type)
elif not is_void_type(data_type):
for field in data_type.all_fields:
data_type, _ = unwrap_nullable(field.data_type)
if (is_struct_type(data_type) or
is_union_type(data_type) or
is_list_type(data_type)):
_unpack_and_store_data_type(data_type)
_unpack_and_store_data_type(route.result_data_type)
_unpack_and_store_data_type(route.error_data_type)
return result
def _cstor_name_from_fields(self, fields):
"""Returns an Obj C appropriate name for a constructor based on
the name of the first argument."""
if fields:
return self._cstor_name_from_field(fields[0])
else:
return 'initDefault'
def _cstor_name_from_field(self, field):
"""Returns an Obj C appropriate name for a constructor based on
the name of the supplied argument."""
return 'initWith{}'.format(fmt_camel_upper(field.name))
def _cstor_name_from_fields_names(self, fields_names):
"""Returns an Obj C appropriate name for a constructor based on
the name of the first argument."""
if fields_names:
return 'initWith{}'.format(fmt_camel_upper(fields_names[0][0]))
else:
return 'initDefault'
def _struct_has_defaults(self, struct):
"""Returns whether the given struct has any default values."""
return [
f for f in struct.all_fields
if f.has_default or is_nullable_type(f.data_type)
]