rtm-activerecord 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/DISCLAIMER +13 -0
  2. data/LICENSE +201 -0
  3. data/README +4 -0
  4. data/lib/rtm/activerecord/001_initial_schema.rb +119 -0
  5. data/lib/rtm/activerecord/association_and_role.rb +54 -0
  6. data/lib/rtm/activerecord/base.rb +83 -0
  7. data/lib/rtm/activerecord/connect.rb +106 -0
  8. data/lib/rtm/activerecord/io/from_xtm2.rb +266 -0
  9. data/lib/rtm/activerecord/io/from_xtm2_libxml.rb +97 -0
  10. data/lib/rtm/activerecord/io/to_jtm.rb +141 -0
  11. data/lib/rtm/activerecord/io/to_string.rb +130 -0
  12. data/lib/rtm/activerecord/io/to_xtm1.rb +162 -0
  13. data/lib/rtm/activerecord/io/to_xtm2.rb +163 -0
  14. data/lib/rtm/activerecord/io/to_yaml.rb +132 -0
  15. data/lib/rtm/activerecord/literal_index.rb +38 -0
  16. data/lib/rtm/activerecord/locators.rb +58 -0
  17. data/lib/rtm/activerecord/merging.rb +310 -0
  18. data/lib/rtm/activerecord/name_variant_occurrence.rb +86 -0
  19. data/lib/rtm/activerecord/persistent_code_output.rb +22 -0
  20. data/lib/rtm/activerecord/quaaxtm2rtm.rb +116 -0
  21. data/lib/rtm/activerecord/quaaxtm2rtmviews.rb +137 -0
  22. data/lib/rtm/activerecord/set_wrapper.rb +101 -0
  23. data/lib/rtm/activerecord/sugar/association/hash_access.rb +22 -0
  24. data/lib/rtm/activerecord/sugar/role/counterparts.rb +56 -0
  25. data/lib/rtm/activerecord/sugar/topic/characteristics.rb +15 -0
  26. data/lib/rtm/activerecord/sugar/topic/counterparts.rb +18 -0
  27. data/lib/rtm/activerecord/sugar/topic/hash_access.rb +78 -0
  28. data/lib/rtm/activerecord/sugar/topic/identifier_direct.rb +14 -0
  29. data/lib/rtm/activerecord/sugar/topic/predefined_associations.rb +53 -0
  30. data/lib/rtm/activerecord/tm_construct.rb +66 -0
  31. data/lib/rtm/activerecord/tm_delegator.rb +417 -0
  32. data/lib/rtm/activerecord/tm_set_delegator.rb +226 -0
  33. data/lib/rtm/activerecord/tmdm.rb +309 -0
  34. data/lib/rtm/activerecord/topic.rb +204 -0
  35. data/lib/rtm/activerecord/topic_map.rb +435 -0
  36. data/lib/rtm/activerecord/traverse_associations.rb +90 -0
  37. data/lib/rtm/activerecord.rb +106 -0
  38. metadata +120 -0
