rtm 0.1.0 → 0.1.1

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.
@@ -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