rtm 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ module RTM::AR
2
+ class TMSetDelegator < TMDelegator
3
+ include Enumerable
4
+ # attr_reader :content_class_name
5
+ def initialize(obj,parent,type)
6
+ @obj = obj
7
+ @parent = parent
8
+ @type = type
9
+ end
10
+
11
+ # This class method wraps a Set completely while the instance method wraps one single contained object
12
+ def self.wrap(obj, parent=nil, type=nil)
13
+ return nil unless obj
14
+ raise "Double wrapping" if obj.respond_to?(:__getobj__)
15
+ self.new(obj, parent, type)
16
+ end
17
+
18
+ def add(obj)
19
+ return unless obj
20
+
21
+ old = @obj.detect { |x| x == obj } # can't that be done easier?
22
+ if old
23
+ old.merge obj
24
+ else
25
+ if obj.respond_to? :__getobj__
26
+ @obj << obj.__getobj__
27
+ else
28
+ case @type
29
+ when :Topic
30
+ @obj << @parent.topic_map.get!(obj).__getobj__
31
+ @parent.__getobj__.reload
32
+
33
+ when :ItemIdentifier
34
+ if @parent.class.name.to_s =~ /::Topic$/
35
+ tbi = @parent.topic_map._item_identifier(obj)
36
+ if tbi && tmc=tbi.topic_map_construct
37
+ if tmc.class.name =~ /::Topic$/
38
+ return @parent if tmc == @parent.__getobj__
39
+ result = Topic.wrap(tmc).merge @parent
40
+ return result
41
+ else
42
+ raise "Duplicate Item Identifier"
43
+ end
44
+ end
45
+ tbsi = @parent.topic_map._subject_identifier(obj.to_s)
46
+ if tbsi
47
+ if t=tbsi.topic
48
+ return @parent if t == @parent.__getobj__
49
+ result = Topic.wrap(t).merge @parent
50
+ # after merging, we still need to add the II
51
+ result.__getobj__.item_identifiers.create(:reference => obj.to_s, :topic_map_id => @parent.topic_map.__getobj__.id)
52
+ return result
53
+ end
54
+ end
55
+ end
56
+ result = @obj << @parent.topic_map._item_identifier!(obj.to_s)
57
+ return result
58
+
59
+ when :SubjectIdentifier
60
+ # check for existing item identifier
61
+ tbi = @parent.topic_map._item_identifier(obj)
62
+ if tbi && tmc=tbi.topic_map_construct
63
+ if tmc.class.name =~ /::Topic$/
64
+ return @parent if tmc == @parent.__getobj__
65
+ result = Topic.wrap(tmc).merge @parent
66
+ # after merging, we still need to add the SI
67
+ result.__getobj__.subject_identifiers.create(:reference => obj.to_s, :topic_map_id => @parent.topic_map.__getobj__.id)
68
+ return result
69
+ else
70
+ warn("This subject identifier IRI already belongs to another topic map construct (not a topic)")
71
+ end
72
+ end
73
+ # check for existing subject identifier
74
+ tbsi = @parent.topic_map._subject_identifier(obj.to_s)
75
+ if tbsi
76
+ if true && t=tbsi.topic #the single = is intentional, the "true &&" just makes netbeans not raise a warning
77
+ return @parent if t == @parent.__getobj__
78
+ result = Topic.wrap(t).merge @parent
79
+ return result
80
+ end
81
+ end
82
+ @obj.create(:reference => obj.to_s, :topic_map_id => @parent.topic_map.__getobj__.id)
83
+
84
+ when :SubjectLocator
85
+ tbsl = @parent.topic_map._subject_locator(obj.to_s)
86
+ if tbsl
87
+ if true && t=tbsl.topic #the single = is intentional, the "true &&" just makes netbeans not raise a warning
88
+ return @parent if t == @parent.__getobj__
89
+ result = Topic.wrap(t).merge @parent
90
+ return result
91
+ end
92
+ end
93
+ @obj.create(:reference => obj.to_s, :topic_map_id => @parent.topic_map.__getobj__.id)
94
+
95
+ end
96
+ end
97
+ end
98
+ end
99
+ alias :<< :add
100
+
101
+ def add_all(objs)
102
+ return unless objs
103
+ objs.each {|obj| add(obj)}
104
+ true
105
+ end
106
+
107
+ def each(&b)
108
+ @obj.each { |e| yield wrap(e)}
109
+ end
110
+
111
+ def size
112
+ @obj.size
113
+ end
114
+ alias :length :size
115
+
116
+ def empty?
117
+ @obj.empty?
118
+ end
119
+
120
+ def delete(obj)
121
+ obj = obj.__getobj__ if obj.respond_to? :__getobj__
122
+ case @type
123
+ when :ItemIdentifier, :SubjectIdentifier, :SubjectLocator
124
+ obj = @obj.find_by_reference(@parent.topic_map.resolve(obj.to_s)) if obj.is_a? String
125
+ end
126
+ @obj.delete(obj)
127
+ # item_identifiers: remove also from topicMap
128
+ #removed_event obj if respond_to? :removed_event
129
+ end
130
+ alias :remove :delete
131
+
132
+ def include?(obj)
133
+ return @obj.include?(obj)
134
+ #@obj.each { |e| return true if e == obj } # T#ODO support for get
135
+ #false
136
+ end
137
+
138
+ def first
139
+ wrap(@obj.entries.first)
140
+ end
141
+ def last
142
+ wrap(@obj.entries.last)
143
+ end
144
+
145
+ def to_s
146
+ "[#{@obj.entries.map { |e| wrap(e).to_s }.join(", ") }]"
147
+ end
148
+ def [](i)
149
+ wrap(@obj[i])
150
+ end
151
+
152
+ def content_class
153
+ # @content_class ||= RTM.const_get(@content_class_name)
154
+ @content_class ||= RTM.const_get("#{@content_class_name}MemImpl")
155
+ end
156
+
157
+ def find(*args)
158
+ res = @obj.find(*args)
159
+ if res.respond_to? :each
160
+ TopicMapConstructs.wrap(res)
161
+ else
162
+ TopicMapConstruct.wrap(res)
163
+ end
164
+ end
165
+
166
+ def &(other)
167
+ @obj.to_a & other.to_a
168
+ end
169
+
170
+ alias :old_method_missing :method_missing
171
+ def method_missing(method_name, *args)
172
+ if @obj.size > 0 && first.respond_to?(method_name) && (![:__getobj__, :__setobj__].include?(method_name))
173
+ a = []
174
+ inject(a) {|all,single| all << single.send(method_name, *args)}
175
+ a
176
+ else
177
+ old_method_missing(method_name, *args)
178
+ end
179
+ end
180
+
181
+ alias :old_respond_to? :respond_to?
182
+ def respond_to?(method_name)
183
+ resp = old_respond_to?(method_name)
184
+ return resp if resp # i.e. if true
185
+ return false if [:__getobj__, :__setobj__].include?(method_name)
186
+ # ... and ask first child otherwise
187
+ @obj.size > 0 && first.respond_to?(method_name)
188
+ end
189
+
190
+ # TMSetDelegator#to_a doesn't help as thought, but maybe we come back to that l8r...
191
+ #def to_a
192
+ # @obj.map {|o| wrap(o)}
193
+ #end
194
+ end
195
+ end
@@ -291,4 +291,3 @@ module RTM::AR
291
291
  end
