note
description: "A generator of wrapper classes for Objective-C."
date: "$Date$"
revision: "$Revision$"
class
WRAPPERS_GENERATOR
inherit
SHARED_CONFIGURATION
OBJC_NAMES_CONVERSION
create
make
feature {NONE} -- Initialization
make (a_classes: HASH_TABLE [OBJC_CLASS_DECL, STRING]; a_protocols: HASH_TABLE [OBJC_PROTOCOL_DECL, STRING])
-- Initialize Current with `a_classes' and `a_protocols'
do
create aliases.make (0)
classes := a_classes
protocols := a_protocols
create struct_classes.make (0)
create struct_types.make (0)
ensure
classes_set: classes = a_classes
protocols_set: protocols = a_protocols
end
feature -- Queries
eiffel_class_exists (objc_type_identifier: STRING): BOOLEAN
-- Does the eiffel class corresponding to `objc_type_identifier' exist in the parsed system?
do
if attached struct_classes.item (objc_class_name_to_eiffel_style (objc_type_identifier)) then
Result := True
end
if attached classes.item (objc_type_identifier) then
Result := True
end
across aliases as cursor loop
if cursor.item.is_equal (objc_class_name_to_eiffel_style (objc_type_identifier)) then
Result := True
end
end
end
feature -- Generation
generate_wrappers
-- Generate the wrappers for the Objective-C `classes' and `protocols'.
-- The Objective-C class names are automatically converted to Eiffel-style names
-- and the output files are grouped by their framework in different folders.
do
create_project_directory
generate_struct_classes
generate_classes
generate_classes_categories
generate_utils_classes
generate_protocols
prepare_wrapper_project
-- Debug aliases
from
aliases.start
until
aliases.after
loop
print ("Generated alias %"" + aliases.item_for_iteration + "%" for type %"" + aliases.key_for_iteration + "%"%N")
aliases.forth
end
create_ecf_file
end
feature {NONE} -- Implementation
create_project_directory
-- Create project directory
local
directory: DIRECTORY
do
create directory.make (configuration.wrapper_name)
if not directory.exists then
directory.create_dir
end
end
prepare_wrapper_project
-- Create the wrapper project's main folder and other auxiliary folders and files.
local
directory: DIRECTORY
destination_file, source_file: RAW_FILE
wrapper_name: STRING
clib_folder: STRING
contents: STRING
auxiliary_folder_path: STRING
temp, temp2, temp3: STRING
i: INTEGER
a_struct: OBJC_STRUCT_TYPE_DECL
struct_name_to_use: STRING
ignore_struct: BOOLEAN
do
wrapper_name := configuration.wrapper_name
-- Create auxiliary folder
auxiliary_folder_path := wrapper_name + "/" + configuration.auxiliary_folder_name
create directory.make (auxiliary_folder_path)
if not directory.exists then
directory.create_dir
end
-- Copy 'objc_names_conversion.e' to auxiliary folder
source_file := configuration.objc_names_conversion_file
create destination_file.make_open_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Copy 'classes_mapper.e' to auxiliary folder
source_file := configuration.classes_mapper_file
source_file.read_stream (source_file.count)
contents := source_file.last_string
contents.replace_substring_all ("$PARSED_CLASSES_COUNT", classes.count.out)
create temp.make_empty
from
classes.start
i := 0
until
classes.after
loop
temp.append ("%T%T%T%Tparsed_classes[" + i.out + "] = objc_getClass(%"" + classes.key_for_iteration + "%");%N")
classes.forth
i := i + 1
end
if temp.count >= 1 then
temp.remove_tail (1)
end
contents.replace_substring_all ("$PARSED_CLASSES_INITIALIZATION", temp)
create destination_file.make_create_read_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
destination_file.put_string (contents)
destination_file.close
-- Copy 'ns_any.e' to auxiliary folder
source_file := configuration.ns_any_file
create destination_file.make_open_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Copy 'ns_common.e' to auxiliary folder
source_file := configuration.ns_common_file
source_file.read_stream (source_file.count)
contents := source_file.last_string
temp := ""
temp2 := ""
temp3 := ""
across struct_types as cursor loop
a_struct := cursor.item
create struct_name_to_use.make_empty
if a_struct.fields.count > 0 then
if a_struct.struct_name.is_equal ("?") then
if not a_struct.name.is_equal ("?") then
struct_name_to_use.append (a_struct.name)
else
ignore_struct := True
end
else
struct_name_to_use.append (a_struct.struct_name)
end
if not ignore_struct then
temp.append ("%T%T%Telseif attached {TYPE [detachable " + objc_class_name_to_eiffel_style (struct_name_to_use) + "]} eiffel_type then%N")
check attached a_struct.encoding as attached_encoding then
temp.append ("%T%T%T%TResult.append (%"" + attached_encoding + "%")%N")
end
temp2.append ("%T%T%T%T%T%Telseif attached {TYPE [detachable " + objc_class_name_to_eiffel_style (struct_name_to_use) + "]} argument_type then%N")
temp2.append ("%T%T%T%T%T%T%Tcreate {" + objc_class_name_to_eiffel_style (struct_name_to_use) + "} a_struct.make%N")
temp2.append ("%T%T%T%T%T%T%Tinitialize_" + objc_class_name_to_eiffel_style (struct_name_to_use).as_lower + "_struct (argument, a_struct.item)%N")
temp2.append ("%T%T%T%T%T%T%Targuments_tuple.put (a_struct, arguments_types_index)%N")
temp2.append ("%T%T%T%T%T%T%Targument.memory_free%N")
temp3.append ("%Tinitialize_" + objc_class_name_to_eiffel_style (struct_name_to_use).as_lower + "_struct (an_item: POINTER; struct_pointer: POINTER)%N")
temp3.append ("%T%T%T-- Auto generated Objective-C wrapper.%N")
temp3.append ("%T%Texternal%N")
temp3.append ("%T%T%T%"C inline%"%N")
temp3.append ("%T%Talias%N")
temp3.append ("%T%T%T%"*(" + struct_name_to_use + " *)$struct_pointer = *(" + struct_name_to_use + " *)$an_item%"%N")
temp3.append ("%T%Tend%N%N")
end
end
end
contents.replace_substring_all ("$STRUCTS_TESTS1", temp)
contents.replace_substring_all ("$STRUCTS_TESTS2", temp2)
contents.replace_substring_all ("$STRUCTS_INITIALIZERS", temp3)
create destination_file.make_create_read_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
destination_file.put_string (contents)
destination_file.close
-- Copy 'ns_category_common.e' to auxiliary folder
source_file := configuration.ns_category_common_file
create destination_file.make_open_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Copy 'ns_named_class.e' to auxiliary folder
source_file := configuration.ns_named_class
create destination_file.make_open_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Copy 'objc_class.e' to auxiliary folder
source_file := configuration.objc_class_file
create destination_file.make_open_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Copy 'objc_selector.e' to auxiliary folder
source_file := configuration.objc_selector_file
create destination_file.make_open_write (auxiliary_folder_path + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Create 'Clib' folder
create directory.make (wrapper_name + "/" + configuration.clib_folder_name)
if not directory.exists then
directory.create_dir
end
clib_folder := wrapper_name + "/" + configuration.clib_folder_name
-- Copy 'objc_callbacks.h' to the Clib folder
source_file := configuration.objc_callbacks_h_file
create destination_file.make_open_write (clib_folder + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
-- Copy 'objc_callbacks.m' to the Clib folder
source_file := configuration.objc_callbacks_m_file
source_file.read_stream (source_file.count)
contents := source_file.last_string
temp := ""
across struct_types as cursor loop
a_struct := cursor.item
create struct_name_to_use.make_empty
if a_struct.fields.count > 0 then
if a_struct.struct_name.is_equal ("?") then
if not a_struct.name.is_equal ("?") then
struct_name_to_use.append (a_struct.name)
else
ignore_struct := True
end
else
struct_name_to_use.append (a_struct.struct_name)
end
if not ignore_struct then
temp.append ("%T%Tif (strcmp(argumentType, @encode(" + struct_name_to_use + ")) == 0) {%N")
temp.append ("%T%T%T" + struct_name_to_use + " *value_pointer = malloc(sizeof(" + struct_name_to_use + ")); // Freeing is done in eiffel code.%N")
temp.append ("%T%T%T*value_pointer = va_arg(variableArguments, " + struct_name_to_use + ");%N")
temp.append ("%T%T%Targuments[i - 2] = value_pointer;%N")
temp.append ("%T%T%TargumentRead = YES;%N")
temp.append ("%T%T}%N")
end
end
end
contents.replace_substring_all ("$STRUCTS_TESTS", temp)
create destination_file.make_create_read_write (clib_folder + "/" + source_file.name.split ('/').last)
destination_file.put_string (contents)
destination_file.close
-- Copy 'Makefile.SH' to the Clib folder
source_file := configuration.makefile
create destination_file.make_open_write (clib_folder + "/" + source_file.name.split ('/').last)
source_file.copy_to (destination_file)
destination_file.close
end
generate_struct_classes
-- Generate wrapper classes for Objective-C structs
local
struct_wrapper_generator: STRUCT_WRAPPER_GENERATOR
do
create struct_wrapper_generator.make (aliases, struct_types)
from
classes.start
until
classes.after
loop
classes.item_for_iteration.accept (struct_wrapper_generator)
classes.forth
end
from
protocols.start
until
protocols.after
loop
protocols.item_for_iteration.accept (struct_wrapper_generator)
protocols.forth
end
struct_wrapper_generator.generate_wrappers
if configuration.generate_structs then
struct_classes := struct_wrapper_generator.struct_classes
from
struct_classes.start
until
struct_classes.after
loop
create_class_file (struct_classes.item_for_iteration)
struct_classes.forth
end
end
end
generate_classes
-- Generate wrapper classes for Objective-C classes.
local
class_wrapper_generator: CLASS_WRAPPER_GENERATOR
generated_classes: HASH_TABLE [EIFFEL_CLASS_DECL, STRING]
do
create class_wrapper_generator.make (Current)
from
classes.start
until
classes.after
loop
classes.item_for_iteration.accept (class_wrapper_generator)
classes.forth
end
generated_classes := class_wrapper_generator.classes
from
generated_classes.start
until
generated_classes.after
loop
create_class_file (generated_classes.item_for_iteration)
generated_classes.forth
end
end
generate_classes_categories
-- Generate wrapper classes for Objective-C categories.
local
category_wrapper_generator: CATEGORY_WRAPPER_GENERATOR
generated_classes: HASH_TABLE [EIFFEL_CLASS_DECL, STRING]
do
create category_wrapper_generator.make (Current)
from
classes.start
until
classes.after
loop
classes.item_for_iteration.accept (category_wrapper_generator)
classes.forth
end
generated_classes := category_wrapper_generator.classes
from
generated_classes.start
until
generated_classes.after
loop
create_class_file (generated_classes.item_for_iteration)
generated_classes.forth
end
end
generate_utils_classes
-- Generate wrapper utils classes for Objective-C classes.
local
utils_class_wrapper_generator: CLASS_METHODS_WRAPPER_GENERATOR
generated_classes: HASH_TABLE [EIFFEL_CLASS_DECL, STRING]
do
create utils_class_wrapper_generator.make (Current)
from
classes.start
until
classes.after
loop
classes.item_for_iteration.accept (utils_class_wrapper_generator)
classes.forth
end
generated_classes := utils_class_wrapper_generator.classes
from
generated_classes.start
until
generated_classes.after
loop
create_class_file (generated_classes.item_for_iteration)
generated_classes.forth
end
end
generate_protocols
-- Generate eiffel classes to wrap Objective-C protocols
local
protocols_generator: PROTOCOLS_GENERATOR
generated_classes: HASH_TABLE [EIFFEL_CLASS_DECL, STRING]
do
create protocols_generator.make (Current)
from
protocols.start
until
protocols.after
loop
protocols.item_for_iteration.accept (protocols_generator)
protocols.forth
end
generated_classes := protocols_generator.classes
from
generated_classes.start
until
generated_classes.after
loop
create_class_file (generated_classes.item_for_iteration)
generated_classes.forth
end
end
create_ecf_file
-- Create the ecf file for the wrapper.
local
wrapper_name: STRING
source_file: RAW_FILE
destination_file: RAW_FILE
contents: STRING
aliases_string: STRING
referenced_frameworks_string: STRING
referenced_frameworks: ARRAYED_LIST [STRING]
visitor: REFERENCED_FRAMEWORKS_VISITOR
do
wrapper_name := configuration.wrapper_name
source_file := configuration.ecf_file_template
source_file.read_stream (source_file.count)
contents := source_file.last_string
contents.replace_substring_all ("$WRAPPER_NAME", wrapper_name)
create aliases_string.make_empty
from
aliases.start
until
aliases.after
loop
aliases_string.append ("%T%T%T%N")
aliases.forth
end
if aliases.count > 0 then
aliases_string.remove_tail (1)
end
contents.replace_substring_all ("$ALIASES", aliases_string)
create visitor.make
from
classes.start
until
classes.after
loop
classes.item_for_iteration.accept (visitor)
classes.forth
end
from
protocols.start
until
protocols.after
loop
protocols.item_for_iteration.accept (visitor)
protocols.forth
end
referenced_frameworks := visitor.referenced_frameworks
from
create referenced_frameworks_string.make_empty
referenced_frameworks.start
until
referenced_frameworks.after
loop
referenced_frameworks_string.append ("%T%T%N")
referenced_frameworks.forth
end
if referenced_frameworks.count > 0 then
referenced_frameworks_string.remove_tail (1)
end
contents.replace_substring_all ("$FRAMEWORKS", referenced_frameworks_string)
create destination_file.make_create_read_write (wrapper_name + "/" + wrapper_name + ".ecf")
destination_file.put_string (contents)
destination_file.close
end
classes: HASH_TABLE [OBJC_CLASS_DECL, STRING]
-- A representation of classes indexed by their names.
protocols: HASH_TABLE [OBJC_PROTOCOL_DECL, STRING]
-- A representation of protocols indexed by their names.
aliases: HASH_TABLE [STRING, STRING]
-- A table of type aliases indexed by the pointed type.
-- E.g. Key: CG_RECT, Value: NS_RECT (alias).
struct_classes: HASH_TABLE [EIFFEL_CLASS_DECL, STRING]
-- A table of eiffel class declarations representing Objective-C structss.
struct_types: HASH_TABLE [OBJC_STRUCT_TYPE_DECL, STRING]
-- A table of structs types that have been visited indexed by their name.
feature {NONE} -- Utilities
create_class_file (a_class: EIFFEL_CLASS_DECL)
-- Create a new file named `a_class.name'.e containing `a_class.debug_output' in
-- a folder named `a_class.framework'. If the folder does not exist, it is created.
local
directory: DIRECTORY
file: RAW_FILE
base_path: STRING
do
base_path := configuration.wrapper_name + "/" + a_class.framework.as_lower
create directory.make (base_path)
if not directory.exists then
directory.create_dir
end
create file.make_open_write (base_path + "/" + a_class.name.as_lower + ".e")
file.put_string (a_class.debug_output)
file.close
end
end