treevisitor 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.txt +20 -0
- data/README.rdoc +42 -0
- data/Rakefile +9 -0
- data/VERSION.yml +5 -0
- data/bin/tree.rb +9 -0
- data/examples/find_files.rb +17 -0
- data/examples/print_files.rb +15 -0
- data/lib/tree_visitor.rb +2 -0
- data/lib/treevisitor.rb +31 -0
- data/lib/treevisitor/abs_node.rb +146 -0
- data/lib/treevisitor/cli/cli_tree.rb +98 -0
- data/lib/treevisitor/dir_processor.rb +46 -0
- data/lib/treevisitor/dir_tree_walker.rb +164 -0
- data/lib/treevisitor/leaf_node.rb +33 -0
- data/lib/treevisitor/tree_node.rb +247 -0
- data/lib/treevisitor/tree_node_visitor.rb +29 -0
- data/lib/treevisitor/visitors/block_tree_node_visitor.rb +21 -0
- data/lib/treevisitor/visitors/build_dir_tree_visitor.rb +53 -0
- data/lib/treevisitor/visitors/callback_tree_node_visitor.rb +43 -0
- data/lib/treevisitor/visitors/callback_tree_node_visitor2.rb +61 -0
- data/lib/treevisitor/visitors/clone_tree_node_visitor.rb +39 -0
- data/lib/treevisitor/visitors/depth_tree_node_visitor.rb +27 -0
- data/lib/treevisitor/visitors/flat_print_tree_node_visitors.rb +20 -0
- data/lib/treevisitor/visitors/print_dir_tree_visitor.rb +21 -0
- data/lib/treevisitor/visitors/print_tree_node_visitor.rb +40 -0
- data/lib/treevisitor_cli.rb +7 -0
- data/spec/fixtures/test_dir/.dir_with_dot/dummy.txt +0 -0
- data/spec/fixtures/test_dir/dir.1/dir.1.2/file.1.2.1 +0 -0
- data/spec/fixtures/test_dir/dir.1/file.1.1 +0 -0
- data/spec/fixtures/test_dir/dir.2/file.2.1 +0 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/treevisitor/cli/cli_tree_spec.rb +58 -0
- data/spec/treevisitor/dir_processor_spec.rb +15 -0
- data/spec/treevisitor/dir_tree_walker_spec.rb +33 -0
- data/spec/treevisitor/tree_node_dsl_spec.rb +153 -0
- data/spec/treevisitor/tree_node_spec.rb +153 -0
- data/spec/treevisitor/tree_node_visitor_spec.rb +70 -0
- data/tasks/jeweler.rake +55 -0
- data/tasks/rspec.rake +34 -0
- data/tasks/rubyforge.rake +28 -0
- data/tasks/yard.rake +36 -0
- metadata +192 -0
| @@ -0,0 +1,164 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
            module TreeVisitor
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # Visit a file system directory
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              class DirTreeWalker
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @param [String] dirname the root of the tree (top level directory)
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                def initialize( dirname )
         | 
| 13 | 
            +
                  @dirname = dirname
         | 
| 14 | 
            +
                  unless File.directory?( dirname )
         | 
| 15 | 
            +
                    raise "#{dirname} is not a directory!"
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  @visitor = nil
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # pattern
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  @ignore_dir_patterns = []
         | 
| 24 | 
            +
                  @ignore_file_patterns = []
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  @match_file_patterns = []
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # options
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  @visit_leaf = true
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                ##########################################################################
         | 
| 35 | 
            +
                # Pattern
         | 
| 36 | 
            +
             | 
| 37 | 
            +
             | 
| 38 | 
            +
                #
         | 
| 39 | 
            +
                # Ignore a node (leaf/Tree) matching pattern
         | 
| 40 | 
            +
                # @param [RegEx] pattern
         | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
                def ignore(pattern)
         | 
| 43 | 
            +
                  @ignore_dir_patterns << pattern
         | 
| 44 | 
            +
                  @ignore_file_patterns << pattern
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                #
         | 
| 48 | 
            +
                # Ignore a directory (Tree) matching pattern
         | 
| 49 | 
            +
                # @param [RegEx] pattern
         | 
| 50 | 
            +
                #
         | 
| 51 | 
            +
                def ignore_dir( pattern )
         | 
| 52 | 
            +
                  @ignore_dir_patterns << pattern
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                #
         | 
| 56 | 
            +
                # Ignore a file (Leaf) matching pattern
         | 