292
292
  end
293
293
  end
294
-
@@ -0,0 +1,153 @@
1
+ module RTM::AR
2
+ class Topic < TopicMapConstruct
3
+ include RTM::Topic
4
+ wrapper_cache
5
+
6
+ parent :topic_map
7
+ property_set :subject_identifiers, :aka => [:si,:sid], :type => :SubjectIdentifier, :wrap => true,
8
+ #:create => :subject_identifier, :create_aka => [:csi,:csid],
9
+ #:create_args => [{:name => :reference, :type => :String}]
10
+ :add => true, :remove => true
11
+ property_set :subject_locators, :aka => [:sl,:slo], :type => :SubjectLocator, :wrap => true,
12
+ #:create => :subject_locator, :create_aka => [:csl, :cslo],
13
+ #:create_args => [{:name => :reference, :type => :String}]
14
+ :add => true, :remove => true
15
+
16
+ property :reified, :computable => true, :type => :Reifiable, :wrap => true
17
+
18
+ property_set :topic_names, :aka => [:n, :names], :type => :TopicName, :wrap => true,
19
+ :create => :topic_name, :create_aka => [:create_name, :cn],
20
+ :create_args => [ {:name => :value, :type => :String}, {:name => :type, :type => :Topic, :optional => true}, {:name => :scope, :type => :Collection} ]
21
+
22
+ property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => true,
23
+ :create => :occurrence, :create_aka => :co,
24
+ :create_args => [ {:name => :value, :type => [:String, :Locator]}, {:name => :type, :type => :Topic}, {:name => :scope, :type => :Collection} ]
25
+
26
+ def characteristics
27
+ topic_names.to_a + occurrences.to_a
28
+ end
29
+ def internal_occurrences
30
+ occurrences.select{|o| o.datatype != RTM::PSI[:IRI]}
31
+ end
32
+ def external_occurrences
33
+ occurrences.select{|o| o.datatype == RTM::PSI[:IRI]}
34
+ end
35
+
36
+ property_set :roles, :aka => [:r, :roles_played, :association_roles], :computable => true, :type => :AssociationRole, :wrap => true
37
+
38
+ property_set :associations, :aka => [:a, :associations_played], :type => :Association, :wrap => true
39
+
40
+ property_set :scoped_objects, :type => :TopicMapConstruct, :wrap => true
41
+ property_set :scoped_associations, :type => :Association, :wrap => true
42
+ property_set :scoped_topic_names, :aka => :scoped_names, :type => :TopicName, :wrap => true
43
+ property_set :scoped_variants, :type => :Variant, :wrap => true
44
+ property_set :scoped_occurrences, :type => :Occurrence, :wrap => true
45
+
46
+ property_set :associations_typed, :aka => :at, :type => :Association, :wrap => true
47
+ property_set :association_roles_typed, :aka => [:roles_typed,:art,:rt], :type => :AssociationRole, :wrap => true
48
+ property_set :topic_names_typed, :aka => [:names_typed,:tnt,:nt], :type => :TopicName, :wrap => true
49
+ property_set :occurrences_typed, :aka => :ot, :type => :Occurrence, :wrap => true
50
+
51
+ # maybe these pairs could be declared each with a single statement and a reversible option
52
+ index_property_set :types, :type => :Topic, :rule => {
53
+ :transitive => false,
54
+ :role_type => RTM::PSI[:instance],
55
+ :association_type => RTM::PSI[:type_instance],
56
+ :association_arity => 2,
57
+ :other_role_type => RTM::PSI[:type],
58
+ :infer_other => :supertypes,
59
+ :add => :type,
60
+ }
61
+ index_property_set :instances, :type => :Topic, :rule => {
62
+ :transitive => false,
63
+ :role_type => RTM::PSI[:type],
64
+ :association_type => RTM::PSI[:type_instance],
65
+ :association_arity => 2,
66
+ :other_role_type => RTM::PSI[:instance],
67
+ :infer => :subtypes,
68
+ :add => :instance,
69
+ }
70
+ index_property_set :supertypes, :type => :Topic, :rule => {
71
+ :transitive => true,
72
+ :role_type => RTM::PSI[:subtype],
73
+ :association_type => RTM::PSI[:supertype_subtype],
74
+ :association_arity => 2,
75
+ :other_role_type => RTM::PSI[:supertype],
76
+ :add => :supertype,
77
+ }
78
+ index_property_set :subtypes, :type => :Topic, :rule => {
79
+ :transitive => true,
80
+ :role_type => RTM::PSI[:supertype],
81
+ :association_type => RTM::PSI[:supertype_subtype],
82
+ :association_arity => 2,
83
+ :other_role_type => RTM::PSI[:subtype],
84
+ :add => :subtype,
85
+ }
86
+
87
+ def [](topicref)
88
+ topicref =~ /^(-\s*)?(.+)?$/
89
+ if $1
90
+ nametype = $2 || RTM::PSI[:name_type]
91
+ names.select{|n| n.type == topic_map.get(nametype)}
92
+ else
93
+ raise "No occurrence type given" unless $2 and !$2.empty?
94
+ occurrences.select{|o| o.type == topic_map.get($2)}
95
+ end
96
+ end
97
+
98
+ def []=(topicref, value)
99
+ topicref =~ /^(-\s*)?(.+)?$/
100
+ if $1
101
+ nametype = $2 || topic_map.get!(RTM::PSI[:name_type])
102
+ n = create_name(value, nametype)
103
+ n
104
+ else
105
+ raise "No occurrence type given" unless $2
106
+ o = create_occurrence(value, $2)
107
+ o
108
+ end
109
+ end
110
+
111
+ def counterparts
112
+ self.roles.map{|sr| sr.parent.roles.reject{|r| r.id==sr.id}}
113
+ end
114
+ def counterplayers
115
+ self.counterparts.map{|r| r.player}
116
+ end
117
+
118
+ # class:Topic equality, http://www.isotopicmaps.org/sam/sam-model/#d0e1029
119
+ #
120
+ # This method is all crap. These ActiveRecord Arrays can't be intersected,
121
+ # so I map the identifiers to their reference which seems long winded.
122
+ # Still I find it better than using their ID in the long.
123
+ #
124
+ def ==(o)
125
+ return false unless o
126
+ # Two topic items are equal if they have:
127
+ # * at least one equal string in their [subject identifiers] properties,
128
+ # -> test if intersection are > 0
129
+ my_si = self.subject_identifiers.map{|si| si.reference}
130
+ ot_si = o.subject_identifiers.map{|si| si.reference}
131
+ return true if ( my_si & ot_si).size > 0
132
+
133
+ # * at least one equal string in their [item identifiers] properties,
134
+ my_ii = self.item_identifiers.map{|ii| ii.reference}
135
+ ot_ii = o.item_identifiers.map{|ii| ii.reference}
136
+ return true if (my_ii & ot_ii).size > 0
137
+
138
+ # * at least one equal string in their [subject locators] properties,
139
+ my_sl = self.subject_locators.map{|sl| sl.reference}
140
+ ot_sl = o.subject_locators.map{|sl| sl.reference}
141
+ return true if (my_sl & ot_sl).size > 0
142
+
143
+ # * an equal string in the [subject identifiers] property of the one topic item and the [item identifiers] property of the other, or
144
+ return true if (my_si & ot_ii).size > 0
145
+ return true if (my_ii & ot_si).size > 0
146
+
147
+ # * the same information item in their [reified] properties.
148
+ return true if self.reified != nil && o.reified != nil && (self.reified == o.reified)
149
+ # ... otherwise
150
+ false
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,285 @@
1
+ module RTM::AR
2
+ class TopicMap < Reifiable
3
+ include RTM::TopicMap
4
+ wrapper_cache
5
+ property_set :topics, :aka => :t, :type => :Topic, :wrap => true,
6
+ :create => :topic, :create_aka => :ct
7
+
8
+ property_set :associations, :aka => [:a, :assocs], :type => :Association, :wrap => true,
9
+ :create => :association, :create_aka => :ca,
10
+ :create_args => [{:name => :type, :type => :Topic}]
11
+
12
+
13
+ delegate :base_locator
14
+
15
+ property_set :association_types, :aka => :at, :type => :Topic, :wrap => true
16
+ property_set :association_role_types, :aka => [:role_types,:art,:rt], :type => :Topic, :wrap => true
17
+ property_set :topic_name_types, :aka => [:name_types,:tnt,:nt], :type => :Topic, :wrap => true
18
+ property_set :occurrence_types, :aka => :ot, :type => :Topic, :wrap => true
19
+
20
+ property_set :topic_names, :aka => [:names,:n], :type => :TopicName, :wrap => :true
21
+ property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => :true
22
+ property_set :association_roles, :aka => [:roles, :r], :type => :AssociationRole, :wrap => :true
23
+
24
+ def types
25
+ topics.select{|t| t.instances.size > 0}
26
+ end
27
+ alias :topic_types :types
28
+ def fast_types
29
+
30
+ end
31
+
32
+ # This fetches all topics who have no topic-instances (but they might be types for associations etc.).
33
+ # See indivduals
34
+ def non_types
35
+ topics.select{|t| t.instances.size == 0}
36
+ end
37
+
38
+ # This fetches all topics which are not type for something else (including topics, associations etc.).
39
+ # See non_types
40
+ def individuals
41
+ non_types.select{|t| t.associations_typed.size==0 && t.roles_typed.size==0 && t.names_typed.size==0 && t.occurrences_typed.size==0}
42
+ end
43
+ def instances
44
+ topics.select{|t| t.types.size > 0}
45
+ end
46
+ def non_instances
47
+ topics.reject{|t| t.types.size > 0}
48
+ end
49
+ def internal_occurrences
50
+ occurrences.select{|o| o.datatype != RTM::PSI[:IRI]}
51
+ end
52
+ def external_occurrences
53
+ occurrences.select{|o| o.datatype == RTM::PSI[:IRI]}
54
+ end
55
+
56
+ def self.create(base_locator, params={})
57
+ tm = self.wrap(TMDM::TopicMap.find_or_create_by_base_locator(base_locator))
58
+ yield tm if block_given?
59
+ tm
60
+ end
61
+
62
+ def self.topic_maps
63
+ TopicMaps.wrap(TMDM::TopicMap.find(:all))
64
+ end
65
+
66
+ # Resolves an IRI or fragment relative to the base_locator of this topic_map or an optional given alternative_base_locator
67
+ #
68
+ # Absolute IRIs are taken as is.
69
+ #
70
+ # Relative IRIs:
71
+ # If the base_locator is a directory (ends with "/") it is appended like a file, i.e. directly
72
+ # If the base_locator is a file it is appended as a fragment (with # in between)
73
+ def resolve(obj,alternative_base_locator=nil)
74
+ uri = obj.to_s
75
+ # TODO uri = URI.decode(obj.to_s) # this InvalidURIError somethimes :(
76
+ begin
77
+ uri_uri = URI.parse(uri)
78
+ rescue URI::InvalidComponentError => ice
79
+ warn "Catched an URI::InvalidComponentError for URI: #{uri}, message was \"#{ice.message}\"\n" +
80
+ "Will continue using the UNRESOLVED IRI, claiming it is absolute."
81
+ return uri
82
+ end
83
+ if uri_uri.absolute?
84
+ return uri_uri.to_s
85
+ end
86
+
87
+ uri = uri[1..-1] if uri[0] == "#"[0]
88
+ bl = alternative_base_locator || base_locator
89
+ if bl[-1,1] == "/"
90
+ return bl + uri
91
+ else
92
+ return bl + "#" + uri
93
+ end
94
+ end
95
+
96
+ #private
97
+
98
+ def _item_identifier(iid)
99
+ __getobj__.locators.find_by_reference(resolve(iid)) # doesn't work :( --> , :include => [ :topic_map_construct ])
100
+ end
101
+ def _item_identifier!(iid)
102
+ __getobj__.locators.find_or_create_by_reference(resolve(iid)) # doesn't work :( --> , :include => [ :topic_map_construct ])
103
+ end
104
+
105
+ def _subject_identifier(iid)
106
+ __getobj__.subject_identifiers.find_by_reference(iid, :include => :topic)
107
+ end
108
+ def _subject_identifier!(iid)
109
+ __getobj__.subject_identifiers.find_or_create_by_reference(iid, :include => :topic)
110
+ end
111
+
112
+ def _subject_locator(iid)
113
+ __getobj__.subject_locators.find_by_reference(iid, :include => :topic)
114
+ end
115
+ def _subject_locator!(iid)
116
+ __getobj__.subject_locators.find_or_create_by_reference(iid, :include => :topic)
117
+ end
118
+
119
+
120
+ # internal helper for by_item_identifier, doesn't wrap result
121
+ def _by_item_identifier(iid)
122
+ ii = __getobj__.locators.find_by_reference(resolve(iid)) #, :include => [ :topic_map_construct ])
123
+ return ii.topic_map_construct if ii
124
+ nil
125
+ end
126
+ def _topic_by_item_identifier(iid)
127
+ t = _by_item_identifier(iid)
128
+ return nil unless t
129
+ return t if t.class.name =~ /::Topic$/ # only return something if the thing is a topic
130
+ nil
131
+ end
132
+
133
+ # internal helper for topic_by_item_identifier!, doesn't wrap result
134
+ def _topic_by_item_identifier!(iid)
135
+ ii = __getobj__.locators.find_or_create_by_reference(resolve(iid))#, :include => [ :topic_map_construct ])
136
+ return ii.topic_map_construct if ii.topic_map_construct && ii.topic_map_construct_type =~ /::Topic$/
137
+ top2 = _topic_by_subject_identifier(resolve(iid))
138
+ if top2
139
+ ii.topic_map_construct = top2
140
+ else
141
+ ii.topic_map_construct = create_topic.__getobj__
142
+ end
143
+ ii.save
144
+ ii.topic_map_construct
145
+ end
146
+ # internal helper for topic_by_subject_identifier, doesn't wrap result
147
+ def _topic_by_subject_identifier(sid)
148
+ si = __getobj__.subject_identifiers.find_by_reference(sid, :include => [ :topic ])
149
+ return si.topic if si
150
+ nil
151
+ end
152
+ # internal helper for topic_by_subject_identifier!, doesn't wrap result
153
+ def _topic_by_subject_identifier!(sid)
154
+ si = __getobj__.subject_identifiers.find_or_create_by_reference(sid, :include => [ :topic ])
155
+ return si.topic if si.topic
156
+ begin
157
+ top2 = _by_item_identifier(sid)
158
+ rescue ActiveRecord::RecordNotFound => rnf
159
+ si.topic = create_topic.__getobj__
160
+ else
161
+ if top2 && top2.respond_to?(:subject_identifiers)
162
+ si.topic = top2
163
+ else
164
+ si.topic = create_topic.__getobj__
165
+ end
166
+ end
167
+ si.save
168
+ si.topic
169
+ end
170
+
171
+ # internal helper for topic_by_subject_locator, doesn't wrap result
172
+ def _topic_by_subject_locator(slo)
173
+ sl = __getobj__.subject_locators.find_by_reference(RTM::LocatorHelpers.slo2iri(slo)) #, :include => [ :topic ])
174
+ return sl.topic if sl
175
+ nil
176
+ end
177
+
178
+ # internal helper for topic_by_subject_locator!, doesn't wrap result
179
+ def _topic_by_subject_locator!(slo)
180
+ sl = __getobj__.subject_locators.find_or_create_by_reference(RTM::LocatorHelpers.slo2iri(slo), :include => [ :topic ])
181
+ return sl.topic if sl.topic
182
+ sl.topic = create_topic.__getobj__
183
+ sl.save
184
+ sl.topic
185
+ end
186
+ def _topic_by_locator(iri)
187
+ raise "Locator may not be nil" unless iri
188
+ if RTM::LocatorHelpers.is_a_slo?(iri)
189
+ _topic_by_subject_locator(iri)
190
+ elsif URI.parse(iri).absolute?
191
+ _topic_by_subject_identifier(iri)
192
+ else
193
+ _topic_by_item_identifier(iri)
194
+ end
195
+ end
196
+
197
+ def _topic_by_locator!(iri)
198
+ raise "Locator may not be nil" unless iri
199
+ if RTM::LocatorHelpers.is_a_slo?(iri)
200
+ _topic_by_subject_locator!(iri)
201
+ elsif URI.parse(iri).absolute?
202
+ _topic_by_subject_identifier!(iri)
203
+ else
204
+ _topic_by_item_identifier!(iri)
205
+ end
206
+ end
207
+
208
+ public
209
+ # returns an item identifier from the topic_map if it exists
210
+ def item_identifier(iid)
211
+ ItemIdentifier.wrap(_item_identifier(iid))
212
+ end
213
+ # returns an item identififier from the topic_map or creates it if it doesn't exist
214
+ def item_identifier!(iid)
215
+ ItemIdentifier.wrap(_item_identifier!(iid))
216
+ end
217
+ alias :create_locator :item_identifier!
218
+ # Returns a topic map construct by it's item identifier or nil if not found.
219
+ # It's the equivalent to TMAPI's TopicMapObjectIndex.getTopicMapObjectBySourceLocator
220
+ def by_item_identifier(iid)
221
+ TopicMapConstruct.wrap(_by_item_identifier(iid))
222
+ end
223
+ # Returns a topic by it's item identifier. The topic will be created if not found.
224
+ def topic_by_item_identifier!(iid)
225
+ Topic.wrap(_topic_by_item_identifier!(iid))
226
+ end
227
+ # Returns a topic by it's subject identifier or nil if not found.
228
+ # It's the equivalent to TMAPI's TopicsIndex.getTopicBySubjectIdentifier.
229
+ def topic_by_subject_identifier(sid)
230
+ Topic.wrap(_topic_by_subject_identifier(sid))
231
+ end
232
+ # returns a topic by it's subject identifier. The topic will be created if not found.
233
+ def topic_by_subject_identifier!(sid)
234
+ Topic.wrap(_topic_by_subject_identifier!(sid))
235
+ end
236
+ # Returns a topic by it's subject locator or nil if not found.
237
+ # It's the equivalent to TMAPI's TopicsIndex.getTopicBySubjectLocator
238
+ def topic_by_subject_locator(slo)
239
+ Topic.wrap(_topic_by_subject_locator(slo))
240
+ end
241
+ # returns a topic by it's subject locator. The topic will be created if not found.
242
+ def topic_by_subject_locator!(slo)
243
+ Topic.wrap(_topic_by_subject_locator!(slo))
244
+ end
245
+
246
+ # Gets a topic from this topic map using its (relative) item identifier,
247
+ # its (absolute) subject identifier or its (absolute and by "=" prepended) subject locator)
248
+ #
249
+ # Returns nil if the Topic does not exist.
250
+ #
251
+ def topic_by_locator(iri)
252
+ return iri if iri.is_a? Topic
253
+ # @tblc ||= {}
254
+ # t = @tblc[iri]
255
+ # return t if t
256
+ t = Topic.wrap(_topic_by_locator(iri))
257
+ # @tblc[iri] = t if t
258
+ # t
259
+ end
260
+ alias :get :topic_by_locator
261
+
262
+ # Gets a topic from this topic map using its (relative) item identifier,
263
+ # its (absolute) subject identifier or its (absolute and by "=" prepended) subject locator)
264
+ #
265
+ # If there is no topic with this item identifier, subject identifier or subject locator, it is created.
266
+ #
267
+ def topic_by_locator!(iri)
268
+ return iri if iri.is_a? Topic
269
+ # @tblc ||= {}
270
+ # t = @tblc[iri]
271
+ # return t if t
272
+ t = Topic.wrap(_topic_by_locator!(iri))
273
+ # @tblc[iri] = t
274
+ # t
275
+ end
276
+ alias :get! :topic_by_locator!
277
+
278
+ # I am not sure if this is at all correct. TMDM doesn't say a word about it
279
+ # and the approach to compare topic maps is CXTM. I chose this because we
280
+ # should (at least at the time of writing) have only one topic with a given
281
+ # base locator in RTM. If you don't like it, drop me a mail and explain why
282
+ # and propose something better.
283
+ equality [:base_locator]
284
+ end
285
+ end