lib/ridl/node.rb



#--------------------------------------------------------------------
# node.rb - IDL nodes
#
# Author: Martin Corino
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the RIDL LICENSE which is
# included with this program.
#
# Copyright (c) Remedy IT Expertise BV
#--------------------------------------------------------------------
module IDL::AST
  REPO_ID_XCHARS = ['.', '-', '_']
  REPO_ID_RE = /^[#{('a'..'z').to_a.join}#{('A'..'Z').to_a.join}#{('0'..'9').to_a.join}\.\-_\/]+$/

  class Annotation
    def initialize(id, fields = {})
      @id = id.to_sym
      # copy field map transforming all keys to symbols and
      # detecting nested annotation objects
      @fields = fields.inject({}) do |m, (k, v)|
          m[k.to_sym] = case v
              when Array
                v.collect { |ve| Hash === ve ? Annotation.new(*ve.to_a.first) : ve }
              when Hash
                Annotation.new(*v.to_a.first)
              else
                v
            end
          m
        end
    end

    attr_reader :id, :fields

    def is_marker?
      @fields.empty?
    end

    def [](fieldid)
      @fields[(fieldid || '').to_sym]
    end

    def each(&block)
      @fields.each(&block)
    end
  end

  class Annotations
    def initialize
      @index = {}
      @stack = []
    end

    def empty?
      @stack.empty?
    end

    def [](annid)
      (@index[annid] || []).collect { |ix| @stack[ix] }
    end

    def <<(ann)
      (@index[ann.id] ||= []) << @stack.size
      @stack << ann
    end

    def each(&block)
      @stack.each(&block)
    end

    def each_for_id(annid, &block)
      self[annid].each(&block)
    end

    def concat(anns)
      anns.each { |_ann| self << _ann } if anns
    end
  end

  class Leaf
    attr_reader :name, :intern, :scopes, :prefix, :annotations
    attr_accessor :enclosure

    def typename
      self.class.name
    end

    def initialize(_name, _enclosure)
      _name ||= ''
      _name = IDL::Scanner::Identifier.new(_name, _name) unless IDL::Scanner::Identifier === _name
      @name = _name
      @lm_name = nil
      @intern = _name.rjust(1).downcase.intern
      @enclosure = _enclosure
      @scopes = if @enclosure then (@enclosure.scopes.dup << self) else [] end
      @prefix = ''
      @repo_id = nil
      @repo_ver = nil
      @annotations = Annotations.new
    end

    def lm_name
      @lm_name ||= @name.checked_name.dup
    end

    def lm_scopes
      @lm_scopes ||= if @enclosure then (@enclosure.lm_scopes.dup << lm_name) else [] end
    end

    def unescaped_name
      @name.unescaped_name
    end

    def scoped_name
      @scoped_name ||= @scopes.collect { |s| s.name }.join("::").freeze
    end

    def scoped_lm_name
      @scoped_lm_name ||= lm_scopes.join("::").freeze
    end

    def marshal_dump
      [@name, lm_name, @intern, @enclosure, @scopes, @prefix, @repo_id, @repo_ver, @annotations]
    end

    def marshal_load(vars)
      @name, @lm_name, @intern, @enclosure, @scopes, @prefix, @repo_id, @repo_ver, @annotations = vars
      @scoped_name = nil
      @scoped_lm_name = nil
      @lm_scopes = nil
    end

    def is_template?
      @enclosure && @enclosure.is_template?
    end

    def instantiate(instantiation_context, _enclosure, _params = {})
      (instantiation_context[self] = self.class.new(self.name, _enclosure, _params)).copy_from(self, instantiation_context)
    end

    def set_repo_id(id)
      if @repo_id
        if id != @repo_id
          raise "#{self.scoped_name} already has a different repository ID assigned: #{@repo_id}"
        end
      end
      id_arr = id.split(':')
      if @repo_ver
        if id_arr.first != 'IDL' or id_arr.last != @repo_ver
          raise "supplied repository ID (#{id}) does not match previously assigned repository version for #{self.scoped_name} = #{@repo_ver}"
        end
      end
      # check validity of IDL format repo IDs
      if id_arr.first == 'IDL'
        id_arr.shift
        id_str = id_arr.shift.to_s
        raise 'ID identifiers should not start or end with \'/\'' if id_str[0, 1] == '/' or id_str[-1, 1] == '/'
        raise "ID identifiers should not start with one of '#{REPO_ID_XCHARS.join("', '")}'" if REPO_ID_XCHARS.include?(id_str[0, 1])
        raise 'Invalid ID! Only a..z, A..Z, 0..9, \'.\', \'-\', \'_\' or \'\/\' allowed for identifiers' unless REPO_ID_RE =~ id_str
      end
      @repo_id = id
    end

    def set_repo_version(ma, mi)
      ver = "#{ma}.#{mi}"
      if @repo_ver
        if ver != @repo_ver
          raise "#{self.scoped_name} already has a repository version assigned: #{@repo_ver}"
        end
      end
      if @repo_id
        l = @repo_id.split(':')
        if l.last != ver
          raise "supplied repository version (#{ver}) does not match previously assigned repository ID for #{self.scoped_name}: #{@repo_id}"
        end
      end
      @repo_ver = ver
    end

    def prefix=(pfx)
      unless pfx.to_s.empty?
        raise 'ID prefix should not start or end with \'/\'' if pfx[0, 1] == '/' or pfx[-1, 1] == '/'
        raise "ID prefix should not start with one of '#{REPO_ID_XCHARS.join("', '")}'" if REPO_ID_XCHARS.include?(pfx[0, 1])
        raise 'Invalid ID prefix! Only a..z, A..Z, 0..9, \'.\', \'-\', \'_\' or \'\/\' allowed' unless REPO_ID_RE =~ pfx
      end
      self.set_prefix(pfx)
    end

    def replace_prefix(pfx)
      self.prefix = pfx
    end

    def repository_id
      if @repo_id.nil?
        @repo_ver = "1.0" unless @repo_ver
        format("IDL:%s%s:%s",
                if @prefix.empty? then "" else @prefix + "/" end,
                self.scopes.collect { |s| s.name }.join("/"),
                @repo_ver)
      else
        @repo_id
      end
    end

    def has_annotations?
      !@annotations.empty?
    end

    def resolve(_name)
      nil
    end

    def is_local?
      false
    end

  protected

    def set_prefix(pfx)
      @prefix = pfx.to_s
    end

    def copy_from(template, _)
      @prefix = template.instance_variable_get(:@prefix)
      @repo_id = template.instance_variable_get(:@repo_id)
      @repo_ver = template.instance_variable_get(:@repo_ver)
      @annotations = template.instance_variable_get(:@annotations)
      self
    end
  end # Leaf

  class Node < Leaf
    def initialize(name, enclosure)
      super
      @introduced = {}
      @children = []
      introduce(self)
    end

    def marshal_dump
      super() << @children << @introduced
    end

    def marshal_load(vars)
      @introduced = vars.pop
      @children = vars.pop
      super(vars)
    end

    def introduce(node)
      n = (@introduced[node.intern] ||= node)
      raise "#{node.name} is already introduced as a #{n.scoped_name} of #{n.typename}." if n != node
    end

    def undo_introduction(node)
      @introduced.delete(node.intern)
    end

    def redefine(node, _params)
      raise "\"#{node.name}\" is already defined."
    end

    def is_definable?(_type)
      self.class::DEFINABLE.any? do |target|
        _type.ancestors.include? target
      end
    end

    def define(_type, _name, params = {})
      unless is_definable?(_type)
        raise "#{_type.to_s} is not definable in #{self.typename}."
      end

      node = search_self(_name)
      if node.nil?
        node = _type.new(_name, self, params)
        node.annotations.concat(params[:annotations])
        node.prefix = @prefix
        introduce(node)
        @children << node
      else
        if _type != node.class
          raise "#{_name} is already defined as a type of #{node.typename}"
        end

        node = redefine(node, params)
      end
      node
    end

    def resolve(_name)
      node = search_enclosure(_name)
      @introduced[node.intern] = node unless node.nil?
      node
    end

    def walk_members(&block)
      @children.each(&block)
    end

    def match_members(&block)
      !(@children.find(&block)).nil?
    end

    def select_members(&block)
      @children.select(&block)
    end

    def replace_prefix(pfx)
      super
      walk_members { |m| m.replace_prefix(pfx) }
    end

  protected
    def children
      @children
    end

    def search_self(_name)
      key = _name.downcase.intern
      node = @introduced[key]
      if not node.nil? and node.name != _name
        raise "\"#{_name}\" clashed with \"#{node.name}\"."
      end

      node
    end

    def search_enclosure(_name)
      node = search_self(_name)
      if node.nil? and not @enclosure.nil?
        node = @enclosure.search_enclosure(_name)
      end
      node
    end

    def walk_members_for_copy(&block)
      self.walk_members(&block)
    end

    def copy_from(_template, instantiation_context)
      super
      _template.__send__(:walk_members_for_copy) do |child|
        _child_copy = child.instantiate(instantiation_context, self)
        @children << _child_copy
        # introduce unless already introduced (happens with module chains)
        @introduced[_child_copy.intern] = _child_copy unless @introduced.has_key?(_child_copy.intern)
      end
      self
    end
  end # Node

  # Forward declarations

  class Module < Node; end
  class Include < Module; end
  class TemplateParam < Leaf; end
  class TemplateModule < Module; end
  class TemplateModuleReference < Leaf; end
  class Derivable < Node; end
  class Interface < Derivable; end
  class ComponentBase < Derivable; end
  class Connector < ComponentBase; end
  class Component < ComponentBase; end
  class Porttype < Node; end
  class Port < Leaf; end
  class Home < ComponentBase; end
  class Valuebox < Leaf; end
  class Valuetype < Derivable; end
  class Typedef < Leaf; end
  class Const < Leaf; end
  class Operation < Node; end
  class Attribute < Leaf; end
  class Parameter < Leaf; end
  class StateMember < Leaf; end
  class Initializer < Leaf; end
  class Finder < Initializer; end
  class Struct < Node; end
  class Member < Leaf; end
  class Union < Node; end
  class UnionMember < Member; end
  class Enum < Leaf; end
  class Enumerator < Leaf; end

  class Module < Node
    DEFINABLE = [
      IDL::AST::Module, IDL::AST::Interface, IDL::AST::Valuebox, IDL::AST::Valuetype, IDL::AST::Const, IDL::AST::Struct,
      IDL::AST::Union, IDL::AST::Enum, IDL::AST::Enumerator, IDL::AST::Typedef, IDL::AST::Include,
      IDL::AST::Home, IDL::AST::Porttype, IDL::AST::Component, IDL::AST::Connector
    ]
    attr_reader :anchor, :next, :template, :template_params

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @anchor = params[:anchor]
      @prefix = params[:prefix] || @prefix
      @template = params[:template]
      @template_params = (params[:template_params] || []).dup
      @next = nil
    end

    def has_anchor?
      !@anchor.nil?
    end

    def is_templated?
      @template ? true : false
    end

    def template_param(param)
      return nil unless @template

      param = param.to_s if ::Symbol === param
      if ::String === param
        @template.params.each_with_index do |tp, ix|
          return @template_params[ix] if tp.name == param
        end
        nil
      else
        @template_params[param] rescue nil
      end
    end

    def annotations
      (has_anchor? ? self.anchor : self).get_annotations
    end

    def marshal_dump
      super() << @anchor << @next
    end

    def marshal_load(vars)
      @next = vars.pop
      @anchor = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      super(instantiation_context, _enclosure, {})
    end

    def redefine(node, params)
      case node
      when IDL::AST::Include
        if node.enclosure == self
          return node
        else
          _inc = IDL::AST::Include.new(node.name, self, params)
          _inc.annotations.concat(params[:annotations])
          @children << _inc
          return _inc
        end
      when IDL::AST::Module
        # Module reopening
        _anchor = node.has_anchor? ? node.anchor : node
        _anchor.annotations.concat(params.delete(:annotations))
        _last = _anchor.find_last
        _params = params.merge({ anchor: _anchor, prefix: node.prefix })
        _next = IDL::AST::Module.new(node.name, self, _params)
        _last.set_next(_next)
        @children << _next
        return _next
      when IDL::AST::Interface
        node.annotations.concat(params[:annotations])
        # in case of a forward declaration in the same module ignore it since a previous declaration already exists
        if params[:forward]
          return node if node.enclosure == self
          # forward declaration in different scope (other module section in same file or other file)
        elsif node.is_defined?
          # multiple full declarations are illegal
          raise "#{node.typename} \"#{node.name}\" is already defined."
        end
        if (node.is_abstract? != params[:abstract]) || (node.is_local? != params[:local]) || (node.is_pseudo? != params[:pseudo])
          raise "\"attributes are not the same: \"#{node.name}\"."
        end

        _intf = IDL::AST::Interface.new(node.name, self, params)
        _intf.annotations.concat(node.annotations)
        _intf.prefix = node.prefix
        _intf.instance_variable_set(:@repo_ver, node.instance_variable_get(:@repo_ver))
        _intf.instance_variable_set(:@repo_id, node.instance_variable_get(:@repo_id))

        @children << _intf

        # in case of a full declaration undo the preceding forward introduction and
        # replace by full node
        # (no need to introduce forward declaration since there is a preceding introduction)
        unless params[:forward]
          # replace forward node registration
          node.enclosure.undo_introduction(node)
          introduce(_intf)
        end

        return _intf
      when IDL::AST::Valuetype
        node.annotations.concat(params[:annotations])
        return node if params[:forward]
        if node.is_defined?
          raise "#{node.typename} \"#{node.name}\" is already defined."
        end
        if (node.is_abstract? != params[:abstract])
          raise "\"attributes are not the same: \"#{node.name}\"."
        end

        _new_node = node.class.new(node.name, self, params)
        _new_node.annotations.concat(node.annotations)
        _new_node.prefix = node.prefix
        _new_node.instance_variable_set(:@repo_ver, node.instance_variable_get(:@repo_ver))
        _new_node.instance_variable_set(:@repo_id, node.instance_variable_get(:@repo_id))

        @children << _new_node
        # replace forward node registration
        node.enclosure.undo_introduction(node)
        introduce(_new_node)

        return _new_node
      when IDL::AST::Struct, IDL::AST::Union
        node.annotations.concat(params[:annotations])
        return node if params[:forward]
        if node.is_defined?
          raise "#{node.typename} \"#{node.name}\" is already defined."
        end

        _new_node = node.class.new(node.name, self, params)
        _new_node.annotations.concat(node.annotations)
        _new_node.prefix = node.prefix
        _new_node.instance_variable_set(:@repo_ver, node.instance_variable_get(:@repo_ver))
        _new_node.instance_variable_set(:@repo_id, node.instance_variable_get(:@repo_id))

        node.switchtype = params[:switchtype] if node.is_a?(IDL::AST::Union)

        @children << _new_node
        # replace forward node registration
        node.enclosure.undo_introduction(node)
        introduce(_new_node)

        return _new_node
      end
      raise "#{node.name} is already introduced as #{node.typename} #{node.scoped_name}."
    end

    def undo_introduction(node)
      _mod = (@anchor || self)
      while _mod
        _mod._undo_link_introduction(node)
        _mod = _mod.next
      end
    end

    def replace_prefix(pfx)
      self.prefix = pfx # handles validation
      if @anchor.nil?
        self.replace_prefix_i(pfx)
      else
        @anchor.replace_prefix_i(pfx)
      end
    end

  protected

    def set_prefix(pfx)
      if @anchor.nil?
        self.set_prefix_i(pfx)
      else
        @anchor.set_prefix_i(pfx)
      end
    end

    def get_annotations
      @annotations
    end

    def copy_from(_template, instantiation_context)
      super
      if _template.has_anchor?
        # module anchor is first to be copied/instantiated and
        # should be registered in instantiation_context
        cp = IDL::AST::TemplateParam.concrete_param(instantiation_context, _template.anchor)
        # concrete param must be a IDL::Type::NodeType and it's node a Module (should never fail)
        raise "Invalid concrete anchor found" unless cp.is_a?(IDL::Type::NodeType) && cp.node.is_a?(IDL::AST::Module)

        @anchor = cp.node
        # link our self into module chain
        @anchor.find_last.set_next(self)
      end
      @next = nil # to be sure
      self
    end

    def replace_prefix_i(pfx)
      walk_members { |m| m.replace_prefix(pfx) }
      # propagate along chain using fast method
      @next.replace_prefix_i(pfx) unless @next.nil?
    end

    def set_prefix_i(pfx)
      @prefix = pfx
      # propagate along chain
      self.next.set_prefix_i(pfx) unless self.next.nil?
    end

    def search_self(_name)
      (@anchor || self).search_links(_name)
    end

    def search_links(_name)
      _key = _name.downcase.intern
      node = @introduced[_key]
      if not node.nil? and node.name != _name
        raise "\"#{_name}\" clashed with \"#{node.name}\"."
      end

      if node.nil? && @next
        node = @next.search_links(_name)
      end
      node
    end

    def _undo_link_introduction(node)
      @introduced.delete(node.intern)
    end

    def set_next(mod)
      @next = mod
    end

    def find_last
      if @next.nil?
        self
      else
        @next.find_last
      end
    end
  end # Module

  class TemplateParam < Leaf
    attr_reader :idltype, :concrete

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
      @concrete = nil
    end

    def marshal_dump
      super() << @idltype
    end

    def marshal_load(vars)
      @idltype = vars.pop
      super(vars)
    end

    def is_template?
      true
    end

    def set_concrete_param(_param)
      @concrete = _param
    end

    def concrete_matches?(idl_type)
      if @concrete
        concrete_type = (@concrete.is_a?(IDL::Type) ? @concrete : @concrete.idltype).resolved_type
        return concrete_type.matches?(idl_type.resolved_type)
      end
      false
    end

    def self.concrete_param(instantiation_context, tpl_elem)
      # is this an element from the template's scope
      if tpl_elem.is_template?
        celem = if tpl_elem.is_a?(IDL::AST::TemplateParam) # an actual template parameter?
          tpl_elem.concrete # get the template parameter's concrete (instantiation argument) value
        else
          # referenced template elements should have been instantiated already and available through context
          ctxelem = instantiation_context[tpl_elem]
          # all items in the context are AST elements but for a concrete parameter value only constants and type
          # elements will be referenced; return accordingly
          ctxelem.is_a?(IDL::AST::Const) ? ctxelem.expression : ctxelem.idltype
        end
        raise "cannot resolve concrete node for template #{tpl_elem.typename} #{tpl_elem.scoped_lm_name}" unless celem

        celem
      else
        tpl_elem.idltype # just return the element's idltype if not from the template scope
      end
    end
  end

  class TemplateModule < Module
    DEFINABLE = [
      IDL::AST::Include, IDL::AST::Module, IDL::AST::Interface, IDL::AST::Valuebox, IDL::AST::Valuetype,
      IDL::AST::Const, IDL::AST::Struct, IDL::AST::Union, IDL::AST::Enum, IDL::AST::Enumerator, IDL::AST::Typedef,
      IDL::AST::Home, IDL::AST::Porttype, IDL::AST::Component, IDL::AST::Connector,
      IDL::AST::TemplateParam, IDL::AST::TemplateModuleReference
    ]
    attr_reader :idltype

    def initialize(_name, _enclosure, _params)
      super(_name, _enclosure, {})
      @idltype = IDL::Type::TemplateModule.new(self)
      @template_params = []
    end

    def define(*args)
      child = super(*args)
      @template_params << child if child.is_a?(IDL::AST::TemplateParam)
      child
    end

    def is_template?
      true
    end

    def params
      @template_params
    end

    def instantiate(_module_instance, instantiation_context = {})
      # process concrete parameters
      @template_params.each_with_index do |_tp, _ix|
        raise "missing template parameter for #{typename} #{scoped_lm_name}: #{_tp.name}" unless _ix < _module_instance.template_params.size

        _cp = _module_instance.template_params[_ix]
        if _cp.is_a?(IDL::Type)
          raise "anonymous type definitions are not allowed!" if _cp.is_anonymous?
          # parameter should be a matching IDL::Type
          unless _tp.idltype.is_a?(IDL::Type::Any) || _tp.idltype.class === _cp.resolved_type
            raise "mismatched instantiation parameter \##{_ix} #{_cp.typename} for #{typename} #{scoped_lm_name}: expected #{_tp.idltype.typename} for #{_tp.name}"
          end

          # verify concrete parameter
          case _tp.idltype
            when IDL::Type::Any # 'typename'
              # no further checks
            when IDL::Type::Interface, # 'interface'
                 IDL::Type::Eventtype, # 'eventtype'
                 IDL::Type::Valuetype, # 'valuetype'
                 IDL::Type::Struct, # 'struct'
                 IDL::Type::Union, # 'union'
                 IDL::Type::Exception, # 'exception'
                 IDL::Type::Enum # 'enum'
               # no further checks
            when IDL::Type::Sequence # 'sequence' or 'sequence<...>'
              _tptype = _tp.idltype
              unless _tptype.basetype.is_a?(IDL::Type::Void) # 'sequence'
                # check basetype
                unless _tptype.basetype.is_a?(IDL::Type::ScopedName) &&
                       _tptype.basetype.is_node?(IDL::AST::TemplateParam) &&
                       _tptype.basetype.node.concrete_matches?(_cp.resolved_type.basetype)
                  raise "invalid sequence type as instantiation parameter for #{typename} #{scoped_lm_name}: expected #{_tp.idltype.typename} for #{_tp.name}"
                end
              end
          end
        elsif _cp.is_a?(IDL::Expression)
          # template param should be 'const <const_type>'
          unless _tp.idltype.is_a?(IDL::Type::Const)
            raise "unexpected expression as instantiation parameter for #{typename} #{scoped_lm_name}: expected #{_tp.idltype.typename} for #{_tp.name}"
          end

          # match constant type
          _tp.idltype.narrow(_cp.value)
        else
          raise "invalid instantiation parameter for #{typename} #{scoped_lm_name}: #{_cp.class.name}"
        end
        # if we  get here all is well -> store concrete param (either IDL type or expression)
        _tp.set_concrete_param(_cp)
      end
      # instantiate template by copying template module state to module instance
      _module_instance.copy_from(self, instantiation_context)
    end

  protected

    def walk_members_for_copy
      @children.each { |c| yield(c) unless c.is_a?(IDL::AST::TemplateParam) }
    end
  end # TemplateModule

  class TemplateModuleReference < Leaf
    def initialize(_name, _enclosure, _params)
      super(_name, _enclosure)
      unless _params[:tpl_type].is_a?(IDL::Type::ScopedName) && _params[:tpl_type].is_node?(IDL::AST::TemplateModule)
        raise "templated module reference type required for #{typename} #{scoped_lm_name}: got #{_params[:tpl_type].typename}"
      end

      @template = _params[:tpl_type].resolved_type.node
      _params[:tpl_params].each do |p|
        unless (p.is_a?(IDL::Type::ScopedName) || p.is_a?(IDL::Expression::ScopedName)) && p.is_node?(IDL::AST::TemplateParam)
          raise "invalid template module parameter for template module reference #{typename} #{scoped_lm_name}: #{p.typename}"
        end
      end
      @params = _params[:tpl_params].collect { |p| p.resolved_node }
    end

    def marshal_dump
      super() << @template << @params
    end

    def marshal_load(vars)
      @params = vars.pop
      @template = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      inst_params = @params.collect do |tp|
        # concrete objects are either Expression or Type
        tp.concrete
      end
      mod_inst = IDL::AST::Module.new(self.name, _enclosure, { template: @template, template_params: inst_params })
      @template.instantiate(mod_inst, instantiation_context)
      mod_inst
    end

    def resolve(_name)
      @template.resolve(_name)
    end
  end # TemplateModuleReference

  class Include < Module
    attr_reader :filename, :fullpath

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure, params)
      @filename = params[:filename]
      @fullpath = params[:fullpath]
      @defined = params[:defined] || false
      @preprocessed = params[:preprocessed] || false
      # overrule
      @scopes = @enclosure.scopes
      @scoped_name = @scopes.collect { |s| s.name }.join("::")
    end

    def lm_scopes
      @lm_scopes ||= @enclosure.lm_scopes
    end

    def marshal_dump
      super() << @filename << @defined << @preprocessed
    end

    def marshal_load(vars)
      @preprocessed = vars.pop
      @defined = vars.pop
      @filename = vars.pop
      super(vars)
      # overrule
      @scopes = @enclosure.scopes || []
      @scoped_name = @scopes.collect { |s| s.name }.join("::")
    end

    def is_defined?
      @defined
    end

    def is_preprocessed?
      @preprocessed
    end

    def introduce(node)
      @enclosure.introduce(node) unless node == self
    end

    def undo_introduction(node)
      @enclosure.undo_introduction(node) unless node == self
    end

    def resolve(_name)
      @enclosure.resolve(_name)
    end

  protected
    def copy_from(_template, instantiation_context)
      super
      @filename = _template.filename
      @defined = _template.is_defined?
      @preprocessed = _template.is_preprocessed?
      # overrule
      @scopes = @enclosure.scopes
      @scoped_name = @scopes.collect { |s| s.name }.join("::")
      self
    end

    def search_self(_name)
      @enclosure.search_self(_name)
    end
  end # Include

  class Derivable < Node
    alias :search_self_before_derived :search_self
    def search_self(_name)
      node = search_self_before_derived(_name)
      node = search_ancestors(_name) if node.nil?
      node
    end

    def has_ancestor?(n)
      self.has_base?(n) || resolved_bases.any? { |b| b.has_ancestor?(n) }
    end

  protected

    def resolved_bases
      []
    end

    def each_ancestors(visited = [], &block)
      resolved_bases.each do |p|
        next if visited.include? p

        yield(p)
        visited.push p
        p.each_ancestors(visited, &block)
      end
    end

    # search inherited interfaces.
    def search_ancestors(_name)
      results = []
      self.each_ancestors do |interface|
        node = interface.search_self(_name)
        results.push(node) unless node.nil?
      end
      if results.size > 1
        # check if the matched name resulted in multiple different nodes or all the same
        r_one = results.shift
        unless results.all? { |r| r_one == r || (r_one.class == r.class && r_one.scoped_name == r.scoped_name) }
          s = results.inject([r_one]) { |l, r| l << r unless l.include?(r)
 l }.collect { |n| n.scoped_name }.join(", ")
          raise "\"#{_name}\" is ambiguous. " + s
        end
      end
      results.first
    end

    # recursively collect operations from bases
    def base_operations(traversed)
      traversed.push self
      ops = []
      resolved_bases.each do |base|
        base = base.idltype.resolved_type.node if base.is_a?(IDL::AST::Typedef)
        ops.concat(base.operations(true, traversed)) unless traversed.include?(base)
      end
      ops
    end

    # recursively collect attributes from bases
    def base_attributes(traversed)
      traversed.push self
      atts = []
      resolved_bases.each do |base|
        base = base.idltype.resolved_type.node if base.is_a?(IDL::AST::Typedef)
        atts.concat(base.attributes(true, traversed)) unless traversed.include?(base)
      end
      atts
    end
  end # Derivable

  class Interface < Derivable
    DEFINABLE = [IDL::AST::Const, IDL::AST::Operation, IDL::AST::Attribute,
                 IDL::AST::Struct, IDL::AST::Union, IDL::AST::Typedef, IDL::AST::Enum, IDL::AST::Enumerator]
    attr_reader :bases, :idltype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @bases = []
      @resolved_bases = []
      @defined = !params[:forward]
      @abstract = params[:abstract]
      @pseudo = params[:pseudo]
      @local = params[:local]
      @idltype = IDL::Type::Interface.new(self)
      add_bases(params[:inherits] || [])
    end

    def marshal_dump
      super() << @bases << @resolved_bases << @defined << @abstract << @local << @pseudo << @idltype
    end

    def marshal_load(vars)
      @idltype = vars.pop
      @pseudo = vars.pop
      @local = vars.pop
      @abstract = vars.pop
      @defined = vars.pop
      @resolved_bases = vars.pop
      @bases = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        forward: self.is_forward?,
        abstract: self.is_abstract?,
        pseudo: self.is_pseudo?,
        local: self.is_local?,
        inherits: self.concrete_bases(instantiation_context)
      }
      # instantiate concrete interface def and validate
      # concrete bases
      super(instantiation_context, _enclosure, _params)
    end

    def is_abstract?
      @abstract
    end

    def is_local?
      @local
    end

    def is_pseudo?
      @pseudo
    end

    def is_defined?
      @defined
    end

    def is_forward?
      not @defined
    end

    def add_bases(inherits_)
      inherits_.each do |tc|
        unless tc.is_a?(IDL::Type::ScopedName) && tc.is_node?(IDL::AST::TemplateParam)
          unless (tc.is_a?(IDL::Type::NodeType) && tc.is_node?(IDL::AST::Interface))
            raise "invalid inheritance identifier for #{typename} #{scoped_lm_name}: #{tc.typename}"
          end

          rtc = tc.resolved_type
          if rtc.node.has_ancestor?(self)
            raise "circular inheritance detected for #{typename} #{scoped_lm_name}: #{tc.node.scoped_lm_name} is descendant"
          end
          unless rtc.node.is_defined?
            raise "#{typename} #{scoped_lm_name} cannot inherit from forward declared #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if rtc.node.is_local? and not self.is_local?
            raise "#{typename} #{scoped_lm_name} cannot inherit from 'local' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if rtc.node.is_pseudo? and not self.is_pseudo?
            raise "#{typename} #{scoped_lm_name} cannot inherit from 'pseudo' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if self.is_abstract? and not rtc.node.is_abstract?
            raise "'abstract' #{typename} #{scoped_lm_name} cannot inherit from non-'abstract' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if self.is_local? and rtc.node.is_abstract?
            raise "'local' #{typename} #{scoped_lm_name} cannot inherit from 'abstract' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if self.has_base?(rtc.node)
            raise "#{typename} #{scoped_lm_name} cannot inherit from #{tc.node.typename} #{tc.node.scoped_lm_name} multiple times"
          end

          # check if we indirectly derive from this base multiple times (which is ok; no further need to check)
          unless @resolved_bases.any? { |b| b.has_ancestor?(rtc.node) }
            # this is a new base so we need to check for member redefinition/ambiguity
            new_op_att_ = []
            rtc.node.walk_members do |m|
              new_op_att_ << m if m.is_a?(IDL::AST::Operation) || m.is_a?(IDL::AST::Attribute)
            end
            if new_op_att_.any? { |n| n_ = self.search_self(n.name)
 n_.is_a?(IDL::AST::Operation) || n_.is_a?(IDL::AST::Attribute) }
              raise "#{typename} #{scoped_lm_name} cannot inherit from #{tc.node.typename} #{tc.node.scoped_lm_name} because of duplicated operations/attributes"
            end
            # no need to check for duplicate member names; this inheritance is ok
          end
          @resolved_bases << rtc.node
        end
        @bases << tc.node
      end
    end

    def has_base?(_base)
      @resolved_bases.any? { |b| b == _base.idltype.resolved_type.node }
    end

    def ancestors
      @resolved_bases
    end

    def operations(include_bases = false, traversed = nil)
      ops = @children.find_all { |c| c.is_a?(IDL::AST::Operation) }
      ops.concat(base_operations(traversed || [])) if include_bases
      ops
    end

    def attributes(include_bases = false, traversed = nil)
      atts = @children.find_all { |c| c.is_a?(IDL::AST::Attribute) }
      atts.concat(base_attributes(traversed || [])) if include_bases
      atts
    end

    def redefine(node, params)
      if node.enclosure == self
        case node
        when IDL::AST::Struct, IDL::AST::Union
          if node.is_defined?
            raise "#{node.typename} \"#{node.name}\" is already defined."
          end

          node.annotations.concat(params[:annotations])

          _new_node = node.class.new(node.name, self, params)
          _new_node.annotations.concat(node.annotations)
          _new_node.prefix = node.prefix
          _new_node.instance_variable_set(:@repo_ver, node.instance_variable_get(:@repo_ver))
          _new_node.instance_variable_set(:@repo_id, node.instance_variable_get(:@repo_id))

          node.switchtype = params[:switchtype] if node.is_a?(IDL::AST::Union)

          @children << _new_node
          # replace forward node registration
          node.enclosure.undo_introduction(node)
          introduce(_new_node)

          return _new_node
        else
          raise "#{node.typename} \"#{node.name}\" is already defined."
        end
      end

      case node
      when IDL::AST::Operation, IDL::AST::Attribute
        raise "#{node.typename} '#{node.scoped_lm_name}' cannot be overridden."
      else
        newnode = node.class.new(node.name, self, params)
        newnode.annotations.concat(params[:annotations])
        introduce(newnode)
        @children << newnode # add overriding child
        return newnode
      end
    end

  protected

    def concrete_bases(instantiation_context)
      # collect all bases and resolve any template param types
      @bases.collect do |base|
        IDL::AST::TemplateParam.concrete_param(instantiation_context, base)
      end
    end

    def resolved_bases
      @resolved_bases
    end
  end # Interface

  class ComponentBase < Derivable
    DEFINABLE = []
    attr_reader :base, :interfaces, :idltype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @base = nil
      @resolved_base = nil
      @interfaces = []
      @resolved_interfaces = []
      set_base(params[:base]) if params[:base]
      add_interfaces(params[:supports] || [])
    end

    def marshal_dump
      super() << @base << @resolved_base << @interfaces << @resolved_interfaces << @idltype
    end

    def marshal_load(vars)
      @idltype = vars.pop
      @resolved_interfaces = vars.pop
      @interfaces = vars.pop
      @resolved_base = vars.pop
      @base = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure, _params = {})
      _params.merge!({
        base: @base ? IDL::AST::TemplateParam.concrete_param(instantiation_context, @base) : @base,
        supports: self.concrete_interfaces(instantiation_context)
      })
      # instantiate concrete def and validate
      super(instantiation_context, _enclosure, _params)
    end

    def set_base(parent)
      unless parent.is_a?(IDL::Type::ScopedName) && parent.is_node?(IDL::AST::TemplateParam)
        unless (parent.is_a?(IDL::Type::NodeType) && parent.is_node?(self.class))
          raise "invalid inheritance identifier for #{typename} #{scoped_lm_name}: #{parent.typename}"
        end
        if parent.resolved_type.node.has_base?(self)
          raise "circular inheritance detected for #{typename} #{scoped_lm_name}: #{parent.node.scoped_lm_name} is descendant"
        end

        @resolved_base = parent.resolved_type.node
      end
      @base = parent.node
    end

    def has_base?(n)
      @resolved_base && (@resolved_base == n.idltype.resolved_type.node) # || @resolved_base.has_base?(n))
    end

    def ancestors
      resolved_bases
    end

    def add_interfaces(intfs)
      intfs.each do |tc|
        unless tc.is_a?(IDL::Type::ScopedName) && tc.is_node?(IDL::AST::TemplateParam)
          unless (tc.is_a?(IDL::Type::ScopedName) && tc.is_node?(IDL::AST::Interface))
            raise "invalid inheritance identifier for #{typename} #{scoped_lm_name}: #{tc.typename}"
          end

          rtc = tc.resolved_type
          unless rtc.node.is_defined?
            raise "#{typename} #{scoped_lm_name} cannot support forward declared #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          ## TODO : is this legal?
          if rtc.node.is_local?
            raise "#{typename} #{scoped_lm_name} cannot support 'local' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if rtc.node.is_pseudo?
            raise "#{typename} #{scoped_lm_name} cannot support 'pseudo' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          ## TODO : is this legal?
          # if tc.node.is_abstract?
          #  raise RuntimeError,
          #        "'abstract' #{typename} #{scoped_lm_name} cannot support 'abstract' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          # end
          if self.has_support?(rtc.node)
            raise "#{typename} #{scoped_lm_name} cannot support #{tc.node.typename} #{tc.node.scoped_lm_name} multiple times"
          end

          # check if we indirectly support this base multiple times (which is ok; no further need to check)
          unless @resolved_interfaces.any? { |b| b.has_ancestor?(rtc.node) }
            # this is a new support interface so we need to check for member redefinition/ambiguity
            new_op_att_ = []
            rtc.node.walk_members do |m|
              new_op_att_ << m if m.is_a?(IDL::AST::Operation) || m.is_a?(IDL::AST::Attribute)
            end
            if new_op_att_.any? { |n| n_ = self.search_self(n.name)
 n_.is_a?(IDL::AST::Operation) || n_.is_a?(IDL::AST::Attribute) }
              raise "#{typename} #{scoped_lm_name} cannot support #{tc.node.typename} #{tc.node.scoped_lm_name} because of duplicated operations/attributes"
            end
            # no need to check for duplicate member names; this support is ok
          end
          @resolved_interfaces << rtc.node
        end
        @interfaces << tc.node
      end
    end

    def has_support?(intf)
      @resolved_interfaces.any? { |b| b == intf.idltype.resolved_type.node }
    end

    def supports?(intf)
      self.has_support?(intf) || (@resolved_base && @resolved_base.supports?(intf)) || @resolved_interfaces.any? { |base_i| i.has_ancestor?(intf) }
    end

    def redefine(node, params)
      if node.enclosure == self
        case node
        when IDL::AST::Struct, IDL::AST::Union
          if node.is_defined?
            raise "#{node.typename} \"#{node.name}\" is already defined."
          end

          node.annotations.concat(params[:annotations])

          _new_node = node.class.new(node.name, self, params)
          _new_node.annotations.concat(node.annotations)
          _new_node.prefix = node.prefix
          _new_node.instance_variable_set(:@repo_ver, node.instance_variable_get(:@repo_ver))
          _new_node.instance_variable_set(:@repo_id, node.instance_variable_get(:@repo_id))

          node.switchtype = params[:switchtype] if node.is_a?(IDL::AST::Union)

          @children << _new_node
          # replace forward node registration
          node.enclosure.undo_introduction(node)
          introduce(_new_node)

          return _new_node
        else
          raise "#{node.typename} \"#{node.name}\" is already defined."
        end
      end

      case node
      when IDL::AST::Operation, IDL::AST::Attribute
        raise "#{node.typename} '#{node.scoped_lm_name}' cannot be overridden."
      else
        newnode = node.class.new(node.name, self, params)
        newnode.annotations.concat(params[:annotations])
        introduce(newnode)
        @children << newnode # add overriding child
        return newnode
      end
    end

  protected

    def resolved_bases
      (@resolved_base ? [@resolved_base] : []).concat(@resolved_interfaces)
    end

    def concrete_interfaces(instantiation_context)
      # collect all bases and resolve any template param types
      @interfaces.collect do |_intf|
        IDL::AST::TemplateParam.concrete_param(instantiation_context, _intf)
      end
    end
  end # ComponentBase

  class Home < ComponentBase
    DEFINABLE = [IDL::AST::Const, IDL::AST::Operation, IDL::AST::Attribute, IDL::AST::Initializer, IDL::AST::Finder,
                 IDL::AST::Struct, IDL::AST::Union, IDL::AST::Typedef, IDL::AST::Enum, IDL::AST::Enumerator]
    attr_reader :component, :primarykey

    def initialize(_name, _enclosure, params)
      @component = nil
      @resolved_comp = nil
      @primarykey = nil
      @resolved_pk = nil
      @idltype = IDL::Type::Home.new(self)
      super(_name, _enclosure, params)
      set_component_and_key(params[:component], params[:primarykey])
    end

    def marshal_dump
      super() << @component << @resolved_comp << @primarykey << @resolved_pk
    end

    def marshal_load(vars)
      @resolved_pk = vars.pop
      @primarykey = vars.pop
      @resolved_comp = vars.pop
      @component = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        component: IDL::AST::TemplateParam.concrete_param(instantiation_context, @component),
        primarykey: @primarykey ? IDL::AST::TemplateParam.concrete_param(instantiation_context, @primarykey) : @primarykey
      }
      # instantiate concrete home def and validate
      super(instantiation_context, _enclosure, _params)
    end

    def set_component_and_key(comp, key)
      unless comp&.is_a?(IDL::Type::ScopedName) && comp.is_node?(IDL::AST::TemplateParam)
        unless comp&.is_a?(IDL::Type::ScopedName) && comp.is_node?(IDL::AST::Component)
          raise (comp ?
                  "invalid managed component for #{typename} #{scoped_lm_name}: #{comp.typename}" :
                  "missing managed component specification for #{typename} #{scoped_lm_name}")
        end
        unless comp.resolved_type.node.is_defined?
          raise "#{scoped_lm_name}: #{comp.typename} cannot manage forward declared component #{comp.node.scoped_lm_name}"
        end

        @resolved_comp = comp.resolved_type.node
      end
      unless key&.is_a?(IDL::Type::ScopedName) && key.is_node?(IDL::AST::TemplateParam)
        ## TODO : add check for Components::PrimaryKeyBase base type
        unless key.nil? || (key.is_a?(IDL::Type::ScopedName) && key.is_node?(IDL::AST::Valuetype))
          raise "invalid primary key for #{typename} #{scoped_lm_name}: #{key.typename}"
        end

        @resolved_pk = key.resolved_type.node if key
      end
      @component = comp.node
      @primarykey = key.node if key
    end

    def operations(include_bases = false, traversed = nil)
      ops = @children.find_all { |c| c.is_a?(IDL::AST::Operation) }
      ops.concat(base_operations(traversed || [])) if include_bases
      ops
    end

    def attributes(include_bases = false, traversed = nil)
      atts = @children.find_all { |c| c.is_a?(IDL::AST::Attribute) }
      atts.concat(base_attributes(traversed || [])) if include_bases
      atts
    end
  end # Home

  class Connector < ComponentBase
    DEFINABLE = [IDL::AST::Attribute, IDL::AST::Port]

    def initialize(_name, _enclosure, params)
      @idltype = IDL::Type::Component.new(self)
      super(_name, _enclosure, params)
    end

    def marshal_dump
      super()
    end

    def marshal_load(vars)
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      # instantiate concrete connector def and validate
      super(instantiation_context, _enclosure, {})
    end

    def is_defined?
      true
    end

    def is_forward?
      false
    end

    def add_interfaces(intfs)
      raise "interface support not allowed for #{typename} #{scoped_lm_name}" if intfs && !intfs.empty?
    end

    def set_base(parent)
      unless parent.is_a?(IDL::Type::ScopedName) && parent.is_node?(IDL::AST::TemplateParam)
        unless (parent.is_a?(IDL::Type::NodeType) && parent.is_node?(self.class))
          raise "invalid inheritance identifier for #{typename} #{scoped_lm_name}: #{parent.typename}"
        end

        @resolved_base = parent.resolved_type.node
        if @resolved_base.has_base?(self)
          raise "circular inheritance detected for #{typename} #{scoped_lm_name}: #{parent.node.scoped_lm_name} is descendant"
        end
      end
      @base = parent.node
    end

    def ports(include_bases = false, traversed = nil)
      ports = @children.inject([]) do |lst, c|
        lst.concat(c.ports) if IDL::AST::Port === c
        lst
      end
      ports.concat(base_ports(traversed || [])) if include_bases
      ports
    end

    def attributes(include_bases = false, traversed = nil)
      atts = @children.inject([]) do |lst, c|
        if IDL::AST::Port === c
          lst.concat(c.attributes)
        else
          lst << c
        end
        lst
      end
      atts.concat(base_attributes(traversed || [])) if include_bases
      atts
    end

  protected

    # recursively collect ports from bases
    def base_ports(traversed)
      traversed.push self
      ports = []
      if (base = @resolved_base)
        base = base.idltype.resolved_type.node if base.is_a?(IDL::AST::Typedef)
        ports = base.ports(true, traversed) unless traversed.include?(base)
      end
      ports
    end
  end # Connector

  class Component < ComponentBase
    DEFINABLE = [IDL::AST::Attribute, IDL::AST::Port]

    def initialize(_name, _enclosure, params)
      @idltype = IDL::Type::Component.new(self)
      super(_name, _enclosure, params)
      @defined = !params[:forward]
    end

    def marshal_dump
      super() << @defined
    end

    def marshal_load(vars)
      @defined = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      # instantiate concrete component def and validate
      super(instantiation_context, _enclosure, { forward: self.is_forward? })
    end

    def is_defined?
      @defined
    end

    def is_forward?
      not @defined
    end

    def set_base(parent)
      unless parent.is_a?(IDL::Type::ScopedName) && parent.is_node?(IDL::AST::TemplateParam)
        unless (parent.is_a?(IDL::Type::NodeType) && parent.is_node?(self.class))
          raise "invalid inheritance identifier for #{typename} #{scoped_lm_name}: #{parent.typename}"
        end

        @resolved_base = parent.resolved_type.node
        unless @resolved_base.is_defined?
          raise "#{typename} #{scoped_lm_name} cannot inherit from forward declared #{parent.node.typename} #{parent.node.scoped_lm_name}"
        end
        if @resolved_base.has_base?(self)
          raise "circular inheritance detected for #{typename} #{scoped_lm_name}: #{parent.node.scoped_lm_name} is descendant"
        end
      end
      @base = parent.node
    end

    def ports(include_bases = false, traversed = nil)
      ports = @children.inject([]) do |lst, c|
        lst.concat(c.ports) if IDL::AST::Port === c
        lst
      end
      ports.concat(base_ports(traversed || [])) if include_bases
      ports
    end

    def operations(include_bases = false, traversed = nil)
      include_bases ? base_operations(traversed || []) : []
    end

    def attributes(include_bases = false, traversed = nil)
      atts = @children.inject([]) do |lst, c|
        if IDL::AST::Port === c
          lst.concat(c.attributes)
        else
          lst << c
        end
        lst
      end
      atts.concat(base_attributes(traversed || [])) if include_bases
      atts
    end

  protected

    # recursively collect ports from bases
    def base_ports(traversed)
      traversed.push self
      ports = []
      if (base = @resolved_base)
        base = base.idltype.resolved_type.node if base.is_a?(IDL::AST::Typedef)
        ports = base.ports(true, traversed) unless traversed.include?(base)
      end
      ports
    end
  end # Component

  class Porttype < Node
    DEFINABLE = [IDL::AST::Attribute, IDL::AST::Port]
    attr_reader :idltype

    def initialize(_name, _enclosure, _params)
      super(_name, _enclosure)
      @idltype = IDL::Type::Porttype.new(self)
    end

    def ports
      @children.select { |c| IDL::AST::Port === c }
    end

    def attributes
      @children.select { |c| IDL::AST::Attribute === c }
    end

    def instantiate(instantiation_context, _enclosure)
      super(instantiation_context, _enclosure, {})
    end
  end # Porttype

  class Port < Leaf
    PORTTYPES = [:facet, :receptacle, :emitter, :publisher, :consumer, :port, :mirrorport]
    PORT_MIRRORS = {facet: :receptacle, receptacle: :facet}
    EXTPORTDEF_ANNOTATION = 'ExtendedPortDef'
    attr_reader :idltype, :porttype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype  = params[:type]
      @porttype = params[:porttype]
      raise "unknown porttype for  #{typename} #{scoped_lm_name}: #{@porttype}" unless PORTTYPES.include?(@porttype)

      case @porttype
      when :facet, :receptacle
        unless @idltype.is_a?(IDL::Type::Object) ||
              (@idltype.is_a?(IDL::Type::NodeType) && (@idltype.is_node?(IDL::AST::Interface) || @idltype.is_node?(IDL::AST::TemplateParam)))
          raise "invalid type for #{typename} #{scoped_lm_name}:  #{@idltype.typename}"
        end
      when :port, :mirrorport
        unless @idltype.is_a?(IDL::Type::NodeType) && (@idltype.is_node?(IDL::AST::Porttype) || @idltype.is_node?(IDL::AST::TemplateParam))
          raise "invalid type for #{typename} #{scoped_lm_name}:  #{@idltype.typename}"
        end
      else
        unless @idltype.is_a?(IDL::Type::NodeType) && (@idltype.is_node?(IDL::AST::Eventtype) || @idltype.is_node?(IDL::AST::TemplateParam))
          raise "invalid type for #{typename} #{scoped_lm_name}:  #{@idltype.typename}"
        end
      end
      @multiple = params[:multiple] ? true : false
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @idltype.instantiate(instantiation_context),
        porttype: @porttype,
        multiple: @multiple
      }
      super(instantiation_context, _enclosure, _params)
    end

    def multiple?
      @multiple
    end

    def expanded_copy(name_pfx, enc)
      p = IDL::AST::Port.new("#{name_pfx}_#{self.name}", enc, {type: @idltype, porttype: @porttype})
      p.annotations << Annotation.new(EXTPORTDEF_ANNOTATION, { extended_port_name: name_pfx, base_name: self.name, mirror: false })
      p # return expanded copy
    end

    def expanded_mirror_copy(name_pfx, enc)
      p = IDL::AST::Port.new("#{name_pfx}_#{self.name}", enc, {type: @idltype, porttype: PORT_MIRRORS[@porttype]})
      p.annotations << Annotation.new(EXTPORTDEF_ANNOTATION, { extended_port_name: name_pfx, base_name: self.name, mirror: true })
      p # return expanded copy
    end

    def ports
      case @porttype
      when :port
        @idltype.resolved_type.node.ports.collect { |p| p.expanded_copy(self.name, self.enclosure) }
      when :mirrorport
        @idltype.resolved_type.node.ports.collect { |p| p.expanded_mirror_copy(self.name, self.enclosure) }
      else
        [self]
      end
    end

    def attributes
      case @porttype
      when :port, :mirrorport
        @idltype.resolved_type.node.attributes.collect { |att|
          exp_a = att.expanded_copy(self.name, self.enclosure)
          exp_a.annotations << Annotation.new(EXTPORTDEF_ANNOTATION, { extended_port_name: self.name, base_name: att.name, mirror: (@porttype == :mirrorport) })
          exp_a # return expanded copy
        }
      else
        []
      end
    end
  end # Port

  class Valuebox < Leaf
    attr_reader :idltype, :boxed_type

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = IDL::Type::Valuebox.new(self)
      @boxed_type = params[:type]
      unless @boxed_type.is_a?(IDL::Type::ScopedName) && @boxed_type.is_node?(IDL::AST::TemplateParam)
        if @boxed_type.resolved_type.is_a?(IDL::Type::Valuetype)
          raise "boxing valuetype #{@boxed_type.scoped_lm_name} in Valuebox #{scoped_lm_name} not allowed"
        end
      end
    end

    def is_local?(recurstk = [])
      boxed_type.is_local?(recurstk)
    end

    def marshal_dump
      super() << @idltype << @boxed_type
    end

    def marshal_load(vars)
      @boxed_type = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @boxed_type.instantiate(instantiation_context)
      }
      super(instantiation_context, _enclosure, _params)
    end
  end # Valuebox

  class Valuetype < Derivable
    DEFINABLE = [IDL::AST::Include, IDL::AST::Const, IDL::AST::Operation, IDL::AST::Attribute, IDL::AST::StateMember, IDL::AST::Initializer,
                 IDL::AST::Struct, IDL::AST::Union, IDL::AST::Typedef, IDL::AST::Enum, IDL::AST::Enumerator]
    attr_reader :bases, :interfaces, :idltype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @bases = []
      @resolved_bases = []
      @interfaces = []
      @resolved_interfaces = []
      @defined = false
      @recursive = false
      @forward = params[:forward] ? true : false
      @abstract = params[:abstract]
      @idltype = IDL::Type::Valuetype.new(self)
      complete_definition(params)
    end

    def complete_definition(params)
      unless params[:forward]
        @custom = params[:custom] || false
        _inherits = params[:inherits] || {}
        _base = _inherits[:base] || {}
        @truncatable = _base[:truncatable] || false
        if @custom && @truncatable
            raise "'truncatable' attribute *not* allowed for 'custom' #{typename} #{scoped_lm_name}"
        end

        add_bases(_base[:list] || [])
        add_interfaces(_inherits[:supports] || [])
      end
    end

    def marshal_dump
      super() << @bases << @resolved_bases <<
                 @interfaces << @resolved_interfaces <<
                 @defined << @recursive <<
                 @forward << @abstract <<
                 @custom << @truncatable << @idltype
    end

    def marshal_load(vars)
      @idltype = vars.pop
      @truncatable = vars.pop
      @custom = vars.pop
      @abstract = vars.pop
      @forward = vars.pop
      @recursive = vars.pop
      @defined = vars.pop
      @resolved_interfaces = vars.pop
      @interfaces = vars.pop
      @resolved_bases = vars.pop
      @bases = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        forward: self.is_forward?,
        abstract: self.is_abstract?,
        custom: self.is_custom?,
        inherits: {
          base: {
            truncatable: self.is_truncatable?,
            list: self.concrete_bases(instantiation_context)
          },
          supports: self.concrete_interfaces(instantiation_context)
        }
      }
      inst = super(instantiation_context, _enclosure, _params)
      inst.defined = true
      inst
    end

    def is_abstract?
      @abstract
    end

    def is_custom?
      @custom
    end

    def is_truncatable?
      @truncatable
    end

    def is_defined?
      @defined
    end

    def defined=(f)
      @defined = f
    end

    def is_forward?
      @forward
    end

    def is_recursive?
      @recursive
    end

    def recursive=(f)
      @recursive = f
    end

    def is_local?(recurstk = [])
      # not local if forward decl or recursion detected
      return false if is_forward? || recurstk.include?(self)

      recurstk.push self # track root node to detect recursion
      ret = state_members.any? { |m| m.is_local?(recurstk) }
      recurstk.pop
      ret
    end

    def modifier
      if is_abstract?
        :abstract
      elsif is_custom?
        :custom
      elsif is_truncatable?
        :truncatable
      else
        :none
      end
    end

    def has_concrete_base?
      (not @resolved_bases.empty?) and (not @resolved_bases.first.is_abstract?)
    end

    def supports_concrete_interface?
      !(@resolved_interfaces.empty? || @resolved_interfaces.first.is_abstract?)
    end

    def supports_abstract_interface?
      @resolved_interfaces.any? { |intf| intf.is_abstract? }
    end

    def truncatable_ids
      ids = [self.repository_id]
      ids.concat(@resolved_bases.first.truncatable_ids) if self.has_concrete_base? && self.is_truncatable?
      ids
    end

    def add_bases(inherits_)
      inherits_.each do |tc|
        unless tc.is_a?(IDL::Type::ScopedName) && tc.is_node?(IDL::AST::TemplateParam)
          unless (tc.is_a?(IDL::Type::ScopedName) && tc.is_node?(IDL::AST::Valuetype))
            raise "invalid inheritance identifier for #{typename} #{scoped_lm_name}: #{tc.typename}"
          end

          rtc = tc.resolved_type
          if rtc.node.has_ancestor?(self)
            raise "circular inheritance detected for #{typename} #{scoped_lm_name}: #{tc.node.scoped_lm_name} is descendant"
          end
          unless rtc.node.is_defined?
            raise "#{typename} #{scoped_lm_name} cannot inherit from forward declared #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if self.is_abstract? and not rtc.node.is_abstract?
            raise "'abstract' #{typename} #{scoped_lm_name} cannot inherit from non-'abstract' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if (not self.is_custom?) and rtc.node.is_custom?
            raise "non-'custom' #{typename} #{scoped_lm_name} cannot inherit from 'custom' #{tc.node.typename} #{tc.node.scoped_lm_name}"
          end
          if @resolved_bases.include?(rtc.node)
            raise "#{typename} #{scoped_lm_name} cannot inherit from #{tc.node.typename} #{tc.node.scoped_lm_name} multiple times"
          end

          if (not rtc.node.is_abstract?) and !@bases.empty?
            raise "concrete basevalue #{tc.node.typename} #{tc.node.scoped_lm_name} MUST " +
                  "be first and only non-abstract in inheritance list for #{typename} #{scoped_lm_name}"
          end
          @resolved_bases << rtc.node
        end
        @bases << tc.node
      end
    end

    def add_interfaces(iflist_)
      iflist_.each do |if_|
        unless if_.is_a?(IDL::Type::ScopedName) && if_.is_node?(IDL::AST::TemplateParam)
          unless (if_.is_a?(IDL::Type::ScopedName) && if_.is_node?(IDL::AST::Interface))
            raise "invalid support identifier for #{typename} #{scoped_lm_name}: #{if_.typename}"
          end

          rif_ = if_.resolved_type
          ### @@TODO@@ further validation
          if (not rif_.node.is_abstract?) and !@interfaces.empty?
            raise "concrete interface '#{rif_.node.scoped_lm_name}' inheritance not allowed for #{typename} #{scoped_lm_name}. Valuetypes can only inherit (support) a single concrete interface."
          end
          if (not rif_.node.is_abstract?) && (not is_interface_compatible?(rif_.node))
            raise "#{typename} #{scoped_lm_name} cannot support concrete interface #{rif_.node.scoped_lm_name} because it does not derive from inherited concrete interfaces"
          end

          @resolved_interfaces << rif_.node
        end
        @interfaces << if_.node
      end
    end

    def has_ancestor?(n)
      @resolved_bases.include?(n.idltype.resolved_type.node) || @resolved_bases.any? { |b| b.has_ancestor?(n) }
    end

    def is_interface_compatible?(n)
      if @resolved_interfaces.empty? || @resolved_interfaces.first.is_abstract?
        @resolved_bases.all? { |b| b.is_interface_compatible?(n) }
      else
        n.idltype.resolved_type.node.has_ancestor?(@interfaces.first)
      end
    end

    def define(_type, _name, *args)
      if self.is_abstract? && [IDL::AST::StateMember, IDL::AST::Initializer].include?(_type)
        raise "cannot define statemember #{_name} on abstract #{typename} #{scoped_lm_name}"
      end

      super(_type, _name, *args)
    end

    def walk_members
      @children.each { |c| yield(c) unless c.is_a?(IDL::AST::StateMember) or
                                           c.is_a?(IDL::AST::Operation) or
                                           c.is_a?(IDL::AST::Attribute) or
                                           c.is_a?(IDL::AST::Initializer)
      }
    end

    def state_members
      @children.find_all { |c| c.is_a?(IDL::AST::StateMember) }
    end

    def interface_members
      @children.find_all { |c| c.is_a?(IDL::AST::Operation) or c.is_a?(IDL::AST::Attribute) }
    end

    def initializers
      @children.find_all { |c| c.is_a?(IDL::AST::Initializer) }
    end

    def has_operations_or_attributes?(include_intf = true)
      @children.any? { |c| c.is_a?(IDL::AST::Operation) || c.is_a?(IDL::AST::Attribute) } ||
        @resolved_bases.any? { |b| b.has_operations_or_attributes? } ||
        (include_intf &&
         @resolved_interfaces.any? { |intf| !intf.operations(true).empty? || !intf.attributes(true).empty? })
    end

    def redefine(node, params)
      if node.enclosure == self
        case node
        when IDL::AST::Struct, IDL::AST::Union
          if node.is_defined?
            raise "#{node.typename} \"#{node.name}\" is already defined."
          end

          node.annotations.concat(params[:annotations])

          _new_node = node.class.new(node.name, self, params)
          _new_node.annotations.concat(node.annotations)
          _new_node.prefix = node.prefix
          _new_node.instance_variable_set(:@repo_ver, node.instance_variable_get(:@repo_ver))
          _new_node.instance_variable_set(:@repo_id, node.instance_variable_get(:@repo_id))

          node.switchtype = params[:switchtype] if node.is_a?(IDL::AST::Union)

          @children << _new_node
          # replace forward node registration
          node.enclosure.undo_introduction(node)
          introduce(_new_node)

          return _new_node
        else
          raise "#{node.typename} \"#{node.name}\" is already defined."
        end
      end

      case node
      when IDL::AST::Operation, IDL::AST::Attribute, IDL::AST::StateMember, IDL::AST::Initializer
        raise "#{node.typename} '#{node.scoped_lm_name}' cannot be overridden."
      else
        newnode = node.class.new(node.name, self, params)
        newnode.annotations.concat(params[:annotations])
        introduce(newnode)
        return newnode
      end
    end

  protected

    def walk_members_for_copy
      @children.each { |c| yield(c) }
    end

    def resolved_bases
      @resolved_bases
    end

    def concrete_bases(instantiation_context)
      # collect all bases and resolve any template param types
      @bases.collect do |_base|
        IDL::AST::TemplateParam.concrete_param(instantiation_context, _base)
      end
    end

    def concrete_interfaces(instantiation_context)
      @interfaces.collect do |_intf|
        IDL::AST::TemplateParam.concrete_param(instantiation_context, _intf)
      end
    end
  end # Valuetype

  class Eventtype < Valuetype
    def initialize(_name, _enclosure, params)
      super(_name, _enclosure, params)
      # overrule
      @idltype = IDL::Type::Eventtype.new(self)
    end
  end # Eventtype

  class StateMember < Leaf
    attr_reader :idltype, :visibility

    def initialize(_name, _enclosure, params)
      @is_recursive = false
      @has_incomplete_type = false
      super(_name, _enclosure)
      @idltype = params[:type]
      @visibility = (params[:visibility] == :public ? :public : :private)
      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        raise "Anonymous type definitions are not allowed!" if params[:type].is_anonymous?

        ## check for use of incomplete types
        unless @idltype.is_complete?
          ## verify type is used in sequence
          if @idltype.resolved_type.is_a?(IDL::Type::Sequence)
            ## find the (non-sequence) elementtype
            seq_ = @idltype.resolved_type
            mtype = seq_.basetype
            while mtype.resolved_type.is_a? IDL::Type::Sequence
              seq_ = mtype.resolved_type
              mtype = seq_.basetype
            end
            ## is it an incomplete struct, union or valuetype?
            if mtype.is_a? IDL::Type::ScopedName
              case mtype.resolved_type
              when IDL::Type::Struct, IDL::Type::Union, IDL::Type::Valuetype
                unless mtype.node.is_defined?
                  ## check if incomplete struct/union/valuetype is contained within definition of self
                  enc = _enclosure
                  while enc.is_a?(IDL::AST::Struct) || enc.is_a?(IDL::AST::Union) || enc.is_a?(IDL::AST::Valuetype)
                    if enc.scoped_name == mtype.node.scoped_name
                      ## mark enclosure as recursive
                      enc.recursive = true
                      ## mark sequence as recursive type !!! DEPRECATED !!!; leave till R2CORBA updated
                      seq_.recursive = true
                      return
                    end
                    enc = enc.enclosure
                  end
                end
              end
              if mtype.resolved_type.is_a?(IDL::Type::Valuetype)
                # mark member as using an incomplete valuetype; allowed but needs special care
                @has_incomplete_type = true
                return
              end
            end
          elsif @idltype.resolved_type.is_a?(IDL::Type::Valuetype)
            mtype = @idltype.resolved_type
            enc = _enclosure
            while enc.is_a?(IDL::AST::Struct) || enc.is_a?(IDL::AST::Union) || enc.is_a?(IDL::AST::Valuetype)
              if enc.is_a?(IDL::AST::Valuetype) && enc.scoped_name == mtype.node.scoped_name
                ## statemember using recursive valuetype
                ## is enclosed in valuetype itself as part of constructed type
                ## which is allowed and not a problem
                @is_recursive = true
                ## mark enclosure as recursive
                enc.recursive = true
                return
              end
              enc = enc.enclosure
            end
            # mark member as using an incomplete valuetype; allowed but needs special care
            @has_incomplete_type = true
            return
          end
          raise "Incomplete type #{@idltype.typename} not allowed here!"
        end
      end
    end

    def marshal_dump
      super() << @idltype << @visibility << @is_recursive << @has_incomplete_type
    end

    def marshal_load(vars)
      @has_incomplete_type = vars.pop
      @is_recursive = vars.pop
      @visibility = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @idltype.instantiate(instantiation_context),
        visibility: self.visibility
      }
      super(instantiation_context, _enclosure, _params)
    end

    def is_local?(recurstk)
      idltype.is_local?(recurstk)
    end

    def is_public?
      @visibility == :public
    end

    def is_recursive?
      @is_recursive
    end

    def has_incomplete_type?
      @has_incomplete_type
    end
  end # StateMember

  class Initializer < Leaf
    attr_reader :raises, :params

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @params = (params[:params] || []).collect do |(ptype, pname)|
        IDL::AST::Parameter.new(pname, self, {attribute: :in, type: ptype})
      end
      @raises = []
      self.raises = params[:raises]
    end

    def raises=(exlist)
      exlist.each do |extype|
        unless extype.is_a?(IDL::Type::ScopedName) &&
                  (extype.is_node?(IDL::AST::Exception) || extype.is_node?(IDL::AST::TemplateParam) || extype.resolved_type.is_a?(IDL::Type::Native))
          raise 'Only IDL Exception types allowed in raises declaration.'
        end

        @raises << extype
      end
    end

    def marshal_dump
      super() << @params << @raises
    end

    def marshal_load(vars)
      @raises = vars.pop
      @params = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        raises: self.concrete_raises(instantiation_context)
      }
      _init = super(instantiation_context, _enclosure, _params)
      _init.set_concrete_parameters(instantiation_context, @params)
      _init
    end

  protected

    def concrete_raises(instantiation_context)
      @raises.collect do |ex|
        ex.instantiate(instantiation_context)
      end
    end

    def set_concrete_parameters(instantiation_context, parms)
      @params = parms.collect do |parm|
        IDL::AST::Parameter.new(parm.name, self,
                           { attribute: :in,
                             type: parm.idltype.instantiate(instantiation_context) })
      end
    end
  end # Initializer

  class Finder < Initializer
  end # Finder

  class Const < Leaf
    attr_reader :idltype, :expression, :value

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
      @expression = params[:expression]
      @value = nil
      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        raise "Anonymous type definitions are not allowed!" if @idltype.is_anonymous?
        raise "Incomplete type #{@idltype.typename} not allowed here!" unless @idltype.is_complete?

        unless @expression.is_a?(IDL::Expression::ScopedName) && @expression.is_node?(IDL::AST::TemplateParam)
          @value = @idltype.narrow(@expression.value)
        end
      end
    end

    def marshal_dump
      super() << @idltype << @expression
    end

    def marshal_load(vars)
      @expression = vars.pop
      @idltype = vars.pop
      super(vars)
      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        unless @expression.is_a?(IDL::Expression::ScopedName) && @expression.is_node?(IDL::AST::TemplateParam)
          @value = @idltype.narrow(@expression.value)
        end
      end
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @idltype.instantiate(instantiation_context),
        expression: @expression.instantiate(instantiation_context)
      }
      super(instantiation_context, _enclosure, _params)
    end
  end # Const

  class Parameter < Leaf
    IN = 0
    OUT = 1
    INOUT = 2
    ATTRIBUTE_MAP = {
      in: IN,
      out: OUT,
      inout: INOUT
    }
    attr_reader :idltype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
      @attribute = params[:attribute]
      unless ATTRIBUTE_MAP.has_key?(@attribute)
        raise "invalid attribute for parameter: #{params[:attribute]}"
      end

      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        raise "Anonymous type definitions are not allowed!" if params[:type].is_anonymous?
        raise "Exception #{@idltype.typename} is not allowed in an argument of an operation!" if @idltype.is_node?(IDL::AST::Exception)

        if @idltype.is_local?
          if _enclosure.enclosure.is_a?(IDL::AST::Interface) && !_enclosure.enclosure.is_local?
            raise "Local type #{@idltype.typename} not allowed for operation on unrestricted interface"
          end
          ## IDL_Valuetype: no problem as valuetype operations are local
        end
        unless @idltype.is_complete?
          if _enclosure.enclosure.is_a?(IDL::AST::Interface)
            raise "Incomplete type #{@idltype.typename} not allowed here!"
          end
          ## IDL_Valuetype: no problem as valuetype operations are local
        end
      end
    end

    def attribute
      ATTRIBUTE_MAP[@attribute]
    end

    def marshal_dump
      super() << @idltype << @attribute
    end

    def marshal_load(vars)
      @attribute = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @idltype.instantiate(instantiation_context),
        attribute: @attribute
      }
      super(instantiation_context, _enclosure, _params)
    end
  end # Parameter

  class Operation < Node
    DEFINABLE = [IDL::AST::Parameter]
    attr_reader :idltype, :oneway, :raises
    attr_accessor :context

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
      @oneway = (params[:oneway] == true)
      @in = []
      @out = []
      @raises = []
      @context = nil
      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        raise "Anonymous type definitions are not allowed!" if params[:type].is_anonymous?

        if @idltype.is_local?
          if _enclosure.is_a?(IDL::AST::Interface) && !_enclosure.is_local?
            raise "Local type #{@idltype.typename} not allowed for operation on unrestricted interface"
          end
          ## IDL_Valuetype: no problem as valuetype operations are local
        end
        unless @idltype.is_complete?
          if _enclosure.is_a?(IDL::AST::Interface)
            raise "Incomplete type #{@idltype.typename} not allowed here!"
          end
          ## IDL_Valuetype: no problem as valuetype operations are local
        end
      end
    end

    def marshal_dump
      super() << @idltype << @oneway << @in << @out << @raises << @context
    end

    def marshal_load(vars)
      @context = vars.pop
      @raises = vars.pop
      @out = vars.pop
      @in = vars.pop
      @oneway = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @idltype.instantiate(instantiation_context),
        oneway: @oneway
      }
      _op = super(instantiation_context, _enclosure, _params)
      _op.raises = self.concrete_raises(instantiation_context)
      _op.context = @context
      _op
    end

    def raises=(exlist)
      exlist.each do |extype|
        unless extype.is_a?(IDL::Type::ScopedName) &&
                (extype.is_node?(IDL::AST::Exception) || extype.is_node?(IDL::AST::TemplateParam) || extype.resolved_type.is_a?(IDL::Type::Native))
          raise 'Only IDL Exception or Native types allowed in raises declaration.'
        end

        @raises << extype
      end
    end

    def define(*args)
      param = super(*args)
      case param.attribute
      when Parameter::IN
        @in << param
      when Parameter::OUT
        @out << param
      when Parameter::INOUT
        @in << param
        @out << param
      end
      param
    end

    def in_params
      @in
    end

    def out_params
      @out
    end

    def params
      self.children
    end

  protected

    def concrete_raises(instantiation_context)
      @raises.collect do |ex|
        ex.instantiate(instantiation_context)
      end
    end

    def copy_from(_template, instantiation_context)
      super
      self.walk_members do |param|
        case param.attribute
        when Parameter::IN
          @in << param
        when Parameter::OUT
          @out << param
        when Parameter::INOUT
          @in << param
          @out << param
        end
      end
      self
    end
  end # Operation

  class Attribute < Leaf
    attr_reader :idltype, :readonly, :get_raises, :set_raises

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
      @get_raises = []
      @set_raises = []
      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        raise "Anonymous type definitions are not allowed!" if @idltype.is_anonymous?
        raise "Exception #{@idltype.typename} is not allowed as an attribute!" if @idltype.is_node?(IDL::AST::Exception)

        if @idltype.is_local?
          if _enclosure.is_a?(IDL::AST::Interface) && !_enclosure.is_local?
            raise "Local type #{@idltype.typename} not allowed for operation on unrestricted interface"
          end
          ## IDL_Valuetype: no problem as valuetype operations are local
        end
        unless @idltype.is_complete?
          if _enclosure.is_a?(IDL::AST::Interface)
            raise "Incomplete type #{@idltype.typename} not allowed here!"
          end
          ## IDL_Valuetype: no problem as valuetype operations are local
        end
      end
      @readonly = params[:readonly]
    end

    def marshal_dump
      super() << @idltype << @readonly << @get_raises << @set_raises
    end

    def marshal_load(vars)
      @set_raises = vars.pop
      @get_raises = vars.pop
      @readonly = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        type: @idltype.instantiate(instantiation_context),
        readonly: @readonly
      }
      _att = super(instantiation_context, _enclosure, _params)
      _att.get_raises = self.concrete_get_raises(instantiation_context)
      _att.set_raises = self.concrete_set_raises(instantiation_context)
      _att
    end

    def get_raises=(exlist)
      exlist.each do |extype|
        unless extype.is_a?(IDL::Type::ScopedName) &&
                  (extype.is_node?(IDL::AST::Exception) || extype.is_node?(IDL::AST::TemplateParam) || extype.resolved_type.is_a?(IDL::Type::Native))
          raise 'Only IDL Exception types allowed in raises declaration.' unless extype.resolved_type.node.is_a?(IDL::AST::Exception)
        end
        @get_raises << extype
      end
    end

    def set_raises=(exlist)
      exlist.each do |extype|
        unless extype.is_a?(IDL::Type::ScopedName) &&
                  (extype.is_node?(IDL::AST::Exception) || extype.is_node?(IDL::AST::TemplateParam) || extype.resolved_type.is_a?(IDL::Type::Native))
          raise 'Only IDL Exception types allowed in raises declaration.' unless extype.resolved_type.node.is_a?(IDL::AST::Exception)
        end
        @set_raises << extype
      end
    end

    def expanded_copy(name_pfx, enc)
      att = IDL::AST::Attribute.new("#{name_pfx}_#{self.name}", enc, {type: @idltype, readonly: @readonly})
      att.get_raises = @get_raises unless @get_raises.empty?
      att.set_raises = @set_raises unless @set_raises.empty?
      att
    end

  protected

    def concrete_get_raises(instantiation_context)
      @get_raises.collect do |ex|
        ex.instantiate(instantiation_context)
      end
    end

    def concrete_set_raises(instantiation_context)
      @set_raises.collect do |ex|
        ex.instantiate(instantiation_context)
      end
    end
  end # Attribute

  class Struct < Node
    DEFINABLE = [IDL::AST::Member, IDL::AST::Struct, IDL::AST::Union, IDL::AST::Enum, IDL::AST::Enumerator]
    attr_reader :idltype

    def initialize(_name, _enclosure, params)
      @defined = false
      @recursive = false
      @forward = params[:forward] ? true : false
      super(_name, _enclosure)
      @idltype = IDL::Type::Struct.new(self)
    end

    def is_defined?
      @defined
    end

    def defined=(f)
      @defined = f
    end

    def is_forward?
      @forward
    end

    def is_recursive?
      @recursive
    end

    def recursive=(f)
      @recursive = f
    end

    def walk_members
      @children.each { |m| yield(m) unless m.is_a? IDL::AST::Member }
    end

    def members
      @children.find_all { |c| c.is_a? IDL::AST::Member }
    end

    def is_local?(recurstk = [])
      # not local if forward decl or recursion detected
      return false if is_forward? || recurstk.include?(self)

      recurstk.push self # track root node to detect recursion
      ret = members.any? { |m| m.is_local?(recurstk) }
      recurstk.pop
      ret
    end

    def marshal_dump
      super() << @idltype << @defined << @recursive << @forward
    end

    def marshal_load(vars)
      @forward = vars.pop
      @recursive = vars.pop
      @defined = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        forward: @forward
      }
      _s = super(instantiation_context, _enclosure, _params)
      _s.defined = self.is_defined?
      _s
    end

  protected

    def walk_members_for_copy
      @children.each { |c| yield(c) }
    end
  end # Struct

  class Exception < IDL::AST::Struct
    DEFINABLE = [IDL::AST::Member, IDL::AST::Struct, IDL::AST::Union, IDL::AST::Enum, IDL::AST::Enumerator]
    def initialize(_name, _enclosure, params)
      super(_name, _enclosure, params)
      @idltype = IDL::Type::Exception.new(self)
    end
  end # Exception

  class Member < Leaf
    attr_reader :idltype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
      unless @idltype.is_a?(IDL::Type::ScopedName) && @idltype.is_node?(IDL::AST::TemplateParam)
        raise "Anonymous type definitions are not allowed!" if @idltype.is_anonymous?
        raise "Exception #{@idltype.typename} is not allowed as member!" if @idltype.is_node?(IDL::AST::Exception)

        ## check for use of incomplete types
        unless @idltype.is_complete?
          ## verify type is used in sequence
          if @idltype.resolved_type.is_a? IDL::Type::Sequence
            ## find the (non-sequence) elementtype
            seq_ = @idltype.resolved_type
            mtype = seq_.basetype
            while mtype.resolved_type.is_a? IDL::Type::Sequence
              seq_ = mtype.resolved_type
              mtype = seq_.basetype
            end
            ## is it an incomplete struct, union or valuetype?
            if mtype.is_a? IDL::Type::ScopedName
              case mtype.resolved_type
              when IDL::Type::Struct, IDL::Type::Union, IDL::Type::Valuetype
                unless mtype.node.is_defined?
                  ## check if incomplete struct/union is contained within definition of self
                  enc = _enclosure
                  while enc.is_a?(IDL::AST::Struct) || enc.is_a?(IDL::AST::Union) || enc.is_a?(IDL::AST::Valuetype)
                    if enc.scoped_name == mtype.node.scoped_name
                      ## mark enclosure as recursive
                      enc.recursive = true
                      ## mark sequence as recursive type !!! DEPRECATED !!!; leave till R2CORBA updated
                      seq_.recursive = true
                      return
                    end
                    enc = enc.enclosure
                  end
                end
                return # incomplete types in sequences allowed
              end
            end
          end
          raise "Incomplete type #{@idltype.typename} not allowed here!"
        end
      end
    end

    def is_local?(recurstk)
      idltype.is_local?(recurstk)
    end

    def marshal_dump
      super() << @idltype
    end

    def marshal_load(vars)
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure, _params = {})
      _params.merge!({
        type: @idltype.instantiate(instantiation_context)
      })
      super(instantiation_context, _enclosure, _params)
    end
  end # Member

  class Union < Node
    DEFINABLE = [IDL::AST::UnionMember, IDL::AST::Struct, IDL::AST::Union, IDL::AST::Enum, IDL::AST::Enumerator]
    attr_reader :idltype
    attr_accessor :switchtype

    def initialize(_name, _enclosure, params)
      @defined = false
      @recursive = false
      @forward = params[:forward] ? true : false
      @switchtype = nil
      super(_name, _enclosure)
      @idltype = IDL::Type::Union.new(self)
    end

    def set_switchtype(_switchtype)
      @switchtype = _switchtype
    end

    def is_defined?
      @defined
    end

    def defined=(f)
      @defined = f
    end

    def is_forward?
      @forward
    end

    def is_recursive?
      @recursive
    end

    def recursive=(f)
      @recursive = f
    end

    def walk_members
      @children.each { |m| yield(m) unless m.is_a? IDL::AST::UnionMember }
    end

    def members
      @children.find_all { |c| c.is_a? IDL::AST::UnionMember }
    end

    def is_local?(recurstk = [])
      # not local if forward decl or recursion detected
      return false if is_forward? || recurstk.include?(self)

      recurstk.push self # track root node to detect recursion
      ret = members.any? { |m| m.is_local?(recurstk) }
      recurstk.pop
      ret
    end

    def has_default?
      members.any? { |m| m.labels.include?(:default) }
    end

    def default_label
      swtype = @switchtype.resolved_type
      lbls = members.collect { |m| m.labels.include?(:default) ? [] : m.labels.collect { |l| l.value } }.flatten
      lbls = lbls.sort unless IDL::Type::Boolean === swtype ## work around bug in Ruby 1.9.2
      def_lbl = swtype.min
      while swtype.in_range?(def_lbl)
        return IDL::Expression::Value.new(@switchtype, def_lbl) unless lbls.include?(def_lbl)
        return nil if def_lbl == swtype.max

        def_lbl = swtype.next(def_lbl)
      end
      nil
    end

    def validate_labels
      return if self.is_template?

      labelvals = []
      default_ = false
      members.each { |m|
        ## check union case labels for validity
        m.labels.each { |lbl|
          if lbl == :default
            raise "duplicate case label 'default' for #{typename} #{lm_name}" if default_

            default_ = true
          else
            # correct type
            lv = @switchtype.resolved_type.narrow(lbl.value)
            # doubles
            if labelvals.include? lv
              raise "duplicate case label #{lv} for #{typename} #{lm_name}"
            end

            labelvals << lv
          end
        }
      }
      ## check if default allowed if defined
      if default_
        if @switchtype.resolved_type.range_length == labelvals.size
          raise "'default' case label superfluous for #{typename} #{lm_name}"
        end
      end
    end

    def marshal_dump
      super() << @defined << @recursive << @forward << @idltype << @switchtype
    end

    def marshal_load(vars)
      @switchtype = vars.pop
      @idltype = vars.pop
      @forward = vars.pop
      @recursive = vars.pop
      @defined = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        forward: @forward
      }
      _u = super(instantiation_context, _enclosure, _params)
      _u.set_switchtype(@switchtype.instantiate(instantiation_context))
      _u.validate_labels
      _u.defined = self.is_defined?
      _u
    end

  protected

      def walk_members_for_copy
        @children.each { |c| yield(c) }
      end
  end # Union

  class UnionMember < Member
    attr_reader :labels

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure, params)
      ## if any of the labels is 'default' forget about the others
      if params[:labels].include?(:default)
        @labels = [:default]
      else
        @labels = params[:labels]
      end
    end

    def marshal_dump
      super() << @labels
    end

    def marshal_load(vars)
      @labels = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      _params = {
        labels: @labels.collect { |l| l == :default ? l : l.instantiate(instantiation_context) }
      }
      super(instantiation_context, _enclosure, _params)
    end
  end # UnionMember

  class Enum < Leaf
    attr_reader :idltype

    def initialize(_name, _enclosure, _params)
      super(_name, _enclosure)
      @enums = []
      @idltype = IDL::Type::Enum.new(self)
    end

    def marshal_dump
      super() << @idltype << @enums
    end

    def marshal_load(vars)
      @enums = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def enumerators
      @enums
    end

    def add_enumerator(n)
      @enums << n
    end

    def instantiate(instantiation_context, _enclosure)
      super(instantiation_context, _enclosure, {})
    end
  end # Enum

  class Enumerator < Leaf
    attr_reader :idltype, :enum, :value

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = IDL::Type::ULong.new
      @enum = params[:enum]
      @value = params[:value]
      @enum.add_enumerator(self)
    end

    def marshal_dump
      super() << @idltype << @enum << @value
    end

    def marshal_load(vars)
      @value = vars.pop
      @enum = vars.pop
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      # find already instantiated Enum parent
      _enum = _enclosure.resolve(@enum.name)
      raise "Unable to resolve instantiated Enum scope for enumerator #{@enum.name}::#{name} instantiation" unless _enum

      super(instantiation_context, _enclosure, { enum: _enum, value: @value })
    end
  end # Enumerator

  class Typedef < Leaf
    attr_reader :idltype

    def initialize(_name, _enclosure, params)
      super(_name, _enclosure)
      @idltype = params[:type]
    end

    def is_local?(recurstk = [])
      @idltype.is_local?(recurstk)
    end

    def marshal_dump
      super() << @idltype
    end

    def marshal_load(vars)
      @idltype = vars.pop
      super(vars)
    end

    def instantiate(instantiation_context, _enclosure)
      super(instantiation_context, _enclosure, { type: @idltype.instantiate(instantiation_context) })
    end
  end # Typedef
end