| 57 | 
            +
                # @param [RegEx] pattern
         | 
| 58 | 
            +
                #
         | 
| 59 | 
            +
                def ignore_file( pattern )
         | 
| 60 | 
            +
                  @ignore_file_patterns << pattern
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                #
         | 
| 64 | 
            +
                # Just the opposite of ignore
         | 
| 65 | 
            +
                # directory/file matching pattern will be visited
         | 
| 66 | 
            +
                #
         | 
| 67 | 
            +
                # @param [RegEx] pattern
         | 
| 68 | 
            +
                #
         | 
| 69 | 
            +
                def match( pattern )
         | 
| 70 | 
            +
                  @match_file_patterns << pattern
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                ##########################################################################
         | 
| 74 | 
            +
                # Options
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                #
         | 
| 77 | 
            +
                # boh
         | 
| 78 | 
            +
                #
         | 
| 79 | 
            +
                attr_accessor :visit_leaf
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                ##########################################################################
         | 
| 82 | 
            +
             | 
| 83 | 
            +
             | 
| 84 | 
            +
                #
         | 
| 85 | 
            +
                # Test directory ignore pattern
         | 
| 86 | 
            +
                #
         | 
| 87 | 
            +
                # @param [String] directory name
         | 
| 88 | 
            +
                # @return [boolean] if dirname match almost one pattern
         | 
| 89 | 
            +
                #
         | 
| 90 | 
            +
                def ignore_dir?( dirname )
         | 
| 91 | 
            +
                  _include?( @ignore_dir_patterns, File.basename( dirname ) )
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                #
         | 
| 95 | 
            +
                # Test file ignore pattern
         | 
| 96 | 
            +
                #
         | 
| 97 | 
            +
                # @param [String] file name
         | 
| 98 | 
            +
                # @return [boolean] if filename match almost one pattern
         | 
| 99 | 
            +
                #
         | 
| 100 | 
            +
                def ignore_file?( filename )
         | 
| 101 | 
            +
                  _include?( @ignore_file_patterns, File.basename( filename ) )
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                #
         | 
| 105 | 
            +
                # Test common ignore pattern
         | 
| 106 | 
            +
                #
         | 
| 107 | 
            +
                # @param [String] file name
         | 
| 108 | 
            +
                # @return [boolean] if filename match almost one pattern
         | 
| 109 | 
            +
                #
         | 
| 110 | 
            +
                def match?( filename )
         | 
| 111 | 
            +
                  return true if @match_file_patterns.empty?
         | 
| 112 | 
            +
                  _include?( @match_file_patterns, File.basename( filename ) )
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                #
         | 
| 116 | 
            +
                # Run the visitor through the directory tree
         | 
| 117 | 
            +
                #
         | 
| 118 | 
            +
                # return the visitor
         | 
| 119 | 
            +
                #
         | 
| 120 | 
            +
                def run( tree_node_visitor )
         | 
| 121 | 
            +
                  @visitor = tree_node_visitor
         | 
| 122 | 
            +
                  process_directory( File.expand_path( @dirname ) )
         | 
| 123 | 
            +
                  tree_node_visitor
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                private
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                def _include?(patterns, basename)
         | 
| 129 | 
            +
                  # return false if the patters.empty?
         | 
| 130 | 
            +
                  patterns.find{ |pattern|
         | 
| 131 | 
            +
                    if pattern.respond_to?(:match) # or if pattern.kind_of? Regexp
         | 
| 132 | 
            +
                      pattern.match( basename )
         | 
| 133 | 
            +
                    else
         | 
| 134 | 
            +
                      basename == pattern
         | 
| 135 | 
            +
                    end
         | 
| 136 | 
            +
                  }
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                #
         | 
| 140 | 
            +
                # recurse on other directories
         | 
| 141 | 
            +
                #
         | 
| 142 | 
            +
                def process_directory( dirname )
         | 
| 143 | 
            +
                  @visitor.enter_tree_node( dirname )
         | 
