require 'puppet/parser/ast/branch'

# Any normal puppet object declaration.  Can result in a class or a 
# component, in addition to builtin types.
class Puppet::Parser::AST
class ResourceDef < AST::Branch
    attr_accessor :title, :type, :exported, :virtual
    attr_reader :params

    # probably not used at all
    def []=(index,obj)
        @params[index] = obj
    end

    # probably not used at all
    def [](index)
        return @params[index]
    end

    # Iterate across all of our children.
    def each
        [@type,@title,@params].flatten.each { |param|
            #Puppet.debug("yielding param %s" % param)
            yield param
        }
    end

    # Does not actually return an object; instead sets an object
    # in the current scope.
    def evaluate(hash)
        scope = hash[:scope]
        @scope = scope
        hash = {}

        # Get our type and name.
        objtype = @type

        # Disable definition inheritance, for now.  8/27/06, luke
        #if objtype == "super"
        #    objtype = supertype()
        #    @subtype = true
        #else
            @subtype = false
        #end

        # Evaluate all of the specified params.
        paramobjects = @params.collect { |param|
            param.safeevaluate(:scope => scope)
        }

        # Now collect info from our parent.
        parentname = nil
        if @subtype
            parentname = supersetup(hash)
        end

        objtitles = nil
        # Determine our name if we have one.
        if self.title
            objtitles = @title.safeevaluate(:scope => scope)
            # it's easier to always use an array, even for only one name
            unless objtitles.is_a?(Array)
                objtitles = [objtitles]
            end
        else
            if parentname
                objtitles = [parentname]
            else
                # See if they specified the name as a parameter instead of
                # as a normal name (i.e., before the colon).
                unless object # we're a builtin
                    if objclass = Puppet::Type.type(objtype)
                        namevar = objclass.namevar

                        tmp = hash["name"] || hash[namevar.to_s] 

                        if tmp
                            objtitles = [tmp]
                        end
                    else
                        # This isn't grammatically legal.
                        raise Puppet::ParseError, "Got a resource with no title"
                    end
                end
            end
        end

        # This is where our implicit iteration takes place; if someone
        # passed an array as the name, then we act just like the called us
        # many times.
        objtitles.collect { |objtitle|
            exceptwrap :type => Puppet::ParseError do
                exp = self.exported || scope.exported
                # We want virtual to be true if exported is true.  We can't
                # just set :virtual => self.virtual in the initialization,
                # because sometimes the :virtual attribute is set *after*
                # :exported, in which case it clobbers :exported if :exported
                # is true.  Argh, this was a very tough one to track down.
                virt = self.virtual || exported
                obj = Puppet::Parser::Resource.new(
                    :type => objtype,
                    :title => objtitle,
                    :params => paramobjects,
                    :file => @file,
                    :line => @line,
                    :exported => exp,
                    :virtual => virt,
                    :source => scope.source,
                    :scope => scope
                )

                # And then store the resource in the scope.
                # XXX At some point, we need to switch all of this to return
                # objects instead of storing them like this.
                scope.setresource(obj)
                obj
            end
        }.reject { |obj| obj.nil? }
    end

    # Create our ResourceDef.  Handles type checking for us.
    def initialize(hash)
        @checked = false
        super

        #self.typecheck(@type.value)
    end

    # Set the parameters for our object.
    def params=(params)
        if params.is_a?(AST::ASTArray)
            @params = params
        else
            @params = AST::ASTArray.new(
                :line => params.line,
                :file => params.file,
                :children => [params]
            )
        end
    end

    def supercomp
        unless defined? @supercomp
            if @scope and comp = @scope.inside
                @supercomp = comp
            else
                error = Puppet::ParseError.new(
                    "'super' is only valid within definitions"
                )
                error.line = self.line
                error.file = self.file
                raise error
            end
        end
        @supercomp
    end

    # Take all of the arguments of our parent and add them into our own,
    # without overriding anything.
    def supersetup(hash)
        comp = supercomp()

        # Now check each of the arguments from the parent.
        comp.arguments.each do |name, value|
            unless hash.has_key? name
                hash[name] = value
            end
        end

        # Return the parent name, so it can be used if appropriate.
        return comp.name
    end

    # Retrieve our supertype.
    def supertype
        unless defined? @supertype
            if parent = supercomp.parentclass
                @supertype = parent
            else
                error = Puppet::ParseError.new(
                    "%s does not have a parent class" % comp.type
                )
                error.line = self.line
                error.file = self.file
                raise error
            end
        end
        @supertype
    end

    # Print this object out.
    def tree(indent = 0)
        return [
            @type.tree(indent + 1),
            @title.tree(indent + 1),
            ((@@indline * indent) + self.typewrap(self.pin)),
            @params.collect { |param|
                begin
                    param.tree(indent + 1)
                rescue NoMethodError => detail
                    Puppet.err @params.inspect
                    error = Puppet::DevError.new(
                        "failed to tree a %s" % self.class
                    )
                    error.set_backtrace detail.backtrace
                    raise error
                end
            }.join("\n")
        ].join("\n")
    end

    def to_s
        return "%s => { %s }" % [@title,
            @params.collect { |param|
                param.to_s
            }.join("\n")
        ]
    end
end
end

# $Id: resourcedef.rb 1922 2006-12-14 22:50:28Z luke $


syntax highlighted by Code2HTML, v. 0.9.1