Fix compiler warning due to missing function prototype.
[svn.git] / subversion / bindings / swig / ruby / svn / delta.rb
blobc822e99df22770040c17db17266299296c77977b
1 require "English"
2 require "forwardable"
4 require "svn/error"
5 require "svn/util"
6 require "svn/core"
7 require "svn/ext/delta"
9 module Svn
10   module Delta
11     Util.set_constants(Ext::Delta, self)
12     Util.set_methods(Ext::Delta, self)
14     class << self
15       alias _path_driver path_driver
16     end
17     alias _path_driver path_driver
19     module_function
20     def svndiff_handler(output, version=nil)
21       args = [output, version || 0]
22       handler, handler_baton = Delta.txdelta_to_svndiff2(*args)
23       handler.baton = handler_baton
24       handler
25     end
27     def read_svndiff_window(stream, version)
28       Delta.txdelta_read_svndiff_window(stream, version)
29     end
31     def skip_svndiff_window(file, version)
32       Delta.txdelta_skip_svndiff_window(file, version)
33     end
35     def path_driver(editor, revision, paths, &callback_func)
36       Delta._path_driver(editor, revision, paths, callback_func)
37     end
39     def send(string_or_stream, handler=nil)
40       if handler.nil? and block_given?
41         handler = Proc.new {|window| yield(window)}
42         Delta.setup_handler_wrapper(handler)
43       end
44       if string_or_stream.is_a?(TextDeltaStream)
45         string_or_stream.send(handler)
46       elsif string_or_stream.is_a?(String)
47         Delta.txdelta_send_string(string_or_stream, handler)
48       else
49         Delta.txdelta_send_stream(string_or_stream, handler)
50       end
51     end
53     def apply(source, target, error_info=nil)
54       result = Delta.txdelta_apply_wrapper(source, target, error_info)
55       digest, handler, handler_baton = result
56       handler.baton = handler_baton
57       [handler, digest]
58     end
60     def parse_svndiff(error_on_early_close=true, &handler)
61       Delta.txdelta_parse_svndiff(handler, error_on_early_close)
62     end
64     def setup_handler_wrapper(wrapper)
65       Proc.new do |window|
66         Delta.txdelta_invoke_window_handler_wrapper(wrapper, window)
67       end
68     end
70     TextDeltaStream = SWIG::TYPE_p_svn_txdelta_stream_t
72     class TextDeltaStream
73       class << self
74         def new(source, target)
75           Delta.txdelta(source, target)
76         end
78         def push_target(source, &handler)
79           Delta.txdelta_target_push(handler, source)
80         end
81       end
83       def md5_digest
84         Delta.txdelta_md5_digest_as_cstring(self)
85       end
87       def next_window
88         Delta.txdelta_next_window(self, Svn::Core::Pool.new)
89       end
91       def each
92         while window = next_window
93           yield(window)
94         end
95       end
97       def send(handler=nil)
98         if handler.nil? and block_given?
99           handler = Proc.new {|window| yield(window)}
100           Delta.setup_handler_wrapper(handler)
101         end
102         Delta.txdelta_send_txstream(self, handler)
103       end
104     end
106     TextDeltaWindow = TxdeltaWindow
108     class TextDeltaWindow
109       def compose(other_window)
110         Delta.txdelta_compose_windows(other_window, self)
111       end
113       def ops
114         Delta.txdelta_window_t_ops_get(self)
115       end
117       def apply_instructions(source_buffer)
118         Delta.swig_rb_txdelta_apply_instructions(self, source_buffer)
119       end
120     end
122     TextDeltaWindowHandler =
123       SWIG::TYPE_p_f_p_svn_txdelta_window_t_p_void__p_svn_error_t
125     class TextDeltaWindowHandler
126       attr_accessor :baton
128       def call(window)
129         Delta.txdelta_invoke_window_handler(self, window, @baton)
130       end
132       def send(string_or_stream)
133         if string_or_stream.is_a?(TextDeltaStream)
134           Delta.txdelta_send_txstream(string_or_stream, self, @baton)
135         elsif string_or_stream.is_a?(String)
136           Delta.txdelta_send_string(string_or_stream, self, @baton)
137         else
138           Delta.txdelta_send_stream(string_or_stream, self, @baton)
139         end
140       end
141     end
143     class Editor
145       %w(set_target_revision open_root delete_entry
146          add_directory open_directory change_dir_prop
147          close_directory absent_directory add_file open_file
148          apply_textdelta change_file_prop close_file
149          absent_file close_edit abort_edit).each do |name|
150         alias_method("_#{name}", name)
151         undef_method("#{name}=")
152       end
154       attr_accessor :baton
155       attr_writer :target_revision_address
156       private :target_revision_address=
158       def target_revision
159         Svn::Delta.swig_rb_delta_editor_get_target_revision(self)
160       end
162       def set_target_revision(target_revision)
163         args = [self, @baton, target_revision]
164         Svn::Delta.editor_invoke_set_target_revision(*args)
165       end
167       def open_root(base_revision)
168         args = [self, @baton, base_revision]
169         Svn::Delta.editor_invoke_open_root_wrapper(*args)
170       end
172       def delete_entry(path, revision, parent_baton)
173         args = [self, path, revision, parent_baton]
174         Svn::Delta.editor_invoke_delete_entry(*args)
175       end
177       def add_directory(path, parent_baton,
178                         copyfrom_path, copyfrom_revision)
179         args = [self, path, parent_baton, copyfrom_path, copyfrom_revision]
180         Svn::Delta.editor_invoke_add_directory_wrapper(*args)
181       end
183       def open_directory(path, parent_baton, base_revision)
184         args = [self, path, parent_baton, base_revision]
185         Svn::Delta.editor_invoke_open_directory_wrapper(*args)
186       end
188       def change_dir_prop(dir_baton, name, value)
189         args = [self, dir_baton, name, value]
190         Svn::Delta.editor_invoke_change_dir_prop(*args)
191       end
193       def close_directory(dir_baton)
194         args = [self, dir_baton]
195         Svn::Delta.editor_invoke_close_directory(*args)
196       end
198       def absent_directory(path, parent_baton)
199         args = [self, path, parent_baton]
200         Svn::Delta.editor_invoke_absent_directory(*args)
201       end
203       def add_file(path, parent_baton,
204                    copyfrom_path, copyfrom_revision)
205         args = [self, path, parent_baton, copyfrom_path, copyfrom_revision]
206         Svn::Delta.editor_invoke_add_file_wrapper(*args)
207       end
209       def open_file(path, parent_baton, base_revision)
210         args = [self, path, parent_baton, base_revision]
211         Svn::Delta.editor_invoke_open_file_wrapper(*args)
212       end
214       def apply_textdelta(file_baton, base_checksum)
215         args = [self, file_baton, base_checksum]
216         handler, handler_baton =
217           Svn::Delta.editor_invoke_apply_textdelta_wrapper(*args)
218         handler.baton = handler_baton
219         handler
220       end
222       def change_file_prop(file_baton, name, value)
223         args = [self, file_baton, name, value]
224         Svn::Delta.editor_invoke_change_file_prop(*args)
225       end
227       def close_file(file_baton, text_checksum)
228         args = [self, file_baton, text_checksum]
229         Svn::Delta.editor_invoke_close_file(*args)
230       end
232       def absent_file(path, parent_baton)
233         args = [self, path, parent_baton]
234         Svn::Delta.editor_invoke_absent_file(*args)
235       end
237       def close_edit
238         args = [self, @baton]
239         Svn::Delta.editor_invoke_close_edit(*args)
240       ensure
241         Core::Pool.destroy(self)
242       end
244       def abort_edit
245         args = [self, @baton]
246         Svn::Delta.editor_invoke_abort_edit(*args)
247       ensure
248         Core::Pool.destroy(self)
249       end
250     end
252     class BaseEditor
253       # open_root -> add_directory -> open_directory -> add_file -> open_file
254       def set_target_revision(target_revision)
255       end
257       def open_root(base_revision)
258       end
260       def delete_entry(path, revision, parent_baton)
261       end
263       def add_directory(path, parent_baton,
264                         copyfrom_path, copyfrom_revision)
265       end
267       def open_directory(path, parent_baton, base_revision)
268       end
270       def change_dir_prop(dir_baton, name, value)
271       end
273       def close_directory(dir_baton)
274       end
276       def absent_directory(path, parent_baton)
277       end
279       def add_file(path, parent_baton,
280                    copyfrom_path, copyfrom_revision)
281       end
283       def open_file(path, parent_baton, base_revision)
284       end
286       # return nil or object which has `call' method.
287       def apply_textdelta(file_baton, base_checksum)
288       end
290       def change_file_prop(file_baton, name, value)
291       end
293       def close_file(file_baton, text_checksum)
294       end
296       def absent_file(path, parent_baton)
297       end
299       def close_edit(baton)
300       end
302       def abort_edit(baton)
303       end
304     end
306     class CopyDetectableEditor < BaseEditor
307       def add_directory(path, parent_baton,
308                         copyfrom_path, copyfrom_revision)
309       end
311       def add_file(path, parent_baton,
312                    copyfrom_path, copyfrom_revision)
313       end
314     end
316     class ChangedDirsEditor < BaseEditor
317       attr_reader :changed_dirs
319       def initialize
320         @changed_dirs = []
321       end
323       def open_root(base_revision)
324         [true, '']
325       end
327       def delete_entry(path, revision, parent_baton)
328         dir_changed(parent_baton)
329       end
331       def add_directory(path, parent_baton,
332                         copyfrom_path, copyfrom_revision)
333         dir_changed(parent_baton)
334         [true, path]
335       end
337       def open_directory(path, parent_baton, base_revision)
338         [true, path]
339       end
341       def change_dir_prop(dir_baton, name, value)
342         dir_changed(dir_baton)
343       end
345       def add_file(path, parent_baton,
346                    copyfrom_path, copyfrom_revision)
347         dir_changed(parent_baton)
348       end
350       def open_file(path, parent_baton, base_revision)
351         dir_changed(parent_baton)
352       end
354       def close_edit(baton)
355         @changed_dirs.sort!
356       end
358       private
359       def dir_changed(baton)
360         if baton[0]
361           # the directory hasn't been printed yet. do it.
362           @changed_dirs << "#{baton[1]}/"
363           baton[0] = nil
364         end
365       end
366     end
368     class ChangedEditor < BaseEditor
370       attr_reader :copied_files, :copied_dirs
371       attr_reader :added_files, :added_dirs
372       attr_reader :deleted_files, :deleted_dirs
373       attr_reader :updated_files, :updated_dirs
375       def initialize(root, base_root)
376         @root = root
377         @base_root = base_root
378         @in_copied_dir = []
379         @copied_files = []
380         @copied_dirs = []
381         @added_files = []
382         @added_dirs = []
383         @deleted_files = []
384         @deleted_dirs = []
385         @updated_files = []
386         @updated_dirs = []
387       end
389       def open_root(base_revision)
390         [true, '']
391       end
393       def delete_entry(path, revision, parent_baton)
394         if @base_root.dir?("/#{path}")
395           @deleted_dirs << "#{path}/"
396         else
397           @deleted_files << path
398         end
399       end
401       def add_directory(path, parent_baton,
402                         copyfrom_path, copyfrom_revision)
403         copyfrom_rev, copyfrom_path = @root.copied_from(path)
404         if in_copied_dir?
405           @in_copied_dir.push(true)
406         elsif Util.copy?(copyfrom_path, copyfrom_rev)
407           @copied_dirs << ["#{path}/", "#{copyfrom_path.sub(/^\//, '')}/", copyfrom_rev]
408           @in_copied_dir.push(true)
409         else
410           @added_dirs << "#{path}/"
411           @in_copied_dir.push(false)
412         end
413         [false, path]
414       end
416       def open_directory(path, parent_baton, base_revision)
417         [true, path]
418       end
420       def change_dir_prop(dir_baton, name, value)
421         if dir_baton[0]
422           @updated_dirs << "#{dir_baton[1]}/"
423           dir_baton[0] = false
424         end
425         dir_baton
426       end
428       def close_directory(dir_baton)
429         unless dir_baton[0]
430           @in_copied_dir.pop
431         end
432       end
434       def add_file(path, parent_baton,
435                    copyfrom_path, copyfrom_revision)
436         copyfrom_rev, copyfrom_path = @root.copied_from(path)
437         if in_copied_dir?
438           # do nothing
439         elsif Util.copy?(copyfrom_path, copyfrom_rev)
440           @copied_files << [path, copyfrom_path.sub(/^\//, ''), copyfrom_rev]
441         else
442           @added_files << path
443         end
444         [nil, nil, nil]
445       end
447       def open_file(path, parent_baton, base_revision)
448         [nil, nil, path]
449       end
451       def apply_textdelta(file_baton, base_checksum)
452         file_baton[0] = :update
453         nil
454       end
456       def change_file_prop(file_baton, name, value)
457         file_baton[1] = :update
458       end
460       def close_file(file_baton, text_checksum)
461         text_mod, prop_mod, path = file_baton
462         # test the path. it will be nil if we added this file.
463         if path
464           if [text_mod, prop_mod] != [nil, nil]
465             @updated_files << path
466           end
467         end
468       end
470       def close_edit(baton)
471         @copied_files.sort! {|a, b| a[0] <=> b[0]}
472         @copied_dirs.sort! {|a, b| a[0] <=> b[0]}
473         @added_files.sort!
474         @added_dirs.sort!
475         @deleted_files.sort!
476         @deleted_dirs.sort!
477         @updated_files.sort!
478         @updated_dirs.sort!
479       end
481       private
482       def in_copied_dir?
483         @in_copied_dir.last
484       end
485     end
487     class WrapEditor
488       extend Forwardable
490       def_delegators :@wrapped_editor, :set_target_revision, :open_root
491       def_delegators :@wrapped_editor, :delete_entry, :add_directory
492       def_delegators :@wrapped_editor, :open_directory, :change_dir_prop
493       def_delegators :@wrapped_editor, :close_directory, :absent_directory
494       def_delegators :@wrapped_editor, :add_file, :open_file, :apply_textdelta
495       def_delegators :@wrapped_editor, :change_file_prop, :close_file
496       def_delegators :@wrapped_editor, :absent_file, :close_edit, :abort_edit
498       def initialize(wrapped_editor)
499         @wrapped_editor = wrapped_editor
500       end
501     end
502   end