| 144 | 
            +
                  # return if ignore_dir?( dirname )
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                  Dir.entries( dirname ).sort.each { |basename|
         | 
| 147 | 
            +
                    next if basename == "." or basename == ".."  # ignore always "." and ".."
         | 
| 148 | 
            +
                    pathname = File.join( dirname, basename )
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    if File.directory?( pathname )
         | 
| 151 | 
            +
                      # directory
         | 
| 152 | 
            +
                      process_directory( pathname ) unless ignore_dir?( basename )
         | 
| 153 | 
            +
                    else
         | 
| 154 | 
            +
                      if @visit_leaf
         | 
| 155 | 
            +
                        if match?( basename ) && ! ignore_file?( basename )
         | 
| 156 | 
            +
                          @visitor.visit_leaf_node( pathname )
         | 
| 157 | 
            +
                        end
         | 
| 158 | 
            +
                      end
         | 
| 159 | 
            +
                    end
         | 
| 160 | 
            +
                  }
         | 
| 161 | 
            +
                  @visitor.exit_tree_node( dirname )
         | 
| 162 | 
            +
                end
         | 
| 163 | 
            +
              end
         | 
| 164 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
            module TreeVisitor
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # Represent a LeafNode
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              class LeafNode < AbsNode
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @param [Object] content of node
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                def initialize( content, parent = nil )
         | 
| 13 | 
            +
                  super( content )
         | 
| 14 | 
            +
                  parent.add_leaf(self) if parent
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                # @return false because a leaf_node cannot be a root
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                def root?
         | 
| 21 | 
            +
                  false
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # @return [TreeNodeVisitor] the visitor
         | 
| 26 | 
            +
                #
         | 
| 27 | 
            +
                def accept( visitor )
         | 
| 28 | 
            +
                  visitor.visit_leaf_node( self )
         | 
| 29 | 
            +
                  visitor
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,247 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
            module TreeVisitor
         | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # TreeNode can contains other TreeNode (children)
         | 
| 5 | 
            +
              # and can contains LeafNode (leaves)
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              # TreeNode @children -1---n-> TreeNode
         | 
| 8 | 
            +
              #          @leaves   -1---n-> LeafNode
         | 
| 9 | 
            +
              #
         | 
| 10 | 
            +
              class TreeNode < AbsNode
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                class << self
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def create(class1 = TreeNode, class2 = LeafNode, &block)
         | 
| 15 | 
            +
                    if class1.ancestors.include?(TreeNode) and class2.ancestors.include?(LeafNode)
         | 
| 16 | 
            +
                      @tree_node_class = class1
         | 
| 17 | 
            +
                      @leaf_node_class = class2
         | 
| 18 | 
            +
                    elsif class1.ancestors.include?(LeafNode) and class2 == LeafNode
         | 
| 19 | 
            +
                      @tree_node_class = self
         | 
| 20 | 
            +
                      @leaf_node_class = class1
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    if @tree_node_class.nil? || @leaf_node_class.nil?
         | 
| 24 | 
            +
                      raise "Must be specified class derived from TreeNode and LeafNode"
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    @scope_stack = []
         | 
| 28 | 
            +
                    class_eval(&block)
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  private
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def node(*args, &block)
         | 
| 34 | 
            +
                    parent_node = @scope_stack.length > 0 ? @scope_stack[-1] : nil
         | 
| 35 | 
            +
                    args << parent_node
         | 
| 36 | 
            +
                    tree_node = @tree_node_class.new(*args)
         | 
| 37 | 
            +
                    @scope_stack.push tree_node
         | 
| 38 | 
            +
                    if block
         | 
| 39 | 
            +
                      if block.arity == 0 || block.arity == -1
         | 
| 40 | 
            +
                        class_eval(&block)
         | 
| 41 | 
            +
                      elsif block.arity == 1
         | 
| 42 | 
            +
                        new_block = Proc.new { block.call(tree_node) }
         | 
| 43 | 
            +
                        class_eval(&new_block)
         | 
| 44 | 
            +
                      else
         | 
| 45 | 
            +
                        raise "block take too much arguments #{block.arity}"
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    @scope_stack.pop
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def leaf(*args, &block)
         | 
| 52 | 
            +
                    tree_node = @scope_stack[-1]
         | 
| 53 | 
            +
                    args << tree_node
         | 
| 54 | 
            +
                    leaf_node = @leaf_node_class.new(*args)
         | 
| 55 | 
            +
                    if block
         | 
| 56 | 
            +
                      if block.arity == 0 || block.arity == -1
         | 
| 57 | 
            +
                        class_eval(&block)
         | 
| 58 | 
            +
                      elsif block.arity == 1
         | 
| 59 | 
            +
                        new_block = Proc.new { block.call(leaf_node) }
         | 
| 60 | 
            +
                        class_eval(&new_block)
         | 
