File: rdoc_jruby.rb

package info (click to toggle)
jruby 1.5.1-1%2Bdeb6u1
  • links: PTS, VCS
  • area: non-free
  • in suites: squeeze-lts
  • size: 47,024 kB
  • ctags: 74,144
  • sloc: ruby: 398,155; java: 169,506; yacc: 3,782; xml: 2,469; ansic: 415; sh: 279; makefile: 78; tcl: 40
file content (266 lines) | stat: -rw-r--r-- 8,756 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

require 'java'
require 'rdoc/rdoc'

module JRuby
  class RDoc
    class AnnotationParser
      attr_accessor :progress
      # prepare to parse a Java class with annotations
      def initialize(top_level, clazz, options, stats)
        @options = options
        @top_level = top_level
        @classes = Hash.new
        @progress = $stderr unless options.quiet
        @clazz = clazz
        @stats = stats
      end

      def scan
        extract_class_information(@clazz)
        @top_level
      end

      #######
      private
      #######

      def progress(char)
        unless @options.quiet
          @progress.print(char)
          @progress.flush
        end
      end

      def warn(msg)
        $stderr.puts
        $stderr.puts msg
        $stderr.flush
      end      

      JRubyMethodAnnotation = org.jruby.anno.JRubyMethod.java_class
      JRubyClassAnnotation = org.jruby.anno.JRubyClass.java_class
      JRubyModuleAnnotation = org.jruby.anno.JRubyModule.java_class
      
      def handle_class_module(clazz, class_mod, annotation, type_annotation, enclosure)
        type_annotation.name.to_a.each do |name|
          progress(class_mod[0, 1])

          parent = class_mod == 'class' ? type_annotation.parent : nil
          

          if class_mod == "class" 
            cm = enclosure.add_class(::RDoc::NormalClass, name, parent)
            @stats.num_classes += 1
          else
            cm = enclosure.add_module(::RDoc::NormalModule, name)
            @stats.num_modules += 1
          end

          cm.record_location(enclosure.toplevel)
          type_annotation.include.to_a.each do |inc|
            cm.add_include(::RDoc::Include.new(inc, ""))
          end
          
          find_class_comment(clazz, annotation, cm)
          
          handle_methods(clazz, cm)
        end
      end
      
      def handle_methods(clazz, enclosure)
        clazz.java_class.declared_class_methods.each do |method|
          if method.annotation_present?(JRubyMethodAnnotation)
            handle_method(clazz, method, enclosure)
          end
        end
        
        clazz.java_class.declared_instance_methods.each do |method|
          if method.annotation_present?(JRubyMethodAnnotation)
            handle_method(clazz, method, enclosure)
          end
        end
      end
      
      def handle_method(clazz, method, enclosure)
        progress(".")

        @stats.num_methods += 1

        anno = method.annotation(JRubyMethodAnnotation)
        
        meth_name = anno.name.to_a.first || method.name
        type = anno.meta ? "singleton_method" : "instance_method"

        if meth_name == "initialize"
          meth_name = "new"
          type = "singleton_method"
        end

        meth_obj = ::RDoc::AnyMethod.new("", meth_name)
        meth_obj.singleton = type == "singleton_method"

        
        p_count = (anno.optional == 0 && !anno.rest) ? anno.required : -1
        
        if p_count < 0
          meth_obj.params = "(...)"
        elsif p_count == 0
          meth_obj.params = "()"
        else
          meth_obj.params = "(" +
                            (1..p_count).map{|i| "p#{i}"}.join(", ") + 
                                                ")"
        end

        find_method_comment(nil,
                            #method.annotation(RDocAnnotation),
                            meth_obj)

        enclosure.add_method(meth_obj)

        meth_obj.visibility = case anno.visibility
                                when org.jruby.runtime.Visibility::PUBLIC: :public
                                when org.jruby.runtime.Visibility::PROTECTED: :protected
                                when org.jruby.runtime.Visibility::PRIVATE: :private
                                when org.jruby.runtime.Visibility::MODULE_FUNCTION: :public
                              end
        
        if anno.name.to_a.length > 1
          anno.name.to_a[1..-1].each do |al|
            new_meth = ::RDoc::AnyMethod.new("", al)
            new_meth.is_alias_for = meth_obj
            new_meth.singleton    = meth_obj.singleton
            new_meth.params       = meth_obj.params
            new_meth.comment = "Alias for \##{meth_obj.name}"
            meth_obj.add_alias(new_meth)
            enclosure.add_method(new_meth)
            new_meth.visibility = meth_obj.visibility
          end
        end
      end

      def find_class_comment(clazz, doc_annotation, class_meth)
        if doc_annotation
          class_meth.comment = doc_annotation.doc
        end
      end

      def find_method_comment(doc_annotation, meth)
        if doc_annotation
          call_seq = (doc_annotation.call_seq || []).to_a.join("\n")

          if call_seq != ""
            call_seq << "\n\n"
          end

          meth.comment = call_seq + doc_annotation.doc
        end
      end
      
      def extract_class_information(clazz)
        class_anno = clazz.java_class.annotation(JRubyClassAnnotation)
        module_anno = clazz.java_class.annotation(JRubyModuleAnnotation)