@@ -0,0 +1,106 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ require 'active_record'
5
+ require 'rtm/backend/active_record/001_initial_schema'
6
+ require "rtm/backend/active_record/io/from_xtm2"
7
+ # require "rtm/backend/active_record/io/from_xtm2_libxml"
8
+
9
+ module RTM
10
+
11
+ class RTMAR
12
+ include Singleton
13
+ include RTM::TopicMapSystem
14
+
15
+
16
+ def create(base_locator)
17
+ AR::TopicMap.create(base_locator)
18
+ end
19
+
20
+ def topic_maps
21
+ AR::TopicMap.topic_maps
22
+ end
23
+
24
+ def [](*args)
25
+ if args.size == 0
26
+ return AR::TopicMap.topic_maps
27
+ end
28
+ args.map { |m| AR::TopicMap.topic_maps[m] }
29
+ end
30
+
31
+ # Assignes a logger to active record (or STDOUT as default) which outputs
32
+ # the sql statements.
33
+ def log(to=STDOUT)
34
+ ActiveRecord::Base.logger = Logger.new(to)
35
+ ActiveRecord::Base.colorize_logging = false if PLATFORM =~ /mswin/
36
+ end
37
+ # This function generates the database schema using the default
38
+ # ActiveRecord connection.
39
+ def generate_database
40
+ RTM::AR::TMDM::InitialSchema.migrate(:up)
41
+ end
42
+
43
+ # Connects to the sqlite3 database given in the path or to
44
+ # a default database if no path is given.
45
+ def connect_sqlite3(dbname="tmdm.sqlite3")
46
+ ActiveRecord::Base.establish_connection(
47
+ :adapter => platform_specific_adapter("sqlite3"),
48
+ :database => dbname
49
+ )
50
+ end
51
+
52
+ # Connects to a mysql database. Parameters are
53
+ # either
54
+ # database, username, password, host
55
+ # or
56
+ # only a keyword hash with these parameters.
57
+ def connect_mysql(database="rtm_development", username=nil, password=nil, host="localhost")
58
+ if database.is_a? Hash
59
+ return ActiveRecord::Base.establish_connection(database)
60
+ end
61
+ ActiveRecord::Base.establish_connection(
62
+ :adapter => platform_specific_adapter("mysql"),
63
+ :database => database,
64
+ :username => username,
65
+ :password => password,
66
+ :host => host
67
+ )
68
+ end
69
+
70
+ # Connects to a in-memory database. No parameters required.
71
+ # Schema will automatically be generated.
72
+ def connect_memory
73
+ ActiveRecord::Base.establish_connection(
74
+ :adapter => platform_specific_adapter("sqlite3"),
75
+ :database => ":memory:"
76
+ )
77
+ no_output do
78
+ generate_database
79
+ end
80
+ true
81
+ end
82
+
83
+ # Connects Active Record to a database or in Memory database if no parameters given.
84
+ def connect(*args)
85
+ if args.size == 0
86
+ return connect_memory
87
+ end
88
+ ActiveRecord::Base.establish_connection(*args)
89
+ end
90
+
91
+ private
92
+ def platform_specific_adapter(adapter)
93
+ if PLATFORM && PLATFORM =~ /java/
94
+ return "jdbc#{adapter}"
95
+ end
96
+ return adapter
97
+ end
98
+
99
+ end
100
+
101
+
102
+
103
+
104
+
105
+
106
+ end
@@ -0,0 +1,266 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ # Ruby Topic Maps (RTM) http://rtm.rubyforge.org/
5
+
6
+ class RTM::RTMAR
7
+ def from_xtm2(*args)
8
+ RTM::AR::IO::FROMXTM2.from_xtm2(self, *args)
9
+ end
10
+ end
11
+ module RTM::AR::IO
12
+ # XTM2 Export.
13
+ # Each Topic Maps Construct gets a to_xtm2 method.
14
+ # The result is a REXML::Element except for TopicMap where it is a REXML::Document.
15
+ module FROMXTM2
16
+ require 'rexml/document'
17
+ require 'rexml/parsers/sax2parser'
18
+ require 'rexml/sax2listener'
19
+ #require 'jrexml'
20
+ XTM2DEBUG = false
21
+
22
+ # Reads XTM2 from source (io object).
23
+ # Example: RTM::IO::FROMXTM2.from_xtm2(File.open(file_name),"http://rtm.rubyforge.org/topicmaps/tm1/")
24
+ # supported options:
25
+ # :strip_whitespace (defaults to false, may be set to true),
26
+ # :deprefix (defaults to nil, may be set to a string (or regex) which will be removed from the beginning of an (unresolved) item_identifier if it is there.
27
+ def self.from_xtm2(base_tms, source, base_locator, target=nil,options={})
28
+ tm = base_tms.create(base_locator) unless target
29
+ list = XTM2Listener.new(base_locator, target || tm, options)
30
+ parser = REXML::Parsers::SAX2Parser.new(source)
31
+ parser.listen(list)
32
+ parser.parse
33
+ true
34
+ end
35
+
36
+ class XTM2Listener
37
+ include REXML::SAX2Listener
38
+ def initialize(base_locator, target,options={})
39
+ @base_locator = base_locator
40
+ @target = target
41
+ @targets = []
42
+ @path = []
43
+ @options=options
44
+ end
45
+
46
+ private
47
+ def resolve(iri)
48
+ warn("resolving nil iri can have unexpected results") unless iri
49
+ # TODO handle %HH sequences, here or in backend/active_record.rb
50
+ if @options[:deprefix]
51
+ iri = iri.gsub(/^#{@options[:deprefix]}/,'')
52
+ end
53
+ @target.resolve(iri, @base_locator)
54
+ end
55
+
56
+ public
57
+ def start_element(uri, name, qname, attrs)
58
+ return unless uri == "http://www.topicmaps.org/xtm/"
59
+ a = attrs #Hash[* attrs.flatten]
60
+ #puts "Start of tag #{name} - attrs: #{a.inspect}"
61
+
62
+ case name
63
+ when "topic"
64
+ @topic = @targets.last.topic_by_item_identifier! resolve(a["id"])
65
+ @targets.push @topic
66
+ when "association"
67
+ assoc = nil
68
+ if a["reifier"]
69
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
70
+ raise "The topic reifiing this association already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::Association)
71
+ if ref.reified
72
+ assoc = ref.reified
73
+ else
74
+ assoc = @targets.last.create_association
75
+ assoc.reifier = ref
76
+ end
77
+ end
78
+ assoc ||= @targets.last.create_association
79
+ @targets.push assoc
80
+ when "role"
81
+ if a["reifier"]
82
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
83
+ raise "The topic reifiing this association role already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::AssociationRole)
84
+ if ref.reified
85
+ role = ref.reified
86
+ else
87
+ role = @targets.last.create_role
88
+ role.reifier = ref
89
+ end
90
+ end
91
+ role ||= @targets.last.create_role
92
+ @targets.push role
93
+ when "name"
94
+ if a["reifier"]
95
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
96
+ raise "The topic reifiing this topic name already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::TopicName)
97
+ if ref.reified
98
+ tname = ref.reified
99
+ else
100
+ tname = @targets.last.create_name
101
+ tname.reifier = ref
102
+ end
103
+ end
104
+ tname ||= @targets.last.create_name
105
+ @targets.push tname
106
+ when "value"
107
+ # this is handled in text()
108
+ when "variant"
109
+ if a["reifier"]
110
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
111
+ raise "The topic reifiing this topic variant already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::Variant)
112
+ if ref.reified
113
+ variant = ref.reified
114
+ else
115
+ variant = @targets.last.create_variant
116
+ variant.reifier = ref
117
+ end
118
+ end
119
+ variant ||= @targets.last.create_variant
120
+ @targets.push variant
121
+ when "scope"
122
+ # nothing to be done here :)
123
+ when "instanceOf"
124
+ # nothing to be done here :)
125
+ when "type"
126
+ # nothing to be done here :)
127
+ when "occurrence"
128
+ if a["reifier"]
129
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
130
+ raise "The topic reifiing this topic occurrence already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::Occurrence)
131
+ if ref.reified
132
+ occur = ref.reified
133
+ else
134
+ occur = @targets.last.create_occurrence
135
+ occur.reifier = ref
136
+ end
137
+ end
138
+ occur ||= @targets.last.create_occurrence
139
+ @targets.push occur
140
+ when "resourceData"
141
+ occur_or_variant = @targets.last
142
+ if a["datatype"]
143
+ occur_or_variant.datatype = a["datatype"]
144
+ # special handling is done in tag_end
145
+ else
146
+ occur_or_variant.datatype = RTM::PSI[:String]
147
+ end
148
+ when "topicRef"
149
+ case @path.last
150
+ when "scope"
151
+ @targets.last.scope << a["href"]
152
+ when "instanceOf"
153
+ assoc = @targets.last.topic_map.create_association :type => RTM::PSI[:type_instance]
154
+ assoc.create_role @targets.last, RTM::PSI[:instance]
155
+ assoc.create_role a["href"], RTM::PSI[:type]
156
+ when "type"
157
+ @targets.last.type = a["href"]
158
+ when "role"
159
+ @targets.last.player = a["href"]
160
+ end
161
+ when "resourceRef"
162
+ occurrence = @targets.last
163
+ occurrence.datatype = RTM::PSI[:IRI]
164
+ occurrence.value = a["href"]
165
+ when "subjectLocator"
166
+ @targets.last.subject_locators << a["href"]
167
+ when "subjectIdentifier"
168
+ @targets.last.subject_identifiers << a["href"]
169
+ when "itemIdentity"
170
+ @targets.last.item_identifiers << resolve(a["href"])
171
+ when "topicMap"
172
+ # check where we are
173
+ raise "unexpected element topicMap" unless @path.empty?
174
+ # check version
175
+ raise "Sorry, only XTM 2.0 is supported" if a["version"] != "2.0"
176
+
177
+ # reifier. if target topic map is already reified we have to merge, if not create topic
178
+ if a["reifier"]
179
+ if @target.reifier # TODO this might be no topic here. In this case something should be raised!
180
+ @target.reifier.item_identifiers << resolve(a["reifier"])
181
+ else
182
+ @target.topic_by_item_identifier! resolve(a["reifier"])
183
+ end
184
+ end
185
+ @targets.push @target
186
+ when "mergeMap"
187
+ # TODO mergeMap
188
+ warn "mergeMap is not supported yet!"
189
+ else
190
+ warn "Unhandled element: #{name}"
191
+ end
192
+
193
+ @path.push name
194
+ end
195
+
196
+ # Called when the end tag is reached. In the case of <tag/>, tag_end
197
+ # will be called immidiately after tag_start
198
+ # @p the name of the tag
199
+ def end_element(uri, name, qname)
200
+ return unless uri == "http://www.topicmaps.org/xtm/"
201
+ #puts "end of tag: #{name}"
202
+
203
+ old_name = @path.pop
204
+ raise "Tag_end did not match: expected: #{old_name}, got: #{name}." unless old_name == name
205
+ case name
206
+ when "topicMap", "topic", "association", "role", "name", "variant", "occurrence"
207
+ @targets.pop
208
+ when "resourceData"
209
+ occur = @targets.last
210
+ # puts "finalizing occurrence #{occur.inspect}"
211
+ if occur.datatype == RTM::PSI[:IRI]
212
+ occur.value = resolve(occur.value)
213
+ elsif occur.datatype == RTM::PSI[:anyType]
214
+ # TODO Content must be canonicalized according to http://www.isotopicmaps.org/sam/sam-xtm/#sect-xml-canonicalization
215
+ warn("Content canonicalization is not yet implemented!")
216
+ end
217
+ # puts "leaving finalizing occurrence"
218
+ end
219
+ # puts "after case"
220
+ end
221
+ # Called when text is encountered in the document
222
+ # @p text the text content.
223
+ def characters(text)
224
+ if @options[:strip_whitespace]
225
+ text.strip!
226
+ return if text.empty?
227
+ end
228
+ case @path.last
229
+ when "value", "resourceData"
230
+ if @targets.last.value
231
+ @targets.last.value += text
232
+ else
233
+ @targets.last.value = text
234
+ end
235
+ else
236
+ puts "Found text but don't know that to do: #{text}" unless text.strip.empty?
237
+ end
238
+ end
239
+
240
+ def cdata content
241
+ if @options[:strip_whitespace]
242
+ content.strip!
243
+ return if content.empty?
244
+ end
245
+ case @path.last
246
+ when "value", "resourceData"
247
+ if @targets.last.value
248
+ @targets.last.value += content
249
+ else
250
+ @targets.last.value = content
251
+ end
252
+ else
253
+ puts "Found cdata but don't know that to do: #{content}" unless content.strip.empty?
254
+ end
255
+ end
256
+
257
+ def xmldecl version, encoding, standalone
258
+ warn "XML Version 1.0 expected. Not sure if we can handle this, but we will try." unless version == "1.0"
259
+ warn "Be aware there is no encoding manipulation done, everything gets in as-is." if encoding
260
+ # what about standalone?
261
+ end
262
+
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,97 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+
5
+ class RTM::RTMAR
6
+ def from_xtm2lx(*args)
7
+ RTM::AR::IO::FROMXTM2LX.from_xtm2(self, *args)
8
+ end
9
+ end
10
+
11
+ # class RTM::RTMAR
12
+ # def self.from_xtm2lx(*args)
13
+ # RTM::AR::IO::FROMXTM2LX.from_xtm2(self, *args)
14
+ # end
15
+ # end
16
+
17
+
18
+
19
+ module RTM::AR::IO
20
+ # XTM2 Export.
21
+ # Each Topic Maps Construct gets a to_xtm2 method.
22
+ # The result is a REXML::Element except for TopicMap where it is a REXML::Document.
23
+ module FROMXTM2LX
24
+ require 'libxml'
25
+ require 'rexml/document'
26
+ require 'rexml/parsers/sax2parser'
27
+ require 'rexml/sax2listener'
28
+ XTM2DEBUG = false
29
+
30
+ # Reads XTM2 from source (io object).
31
+ # Example: RTM::IO::FROMXTM2.from_xtm2(File.open(file_name),"http://rtm.rubyforge.org/topicmaps/tm1/")
32
+ # supported options:
33
+ # :strip_whitespace (defaults to false, may be set to true),
34
+ # :deprefix (defaults to nil, may be set to a string (or regex) which will be removed from the beginning of an (unresolved) item_identifier if it is there.
35
+ def self.from_xtm2(base_tms, source, base_locator, target=nil,options={})
36
+ tm = base_tms.create(base_locator) unless target
37
+ parser = XML::SaxParser.new
38
+ parser.callbacks = XML::LibXMLSax2wrapper.new(FROMXTM2::XTM2Listener.new(base_locator, target || tm, options))
39
+ parser.filename = source
40
+ parser.parse
41
+ #true
42
+ end
43
+ end
44
+ end
45
+
46
+ module XML
47
+ # Acts as Callback structure for the LibXML-Ruby SAX Parser and calls
48
+ # a REXML SAX2Listener API.
49
+ class LibXMLSax2wrapper
50
+ include LibXML::XML::SaxParser::Callbacks
51
+ def initialize(rexml_sax2listener)
52
+ @dest = rexml_sax2listener
53
+ @ns = Hash.new("http://www.topicmaps.org/xtm/")
54
+ end
55
+ def on_start_document
56
+ @dest.start_document
57
+ end
58
+ def on_end_document
59
+ @dest.end_document
60
+ end
61
+ def on_start_element(qname, attr)
62
+ prefix, localname = qname.split(":")
63
+ unless localname
64
+ localname = prefix
65
+ uri = @ns.default
66
+ else
67
+ uri = @ns[prefix]
68
+ end
69
+ @dest.start_element(uri, localname, qname, attr)
70
+ end
71
+ def on_end_element(qname)
72
+ prefix, localname = qname.split(":")
73
+ unless localname
74
+ localname = prefix
75
+ uri = @ns.default
76
+ else
77
+ uri = @ns[prefix]
78
+ end
79
+ @dest.end_element(uri, localname, qname)
80
+ end
81
+ def on_characters(text)
82
+ @dest.characters(text)
83
+ end
84
+ def on_cdata_block(content)
85
+ @dest.cdata(content)
86
+ end
87
+ def on_parser_error(msg)
88
+ warn("SAX Parser Error: #{msg}")
89
+ end
90
+ def on_parser_warning(*msg)
91
+ warn("SAX Parser Warning: #{msg}")
92
+ end
93
+ def on_parser_fatal_error(msg)
94
+ warn("SAX Parser Fatal Error: #{msg}")
95
+ end
96
+ end
97
+ end