# Copyright (C) 2001 Tobias Peters # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. class Idl_Interface attr_reader :name attr_reader :parent attr_reader :constants attr_reader :attributes attr_reader :methods attr_reader :children attr_reader :idl_module def to_s @name end # parsing the first IDL interface definition contained in remaining_lines def initialize (idl_module, # a reference to the module object remaining_lines) # the IDL text. the parsed interface # # definition is removed from this array @methods = [] @attributes = [] @constants = [] @children = [] @idl_module = idl_module # extract the name of this interface from the idl text if (remaining_lines[0] =~ /:/) # 1st line contains colon: inheritance! # extract this interface's and its parent's name regex = /\s*interface\s+([^\s]+)\s*:\s*([^\s]+)\s*\{/ match = regex.match(remaining_lines[0]) # first regex group match contains name of this interface @name = match[1] # second regex group match contains name of parent interface. Since the # IDL file is in inheritance order, the IDL_Interface instance for the # parent inteface has already been created. @parent = @idl_module.get_interface_with_name(match[2]) # for debugging: puts "interface #{@name} < #{parent}" if ($verbose) # let the parent know there is a child @parent.add_child(self) else # this interface is a base class @parent = nil # extract this interface's name regex = /\s*interface\s+([^\s]+)\s*\{/ match = regex.match(remaining_lines[0]) # first regex group match contains name of this interface @name = match[1] # for debugging: puts "interface #{@name}" if ($verbose) end # parse the interface's contents until the closing brace until(remaining_lines[0] =~ /}/) case (remaining_lines[0]) when /^\s*const/ # parse an interface constant @constants << Idl_Constant.new(@idl_module,self, remaining_lines.shift) when /\sattribute\s/ # parse an attribute @attributes << Idl_Attribute.new(@idl_module, self, remaining_lines) when /\/\// # skip a comment remaining_lines.shift when /\(/ # parse a method (line contains "(") @methods << Idl_Method.new(@idl_module, self, remaining_lines) else # skip blank lines remaining_lines.shift end end remaining_lines.shift # skip line containing closing brace end # learn that there is a derived class def add_child(new_child) @children << new_child end def has_children?() @children.size() > 0 end # return true if the "interface" is either equal to this or any of its # derived interfaces def is_or_has_child?(interface) if (@name == interface.to_s()) return true else inject = false @children.each{|c| inject = inject || c.is_or_has_child?(interface) } return inject end end # if this interface is derived from Node and has an assigned node type # constant (abstract interfaces don't have this), return a reference to # the object representing this constant, else return nil def get_matching_node_type_constant() constant = nil mangled_name = @name.downcase() @idl_module.node_constants().each {|c| if (c.name().sub(/_NODE$/, "").gsub(/_/, "").downcase() == mangled_name) constant = c end } constant end # return a list of all interfaces from which this interface is derived, # directly or indirectly def all_parents parent ? (parent.all_parents.push(parent)) : [] end # return a list of all interfaces that derive from this interface, directly # or indirectly def all_children children + children.collect{|child| child.all_children}.flatten end # return the topmost parent of this interface, or the interface itself if it # has no parent def top_parent parent ? parent.top_parent : self end # return a list of all interfaces that have a parent in common with this # interface, but are not derived from this interface nor is this interface # derived from any of them. def far_relatives near_relatives = all_parents + all_children + [self] all_relatives = top_parent.all_children return (all_relatives - near_relatives) end #################### def create_ruby_code() # the class object VALUE @idl_module.add_ruby_code("values", "static VALUE #{rbdom_class_name(@name)};\n") # create the function to extract the gdome pointer from the ruby object: if (@parent == nil) conversion_rb2c_code = ("static #{gdome_class_name(self)} * #{rb2c_name(self)} (VALUE rb){\n" + " #{gdome_class_name(self)} * c = NULL; \n" + " if (rb != Qnil) { \n" + " Data_Get_Struct(rb, #{gdome_class_name(self)}, c); \n" + " } \n" + " return c; \n" + "} \n") else # == if (@parent != nil) # This interface has a parent. This means, it is derived from Node. # Descendants from Node are handled differently: conversion_rb2c_code = ("static #{gdome_class_name(self)} * #{rb2c_name(self)}(VALUE rb) {\n" + " #{gdome_class_name(self)} * c = NULL; \n" + " #{gdome_class_name('Node')} *c_node = #{rb2c_name('Node')}(rb);\n" + " if (c_node != NULL) { \n" + " c = (#{gdome_class_name(self)} *) c_node; \n" + " } \n" + " return c; \n" + "} \n") end @idl_module.add_ruby_code("conversion_rb2c", conversion_rb2c_code) # create the function that wraps a gdome pointer. We also have to provide a # function that is called when the ruby object is garbage collected. if ((@parent == nil) && (@name != 'Node')) unref_method_name = ("unref_" + gdome_class_name(self)).gsub(/\s/, "") conversion_c2rb_code = ("static void #{unref_method_name}(#{gdome_class_name(self)} * c) {\n" + " GdomeException e = GDOME_NOEXCEPTION_ERR; \n" + " #{gdome_method_name(self, 'unref')} (c, &e); \n" + "} \n") conversion_c2rb_code << ("static VALUE #{c2rb_name(self)} (#{gdome_class_name(self)} * c) {\n" + " VALUE rb_wrapper = Qnil; \n" + " if (c != NULL) { \n" + " rb_wrapper = Data_Wrap_Struct(#{rbdom_class_name(self)}, \n" + " NULL, &#{unref_method_name}, \n" + " c); \n" + " } \n" + " return rb_wrapper; \n" + "} \n\n") elsif (@parent != nil) # descendant from Node unref_method_name = ("unref_" + gdome_class_name('Node')).gsub(/\s/, "") conversion_c2rb_code = ("static VALUE #{c2rb_name(self)} (#{gdome_class_name(self)} * c) {\n" + " #{gdome_class_name('Node')} * c_node = \n" + " (#{gdome_class_name('Node')} *) c; \n" + " return #{c2rb_name('Node')}(c_node); \n" + "} \n\n") else # @name=='Node'. In this case we have to return the most derived type unref_method_name = ("unref_" + gdome_class_name(self)).gsub(/\s/, "") conversion_c2rb_code = ("static void #{unref_method_name}(#{gdome_class_name(self)} * c) {\n" + " GdomeException e = GDOME_NOEXCEPTION_ERR; \n" + " #{gdome_method_name(self, 'unref')} (c, &e); \n" + "} \n") conversion_c2rb_code << ("static VALUE #{c2rb_name(self)} (#{gdome_class_name(self)} * c) {\n" + " GdomeException e = GDOME_NOEXCEPTION_ERR; \n" + " VALUE rbdom_class = Qnil; \n" + " unsigned short nodeType; \n" + " if (c == NULL) return Qnil; \n" + " nodeType = #{gdome_method_name(self, 'nodeType')}(c, &e); \n" + " if (e != GDOME_NOEXCEPTION_ERR) { \n" + " rbdom_raise(e); \n" + " return Qnil; \n" + " } \n" + " switch (#{gdome_method_name(self, 'nodeType')}(c, &e)) { \n") @idl_module.node_constants.each {|constant| interface = constant.get_matching_interface() if (interface) conversion_c2rb_code << (" case #{constant.value}: \n" + " rbdom_class = #{rbdom_class_name(interface)}; \n" + " break; \n") end } conversion_c2rb_code << (" } \n" + " return Data_Wrap_Struct(rbdom_class, NULL, \n" + " &#{unref_method_name}, c); \n" + "} \n\n") end @idl_module.add_ruby_code("conversion_c2rb", conversion_c2rb_code) # initialize the class object ruby_parent_class_name = @parent ? rbdom_class_name(@parent) : "rb_cObject" init_func_code = (" #{rbdom_class_name(self)} = \n" + " rb_define_class_under(#{@idl_module.get_c_value_name}, \n" + " \"#{@name}\", #{ruby_parent_class_name});\n") @idl_module.add_ruby_code("init_func", init_func_code) # create code for the constants, attributes, and methods of this interface @constants.each{|c| c.create_ruby_code() } @attributes.each{|a| a.create_ruby_code() } @methods.each{|m| m.create_ruby_code() } # debugging support: ref_count and gdome_id debug_methods = ("static VALUE #{rbdom_method_name(self, 'ref_count')} (VALUE self)\n" + "{ \n" + " int ref_count = 0; \n" + " #{gdome_class_name(self)} * c = #{rb2c_name(self)}(self); \n" + " if (c) ref_count = *(int*)(c+2); \n" + " return INT2NUM(ref_count); \n" + "} \n" + "static VALUE #{rbdom_method_name(self, 'gdome_id')} (VALUE self) \n" + "{ \n" + " #{gdome_class_name(self)} * c = #{rb2c_name(self)}(self); \n" + " return INT2NUM(((unsigned long)c) >> 1); \n" + "} \n") init_func_code = (" rb_define_method(#{rbdom_class_name(self)}, \"ref_count\", \n" + " &#{rbdom_method_name(self, 'ref_count')}, 0); \n" + " rb_define_method(#{rbdom_class_name(self)}, \"gdome_id\", \n" + " &#{rbdom_method_name(self, 'gdome_id')}, 0); \n") @idl_module.add_ruby_code("methods", debug_methods) @idl_module.add_ruby_code("init_func", init_func_code) end ################### def create_cpp_code(gdome_cpp_hh, gdome_cpp_cc, extra_declarations) # start class declaration gdome_cpp_hh << ("\n" + "class #{@name} ") if (@parent) gdome_cpp_hh << " : public #{@parent}" end gdome_cpp_hh << ("\n" + "{\n") # create code for the contained elements: gdome_cpp_hh << ("public: \n" + " /* ## Begin of official DOM level 2 core API declarations: */ \n") # constants: if (@constants.size > 0) gdome_cpp_hh << ("\n /* Constants (as enums, for possible use in case labels): */\n" + " enum { \n") @constants.each{|c| c.create_cpp_code(gdome_cpp_hh) } gdome_cpp_hh << (" }; \n") end # attributes: if (@attributes.size > 0) gdome_cpp_hh << ("\n /* Attribute accessing methods: */ \n") @attributes.each{|a| a.create_cpp_code(gdome_cpp_hh, gdome_cpp_cc) } end # methods: if (@methods.size > 0) gdome_cpp_hh << ("\n /* DOM interface methods: */ \n") @methods.each{|m| m.create_cpp_code(gdome_cpp_hh, gdome_cpp_cc) } end gdome_cpp_hh << (" /* ## End of official DOM level 2 core API declarations. */\n\n\n") # gdome specific methods: if (extra_declarations[@name]) gdome_cpp_hh << (" /* ## Begin of Gdome specific extensions of the DOM API: */ \n" + extra_declarations[@name].to_a.join("\n") + " \n" + " /* ## End of Gdome specific extensions of the DOM API. */\n\n\n") end # Check for NULL gdome_cpp_hh << ( " /* ## Begin of libgdome-cpp specific methods required for object" + " handling */ \n") if (@parent == nil) gdome_cpp_hh << (' // Check if this object is NULL: ' + " \n" + " bool \n" + " null(void) const; \n" + " // Another way to check for NULL: \n" + " bool \n" + " operator==(int) const; \n\n") gdome_cpp_cc << ("bool #{self}::null(void) const \n" + "{ \n" + " return gdome_obj == 0; \n" + "} \n\n" + "bool #{self}::operator==(int i) const \n" + "{ \n" + " return (i == 0) && null(); \n" + "} \n\n") end # copy constructor and assignment operator gdome_cpp_hh << (" // Copy constructor and assignment operator allow you to push\n" + " // DOM objects around: \n" + " #{self}(const #{self} &); \n" + " #{self} & \n" + " operator=(const #{self} &); \n\n") if (@parent) gdome_cpp_cc << ("#{self}::#{self}(const #{self} & other) \n" + " : #{@parent}(other) \n" + "{} \n\n" + "#{self} & #{self}::operator=(const #{self} & other) \n" + "{ \n" + " #{top_parent}::operator=(other); \n" + " return *this; \n" + "} \n\n") else gdome_cpp_cc << ("#{self}::#{self}(const #{self} & other) \n" + " : gdome_obj(other.gdome_obj) \n" + "{ \n" + " if (gdome_obj) { \n" + " unsigned short e = 0; \n" + " #{gdome_method_name(self, 'ref')}(gdome_obj, &e); \n" + " } \n" + "} \n\n" + "#{self} & #{self}::operator=(const #{self} & other) \n" + "{ \n" + " if (this == &other) return *this; \n" + " unsigned short e = 0; \n" + " if (gdome_obj) { \n" + " #{gdome_method_name(self, 'unref')}(gdome_obj, &e); \n" + " } \n" + " gdome_obj = other.gdome_obj; \n" + " if (gdome_obj) { \n" + " #{gdome_method_name(self, 'ref')}(gdome_obj, &e); \n" + " } \n" + " return *this; \n" + "} \n\n") end # cast from topmost parent to this class if (@parent) gdome_cpp_hh << (" // Replacement for dynamic_cast. The following constructor \n" + " // and assignment operator allow you to downcast an object \n" + " // from a base type to a more derived type. If the object \n" + " // in question is not of the expected type, then it will be\n" + " // null (see method null and operator==(int)) \n" + " #{self}(#{top_parent} &); \n" + " #{self} & \n" + " operator=(const #{top_parent} &); \n\n") gdome_cpp_cc << ("#{self}::#{self}(#{top_parent} & p) \n" + " : #{@parent}(p) \n" + "{} \n\n" + "#{self} & #{self}::operator=(const #{top_parent} & p) \n" + "{ \n" + " #{top_parent}::operator=(p); \n" + " return *this; \n" + "} \n\n") end # the constructor to wrap gdome objects gdome_cpp_hh << (" // Use this constructor only to create 0 objects. You never \n" + " // need to deal with objects of class #{gdome_class_name(self)}\n" + " // directly. This constructor only enables you to declare null \n" + " // objects on the stack and to use a literal 0 for null objects\n" + " // in some API methods, where a #{self} is expected that may \n" + " // be null. \n" + " #{self}(#{gdome_class_name(self)} * = 0); \n\n") if (@parent) gdome_cpp_cc << ("#{self}::#{self}(#{gdome_class_name(self)} * gdome_obj_) \n" + " : #{@parent}(reinterpret_cast<#{gdome_class_name(@parent)} *>\n" + " (gdome_obj_)) \n" + "{} \n\n") else gdome_cpp_cc << ("#{self}::#{self}(#{gdome_class_name(self)} * gdome_obj_) \n" + " : gdome_obj(gdome_obj_) \n" + "{} \n\n") end # operator== if (@parent == nil) gdome_cpp_hh << (" // Check if two wrapper objects wrap the same XML object: \n" + " bool \n" + " operator==(const #{self} &) const; \n\n") gdome_cpp_cc << ("bool #{self}::operator==(const #{self} & other) const \n" + "{ \n" + " return gdome_id() == other.gdome_id(); \n" + "} \n\n") end # the destructor is *not* virtual, by intention: it saves the space for the # vtable pointer and it is really not needed -- only base classes have data # members, derived classes just add methods. Derived classes have empty # destructors, wo do not need to specify them: if (@parent == nil) # interface is a base class, implement destructor: gdome_cpp_hh << " ~#{self}(); \n" gdome_cpp_cc << ("#{self}::~#{self}() \n" + "{ \n" + " GdomeException e = 0; \n" + " #{gdome_method_name(self, 'unref')} (gdome_obj, &e); \n" + "} \n\n") end gdome_cpp_hh << ( " /* ## End of libgdome-cpp specific methods required for object" + " handling */ \n\n") # debugging support: ref_count und gdome_id if (@parent == nil) gdome_cpp_hh << ("\n /* ## Begin of debugging support methods */ \n\n" + " // access to the internal reference count of the wrapped gdome\n" + " // object: \n" + " int \n" + " ref_count (void) const; \n\n" + " // an integer id that identifies a wrapped gdome object: \n" + " int \n" + " gdome_id (void) const; \n\n" + " /* ## End of debugging support methods */ \n\n") gdome_cpp_cc << ("int #{self}::ref_count (void) const \n" + "{ \n" + " return * reinterpret_cast(get_gdome_obj() + 1); \n" + "} \n\n" + "int #{self}::gdome_id (void) const \n" + "{ \n" + " return reinterpret_cast(get_gdome_obj()) >> 1; \n" + "} \n\n") end gdome_cpp_hh << (" // All methods of #{self} that may be called by the user are \n" + " // declared above this line. Following declarations in this \n" + " // class are required by libgdome-cpp itself only. \n\n" + "private: \n\n") # prohibit clearly forbidden casts, e.g. from Element to Entity. We declare # these conversion methods private and then don't implement them. This will # give a compilation error. if (far_relatives.size > 0) gdome_cpp_hh << (" // Here we prohibit clearly forbidden casts. We declare these\n" + " // conversion methods private and then don't implement them. \n" + "// This will result in a compilation error when trying this cast.\n") far_relatives.each{|relative| gdome_cpp_hh << (" #{self}(#{relative} &); \n" + " #{self} & \n" + " operator=(#{relative} &); \n") } gdome_cpp_hh << (" // End of forbidden casts \n\n") end # only base classes have a pointer to a gdome object if (@parent == nil) gdome_cpp_hh << (" // A pointer to the wrapped gdome object \n" + " // (only present in base classes) \n" + " #{gdome_class_name(self)} * gdome_obj; \n\n") end # C object accessor: gdome_cpp_hh << (" // An accessor to the pointer to the wrapped gdome object \n" + " protected: \n" + " #{gdome_class_name(self)} * \n" + " get_gdome_obj(void) const; \n\n") if (parent) gdome_cpp_cc << ("#{gdome_class_name(self)} * #{self}::get_gdome_obj(void) const \n" + "{ \n" + " return reinterpret_cast<#{gdome_class_name(self)}*> \n" + " (#{@parent}::get_gdome_obj()); \n" + "} \n") else gdome_cpp_cc << ("#{gdome_class_name(self)} * #{self}::get_gdome_obj(void) const \n" + "{ \n" + " return gdome_obj; \n" + "} \n") end # also non-related interface classes need access to the pointer in case # they have an object of this class as a parameter to one of their methods friend_declarations = "" @idl_module.interfaces.each{|i| if (i != self) needs_to_be_friend = false i.methods.each{|m| m.parameters.each{|p| if (p.ptype == @name) needs_to_be_friend = true end } } if (needs_to_be_friend) friend_declarations << " friend class #{i}; \n" end end } CPP_EXTRA_FRIENDSHIPS[@name].to_a.each{|i| friend_declarations << " friend class #{i}; \n" } if (friend_declarations != "") gdome_cpp_hh << (" // These unrelated classes need access to the get_gdome_obj()\n" + " // call: \n" + " /* ## Begin of list of friend classes: */ \n") gdome_cpp_hh << friend_declarations gdome_cpp_hh << " /* ## End of list of friend classes */ \n" end gdome_cpp_hh << "};\n" end def create_forward_declaration(gdome_cpp_hh) gdome_cpp_hh << "class #{self};\n" end end