#        doc_anno = clazz.java_class.annotation(RDocAnnotation)
        doc_anno = nil
        if (class_anno || module_anno)
          $stderr.printf("%70s: ", clazz.java_class.to_s) unless @options.quiet
          class_mod = if class_anno
                        "class"
                      else
                        "module"
                      end
          
          handle_class_module(clazz, class_mod, doc_anno, class_anno || module_anno, @top_level)

          if class_anno && module_anno
            handle_class_module(clazz, "module", doc_anno, module_anno, @top_level)
          end
          
          $stderr.puts unless @options.quiet
        end
      end
    end
    
    INTERNAL_PACKAGES = %w(org.jruby.yaml org.jruby.util org.jruby.runtime org.jruby.ast org.jruby.internal org.jruby.lexer org.jruby.evaluator org.jruby.compiler org.jruby.parser org.jruby.exceptions org.jruby.demo org.jruby.environment org.jruby.JRubyApplet)
    INTERNAL_PACKAGES_RE = INTERNAL_PACKAGES.map{ |ip| /^#{ip}/ }
    INTERNAL_PACKAGE_RE = Regexp::union(*INTERNAL_PACKAGES_RE)
    
    class << self
      def find_classes_from_jar(jar, package)
        file = java.util.jar.JarFile.new(jar.toString[5..-1], false)
        beginning = %r[^(#{package.empty? ? '' : (package.join("/") + "/")}.*)\.class$]

        result = []
        file.entries.each do |e|
          if /Invoker\$/ !~ e.to_s && beginning =~ e.to_s
            class_name = $1.gsub('/', '.')
            if INTERNAL_PACKAGE_RE !~ class_name
              result << class_name
            end
          end
        end

        result
      end

      def find_classes_from_directory(dir, package)
        raise "not implemented yet"
      end

      def find_classes_from_location(location, package)
        if /\.jar$/ =~ location.to_s
          find_classes_from_jar(location, package)
        else 
          find_classes_from_directory(location, package)
        end
      end

      
      # Executes a block inside of a context where the named method on the object in question will just return nil
      # without actually doing anything. Useful to stub out things temporarily
      def returning_nil(object, method_name) 
        singleton_object = (class << object; self; end)
        singleton_object.send :alias_method, :"#{method_name}_with_real_functionality", method_name
        singleton_object.send :define_method, method_name do |*args|
          nil
        end

        begin 
          result = yield
        ensure
          singleton_object.send(:alias_method, method_name, :"#{method_name}_with_real_functionality")
        end
        result
      end
    
      # Returns an array of TopLevel
      def extract_rdoc_information_from_classes(classes, options, stats)
        result = []
        classes.each do |clzz|
          tp = returning_nil(File, :stat) { ::RDoc::TopLevel.new(clzz.java_class.to_s) }
          result << AnnotationParser.new(tp, clzz, options, stats).scan
          stats.num_files += 1
        end
        result
      end
      
      def install_doc(package = [])
        r = ::RDoc::RDoc.new
        
        # So parse_files should actually return an array of TopLevel objects
        (class << r; self; end).send(:define_method, :parse_files) do |options|
          location = org.jruby.Ruby.java_class.protection_domain.code_source.location

          class_names = JRuby::RDoc::find_classes_from_location(location, package)

          classes = class_names.map {|c| JavaUtilities.get_proxy_class(c) }

          JRuby::RDoc::extract_rdoc_information_from_classes(classes, options, @stats)
        end
        
        r.document(%w(--all --ri-system))
      end
    end
  end
end