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
|