| 61 | 
            +
                      else
         | 
| 62 | 
            +
                        raise "block take too much arguments #{block.arity}"
         | 
| 63 | 
            +
                      end
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                    leaf_node
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                attr_reader :leaves
         | 
| 70 | 
            +
                attr_reader :children
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                #
         | 
| 73 | 
            +
                # @param [Object] content of this node
         | 
| 74 | 
            +
                #
         | 
| 75 | 
            +
                def initialize(content, parent = nil)
         | 
| 76 | 
            +
                  @leaves   = []
         | 
| 77 | 
            +
                  @children = []
         | 
| 78 | 
            +
                  super(content)
         | 
| 79 | 
            +
                  parent.add_child(self) if parent
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                #
         | 
| 83 | 
            +
                # Test if is a root
         | 
| 84 | 
            +
                #
         | 
| 85 | 
            +
                def root?
         | 
| 86 | 
            +
                  @parent.nil?
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                #
         | 
| 90 | 
            +
                # invalidate cached info
         | 
| 91 | 
            +
                # invalidate propagates form parent to children and leaves
         | 
| 92 | 
            +
                #
         | 
| 93 | 
            +
                def invalidate
         | 
| 94 | 
            +
                  super
         | 
| 95 | 
            +
                  @children.each { |c| c.invalidate }
         | 
| 96 | 
            +
                  @leaves.each { |l| l.invalidate }
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                #
         | 
| 100 | 
            +
                # @return [FixNum] total number of nodes
         | 
| 101 | 
            +
                #
         | 
| 102 | 
            +
                def nr_nodes
         | 
| 103 | 
            +
                  nr = @leaves.length + @children.length
         | 
| 104 | 
            +
                  @children.inject(nr) { |sum, c| sum + c.nr_nodes }
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                #
         | 
| 108 | 
            +
                # @return [FixNum] total number of leaves
         | 
| 109 | 
            +
                #
         | 
| 110 | 
            +
                def nr_leaves
         | 
| 111 | 
            +
                  @leaves.length + @children.inject(0) { |sum, child| sum + child.nr_leaves }
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                #
         | 
| 115 | 
            +
                # @return [FixNum] total number of children
         | 
| 116 | 
            +
                #
         | 
| 117 | 
            +
                def nr_children
         | 
| 118 | 
            +
                  @children.length + @children.inject(0) { |sum, child| sum + child.nr_children }
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                #
         | 
| 122 | 
            +
                # Add a Leaf
         | 
| 123 | 
            +
                # @param [LeafNode]
         | 
| 124 | 
            +
                #
         | 
| 125 | 
            +
                # @return self
         | 
| 126 | 
            +
                #
         | 
| 127 | 
            +
                def add_leaf(leaf)
         | 
| 128 | 
            +
                  return if leaf.parent == self
         | 
| 129 | 
            +
                  if not leaf.parent.nil?
         | 
| 130 | 
            +
                    leaf.remove_from_parent
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                  leaf.parent = self
         | 
| 133 | 
            +
                  if @leaves.length > 0
         | 
| 134 | 
            +
                    @leaves.last.next = leaf
         | 
| 135 | 
            +
                    leaf.prev         = @leaves.last
         | 
| 136 | 
            +
                  else
         | 
| 137 | 
            +
                    leaf.prev = nil
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
                  leaf.next = nil
         | 
| 140 | 
            +
                  leaf.invalidate
         | 
| 141 | 
            +
                  @leaves << leaf
         | 
| 142 | 
            +
                  self
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                #
         | 
| 146 | 
            +
                # Add a Tree
         | 
| 147 | 
            +
                # @param [LeafNode]
         | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                # @return self
         | 
| 150 | 
            +
                #
         | 
| 151 | 
            +
                def add_child(tree_node)
         | 
| 152 | 
            +
                  return if tree_node.parent == self
         | 
| 153 | 
            +
                  if not tree_node.parent.nil?
         | 
| 154 | 
            +
                    tree_node.remove_from_parent
         | 
| 155 | 
            +
                  else
         | 
| 156 | 
            +
                    tree_node.prefix_path = nil
         | 
| 157 | 
            +
                  end
         | 
| 158 | 
            +
                  tree_node.invalidate
         | 
| 159 | 
            +
                  tree_node.parent = self
         | 
| 160 | 
            +
                  if @children.length > 0
         | 
| 161 | 
            +
                    @children.last.next = tree_node
         | 
