rakelib/dsl.rb
author Jan Vrany <jan.vrany@fit.cvut.cz>
Sun, 22 May 2016 00:32:07 +0100
changeset 0 f46260ba26b1
child 102 fc572bd895f2
permissions -rw-r--r--
Initial shot of "new" rake-based builder Based on SVN https://swing.fit.cvut.cz/svn/stx/goodies/builder/trunk/rake@592

module Rake
end

module Rake::StX
end

class Object
  def self.new_property_value(*args, &block)
    if self == Object
      return args[0]
    else
      return self.new(*args, &block)
    end
  end
end

class Proc 
  def self.new_property_value(*args, &block)
    # TODO: add check
    return args[0]
  end
end


class Fixnum 
  def self.new_property_value(*args, &block)
    # TODO: add check
    return args[0]
  end
end

class Symbol
  def self.new_property_value(*args, &block)
    # TODO: add check
    return args[0].to_sym
  end
end


module Rake::StX::DSL

  # Base class for DSL object that may have properties
  class Object

    # Class-instance variable to hold property definitions
    @property_definitions = {} 

    attr_accessor :properties
        
    def self.property(name, properties = {})      
      @property_definitions ||= {}
      if @property_definitions.include? name
        raise Exception.new("Property #{name} already defined in class #{self.name}")
      else
        prop_def_class = properties[:property_definition_class] || PropertyDefinition
        @property_definitions[name] = prop_def_class.new(name, self, properties)
      end
    end

    def self.property_defined?(name)
      @property_definitions ||= {}
      if @property_definitions.include? name
        return @property_definitions[name]
      else
        if self == Rake::StX::DSL::Object
          return nil
        else
          return self.superclass.property_defined?(name)
        end
      end
    end

    def self.property_definition(name)
      prop_def = self.property_defined?(name)
      if not prop_def 
        raise Exception.new("Property #{name} not defined in class #{self.class.name}")
      end
      return prop_def
    end

    def _get_property(name)
      @properties ||= {}
      prop = @properties[name]
      if prop
        return prop.value
      else
        return self.class.property_definition(name).default
      end
    end

    def _set_property(name, source = '<unknown source>', *args, &block)
      @properties ||= {}      
      prop_def = self.class.property_definition(name)
      prop = prop_def.instantiate(name, source, @properties[name], args, &block)      
      @properties[name] = prop     
    end

    def property_defined?(name)
      return false if not self.class.property_defined? name
      return false if @properties == nil
      return @properties.has_key? name
    end


    def _set_property_value(name, value, source = '<unknown source>')
      @properties ||= {}      
      self.class.property_definition(name).validate(value)
      prop = Property.new(name, value, source, @properties[name])      
      @properties[name] = prop
    end

    def clone()
      c = super()
      c.properties = {}
      @properties.each do | name, prop |
        c.properties[name] = prop.clone()        
      end
      return c
    end

    def merge(other)
      
      _merge_properties(other)
	  return self
    end
    
    def _merge_properties(other)
      other.properties.each do | name, prop |
        if not @properties.key? name
          @properties[name] = prop.clone()
        end
      end
    end

      
         
  end # Object


  class PropertyDefinition < Object

    attr_accessor :name, :default

    def initialize(name, owning_class, properties = {})
      @name = name
      @owning_class = owning_class
      @values = properties[:values]      
      @klass = @values ? nil : (properties[:class] || String)
      @type = properties[:type] || :scalar
      if properties.include? :default
        @default = properties[:default]
      else
        @default = nil
      end
      define_getter()      
      define_setter()
    end
    
    def default     
      return @default
    end

    def valid?(value)
      #return true
      if @values 
        return @values.include? value
      else
        return value.kind_of? @klass
      end

    end

    def validate(value)
      if not valid? value
        raise Exception.new("Invalid value (#{value.inspect}) for property :'#{@name}' defined in #{@owning_class.name}")
      end
    end

    def instantiate(name, source, previous, args, &block)
      
      begin
        if @values 
          if not @values.include? args[0]
            raise Exception.new("Value #{args[0]} not allowed (not one of: #{@values.join(", ")})")
          else
            value = args[0]
          end
        else
          value = @klass.new_property_value(*args, &block)
        end
        return Property.new(name, value, source, previous)
      rescue => e
        puts e.backtrace.join("\n")
        raise e.exception("Error when instantiating property :'#{name}' in class #{@owning_class.name}: #{e.message}")
        
      end
    end

    def define_getter()
      name = @name
      @owning_class.send(:define_method, name) do | *args, &block |        
        if (args.empty? and not block_given?)
          return self._get_property(name)
        else
          return self._set_property(name, caller()[1], *args, &block)
        end
      end
    end

    def define_setter()
      name = @name
      @owning_class.send(:define_method, "#{@name}=") do | value |
        self._set_property_value(name, value, caller()[1])
      end
    end
    
  end # PropertyDefinition

  class Property
    attr_reader :name, :value, :source, :overridden
    

    def initialize(name, value, source = '<unknown source>' , overridden = nil)
      @name = name
      @value = value
      @source = source || caller(4).first
      @overridden = overridden
    end  
   
    def clone()
      if (@value.class == Symbol)
        value = @value
      elsif @value.class == NilClass
        value = nil
      elsif @value == true || @value == false
        value = @value
      else 
        value = @value.clone
      end      
      overriden = @overriden == nil ? nil : @overridden.clone()
      return self.class.new(@name, value, @source, overridden)      
    end

  end # class Property

  class PropertyException < Exception    
  end
  
  class PropertyNotFoundException < PropertyException
  end

  class InvalidPropertyValueException < PropertyException
  end


end