require 'set'
require File.dirname(__FILE__) + '/../utils/array_utils'
require File.dirname(__FILE__) + '/../utils/invalid_options'
require File.dirname(__FILE__) + '/../extensions/regexp'
require File.dirname(__FILE__) + '/../extensions/symbol'
require File.dirname(__FILE__) + '/finder_result'
# Finds types known to the runtime environment.
module Aquarium
module Finders
class TypeFinder
include Aquarium::Utils::ArrayUtils
# Usage:
# finder_result = TypeFinder.new.find [ :types => ... | :names => ... ], [ :options => [...] ]
# where
# :types => types_and_type_names_and_regexps::
# The types or type names/regular expessions to match.
# Specify one or an array of values.
#
# :names => types_and_type_names_and_regexps::
# A synonym for :types. (Sugar)
#
# :type => type_or_type_name_or_regexp::
# Sugar for specifying one type
#
# :name => type_or_type_name_or_regexp::
# Sugar for specifying one type name.
#
# Actually, there is actually no difference between :types,
# :type, :names, and :name. The extra forms are "sugar"...
def find options = {}
result = Aquarium::Finders::FinderResult.new
unknown_options = []
options.each do |option, value|
case option.to_s
when "names", "types", "name", "type"
result << find_all_by(value)
else
unknown_options << option
end
end
raise Aquarium::Utils::InvalidOptions.new("Unknown options: #{unknown_options.inspect}.") if unknown_options.size > 0
return result
end
# For a name (not a regular expression), return the corresponding type.
# (Adapted from the RubyQuiz #113 solution by James Edward Gray II)
def find_by_name type_name
name = type_name.to_s # in case it's a symbol...
return nil if name.nil? || name.strip.empty?
name.strip!
found = []
begin
found << name.split("::").inject(Object) { |parent, const| parent.const_get(const) }
Aquarium::Finders::FinderResult.new(make_return_hash(found, []))
rescue NameError => ne
Aquarium::Finders::FinderResult.new(make_return_hash([], [type_name]))
end
end
alias :find_by_type :find_by_name
def find_all_by regexpes_or_names
raise Aquarium::Utils::InvalidOptions.new("Input type(s) can't be nil!") if regexpes_or_names.nil?
result = Aquarium::Finders::FinderResult.new
expressions = make_array regexpes_or_names
expressions.each do |expression|
expr = strip expression
next if empty expr
result_for_expression = find_namespace_matched expr
if result_for_expression.size > 0
result.append_matched result_for_expression
else
result.append_not_matched({expression => Set.new([])})
end
end
result
end
def self.is_recognized_option option_or_symbol
%w[name names type types].include? option_or_symbol.to_s
end
private
def strip expression
return nil if expression.nil?
expression.respond_to?(:strip) ? expression.strip : expression
end
def empty expression
expression.nil? || (expression.respond_to?(:empty?) && expression.empty?)
end
def find_namespace_matched expression
return {} if expression.nil?
found_types = [Module]
expr = expression.class.eql?(Regexp) ? expression.source : expression.to_s
return {} if expr.empty?
expr.split("::").each do |subexp|
found_types = find_next_types found_types, subexp
break if found_types.size == 0
end
make_return_hash found_types, []
end
def find_next_types parent_types, subname
# grep .constants because "subname" may be a regexp string!.
# Then use const_get to get the type itself.
found_types = []
parent_types.each do |parent|
matched = parent.constants.grep(/^#{subname}$/)
matched.each {|m| found_types << parent.const_get(m)}
end
found_types
end
def make_return_hash found, unmatched
h={}
h[:not_matched] = unmatched if unmatched.size > 0
found.each {|x| h[x] = Set.new([])}
h
end
end
end
end