require 'puppet/parser/ast/branch'

class Puppet::Parser::AST
    # Evaluate the stored parse tree for a given component.  This will
    # receive the arguments passed to the component and also the type and
    # name of the component.
    class Component < AST::Branch
        include Puppet::Util
        include Puppet::Util::Warnings
        include Puppet::Util::MethodHelper
        class << self
            attr_accessor :name
        end

        # The class name
        @name = :definition

        attr_accessor :classname, :arguments, :code, :scope, :keyword
        attr_accessor :exported, :namespace, :parser, :virtual

        # These are retrieved when looking up the superclass
        attr_accessor :name

        attr_reader :parentclass

        def child_of?(klass)
            false
        end

        def evaluate(hash)
            origscope = hash[:scope]
            title = hash[:title]
            args = symbolize_options(hash[:arguments] || {})

            name = args[:name] || title

            exported = hash[:exported]
            virtual = hash[:virtual]

            pscope = origscope
            scope = subscope(pscope, title)

            if virtual or origscope.virtual?
                scope.virtual = true
            end

            if exported or origscope.exported?
                scope.exported = true
            end

            # Additionally, add a tag for whatever kind of class
            # we are
            if @classname != "" and ! @classname.nil?
                @classname.split(/::/).each { |tag| scope.tag(tag) }
            end

            [name, title].each do |str|
                unless str.nil? or str =~ /[^\w]/ or str == ""
                    scope.tag(str)
                end
            end

            # define all of the arguments in our local scope
            if self.arguments
                # Verify that all required arguments are either present or
                # have been provided with defaults.
                self.arguments.each { |arg, default|
                    arg = symbolize(arg)
                    unless args.include?(arg)
                        if defined? default and ! default.nil?
                            default = default.safeevaluate :scope => scope
                            args[arg] = default
                            #Puppet.debug "Got default %s for %s in %s" %
                            #    [default.inspect, arg.inspect, @name.inspect]
                        else
                            parsefail "Must pass %s to %s of type %s" %
                                    [arg,title,@classname]
                        end
                    end
                }
            end

            # Set each of the provided arguments as variables in the
            # component's scope.
            args.each { |arg,value|
                unless validattr?(arg)
                    parsefail "%s does not accept attribute %s" % [@classname, arg]
                end

                exceptwrap do
                    scope.setvar(arg.to_s,args[arg])
                end
            }

            unless args.include? :title
                scope.setvar("title",title)
            end

            unless args.include? :name
                scope.setvar("name",name)
            end

            if self.code
                return self.code.safeevaluate(:scope => scope)
            else
                return nil
            end
        end

        def initialize(hash = {})
            @arguments = nil
            @parentclass = nil
            super

            # Convert the arguments to a hash for ease of later use.
            if @arguments
                unless @arguments.is_a? Array
                    @arguments = [@arguments]
                end
                oldargs = @arguments
                @arguments = {}
                oldargs.each do |arg, val|
                    @arguments[arg] = val
                end
            else
                @arguments = {}
            end

            # Deal with metaparams in the argument list.
            @arguments.each do |arg, defvalue|
                next unless Puppet::Type.metaparamclass(arg)
                if defvalue
                    warnonce "%s is a metaparam; this value will inherit to all contained elements" % arg
                else
                    raise Puppet::ParseError,
                        "%s is a metaparameter; please choose another name" %
                        name
                end
            end
        end

        def find_parentclass
            @parser.findclass(namespace, parentclass)
        end

        # Set our parent class, with a little check to avoid some potential
        # weirdness.
        def parentclass=(name)
            if name == self.classname
                parsefail "Parent classes must have dissimilar names"
            end

            @parentclass = name
        end

        # Hunt down our class object.
        def parentobj
            if @parentclass
                # Cache our result, since it should never change.
                unless defined?(@parentobj)
                    unless tmp = find_parentclass
                        parsefail "Could not find %s %s" % [self.class.name, @parentclass]
                    end

                    if tmp == self
                        parsefail "Parent classes must have dissimilar names"
                    end

                    @parentobj = tmp
                end
                @parentobj
            else
                nil
            end
        end

        # Create a new subscope in which to evaluate our code.
        def subscope(scope, name = nil)
            args = {
                :type => self.classname,
                :keyword => self.keyword,
                :namespace => self.namespace
            }

            args[:name] = name if name
            scope = scope.newscope(args)
            scope.source = self

            return scope
        end

        def to_s
            classname
        end

        # Check whether a given argument is valid.  Searches up through
        # any parent classes that might exist.
        def validattr?(param)
            param = param.to_s

            if @arguments.include?(param)
                # It's a valid arg for us
                return true
            elsif param == "name"
                return true
#            elsif defined? @parentclass and @parentclass
#                # Else, check any existing parent
#                if parent = @scope.lookuptype(@parentclass) and parent != []
#                    return parent.validarg?(param)
#                elsif builtin = Puppet::Type.type(@parentclass)
#                    return builtin.validattr?(param)
#                else
#                    raise Puppet::Error, "Could not find parent class %s" %
#                        @parentclass
#                end
            elsif Puppet::Type.metaparam?(param)
                return true
            else
                # Or just return false
                return false
            end
        end
    end
end

# $Id: component.rb 2742 2007-08-03 23:49:53Z luke $


syntax highlighted by Code2HTML, v. 0.9.1