rtm-activerecord 0.2.0

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.
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,204 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR
5
+ class Topic < Construct
6
+ include RTM::Topic
7
+ wrapper_cache
8
+
9
+ # parent :topic_map
10
+ alias :parent :topic_map
11
+ property_set :subject_identifiers, :aka => [:si,:sid], :type => :SubjectIdentifier, :wrap => true,
12
+ #:create => :subject_identifier, :create_aka => [:csi,:csid],
13
+ #:create_args => [{:name => :reference, :type => :String}]
14
+ :add => :subject_identifier, :remove => :subject_identifier
15
+ alias :getSubjectIdentifiers :subject_identifiers
16
+
17
+ property_set :subject_locators, :aka => [:sl,:slo], :type => :SubjectLocator, :wrap => true,
18
+ #:create => :subject_locator, :create_aka => [:csl, :cslo],
19
+ #:create_args => [{:name => :reference, :type => :String}]
20
+ :add => :subject_locator, :remove => :subject_locator
21
+ alias :getSubjectLocators :subject_locators
22
+
23
+ property :reified, :computable => true, :type => :Reifiable, :wrap => true
24
+
25
+ property_set :names, :aka => [:n, :topic_names], :type => :Name, :wrap => true,
26
+ :create => :name_internal, # :create_aka => [:create_topic_name, :cn],
27
+ :create_args => [ {:name => :value, :type => :String}, {:name => :type, :type => :Topic, :optional => true}, {:name => :scope, :type => :Collection} ]
28
+
29
+ alias :names_by :names # intermediate solution, check real TMAPI usage
30
+
31
+
32
+ def create_name(arg1,arg2 = :nothing, arg3 = :nothing)
33
+ if (arg2 == :nothing) && (arg3 == :nothing) #arg1 = value
34
+ raise "create_name(value): value must be a String" unless arg1.is_a?(String)
35
+ return create_name_internal(:value => arg1, :type => self.parent._topic_by_locator!(RTM::PSI[:name_type]))
36
+ end
37
+ if (arg3 == :nothing)
38
+ if arg2.is_a?(Array) #arg1 = value, arg2 = scope
39
+ raise "create_name(value, scope): value must be a String" unless arg1.is_a?(String)
40
+ arg2.each do |theme|
41
+ raise "create_name(value, scope): scope must be an Array of Topics/Topic-References" unless (theme.is_a?(String) || theme.is_a?(RTM::Topic) || theme.is_a?(RTM::Locator))
42
+ end
43
+ return arg2.empty? ? create_name_internal(:value => arg1) : create_name_internal(:value => arg1, :scope => self.parent._topic_by_locator!(arg2))
44
+ elsif arg2.is_a?(String) #arg1 = type, arg2 = value
45
+ raise "create_name(type, value): type must be a Topic/Topic-Reference" unless (arg1.is_a?(String) || arg1.is_a?(RTM::Topic) || arg1.is_a?(RTM::Locator))
46
+ return create_name_internal(:type => self.parent._topic_by_locator!(arg1), :value => arg2)
47
+ else
48
+ raise "create_name(?,?): arguments don't match"
49
+ end
50
+ end
51
+ #arg1 = type, arg2 = value, arg3 = scope
52
+ raise "create_name(type, value, scope): type must be a Topic/Topic-Reference" unless (arg1.is_a?(String) || arg1.is_a?(RTM::Topic) || arg1.is_a?(RTM::Locator))
53
+ raise "create_name(type, value, scope): value must be a String" unless arg2.is_a?(String)
54
+ raise "create_name(type, value, scope): scope must be an Array" unless arg3.is_a?(Array)
55
+ arg3.each do |theme|
56
+ raise "create_name(type, value, scope): scope must be an Array of Topics/Topic-References" unless (theme.is_a?(String) || theme.is_a?(RTM::Topic) || theme.is_a?(RTM::Locator))
57
+ end
58
+ return create_name_internal(:type => self.parent._topic_by_locator!(arg1), :value => arg2, :scope => self.parent._topic_by_locator!(arg3))
59
+ end
60
+ alias :cn :create_name
61
+ alias :create_topic_name :create_name
62
+
63
+ property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => true,
64
+ :create => :occurrence_internal, :create_aka => :co,
65
+ :create_args => [ {:name => :type, :type => :Topic}, {:name => :value, :type => [:String, :Locator]}, {:name => :scope, :type => :Collection} ]
66
+
67
+ alias :occurrences_by :occurrences # intermediate solution, check real TMAPI usage
68
+
69
+ def create_occurrence(type,value,params={})
70
+ if params[:scope]
71
+ if value.is_a?(RTM::Locator)
72
+ return create_occurrence_internal(topic_map.get!(type),value,topic_map._topic_by_locator!(params[:scope]), :datatype => RTM::PSI[:IRI])
73
+ elsif !params[:datatype]
74
+ return create_occurrence_internal(topic_map.get!(type),value,topic_map._topic_by_locator!(params[:scope]))
75
+ else # datatype given, no locator
76
+ return create_occurrence_internal(topic_map.get!(type),value,topic_map.create_locator(params[:datatype]),topic_map._topic_by_locator!(params[:scope]))
77
+ end
78
+ else #no scope
79
+ if value.is_a?(RTM::Locator)
80
+ return create_occurrence_internal(topic_map.get!(type),value, :datatype => RTM::PSI[:IRI])
81
+ elsif !params[:datatype]
82
+ return create_occurrence_internal(topic_map.get!(type),value, params)
83
+ else #datatype given, no locator
84
+ return create_occurrence_internal(topic_map.get!(type),value,topic_map.create_locator(params[:datatype]))
85
+ end
86
+ end
87
+ end
88
+
89
+ def characteristics(*args)
90
+ names(*args).to_a + occurrences(*args).to_a
91
+ end
92
+ alias :children :characteristics
93
+ alias :characteristics_by :characteristics # intermediate solution, check real TMAPI usage
94
+
95
+ property_set :roles, :aka => [:r, :roles_played, :association_roles], :computable => true, :type => :Role, :wrap => true
96
+
97
+ def filter_roles(*args)
98
+ puts
99
+ puts "topic filter roles:"
100
+ puts args.inspect
101
+ if args.size == 1
102
+ a1 = args.shift
103
+ # if a1 == :any
104
+ # a1 = nil
105
+ # end
106
+ role_type = topic_map.get(a1)
107
+ return self.roles unless role_type
108
+ if role_type.respond_to?(:each)
109
+ query_conditions = (["ttype_id = ?"] * role_type.size).join(" OR ")
110
+ puts
111
+ puts "qq:"
112
+ puts query_conditions
113
+ puts role_type.map{|rt| rt.id}
114
+ puts
115
+ return self.roles.find(:all,:conditions => [query_conditions, role_type.map{|rt| rt.id}])
116
+ else
117
+ return self.roles.find(:all,:conditions => ["ttype_id = ?", role_type.id])
118
+ end
119
+ elsif args.size == 2
120
+ role_type = args.shift
121
+ # if role_type == :any
122
+ # role_type = nil
123
+ # end
124
+ role_type = topic_map.get(role_type)
125
+
126
+ assoc_type = args.shift
127
+ # if assoc_type == :any
128
+ # assoc_type = nil
129
+ # end
130
+ assoc_type = topic_map.get(assoc_type)
131
+
132
+ if role_type && assoc_type
133
+ puts "trying to return topic.roles for #{role_type.inspect} and #{assoc_type.inspect}"
134
+ res = self.roles.find(:all, :conditions => ["roles.ttype_id = ? and associations.ttype_id = ?", role_type.id, assoc_type.id])
135
+ puts res.inspect
136
+ return res
137
+ elsif assoc_type
138
+ return self.roles.find(:all, :conditions => ["associations.ttype_id = ?", assoc_type.id])
139
+ elsif role_type
140
+ return self.roles.find(:all,:conditions => ["ttype_id = ?", role_type.id])
141
+ else
142
+ warn("Topic#roles got 2 arguments but doesn't know how to handle (or referenced topics where not found)")
143
+ end
144
+ end
145
+ false
146
+ end
147
+
148
+ property_set :associations, :aka => [:a, :associations_played], :type => :Association, :wrap => true
149
+
150
+ property_set :scoped, :aka => :scoped_objects, :type => :Construct, :wrap => true
151
+ property_set :scoped_associations, :type => :Association, :wrap => true
152
+ property_set :scoped_names, :aka => :scoped_topic_names, :type => :Name, :wrap => true
153
+ property_set :scoped_variants, :type => :Variant, :wrap => true
154
+ property_set :scoped_occurrences, :type => :Occurrence, :wrap => true
155
+
156
+ property_set :typed, :aka => :typed_objects, :type => :Construct, :wrap => true
157
+ property_set :typed_associations, :aka => [:ta,:associations_typed,:at], :type => :Association, :wrap => true
158
+ property_set :typed_roles, :aka => [:rt,:roles_typed,:association_roles_typed,:art,:rt], :type => :Role, :wrap => true
159
+ property_set :typed_names, :aka => [:nt,:names_typed,:topic_names_typed,:tnt,:nt], :type => :Name, :wrap => true
160
+ property_set :typed_occurrences, :aka => [:to,:occurrences_typed,:ot], :type => :Occurrence, :wrap => true
161
+
162
+ def id
163
+ __getobj__.id
164
+ end
165
+
166
+ # class:Topic equality, http://www.isotopicmaps.org/sam/sam-model/#d0e1029
167
+ #
168
+ # This method is all crap. These ActiveRecord Arrays can't be intersected,
169
+ # so I map the identifiers to their reference which seems long winded.
170
+ # Still I find it better than using their ID in the long.
171
+ #
172
+ def ==(o)
173
+ return false unless o
174
+ return false unless o.respond_to?(:subject_identifiers)
175
+ return false unless o.respond_to?(:subject_locators)
176
+ return false unless o.respond_to?(:names)
177
+ # Two topic items are equal if they have:
178
+ # * at least one equal string in their [subject identifiers] properties,
179
+ # -> test if intersection are > 0
180
+ my_si = self.subject_identifiers.map{|si| si.reference}
181
+ ot_si = o.subject_identifiers.map{|si| si.reference}
182
+ return true if ( my_si & ot_si).size > 0
183
+
184
+ # * at least one equal string in their [item identifiers] properties,
185
+ my_ii = self.item_identifiers.map{|ii| ii.reference}
186
+ ot_ii = o.item_identifiers.map{|ii| ii.reference}
187
+ return true if (my_ii & ot_ii).size > 0
188
+
189
+ # * at least one equal string in their [subject locators] properties,
190
+ my_sl = self.subject_locators.map{|sl| sl.reference}
191
+ ot_sl = o.subject_locators.map{|sl| sl.reference}
192
+ return true if (my_sl & ot_sl).size > 0
193
+
194
+ # * an equal string in the [subject identifiers] property of the one topic item and the [item identifiers] property of the other, or
195
+ return true if (my_si & ot_ii).size > 0
196
+ return true if (my_ii & ot_si).size > 0
197
+
198
+ # * the same information item in their [reified] properties.
199
+ return true if self.reified != nil && o.reified != nil && (self.reified == o.reified)
200
+ # ... otherwise
201
+ false
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,435 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR
5
+ class TopicMap < Reifiable
6
+ include RTM::TopicMap
7
+
8
+ def close
9
+ end
10
+
11
+ wrapper_cache
12
+ property_set :topics, :aka => :t, :type => :Topic, :wrap => true,
13
+ :create => :topic, :create_aka => :ct
14
+
15
+ property_set :associations, :aka => [:a, :assocs], :type => :Association, :wrap => true,
16
+ :create => :association_internal, :create_aka => :ca,
17
+ :create_args => [{:name => :type, :type => :Topic}]
18
+
19
+ def create_association(type, scope = :default)
20
+ raise "type must be a Topic or Topic-Reference" unless type.is_a?(RTM::Topic) || type.is_a?(String) || type.is_a?(RTM::Locator)
21
+ if scope.eql? :default
22
+ return create_association_internal(get!(type))
23
+ else
24
+ raise "scope must be an Array" unless scope.is_a?(Array)
25
+ a = create_association_internal(get!(type))
26
+ scope.each {|s| a.add_scope(get!(s))}
27
+ return a
28
+ end
29
+ end
30
+
31
+ delegate :base_locator
32
+
33
+ property_set :association_types, :aka => :at, :type => :Topic, :wrap => true
34
+ property_set :role_types, :aka => [:association_role_types,:art,:rt], :type => :Topic, :wrap => true
35
+ property_set :name_types, :aka => [:topic_name_types,:tnt,:nt], :type => :Topic, :wrap => true
36
+ property_set :occurrence_types, :aka => :ot, :type => :Topic, :wrap => true
37
+
38
+ property_set :names, :aka => [:topic_names,:n], :type => :Name, :wrap => :true
39
+ property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => :true
40
+ property_set :roles, :aka => [:association_roles, :r], :type => :Role, :wrap => :true
41
+
42
+ def types
43
+ topics.select{|t| t.instances.size > 0}
44
+ end
45
+ alias :topic_types :types
46
+ def fast_types
47
+
48
+ end
49
+
50
+ # This fetches all topics who have no topic-instances (but they might be types for associations etc.).
51
+ # See indivduals
52
+ def non_types
53
+ topics.select{|t| t.instances.size == 0}
54
+ end
55
+
56
+ # This fetches all topics which are not type for something else (including topics, associations etc.).
57
+ # See non_types
58
+ def individuals
59
+ non_types.select{|t| t.associations_typed.size==0 && t.roles_typed.size==0 && t.names_typed.size==0 && t.occurrences_typed.size==0}
60
+ end
61
+ def instances
62
+ topics.select{|t| t.types.size > 0}
63
+ end
64
+ def non_instances
65
+ topics.reject{|t| t.types.size > 0}
66
+ end
67
+ def internal_occurrences
68
+ occurrences.select{|o| o.datatype != RTM::PSI[:IRI]}
69
+ end
70
+ def external_occurrences
71
+ occurrences.select{|o| o.datatype == RTM::PSI[:IRI]}
72
+ end
73
+
74
+ def self.create(base_locator, params={})
75
+ bl_uri = URI.parse(base_locator)
76
+ raise "The locator for the Topic Map must be absolute! \"#{base_locator}\" is not an absolute locator." unless bl_uri.absolute?
77
+ tm = self.wrap(TMDM::TopicMap.find_or_create_by_base_locator(base_locator))
78
+ yield tm if block_given?
79
+ tm
80
+ end
81
+
82
+ def self.topic_maps
83
+ TopicMaps.wrap(TMDM::TopicMap.find(:all))
84
+ end
85
+
86
+ # Resolves an IRI or fragment relative to the base_locator of this topic_map or an optional given alternative_base_locator
87
+ #
88
+ # Absolute IRIs are taken as is.
89
+ #
90
+ # Relative IRIs:
91
+ # If the base_locator is a directory (ends with "/") it is appended like a file, i.e. directly
92
+ # If the base_locator is a file it is appended as a fragment (with # in between)
93
+ def resolve(obj,alternative_base_locator=nil)
94
+ uri = obj.to_s
95
+ # TODO uri = URI.decode(obj.to_s) # this InvalidURIError somethimes :(
96
+ begin
97
+ uri_uri = URI.parse(uri)
98
+ rescue URI::InvalidComponentError => ice
99
+ warn "Catched an URI::InvalidComponentError for URI: #{uri}, message was \"#{ice.message}\"\n" +
100
+ "Will continue using the UNRESOLVED IRI, claiming it is absolute."
101
+ return uri
102
+ end
103
+ if uri_uri.absolute?
104
+ return uri_uri.to_s
105
+ end
106
+
107
+ uri = uri[1..-1] if uri[0] == "#"[0]
108
+ bl = alternative_base_locator || base_locator
109
+ if bl[-1,1] == "/"
110
+ return bl + uri
111
+ else
112
+ return bl + "#" + uri
113
+ end
114
+ end
115
+
116
+ #private
117
+
118
+ def _item_identifier(iid)
119
+ __getobj__.locators.find_by_reference(resolve(iid)) # doesn't work :( --> , :include => [ :construct ])
120
+ end
121
+ def _item_identifier!(iid)
122
+ __getobj__.locators.find_or_create_by_reference(resolve(iid)) # doesn't work :( --> , :include => [ :construct ])
123
+ end
124
+
125
+ def _subject_identifier(iid)
126
+ __getobj__.subject_identifiers.find_by_reference(iid, :include => :topic)
127
+ end
128
+ def _subject_identifier!(iid)
129
+ __getobj__.subject_identifiers.find_or_create_by_reference(iid, :include => :topic)
130
+ end
131
+
132
+ def _subject_locator(iid)
133
+ __getobj__.subject_locators.find_by_reference(iid, :include => :topic)
134
+ end
135
+ def _subject_locator!(iid)
136
+ __getobj__.subject_locators.find_or_create_by_reference(iid, :include => :topic)
137
+ end
138
+
139
+
140
+ # internal helper for by_item_identifier, doesn't wrap result
141
+ def _by_item_identifier(iid)
142
+ iid = RTM::LocatorHelpers.iid2iri(iid) if RTM::LocatorHelpers.is_a_iid?(iid)
143
+ ii = __getobj__.locators.find_by_reference(resolve(iid.to_s.sub(/^(\^|ii:)\s*/,""))) #, :include => [ :construct ])
144
+ return ii.construct if ii
145
+ nil
146
+ end
147
+ def _topic_by_item_identifier(iid)
148
+ t = _by_item_identifier(iid)
149
+ return nil unless t
150
+ return t if t.class.name =~ /::Topic$/ # only return something if the thing is a topic
151
+ nil
152
+ end
153
+
154
+ # internal helper for topic_by_item_identifier!, doesn't wrap result
155
+ def _topic_by_item_identifier!(iid)
156
+ iid = RTM::LocatorHelpers.iid2iri(iid) if RTM::LocatorHelpers.is_a_iid?(iid)
157
+ ii = __getobj__.locators.find_or_create_by_reference(resolve(iid.to_s.sub(/^(\^|ii:)\s*/,"")))#, :include => [ :construct ])
158
+ return ii.construct if ii.construct && ii.construct_type =~ /::Topic$/
159
+ top2 = _topic_by_subject_identifier(resolve(iid))
160
+ if top2
161
+ ii.construct = top2
162
+ else
163
+ ii.construct = create_topic.__getobj__
164
+ end
165
+ ii.save
166
+ ii.construct
167
+ end
168
+ # internal helper for topic_by_subject_identifier, doesn't wrap result
169
+ def _topic_by_subject_identifier(sid)
170
+ si = __getobj__.subject_identifiers.find_by_reference(resolve(sid.to_s.sub(/^(si:)\s*/,"")), :include => [ :topic ])
171
+ return si.topic if si
172
+ nil
173
+ end
174
+ # internal helper for topic_by_subject_identifier!, doesn't wrap result
175
+ def _topic_by_subject_identifier!(sid)
176
+ si = __getobj__.subject_identifiers.find_or_create_by_reference(resolve(sid.to_s.sub(/^(si:)\s*/,"")), :include => [ :topic ])
177
+ return si.topic if si.topic
178
+ begin
179
+ top2 = _by_item_identifier(sid)
180
+ rescue ActiveRecord::RecordNotFound => rnf
181
+ si.topic = create_topic.__getobj__
182
+ else
183
+ if top2 && top2.respond_to?(:subject_identifiers)
184
+ si.topic = top2
185
+ else
186
+ si.topic = create_topic.__getobj__
187
+ end
188
+ end
189
+ si.save
190
+ si.topic
191
+ end
192
+
193
+ # internal helper for topic_by_subject_locator, doesn't wrap result
194
+ def _topic_by_subject_locator(slo)
195
+ sl = __getobj__.subject_locators.find_by_reference(resolve(RTM::LocatorHelpers.slo2iri(slo))) #, :include => [ :topic ])
196
+ return sl.topic if sl
197
+ nil
198
+ end
199
+
200
+ # internal helper for topic_by_subject_locator!, doesn't wrap result
201
+ def _topic_by_subject_locator!(slo)
202
+ sl = __getobj__.subject_locators.find_or_create_by_reference(resolve(RTM::LocatorHelpers.slo2iri(slo)), :include => [ :topic ])
203
+ return sl.topic if sl.topic
204
+ sl.topic = create_topic.__getobj__
205
+ sl.save
206
+ sl.topic
207
+ end
208
+ def _topic_by_locator(iri)
209
+ raise "Locator may not be nil" unless iri
210
+ return iri if iri.is_a?(RTM::AR::TMDM::Topic)
211
+ return iri.__getobj__ if iri.is_a?(RTM::AR::Topic)
212
+ if iri.is_a?(Array)
213
+ return iri.map{|i| _topic_by_locator(i)}
214
+ end
215
+ if RTM::LocatorHelpers.is_a_slo?(iri)
216
+ _topic_by_subject_locator(iri)
217
+ elsif RTM::LocatorHelpers.is_a_iid?(iri)
218
+ _topic_by_item_identifier(iri)
219
+ #elsif URI.parse(iri).absolute?
220
+ # _topic_by_subject_identifier(iri)
221
+ else
222
+ _topic_by_subject_identifier(iri)
223
+ end
224
+ end
225
+
226
+ def _topic_by_locator!(iri)
227
+ raise "Locator may not be nil" unless iri
228
+ return iri if iri.is_a?(RTM::AR::TMDM::Topic)
229
+ return iri.__getobj__ if iri.is_a?(RTM::AR::Topic)
230
+ if iri.is_a?(Array)
231
+ return iri.map{|i| _topic_by_locator!(i)}
232
+ end
233
+ if RTM::LocatorHelpers.is_a_slo?(iri)
234
+ _topic_by_subject_locator!(iri)
235
+ elsif RTM::LocatorHelpers.is_a_iid?(iri)
236
+ _topic_by_item_identifier!(iri)
237
+ #elsif RTM::LocatorHelpers.is_a_sid?(iri)
238
+ # _topic_by_subject_identifier!(iri)
239
+ else
240
+ _topic_by_subject_identifier!(iri)
241
+ end
242
+ end
243
+
244
+ %w[topic association role name occurrence variant].each do |x|
245
+ eval <<-EOI
246
+ def _#{x}_by_id(id)
247
+ __getobj__.#{x}s.find(id)
248
+ end
249
+ EOI
250
+ end
251
+ public
252
+ # returns an item identifier from the topic_map if it exists
253
+ def item_identifier(iid)
254
+ ItemIdentifier.wrap(_item_identifier(iid))
255
+ end
256
+ # returns an item identififier from the topic_map or creates it if it doesn't exist
257
+ def item_identifier!(iid)
258
+ ItemIdentifier.wrap(_item_identifier!(iid))
259
+ end
260
+ alias :create_locator :item_identifier!
261
+
262
+ # Returns a topic map construct by it's item identifier or nil if not found.
263
+ # It's the equivalent to TMAPI's TopicMapObjectIndex.getTopicMapObjectBySourceLocator
264
+ def by_item_identifier(iid)
265
+ Construct.wrap(_by_item_identifier(iid))
266
+ end
267
+ alias :get_construct_by_item_identifier :by_item_identifier
268
+
269
+ # Returns a topic by it's item identifier. The topic will be created if not found.
270
+ def topic_by_item_identifier!(iid)
271
+ Topic.wrap(_topic_by_item_identifier!(iid))
272
+ end
273
+ alias :create_topic_by_item_identifier :topic_by_item_identifier!
274
+
275
+ # Returns a topic by it's subject identifier or nil if not found.
276
+ # It's the equivalent to TMAPI's TopicsIndex.getTopicBySubjectIdentifier.
277
+ def topic_by_subject_identifier(sid)
278
+ Topic.wrap(_topic_by_subject_identifier(sid))
279
+ end
280
+ alias :get_topic_by_subject_identifier :topic_by_subject_identifier
281
+
282
+ # returns a topic by it's subject identifier. The topic will be created if not found.
283
+ def topic_by_subject_identifier!(sid)
284
+ Topic.wrap(_topic_by_subject_identifier!(sid))
285
+ end
286
+ alias :create_topic_by_subject_identifier :topic_by_subject_identifier!
287
+
288
+ # Returns a topic by it's subject locator or nil if not found.
289
+ # It's the equivalent to TMAPI's TopicsIndex.getTopicBySubjectLocator
290
+ def topic_by_subject_locator(slo)
291
+ Topic.wrap(_topic_by_subject_locator(slo))
292
+ end
293
+ alias :get_topic_by_subject_locator :topic_by_subject_locator
294
+
295
+ # returns a topic by it's subject locator. The topic will be created if not found.
296
+ def topic_by_subject_locator!(slo)
297
+ Topic.wrap(_topic_by_subject_locator!(slo))
298
+ end
299
+ alias :create_topic_by_subject_locator :topic_by_subject_locator!
300
+
301
+ # Gets a topic from this topic map using its (relative) item identifier,
302
+ # its (absolute) subject identifier or its (absolute and by "=" prepended) subject locator)
303
+ #
304
+ # Returns nil if the Topic does not exist.
305
+ #
306
+ # It's also possible to pass a number, which is used to fetch a topic using the internal id. In this case, an exception is thrown, if the topic does not exist.
307
+ #
308
+ def topic_by_locator(iri)
309
+ return nil unless iri
310
+ return iri if iri.is_a? Topic
311
+ return topic_by_id(iri) if iri.is_a? Integer
312
+ if iri.is_a?(Array)
313
+ return iri.map{|i| topic_by_locator(i)}
314
+ end
315
+ # @tblc ||= {}
316
+ # t = @tblc[iri]
317
+ # return t if t
318
+ Topic.wrap(_topic_by_locator(iri)) # t = ...
319
+ # @tblc[iri] = t if t
320
+ # t
321
+ end
322
+ alias :get :topic_by_locator
323
+
324
+ # Gets a topic from this topic map using its (relative) item identifier,
325
+ # its (absolute) subject identifier or its (absolute and by "=" prepended) subject locator)
326
+ #
327
+ # If there is no topic with this item identifier, subject identifier or subject locator, it is created.
328
+ # Please be aware, the creation does not work with the internal (numeric) ids.
329
+ #
330
+ def topic_by_locator!(iri)
331
+ return nil unless iri
332
+ return iri if iri.is_a? Topic
333
+ return topic_by_id(iri) if iri.is_a? Integer
334
+ if iri.is_a?(Array)
335
+ return iri.map{|i| topic_by_locator!(i)}
336
+ end
337
+ # @tblc ||= {}
338
+ # t = @tblc[iri]
339
+ # return t if t
340
+ Topic.wrap(_topic_by_locator!(iri)) # t = ...
341
+ # @tblc[iri] = t
342
+ # t
343
+ end
344
+ alias :get! :topic_by_locator!
345
+
346
+ # Assumes identifier is an IRI (Locator or String) or an Array of IRIs.
347
+ # Returns an existing or created Topic or an Array of Topics according
348
+ # to the nature of the IRI.
349
+ #
350
+ # :call-seq:
351
+ # create_topic_by(identifier) -> Topic
352
+ # create_topic_by(identifier-Array) -> Array of Topics
353
+ #
354
+ def create_topic_by(identifier)
355
+ case identifier
356
+ when RTM::Locator
357
+ return create_topic_by_subject_identifier(identifier)
358
+ when String
359
+ reroute_for_create(identifier)
360
+ when Array
361
+ return identifier.map{|i| create_topic_by(i)}
362
+ else
363
+ return nil
364
+ end
365
+ end
366
+
367
+ # Identifies the identifier as item identifier, subject locator or
368
+ # subject identifier and calls create_topic_by_.. accordingly. Creates
369
+ # new topic if topic does not exist yet. Returns the topic.
370
+ #
371
+ # :call-seq:
372
+ # reroute_for_create(identifier) -> Topic
373
+ #
374
+ def reroute_for_create(identifier)
375
+ case identifier
376
+ when /^(\^|ii:)\s*(.*)/ #identifiers starts with ^ or ii:
377
+ create_topic_by_item_identifier(create_locator($2))
378
+ when /^(=|sl:)\s*(.*)/ #identifier starts with = or sl:
379
+ create_topic_by_subject_locator(create_locator($2))
380
+ when /^(si:)\s*(.*)/ #identifier starts with si:
381
+ create_topic_by_subject_identifier(create_locator($2))
382
+ else #identifier does not start with a special character
383
+ create_topic_by_subject_identifier(create_locator(identifier.lstrip))#lstrip: leading whitespace chars removed
384
+ end
385
+ end
386
+
387
+ # Identifies the identifier as item identifier, subject locator or
388
+ # subject identifier and calls get_construct/topic_by_.. accordingly.
389
+ # Returns the topic.
390
+ #
391
+ # :call-seq:
392
+ # reroute_for_get(identifier) -> Topic
393
+ #
394
+ def reroute_for_get(identifier)
395
+ case identifier
396
+ when /^(si:|sl:|ii:|=|^)$/
397
+ return nil
398
+ when /^(\^|ii:)\s*(.*)/ #identifiers starts with ^ or ii:
399
+ get_construct_by_item_identifier(create_locator($2))
400
+ when /^(=|sl:)\s*(.+)/ #identifier starts with = or sl:
401
+ get_topic_by_subject_locator(create_locator($2))
402
+ when /^(si:)\s*(.+)/ #identifier starts with si:
403
+ get_topic_by_subject_identifier(create_locator($2))
404
+ else #identifier does not start with a special character
405
+ get_topic_by_subject_identifier(create_locator(identifier.lstrip)) #lstrip: leading whitespace chars removed
406
+ end
407
+ end
408
+
409
+ %w[topic association role name occurrence variant].each do |x|
410
+ eval <<-EOI
411
+ def #{x}_by_id(id)
412
+ #{x.camelize}.wrap(_#{x}_by_id(id))
413
+ end
414
+ alias :#{x[0].chr}id :#{x}_by_id
415
+ EOI
416
+ end
417
+
418
+ # I am not sure if this is at all correct. TMDM doesn't say a word about it
419
+ # and the approach to compare topic maps is CXTM. I chose this because we
420
+ # should (at least at the time of writing) have only one topic with a given
421
+ # base locator in RTM. If you don't like it, drop me a mail and explain why
422
+ # and propose something better.
423
+ equality [:base_locator]
424
+
425
+ def children
426
+ topics.to_a + associations.to_a
427
+ end
428
+
429
+ def literal_index
430
+ LiteralIndex.new(self)
431
+ end
432
+ #def type_instance_index
433
+ #end
434
+ end
435
+ end