File: name_tree.rb

package info (click to toggle)
ruby-prawn 1.0.0~rc1%2Bdfsg1-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 4,248 kB
  • sloc: ruby: 17,499; sh: 44; makefile: 17
file content (177 lines) | stat: -rw-r--r-- 4,361 bytes parent folder | download
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
# encoding: utf-8

# name_tree.rb : Implements NameTree for PDF
#
# Copyright November 2008, Jamis Buck. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
#
module Prawn
  module Core
    module NameTree #:nodoc:
      class Node #:nodoc:
        attr_reader :children
        attr_reader :limit
        attr_reader :document
        attr_accessor :parent
        attr_accessor :ref

        def initialize(document, limit, parent=nil)
          @document = document
          @children = []
          @limit = limit
          @parent = parent
          @ref = nil
        end

        def empty?
          children.empty?
        end

        def size
          leaf? ? children.size : children.inject(0) { |sum, child| sum + child.size }
        end

        def leaf?
          children.empty? || children.first.is_a?(Value)
        end

        def add(name, value)
          self << Value.new(name, value)
        end

        def to_hash
          hash = {}

          hash[:Limits] = [least, greatest] if parent
          if leaf?
            hash[:Names] = children if leaf?
          else
            hash[:Kids] = children.map { |child| child.ref }
          end

          return hash
        end

        def least
          if leaf?
            children.first.name
          else
            children.first.least
          end
        end

        def greatest
          if leaf?
            children.last.name
          else
            children.last.greatest
          end
        end

        def <<(value)
          if children.empty?
            children << value
          elsif leaf?
            children.insert(insertion_point(value), value)
            split! if children.length > limit
          else
            fit = children.detect { |child| child >= value }
            fit = children.last unless fit
            fit << value
          end

          value
        end

        def >=(value)
          children.empty? || children.last >= value
        end

        def split!
          if parent
            parent.split(self)
          else
            left, right = new_node(self), new_node(self)
            split_children(self, left, right)
            children.replace([left, right])
          end
        end

        # Returns a deep copy of this node, without copying expensive things
        # like the ref to @document.
        #
        def deep_copy
          node = dup
          node.instance_variable_set("@children",
                                     Marshal.load(Marshal.dump(children)))
          node.instance_variable_set("@ref",
                                     node.ref ? node.ref.deep_copy : nil)
          node
        end

        protected

          def split(node)
            new_child = new_node(self)
            split_children(node, node, new_child)
            index = children.index(node)
            children.insert(index+1, new_child)
            split! if children.length > limit
          end

        private

          def new_node(parent=nil)
            node = Node.new(document, limit, parent)
            node.ref = document.ref!(node)
            return node
          end

          def split_children(node, left, right)
            half = (node.limit+1)/2

            left_children, right_children = node.children[0...half], node.children[half..-1]

            left.children.replace(left_children)
            right.children.replace(right_children)

            unless node.leaf?
              left_children.each { |child| child.parent = left }
              right_children.each { |child| child.parent = right }
            end
          end

          def insertion_point(value)
            children.each_with_index do |child, index|
              return index if child >= value
            end
            return children.length
          end
      end

      class Value #:nodoc:
        include Comparable

        attr_reader :name
        attr_reader :value

        def initialize(name, value)
          @name, @value = Prawn::Core::LiteralString.new(name), value
        end

        def <=>(leaf)
          name <=> leaf.name
        end

        def inspect
          "#<Value: #{name.inspect} : #{value.inspect}>"
        end

        def to_s
          "#{name} : #{value}"
        end
      end
    end
  end
end