1# Copyright (c) 2006-2008, The RubyCocoa Project.
2# Copyright (c) 2001-2006, FUJIMOTO Hisakuni.
3# All Rights Reserved.
4#
5# RubyCocoa is free software, covered under either the Ruby's license or the 
6# LGPL. See the COPYRIGHT file for more information.
7
8module OSX
9
10  class << self
11    attr_accessor :relaxed_syntax
12
13    # Backward compatibility check; get C constants
14    def method_missing(mname, *args)
15      if args.length == 0
16        begin
17          ret = const_get(mname)
18          warn "#{caller[0]}: syntax 'OSX.#{mname}' to get the constant is deprecated and its use is discouraged, please use 'OSX::#{mname}' instead."
19          return ret
20        rescue
21        end
22      end
23      super
24    end
25  end
26
27  module OCObjWrapper
28
29    # A convenience method to dispatch a message to ObjC as symbol/value/...
30    # For example, [myObj doSomething:arg1 withObject:arg2] would translate as:
31    #   myObj.objc_send(:doSomething, arg1, :withObject, arg2)
32    def objc_send(*args)
33      raise ArgumentError, "wrong number of arguments (#{args.length} for at least 1)" if args.empty?
34      mname = ""
35      margs = []
36      args.each_with_index do |val, index|
37        if index % 2 == 0 then
38          mname << val.to_s << ':'
39        else
40          margs << val
41        end
42      end
43      mname.chomp!(':') if args.size == 1
44      return self.ocm_send(mname.to_sel, nil, false, *margs)
45    end
46
47    def method_missing(mname, *args)
48      m_name, m_args, as_predicate = analyze_missing(mname, args)
49      begin
50        result = self.ocm_send(m_name, mname, as_predicate, *m_args)
51      rescue OCMessageSendException => e
52        if self.private_methods.include?(mname.to_s)
53          raise NoMethodError, "private method `#{mname}' called for ##{self}"
54        else
55          raise e
56        end 
57      end
58    end
59
60    def ocnil?
61      self.__ocid__ == 0
62    end
63
64    private
65
66    def analyze_missing(mname, args)
67      m_name = mname.to_s
68      m_args = args
69
70      # remove `oc_' prefix
71      m_name.sub!(/^oc_/, '')
72
73      # remove `?' suffix (to keep compatibility)
74      # explicit predicate if `?' suffix with OSX.relaxed_syntax
75      as_predicate = false
76      if m_name[-1] == ??
77        m_name.chop!
78        as_predicate = OSX.relaxed_syntax
79        # convert foo? to isFoo
80        orig_sel = m_args.size > 0 ? m_name.sub(/[^_:]$/, '\0_')  : m_name
81        unless ocm_responds?(orig_sel)
82          m_name = 'is' + m_name[0].chr.upcase + m_name[1..-1]
83        end
84      end
85
86      # convert foo= to setFoo
87      if m_name[-1] == ?= and m_name.index(?_, 1).nil?
88        m_name.chop!
89        m_name = 'set' + m_name[0].chr.upcase + m_name[1..-1]
90      end
91
92      # check call style
93      #   as Objective-C: [self aaa: a0 Bbb: a1 Ccc: a2]
94      #   as Ruby:   self.aaa_Bbb_Ccc_ (a0, a1, a2)
95      # only if OSX.relaxed_syntax == true, check missing final underscore
96      #   as Ruby:   self.aaa_Bbb_Ccc (a0, a1, a2)
97      # other syntaxes are now deprecated
98      #   as Ruby:   self.aaa (a0, :Bbb, a1, :Ccc, a2)
99      #   as Ruby:   self.aaa (a0, :Bbb => a1, :Ccc => a2)
100      if OSX.relaxed_syntax
101        if (m_args.size == 2) && (not m_name.include?('_')) && m_args[1].is_a?(Hash) && (m_args[1].size >= 1) then
102          # Parse the inline Hash case.
103          mname = m_name.dup
104          args = []
105          args << m_args[0]
106          if m_args[1].size == 1
107            m_args[1].each do |key, val|
108              mname << "_#{key}"
109              args << val
110            end
111          else
112            # FIXME: do some caching here
113            self.objc_methods.each do |sel|
114              selname, *selargs = sel.split(/:/)
115              if selname == m_name and selargs.all? { |selarg| m_args[1].has_key?(selarg.to_sym) } then
116                selargs.each do |selarg|
117                  mname << "_#{selarg}"
118                  args << m_args[1][selarg.to_sym]
119                end
120                break
121              end
122            end
123          end
124          m_name = "#{mname}_"
125          m_args = args
126          warn "#{caller[1]}: inline Hash dispatch syntax is deprecated and its use is discouraged, please use '#{m_name}(...)' or 'objc_send(...)' instead."
127        elsif (m_args.size >= 3) && ((m_args.size % 2) == 1) && (not m_name.include?('_')) && (m_args.values_at(*(1..m_args.size - 1).to_a.select { |i| i % 2 != 0 }).all? { |x| x.is_a?(Symbol) }) then
128          # Parse the symbol-value-symbol-value-... case.
129          mname = m_name.dup
130          args = []
131          m_args.each_with_index do |val, index|
132            if (index % 2) == 0 then
133              args << val
134            else
135              mname << "_#{val.to_s}"
136            end
137          end
138          m_name = "#{mname}_"
139          warn "#{caller[1]}: symbol-value-... dispatch syntax is deprecated and its use is discouraged, please use '#{m_name}(...)' or 'objc_send(...)' instead."
140          m_args = args
141        else
142          m_name.sub!(/[^_:]$/, '\0_') if m_args.size > 0
143        end
144      end
145      return [ m_name, m_args, as_predicate ]
146    end
147
148  end
149
150end				# module OSX
151