| 162 | 
            +
                    tree_node.prev      = @children.last
         | 
| 163 | 
            +
                  else
         | 
| 164 | 
            +
                    tree_node.prev = nil
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
                  tree_node.next = nil
         | 
| 167 | 
            +
                  @children << tree_node
         | 
| 168 | 
            +
                  self
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                #
         | 
| 172 | 
            +
                # Find a node down the hierarchy with content
         | 
| 173 | 
            +
                # @param [Object] content of searched node
         | 
| 174 | 
            +
                # @return [Object, nil] nil if no
         | 
| 175 | 
            +
                #
         | 
| 176 | 
            +
                def find(content = nil, &block)
         | 
| 177 | 
            +
                  if content and block_given?
         | 
| 178 | 
            +
                    raise "TreeNode::find - passed content AND block"
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  if content
         | 
| 182 | 
            +
                    block = proc { |c| c == content }
         | 
| 183 | 
            +
                  end
         | 
| 184 | 
            +
                  return self if block.call(self.content)
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                  leaf = @leaves.find { |l| block.call(l.content) }
         | 
| 187 | 
            +
                  return leaf if leaf
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                  @children.each do |child|
         | 
| 190 | 
            +
                    node = child.find &block
         | 
| 191 | 
            +
                    return node if node
         | 
| 192 | 
            +
                  end
         | 
| 193 | 
            +
                  nil
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                #
         | 
| 197 | 
            +
                # return the visitor
         | 
| 198 | 
            +
                #
         | 
| 199 | 
            +
                def accept(visitor)
         | 
| 200 | 
            +
                  visitor.enter_tree_node(self)
         | 
| 201 | 
            +
                  @leaves.each { |leaf|
         | 
| 202 | 
            +
                    leaf.accept(visitor)
         | 
| 203 | 
            +
                  }
         | 
| 204 | 
            +
                  @children.each { |child|
         | 
| 205 | 
            +
                    child.accept(visitor)
         | 
| 206 | 
            +
                  }
         | 
| 207 | 
            +
                  visitor.exit_tree_node(self)
         | 
| 208 | 
            +
                  visitor
         | 
| 209 | 
            +
                end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                #
         | 
| 212 | 
            +
                # Format the content of tree
         | 
| 213 | 
            +
                #
         | 
| 214 | 
            +
                def to_str(prefix= "")
         | 
| 215 | 
            +
                  str = ""
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                  if root?
         | 
| 218 | 
            +
                    str << to_s << "\n"
         | 
| 219 | 
            +
                  else
         | 
| 220 | 
            +
                    str << prefix
         | 
| 221 | 
            +
                    if self.next
         | 
| 222 | 
            +
                      str << '|-- '
         | 
| 223 | 
            +
                    else
         | 
| 224 | 
            +
                      str << '`-- '
         | 
| 225 | 
            +
                    end
         | 
| 226 | 
            +
                    str << to_s << "\n"
         | 
| 227 | 
            +
                    prefix += self.next ? "|   " : "    "
         | 
| 228 | 
            +
                  end
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                  @leaves.each do |leaf|
         | 
| 231 | 
            +
                    str << prefix
         | 
| 232 | 
            +
                    if !leaf.next.nil? or !@children.empty?
         | 
| 233 | 
            +
                      str << '|-- '
         | 
| 234 | 
            +
                    else
         | 
| 235 | 
            +
                      str << '`-- '
         | 
| 236 | 
            +
                    end
         | 
| 237 | 
            +
                    str << leaf.to_s << "\n"
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                  @children.each do |child|
         | 
| 241 | 
            +
                    str << child.to_str(prefix)
         | 
| 242 | 
            +
                  end
         | 
| 243 | 
            +
                  str
         | 
| 244 | 
            +
                end
         | 
| 245 | 
            +
             | 
| 246 | 
            +
              end
         | 
| 247 | 
            +
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
            module TreeVisitor
         | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # Callback methods used to visit a tree
         | 
| 5 | 
            +
              # Are empty so it is possible to define only a subset
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              class TreeNodeVisitor
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # called on tree node at start of the visit i.e. we start to visit the subtree
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                def enter_tree_node( tree_node )
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # called on tree node at end of the visit i.e. oll subtree are visited
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                def exit_tree_node( tree_node )
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # called when visit leaf node
         | 
| 23 | 
            +
                #
         | 
| 24 | 
            +
                def visit_leaf_node( leaf_node )
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            end
         |