epub-parser 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +3 -3
- data/.yardopts +1 -0
- data/CHANGELOG.markdown +12 -0
- data/README.markdown +15 -11
- data/Rakefile +11 -6
- data/bin/epub-open +1 -1
- data/bin/epubinfo +1 -1
- data/docs/AggregateContentsFromWeb.markdown +1 -1
- data/docs/UnpackedArchive.markdown +2 -2
- data/epub-parser.gemspec +3 -2
- data/lib/epub/book/features.rb +5 -1
- data/lib/epub/cfi.rb +301 -0
- data/lib/epub/content_document/navigation.rb +6 -1
- data/lib/epub/content_document/xhtml.rb +1 -2
- data/lib/epub/ocf/physical_container.rb +2 -1
- data/lib/epub/ocf/physical_container/{file.rb → unpacked_directory.rb} +1 -1
- data/lib/epub/parser/cfi.rb +84 -0
- data/lib/epub/parser/cfi.y +187 -0
- data/lib/epub/parser/content_document.rb +1 -1
- data/lib/epub/parser/publication.rb +3 -4
- data/lib/epub/parser/version.rb +1 -1
- data/lib/epub/publication/package/guide.rb +6 -1
- data/lib/epub/publication/package/manifest.rb +24 -8
- data/lib/epub/publication/package/metadata.rb +9 -5
- data/lib/epub/searcher/publication.rb +0 -2
- data/test/helper.rb +2 -0
- data/test/test_cfi.rb +227 -0
- data/test/test_content_document.rb +1 -1
- data/test/test_ocf_physical_container.rb +2 -2
- data/test/test_parser.rb +3 -3
- data/test/test_parser_cfi.rb +38 -0
- data/test/test_parser_content_document.rb +1 -1
- data/test/test_publication.rb +12 -4
- metadata +25 -23
- data/features/epubinfo.feature +0 -6
- data/features/step_definitions/epubinfo_steps.rb +0 -5
- data/features/support/env.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db9a8abb6d8ece39b51e5432c33caffdbd5efc65
|
4
|
+
data.tar.gz: bd9eb66c3af2ad86c1def37514c60db5d687a26d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab94d4c2c5104e966d6f2ed8368491189ca5a215a687bd6fdd2a43424b02935c44c36f8b14f39514858d2ad92c283e6ae7780041339f28f63dc283e1c8f72c30
|
7
|
+
data.tar.gz: 4d8c2f4f3a8dc584386cf55a4a6be890856c466b9ae1e7c2d6dfbe4cf31362730b1cbadf89c125910f9ab86ee27ccd7776fa099efefbeee4727698e140629cac
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
rvm:
|
2
|
-
- "2.
|
3
|
-
- "2.
|
4
|
-
- "2.
|
2
|
+
- "2.1.8"
|
3
|
+
- "2.2.4"
|
4
|
+
- "2.3.0"
|
data/.yardopts
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
CHANGELOG
|
2
2
|
=========
|
3
3
|
|
4
|
+
0.2.3
|
5
|
+
-----
|
6
|
+
|
7
|
+
* Change the name of physical container adapter for file system: :File -> :UnpackedDirectory
|
8
|
+
* Add `EPUB::Publication::Package::Manifest::Item#full_path`
|
9
|
+
* Make #href= acceptable String
|
10
|
+
* Implement `EPUB::CFI` and `EPUB::Parser::CFI`
|
11
|
+
* Remove [nokogumbo][] from dependencies. It ommits `head` and `body` elements
|
12
|
+
* Remove Cucumber and Cucumber features
|
13
|
+
* Add `EPUB::Publication::Package::Metadata#modified` and `EPUB::Book::Features#modified`
|
14
|
+
* Add `EPUB::Book::Features#release_identifier`
|
15
|
+
|
4
16
|
0.2.2
|
5
17
|
-----
|
6
18
|
|
data/README.markdown
CHANGED
@@ -2,6 +2,7 @@ EPUB Parser
|
|
2
2
|
===========
|
3
3
|
[![Build Status](https://secure.travis-ci.org/KitaitiMakoto/epub-parser.png?branch=master)](http://travis-ci.org/KitaitiMakoto/epub-parser)
|
4
4
|
[![Dependency Status](https://gemnasium.com/KitaitiMakoto/epub-parser.png)](https://gemnasium.com/KitaitiMakoto/epub-parser)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/epub-parser.svg)](http://badge.fury.io/rb/epub-parser)
|
5
6
|
|
6
7
|
INSTALLATION
|
7
8
|
-------
|
@@ -95,7 +96,7 @@ DOCUMENTATION
|
|
95
96
|
|
96
97
|
Documentation is available in [homepage][].
|
97
98
|
|
98
|
-
If you installed EPUB Parser by gem command, you can also generate documentaiton
|
99
|
+
If you installed EPUB Parser by gem command, you can also generate documentaiton yourself([rubygems-yardoc][] gem is needed):
|
99
100
|
|
100
101
|
$ gem install epub-parser
|
101
102
|
$ gem yardoc epub-parser
|
@@ -150,6 +151,19 @@ If you find other gems, please tell me or request a pull request.
|
|
150
151
|
RECENT CHANGES
|
151
152
|
--------------
|
152
153
|
|
154
|
+
### 0.2.3
|
155
|
+
|
156
|
+
* Change the name of physical container adapter for file system: :File -> :UnpackedDirectory
|
157
|
+
* Add `EPUB::Publication::Package::Manifest::Item#full_path`
|
158
|
+
* Make #href= acceptable String
|
159
|
+
* Implement `EPUB::CFI` and `EPUB::Parser::CFI`
|
160
|
+
* Remove [nokogumbo][] from dependencies. It ommits `head` and `body` elements
|
161
|
+
* Remove Cucumber and Cucumber features
|
162
|
+
* Add `EPUB::Publication::Package::Metadata#modified` and `EPUB::Book::Features#modified`
|
163
|
+
* Add `EPUB::Book::Features#release_identifier`
|
164
|
+
|
165
|
+
[nokogumbo]: https://github.com/rubys/nokogumbo/
|
166
|
+
|
153
167
|
### 0.2.2
|
154
168
|
|
155
169
|
* [BUGFIX]Item#entry_name returns normalized IRI
|
@@ -167,16 +181,6 @@ RECENT CHANGES
|
|
167
181
|
|
168
182
|
* Make it possible to parse file system directory as an EPUB file. See {file:docs/UnpackedArchive.markdown} for details.
|
169
183
|
|
170
|
-
### 0.1.9
|
171
|
-
|
172
|
-
* Introduce [Nokogumbo][] for XHTML Content Documents
|
173
|
-
* Stop support for Ruby 1.9
|
174
|
-
* Remove `EPUB.included` method. Now including `EPUB` module empowers nothing of EPUB features. Include `EPUB::Book::Features` instead.
|
175
|
-
* Add `EPUB::Searcher::XHTML::Seamless` and make it default searcher
|
176
|
-
* Add `EPUB::Publication::Package::Manifest#each_nav`
|
177
|
-
|
178
|
-
[nokogumbo]: https://github.com/rubys/nokogumbo/
|
179
|
-
|
180
184
|
See {file:CHANGELOG.markdown} for older changelogs and details.
|
181
185
|
|
182
186
|
TODOS
|
data/Rakefile
CHANGED
@@ -3,22 +3,28 @@ require 'rake/clean'
|
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'yard'
|
5
5
|
require 'rdoc/task'
|
6
|
-
require 'cucumber'
|
7
|
-
require 'cucumber/rake/task'
|
8
6
|
require 'epub/parser/version'
|
9
7
|
require 'zipruby'
|
10
8
|
|
9
|
+
CFI_TAB = 'lib/epub/parser/cfi.tab.rb'
|
10
|
+
CFI_Y = 'lib/epub/parser/cfi.y'
|
11
|
+
CLEAN.include(CFI_TAB)
|
12
|
+
|
11
13
|
task :default => :test
|
12
14
|
task :test => 'test:default'
|
13
15
|
|
16
|
+
file CFI_TAB do
|
17
|
+
sh "racc #{CFI_Y}"
|
18
|
+
end
|
19
|
+
|
14
20
|
namespace :test do
|
15
21
|
task :default => [:build, :test]
|
16
22
|
|
17
23
|
desc 'Run all tests'
|
18
|
-
task :all => [:build, :test
|
24
|
+
task :all => [:build, :test]
|
19
25
|
|
20
26
|
desc 'Build test fixture EPUB file'
|
21
|
-
task :build => :clean do
|
27
|
+
task :build => [:clean, CFI_TAB] do
|
22
28
|
input_dir = 'test/fixtures/book'
|
23
29
|
sh "epzip #{input_dir}"
|
24
30
|
small_file = File.read("#{input_dir}/OPS/case-sensitive.xhtml")
|
@@ -32,8 +38,6 @@ namespace :test do
|
|
32
38
|
task.warning = true
|
33
39
|
task.options = '--no-show-detail-immediately --verbose'
|
34
40
|
end
|
35
|
-
|
36
|
-
Cucumber::Rake::Task.new
|
37
41
|
end
|
38
42
|
|
39
43
|
task :doc => 'doc:default'
|
@@ -52,4 +56,5 @@ end
|
|
52
56
|
|
53
57
|
namespace :gem do
|
54
58
|
Bundler::GemHelper.install_tasks
|
59
|
+
task :build => [:clean, CFI_TAB]
|
55
60
|
end
|
data/bin/epub-open
CHANGED
@@ -21,7 +21,7 @@ EOB
|
|
21
21
|
$0 = File.basename($PROGRAM_NAME)
|
22
22
|
include EPUB::Book::Features
|
23
23
|
file = ARGV.shift
|
24
|
-
EPUB::OCF::PhysicalContainer.adapter = :
|
24
|
+
EPUB::OCF::PhysicalContainer.adapter = :UnpackedDirectory if File.directory? file
|
25
25
|
unless File.readable? file
|
26
26
|
uri = URI.parse(file) rescue nil
|
27
27
|
if uri
|
data/bin/epubinfo
CHANGED
@@ -30,7 +30,7 @@ unless file
|
|
30
30
|
abort
|
31
31
|
end
|
32
32
|
|
33
|
-
EPUB::OCF::PhysicalContainer.adapter = :
|
33
|
+
EPUB::OCF::PhysicalContainer.adapter = :UnpackedDirectory if File.directory? file
|
34
34
|
unless File.readable? file
|
35
35
|
uri = URI.parse(file) rescue nil
|
36
36
|
if uri
|
@@ -21,7 +21,7 @@ EPUB Parser can treat the URI as EPUB book file path and parse contents from it
|
|
21
21
|
The trick is to set {EPUB::OCF::PhysicalContainer.adapter container adapter} to {EPUB::OCF::PhysicalContainer::UnpackedURI :UnpackedURI}. It makes it possible to parse EPUB book from the web.
|
22
22
|
Now we can play with EPUB books as always!
|
23
23
|
|
24
|
-
As an example, I will show you a script to download all the files of specified EPUB book to local directory(source code is available in repository's examples/aggregate-contents-from-web.rb).
|
24
|
+
As an example, I will show you a script to download all the files of specified EPUB book to local directory(source code is available in repository's {file:examples/aggregate-contents-from-web.rb}).
|
25
25
|
|
26
26
|
{include:file:examples/aggregate-contents-from-web.rb}
|
27
27
|
|
@@ -45,7 +45,7 @@ To load EPUB books from directory, you need specify file adapter via {EPUB::OCF:
|
|
45
45
|
|
46
46
|
require 'epub/parser'
|
47
47
|
|
48
|
-
EPUB::OCF::PhysicalContainer.adapter = :
|
48
|
+
EPUB::OCF::PhysicalContainer.adapter = :UnpackedDirectory
|
49
49
|
|
50
50
|
And then, directory path as EPUB path:
|
51
51
|
|
@@ -67,7 +67,7 @@ If set {EPUB::OCF::PhysicalContainer.adapter}, it is used every time EPUB Parser
|
|
67
67
|
archived_book = EPUB::Parser.parse('./page-blanche.epub') # => EPUB::Book
|
68
68
|
# From directory
|
69
69
|
File.ftype './page-blanche' # => "directory"
|
70
|
-
unpacked_book = EPUB::Parser.parse('./page-blanche', container_adapter: :
|
70
|
+
unpacked_book = EPUB::Parser.parse('./page-blanche', container_adapter: :UnpackedDirectory) # => EPUB::Book
|
71
71
|
|
72
72
|
Command-line tools
|
73
73
|
------------------
|
data/epub-parser.gemspec
CHANGED
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.required_ruby_version = '> 2'
|
15
15
|
|
16
16
|
s.files = `git ls-files`.split("\n")
|
17
|
+
.push('lib/epub/parser/cfi.tab.rb')
|
17
18
|
.push('test/fixtures/book/OPS/ルートファイル.opf')
|
18
19
|
.push('test/fixtures/book/OPS/日本語.xhtml')
|
19
20
|
.push(Dir['docs/*.md'])
|
@@ -37,11 +38,11 @@ Gem::Specification.new do |s|
|
|
37
38
|
s.add_development_dependency 'gem-man'
|
38
39
|
s.add_development_dependency 'ronn'
|
39
40
|
s.add_development_dependency 'epzip'
|
40
|
-
s.add_development_dependency '
|
41
|
+
s.add_development_dependency 'racc'
|
42
|
+
s.add_development_dependency 'nokogiri-diff'
|
41
43
|
|
42
44
|
s.add_runtime_dependency 'zipruby'
|
43
45
|
s.add_runtime_dependency 'nokogiri', '~> 1.6'
|
44
|
-
s.add_runtime_dependency 'nokogumbo'
|
45
46
|
s.add_runtime_dependency 'addressable', '>= 2.3.5'
|
46
47
|
s.add_runtime_dependency 'rchardet', '>= 1.6.1'
|
47
48
|
end
|
data/lib/epub/book/features.rb
CHANGED
@@ -17,7 +17,7 @@ module EPUB
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
%w[title main_title subtitle short_title collection_title edition_title extended_title description date unique_identifier].each do |met|
|
20
|
+
%w[title main_title subtitle short_title collection_title edition_title extended_title description date unique_identifier modified].each do |met|
|
21
21
|
define_method met do
|
22
22
|
metadata.__send__(met)
|
23
23
|
end
|
@@ -29,6 +29,10 @@ module EPUB
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def release_identifier
|
33
|
+
"#{unique_identifier}@#{modified}"
|
34
|
+
end
|
35
|
+
|
32
36
|
def container_adapter
|
33
37
|
@adapter || OCF::PhysicalContainer.adapter
|
34
38
|
end
|
data/lib/epub/cfi.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
module EPUB
|
2
|
+
module CFI
|
3
|
+
SPECIAL_CHARS = '^[](),;=' # "5E", "5B", "5D", "28", "29", "2C", "3B", "3D"
|
4
|
+
RE_ESCAPED_SPECIAL_CHARS = Regexp.escape(SPECIAL_CHARS)
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def escape(string)
|
8
|
+
string.gsub(/([#{RE_ESCAPED_SPECIAL_CHARS}])/o, '^\1')
|
9
|
+
end
|
10
|
+
|
11
|
+
def unescape(string)
|
12
|
+
string.gsub(/\^([#{RE_ESCAPED_SPECIAL_CHARS}])/o, '\1')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Location
|
17
|
+
attr_reader :paths
|
18
|
+
|
19
|
+
def initialize(paths=[])
|
20
|
+
@paths = paths
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize_copy(original)
|
24
|
+
@paths = original.paths.collect(&:dup)
|
25
|
+
end
|
26
|
+
|
27
|
+
def type
|
28
|
+
@paths.last.type
|
29
|
+
end
|
30
|
+
|
31
|
+
def <=>(other)
|
32
|
+
index = 0
|
33
|
+
other_paths = other.paths
|
34
|
+
cmp = nil
|
35
|
+
paths.each do |path|
|
36
|
+
other_path = other_paths[index]
|
37
|
+
return 1 unless other_path
|
38
|
+
cmp = path <=> other_path
|
39
|
+
break unless cmp == 0
|
40
|
+
index += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
unless cmp == 0
|
44
|
+
if cmp == 1 and other_paths[index + 1]
|
45
|
+
return nil
|
46
|
+
else
|
47
|
+
return cmp
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
return nil if paths.last.offset && other_paths[index]
|
52
|
+
|
53
|
+
return -1 if other_paths[index]
|
54
|
+
|
55
|
+
0
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
paths.join('!')
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_fragment
|
63
|
+
"epubcfi(#{self})"
|
64
|
+
end
|
65
|
+
|
66
|
+
def join(*other_paths)
|
67
|
+
new_paths = paths.dup
|
68
|
+
other_paths.each do |path|
|
69
|
+
new_paths << path
|
70
|
+
end
|
71
|
+
self.class.new(new_paths)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Path
|
76
|
+
attr_reader :steps, :offset
|
77
|
+
|
78
|
+
def initialize(steps=[], offset=nil)
|
79
|
+
@steps, @offset = steps, offset
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize_copy(original)
|
83
|
+
@steps = original.steps.collect(&:dup)
|
84
|
+
@offset = original.offset.dup if original.offset
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
@string_cache ||= (steps.join + offset.to_s).freeze
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_fragment
|
92
|
+
@fragment_cache ||= "epubcfi(#{self})".freeze
|
93
|
+
end
|
94
|
+
|
95
|
+
def <=>(other)
|
96
|
+
other_steps = other.steps
|
97
|
+
index = 0
|
98
|
+
steps.each do |step|
|
99
|
+
other_step = other_steps[index]
|
100
|
+
return 1 unless other_step
|
101
|
+
cmp = step <=> other_step
|
102
|
+
return cmp unless cmp == 0
|
103
|
+
index += 1
|
104
|
+
end
|
105
|
+
|
106
|
+
return -1 if other_steps[index]
|
107
|
+
|
108
|
+
other_offset = other.offset
|
109
|
+
if offset
|
110
|
+
if other_offset
|
111
|
+
offset <=> other_offset
|
112
|
+
else
|
113
|
+
1
|
114
|
+
end
|
115
|
+
else
|
116
|
+
if other_offset
|
117
|
+
-1
|
118
|
+
else
|
119
|
+
0
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def each_step_with_instruction
|
125
|
+
yield [step, nil]
|
126
|
+
local_path.each_step_with_instruction do |s, instruction|
|
127
|
+
yield [s, instruction]
|
128
|
+
end
|
129
|
+
self
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Range < ::Range
|
134
|
+
attr_accessor :parent, :start, :end
|
135
|
+
|
136
|
+
# @todo consider the case subpaths are redirected path
|
137
|
+
# @todo FIXME: too dirty
|
138
|
+
class << self
|
139
|
+
def from_parent_and_start_and_end(parent_path, start_subpath, end_subpath)
|
140
|
+
start_str = start_subpath.join
|
141
|
+
end_str = end_subpath.join
|
142
|
+
|
143
|
+
first_paths = parent_path.collect(&:dup)
|
144
|
+
if start_subpath
|
145
|
+
offset_of_first = start_subpath.last.offset
|
146
|
+
offset_of_first = offset_of_first.dup if offset_of_first
|
147
|
+
last_of_first_paths = first_paths.pop
|
148
|
+
first_paths << last_of_first_paths
|
149
|
+
last_of_first_paths.steps.concat start_subpath.shift.steps
|
150
|
+
first_paths.concat start_subpath
|
151
|
+
first_paths.last.instance_variable_set :@offset, offset_of_first
|
152
|
+
end
|
153
|
+
offset_of_last = end_subpath.last.offset
|
154
|
+
offset_of_last = offset_of_last.dup if offset_of_last
|
155
|
+
last_paths = parent_path.collect(&:dup)
|
156
|
+
last_of_last_paths = last_paths.pop
|
157
|
+
last_paths << last_of_last_paths
|
158
|
+
last_of_last_paths.steps.concat end_subpath.shift.steps
|
159
|
+
last_paths.concat end_subpath
|
160
|
+
last_paths.last.instance_variable_set :@offset, offset_of_last
|
161
|
+
|
162
|
+
first = CFI::Location.new(first_paths)
|
163
|
+
last = CFI::Location.new(last_paths)
|
164
|
+
|
165
|
+
new_range = new(first, last)
|
166
|
+
|
167
|
+
new_range.parent = Location.new(parent_path)
|
168
|
+
new_range.start = start_str
|
169
|
+
new_range.end = end_str
|
170
|
+
|
171
|
+
new_range
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_s
|
176
|
+
@string_cache ||= (first.to_fragment + (exclude_end? ? '...' : '..') + last.to_fragment).freeze
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_fragment
|
180
|
+
@fragment_cache ||= "epubcfi(#{@parent},#{@start},#{@end})".freeze
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class Step
|
185
|
+
attr_reader :step, :assertion
|
186
|
+
|
187
|
+
def initialize(step, assertion=nil)
|
188
|
+
@step, @assertion = step, assertion
|
189
|
+
@string_cache = nil
|
190
|
+
end
|
191
|
+
|
192
|
+
def initialize_copy(original)
|
193
|
+
@step = original.step
|
194
|
+
@assertion = original.assertion.dup if original.assertion
|
195
|
+
end
|
196
|
+
|
197
|
+
def to_s
|
198
|
+
@string_cache ||= "/#{step}#{assertion}".freeze # need escape?
|
199
|
+
end
|
200
|
+
|
201
|
+
def <=>(other)
|
202
|
+
step <=> other.step
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class IDAssertion
|
207
|
+
attr_reader :id, :parameters
|
208
|
+
|
209
|
+
def initialize(id, parameters={})
|
210
|
+
@id, @parameters = id, parameters
|
211
|
+
@string_cache = nil
|
212
|
+
end
|
213
|
+
|
214
|
+
def to_s
|
215
|
+
return @string_cache if @string_cache
|
216
|
+
@string_cache = '['
|
217
|
+
@string_cache << CFI.escape(id) if id
|
218
|
+
parameters.each_pair do |key, values|
|
219
|
+
value = values.join(',')
|
220
|
+
@string_cache << ";#{CFI.escape(key)}=#{CFI.escape(value)}"
|
221
|
+
end
|
222
|
+
@string_cache << ']'
|
223
|
+
@string_cache.freeze
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class TextLocationAssertion
|
228
|
+
attr_reader :preceded, :followed, :parameters
|
229
|
+
|
230
|
+
def initialize(preceded=nil, followed=nil, parameters={})
|
231
|
+
@preceded, @followed, @parameters = preceded, followed, parameters
|
232
|
+
@string_cache = nil
|
233
|
+
end
|
234
|
+
|
235
|
+
def to_s
|
236
|
+
return @string_cache if @string_cache
|
237
|
+
@string_cache = '['
|
238
|
+
@string_cache << CFI.escape(preceded) if preceded
|
239
|
+
@string_cache << ',' << CFI.escape(followed) if followed
|
240
|
+
parameters.each_pair do |key, values|
|
241
|
+
value = values.join(',')
|
242
|
+
@string_cache << ";#{CFI.escape(key)}=#{CFI.escape(value)}"
|
243
|
+
end
|
244
|
+
@string_cache << ']'
|
245
|
+
@string_cache.freeze
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class CharacterOffset
|
250
|
+
attr_reader :offset, :assertion
|
251
|
+
|
252
|
+
def initialize(offset, assertion=nil)
|
253
|
+
@offset, @assertion = offset, assertion
|
254
|
+
@string_cache = nil
|
255
|
+
end
|
256
|
+
|
257
|
+
def to_s
|
258
|
+
@string_cache ||= ":#{offset}#{assertion}".freeze # need escape?
|
259
|
+
end
|
260
|
+
|
261
|
+
def <=>(other)
|
262
|
+
offset <=> other.offset
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class TemporalSpatialOffset
|
267
|
+
attr_reader :temporal, :x, :y, :assertion
|
268
|
+
|
269
|
+
def initialize(temporal=nil, x=nil, y=nil, assertion=nil)
|
270
|
+
raise RangeError, "dimension must be in 0..100 but passed #{x}" unless (0.0..100.0).cover?(x) if x
|
271
|
+
raise RangeError, "dimension must be in 0..100 but passed #{y}" unless (0.0..100.0).cover?(y) if y
|
272
|
+
warn "Assertion is passed to #{__class__} but cannot know how to handle with it: #{assertion}" if assertion
|
273
|
+
@temporal, @x, @y, @assertion = temporal, x, y, assertion
|
274
|
+
@string_cache
|
275
|
+
end
|
276
|
+
|
277
|
+
def to_s
|
278
|
+
return @string_cache if @string_cache
|
279
|
+
@string_cache = ''
|
280
|
+
@string_cache << "~#{temporal}" if temporal
|
281
|
+
@string_cache << "@#{x}:#{y}" if x or y
|
282
|
+
@string_cache.freeze
|
283
|
+
end
|
284
|
+
|
285
|
+
# @note should split the class to spatial offset and temporal-spatial offset?
|
286
|
+
def <=>(other)
|
287
|
+
return -1 if temporal.nil? and other.temporal
|
288
|
+
return 1 if temporal and other.temporal.nil?
|
289
|
+
cmp = temporal <=> other.temporal
|
290
|
+
return cmp unless cmp == 0
|
291
|
+
return -1 if y.nil? and other.y
|
292
|
+
return 1 if y and other.y.nil?
|
293
|
+
cmp = y <=> other.y
|
294
|
+
return cmp unless cmp == 0
|
295
|
+
return -1 if x.nil? and other.x
|
296
|
+
return 1 if x and other.x.nil?
|
297
|
+
cmp = x <=> other.x
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|