# Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig. # License: Apache License, Version 2.0 module Java::OrgTmapiCore::TopicMap URI_PATTERN = ":" include RTM::TopicMap extend Superiseable attr_accessor :prefixes #hash attr_reader :base_iri def base_iri=(base_iri) class << self;undef :base_iri=;end @base_iri = base_iri end alias :base_locator :base_iri # Adds the identifier (key) and reference (value) to the prefixes-Hash and # returns all prefixes. Qnames consisting of "identifier:localpart" # will be converted to "referencelocalpart". # # The identifier and reference must be Strings. # # Example: # add_prefix("tml","http://www.topicmapslab.de/") # # :call-seq: # add_prefix(identifier,reference) -> Hash # def add_prefix(identifier,reference) unless identifier.is_a?(String) && reference.is_a?(String) raise("add_prefix: identifier and reference must be Strings") end #if (identifier == "sl") || (identifier == "si") || (identifier == "ii") # warn("prefixes: identifier is either sl, si or ii") #end prefixes[identifier]= reference prefixes end # Deletes a prefix (given the identifier) from the prefixes-Hash and # returnes the remaining prefixes. # # If reference is given, it must match the value of the identifier (key) # in the prefixes-Hash - # if not, the prefix is not deleted. # # The result may be empty. # # :call-seq: # delete_prefix(identifier) -> Hash # delete_prefix(identifier,reference) -> Hash # def delete_prefix(identifier,reference = :any) #warn("delete_prefix: identifier not found") unless prefixes.has_key?(identifier) #warn("delete_prefix: reference does not match") unless reference == :any || reference == prefixes[identifier] if reference == :any prefixes.delete(identifier) else prefixes.delete_if{|k,v| k == identifier && v == reference} end prefixes end alias :del_prefix :delete_prefix # Returns the prefixes that match the given the # identifier and/or reference. # # :call-seq: # find_prefix -> Hash # find_prefix({:identifier => String}) -> Hash # find_prefix({:reference => String}) -> Hash # find_prefix({:identifier => String, :reference => String}) -> Hash # def find_prefix(filter = {}) #TODO Specs identifier = filter[:identifier] reference = filter[:reference] if identifier && reference return prefixes.select{|k,v| k == identifier && v == reference} end if identifier return prefixes.select{|k,v| k == identifier} elsif reference return prefixes.select{|k,v| v == reference} else prefixes end end private def show_prefixes #TODO to be implemented? prefixes end public superised # Returns nil. # # :call-seq: # parent -> nil # def parent getParent end alias :reverse_children :parent # Returns all Associations and Topics contained in this TopicMap. # # :call-seq: # children -> Array of Associations and/or Topics # def children getAssociations.to_a + getTopics.to_a end alias :reverse_parent :children superised # Creates an Association in this TopicMap. # # Type specifies the Association type. # # If given, scope specifies the association scope; # else the Association is in the unconstrained scope. # Scope must be an Array of identifiers. # # The last argument - a roles-Hash - is optional. If given, # all specified roles will be created additionally. A role is # specified by the role type: the key in the Hash and # the role player: the corresponding value in the Hash. # # Each identifier (type, scope-identifier, roletype-identifier and # player-identifier) may be a topic reference. # # :call-seq: # create_association(type) -> Association # create_association(type, scope-Array) -> Association # create_association(type, roletype-identifier => player-identifier, ...) -> Association # create_association(type, scope-Array, roletype-identifier => player-identifier, ...) -> Association # def create_association(type, *args) roles = {} if args.size == 0 # no args -> no scope and no roles given scope = :ucs elsif args.size == 1 # args may be an array (scope) or hash (roles) argument = args[0] if argument.is_a?(Array) scope = argument elsif argument.is_a?(Hash) scope = :ucs roles = argument else raise("create_association: arguments after type may be an Array or Hash") end else # several arguments after type argument1 = args[0] argument2 = args[1] if argument1.is_a?(Array) scope = argument1 roles = argument2 elsif argument1.is_a?(Hash) scope = :ucs roles = argument1 else raise("create_association: arguments after type may be an Array or Hash") end end raise("create_association: type must be a Topic or Topic-Reference") unless type.is_a?(Java::OrgTmapiCore::Topic) || type.is_a?(String) || type.is_a?(Java::OrgTmapiCore::Locator) if scope == :ucs assoc = createAssociation(get!(type),[].to_java(org.tmapi.core.Topic)) else raise("create_association: scope must be an Array") unless scope.is_a?(Array) assoc = scope.empty? ? createAssociation(get!(type),[].to_java(org.tmapi.core.Topic)) : createAssociation(get!(type),get!(scope)) end raise("create_association: roles must be a Hash") unless roles.is_a?(Hash) roles.each do |k,v| if v.is_a?(Array) v.each{|player| assoc.create_role(k,player)} else assoc.create_role(k,v) end end yield assoc if block_given? return assoc end # Calls TMAPI TopicMap.getIndex which # returns the index for the TypeInstanceIndex. # # :call-seq: # type_instance_index -> TypeInstanceIndex # def type_instance_index # Equals _index(:TypeInstanceIndex), which doesn't work yet getIndex(org.tmapi.index.TypeInstanceIndex.java_class) end # Calls TMAPI TopicMap.getIndex which # returns the index for the LiteralIndex. # # :call-seq: # literal_index -> LiteralIndex # def literal_index # Equals _index(:LiteralIndex), which doesn't work yet getIndex(org.tmapi.index.LiteralIndex.java_class) end # Calls TMAPI TopicMap.getIndex which # returns the index for the ScopedIndex. # # :call-seq: # scoped_index -> ScopedIndex # def scoped_index # Equals _index(:ScopedIndex), which doesn't work yet getIndex(org.tmapi.index.ScopedIndex.java_class) end # Returns all topics in the topic map that are used as instance # in an "type-instance"-relationship. # # The optional argument specifies the type the instances should have. # Type may be a topic reference. This method equals topics(type). # # The result may be empty. # # :call-seq: # instances -> Array of Topics # instances(type) -> Array of Topics # def instances(type = :any) return types.map{|t| t.instances.to_a}.flatten.uniq if type == :any type = self.get(type) unless type.is_a?(RTM::Topic) return type ? type_instance_index.getTopics(type).to_a : [] end # Returns all topics in the topic map that are used as type # in an "type-instance"-relationship. # # :call-seq: # types -> Set of Topics # def types type_instance_index.getTopicTypes end superised # Returns all associations contained in this topic map. # # The optional argument specifies the type the associations should have, # i.e. all instances of the specified type are returned. Type may be a # topic reference. # # The return value may be empty. # # :call_spec: # associations -> Set of Associations # associations(type) -> Array of Associations # def associations(type = :any) return getAssociations if type == :any raise("associations(type): type must be a topic reference.") unless (type.is_a?(RTM::Topic) || type.is_a?(RTM::Locator) || type.is_a?(String)) type = self.get(type) unless type.is_a?(RTM::Topic) return type ? type_instance_index.getAssociations(type).to_a : [] end # Returns all topics in this topic map that are used # as types of associations. # # :call-seq: # association_types -> Set of Topics # def association_types type_instance_index.getAssociationTypes end # Returns the all Topics in this TopicMap that are used # as types of Roles. # # :call-seq: # role_types -> Set of Topics # def role_types type_instance_index.getRoleTypes end # Returns all Topics in this TopicMap that are used # as types of Names. # # :call-seq: # name_types -> Set of Topics # def name_types type_instance_index.getNameTypes end # Returns all Topics in this TopicMap that are used # as types of Occurrences. # # :call-seq: # occurrence_types -> Set of Topics # def occurrence_types type_instance_index.getOccurrenceTypes end superised # Returns all topics contained in this topic map. # # The optional argument specifies the type of the topics should have, # i.e. all instances of the specified type are returned. Type may be a # topic reference. This method equals instances (type). # # The return value may be empty. # # :call_spec: # topics -> Set of Topics # topics(type) -> Array of Topics # def topics(type = :any) return getTopics if type == :any return instances(type) end superised # Returns a Locator representing the identifier. # Identifier may be a String (IRI). # # :call-seq: # create_locator(identifier) -> Locator # def create_locator(identifier) return identifier if identifier.is_a?(Java::OrgTmapiCore::Locator) identifier = check_for_qname(identifier) unless prefixes.empty? identifier = proper_base_iri + identifier if identifier.is_a?(String) && !identifier.include?(URI_PATTERN) createLocator(identifier) end superised # Assumes identifier is an item identifier (Locator or String) # or an Array of item identifiers (Locators and/or Strings) # and returns a Construct or an Array of Constructs # by its/their item identifier/s. # # :call-seq: # get_construct_by_item_identifier(identifier) -> Construct # get_construct_by_item_identifier(identifier-Array) -> Array of Constructs # def get_construct_by_item_identifier(identifier) identifier = create_locator(identifier) if identifier.is_a?(String) return getConstructByItemIdentifier(identifier) if identifier.is_a?(Java::OrgTmapiCore::Locator) return identifier.map{|i| get_topic_by_subject_locator(i)} if identifier.is_a?(Array) return nil end superised # Assumes identifier is a subject identifier (Locator or String) # or an Array of subject identifiers (Locators and/or Strings) # and returns a Topic or an Array of Topics # by its/their subject identifier/s. # # :call-seq: # get_topic_by_subject_identifier(identifier) -> Topic # get_topic_by_subject_identifier(identifier-Array) -> Array of Topics # def get_topic_by_subject_identifier(identifier) identifier = create_locator(identifier) if identifier.is_a?(String) return getTopicBySubjectIdentifier(identifier) if identifier.is_a?(Java::OrgTmapiCore::Locator) return identifier.map{|i| get_topic_by_subject_locator(i)} if identifier.is_a?(Array) return nil end superised # Assumes identifier is a subject locator (Locator or String) # or an Array of subject locators (Locators and/or Strings) # and returns a Topic or an Array of Topics # by its/their subject locator/s. # # :call-seq: # get_topic_by_subject_locator(identifier) -> Topic # get_topic_by_subject_locator(identifier-Array) -> Array of Topics # def get_topic_by_subject_locator(identifier) identifier = create_locator(identifier) if identifier.is_a?(String) return getTopicBySubjectLocator(identifier) if identifier.is_a?(Java::OrgTmapiCore::Locator) return identifier.map{|i| get_topic_by_subject_locator(i)} if identifier.is_a?(Array) return nil end superised # Assumes identifier is a subject identifier (Locator or String) # or an Array of subject identifiers (Locators and/or Strings). # Either returns an existing Topic (or Array of Topics) or creates a/ # new Topic instance/s with the specified subject identifier/s. # # :call-seq: # create_topic_by_subject_identifier(identifier) -> Topic # create_topic_by_subject_identifier(identifier-Array) -> Array of Topics # # If a topic with the specified subject identifier exists in the topic map, # that topic is returned. If a topic with an item identifier equals to the # specified subject identifier exists, the specified subject identifier is # added to that topic and the topic is returned. If neither a topic with # the specified subject identifier nor with an item identifier equals to # the subject identifier exists, a topic with the subject identifier is # created. def create_topic_by_subject_identifier(identifier) get_stuff(identifier){|x| createTopicBySubjectIdentifier(x)} end superised # Assumes identifier is a subject locator (Locator or String) # or an Array of subject locators (Locators and/or Strings). # Either returns an existing Topic (or Array of Topics) or creates a/ # new Topic instance/s with the specified subject locators/s. # # :call-seq: # create_topic_by_subject_locator(identifier) -> Topic # create_topic_by_subject_locator(identifier-Array) -> Array of Topics # def create_topic_by_subject_locator(identifier) get_stuff(identifier){|x| createTopicBySubjectLocator(x)} end superised # Assumes identifier is a item identifier (Locator or String) # or an Array of item identifiers (Locators and/or Strings). # Either returns an existing Topic (or Array of Topics) or creates a/ # new Topic instance/s with the specified item identifier/s. # # :call-seq: # create_topic_by_item_identifier(identifier) -> Topic # create_topic_by_item_identifier(identifier-Array) -> Array of Topics # # If a topic with the specified item identifier exists in the topic map, # that topic is returned. If a topic with a subject identifier equals to # the specified item identifier exists, the specified item identifier is # added to that topic and the topic is returned. If neither a topic with # the specified item identifier nor with a subject identifier equals to # the subject identifier exists, a topic with the item identifier is created. def create_topic_by_item_identifier(identifier) get_stuff(identifier){|x| createTopicByItemIdentifier(x)} end # Assumes identifier is an IRI (Locator or String) or an Array of IRIs. # Returns an existing or created Topic or an Array of Topics according # to the nature of the IRI. # # :call-seq: # create_topic_by(identifier) -> Topic # create_topic_by(identifier-Array) -> Array of Topics # def create_topic_by(identifier) case identifier when Java::OrgTmapiCore::Locator return create_topic_by_subject_identifier(identifier) when String reroute_for_create(identifier) when Array return identifier.map{|i| create_topic_by(i)} else return nil end end # If identifier is a Topic, returns this Topic. # # If identifier is a Locator, returns the Topic specified by the Locator (as item # identifier or subject identifier). # # If identifier is a String, returnes the Topic which has this # String as subject identifier (by default or if the String starts with "si:") # or as subject locator (if the String starts with a "=" or "sl:") # or as item identifier (if the String starts with a "^" or "ii:"). # # Returns an Array of Topics if an Array of identifiers is given according to the # rules above. # # Given one identifier, if no Topic is found, returns nil. # # Given an Array of identifiers, if no Topics are found, returnes an empty Array. # # :call-seq: # get(identifier) -> Topic or nil # get(identifier-Array) -> Array of Topics or empty Array # def get(identifier) self.cached(self, :get, identifier) { case identifier when nil nil when Java::OrgTmapiCore::Topic identifier when Java::OrgTmapiCore::Locator if get_construct_by_item_identifier(identifier).is_a?(Java::OrgTmapiCore::Topic) get_construct_by_item_identifier(identifier) elsif get_topic_by_subject_identifier(identifier) get_topic_by_subject_identifier(identifier) else #return get_topic_by_subject_locator(identifier) if get_topic_by_subject_locator(identifier) #not allowed because Topics with equals si and ii are merged but sl not nil end when String reroute_for_get(identifier) when Array # in: multi array -> out: multi array identifier.map{|i| get(i)} #when Hash #TODO: use case for getting Topics if argument is a hash else raise("get(#{identifier.inspect}): arguments do not match") end } end alias :topic :get # alias :[] :get # not sure if this is a good idea, for now we leave it out # Calls get(identifier) and returns the Topic or Array of Topics according # to the nature of the identifier (Topic/Topic-References or # Array of those types). # # Calls create_topic_by(identifier) if a Topic specified by identifier does # not exist in the TopicMap. Returns the created and/our found Topic/s. # # If identifier is a String, returnes/creates the Topic which has this # String as subject identifier (by default or if the String starts with "si:") # or as subject locator (if the String starts with a "=" or "sl:") # or as item identifier (if the String starts with a "^" or "ii:"). # # :call-seq: # get!(identifier) -> Topic # get!(identifier-Array) -> Array of Topics # def get!(identifier) return nil unless identifier if identifier.is_a?(Array) return identifier.map{|i| get!(i)} end found = get(identifier) return found ? found : create_topic_by(identifier) end alias :topic! :get! def construct_by_id(id) getConstructById(id) end private # If the identifier is a qname (= "prefix_identifier" + ":" + "localpart"), # substitutes the prefix_identifier with the reference # (=> "reference" + "localpart") if the prefix_identifier is # found in the prefixes-Hash. # # Returnes the enhanced identifier if successfull. # Returns the original identifier if no substitution # occured. # # :call-seq: # check_for_qname(identifier) -> String # def check_for_qname(identifier) sections = identifier.split(":") key = sections[0] if (sections.size > 1) && (prefixes.has_key?(key)) identifier = identifier.sub(key + ":", prefixes[key]) # TODO check NOW if identifier is a valid URI ? or may the identifier still be relative? end return identifier end # Calls TMAPI TopicMap.getIndex which # returns the specified index for the # indexInterface i. # # :call-seq: # _index(i) -> extended index # # doesn't work; # JRuby doesn't seem to like "send" there; # solution: use getIndex directly def _index(i) getIndex(org.tmapi.index.send(i).java_class) end # If identifier is not a String or Locator -> assumes it is an Array. # If Array entries are not locators -> create_locator(entry). # Calls yield on each Array entry and returns Array. # # If identifier is not a Locator -> assumes it is a String and # calls create_locator(idenifier). # Executes yield on locator and returns the result. def get_stuff(identifier) unless (identifier.is_a?(String)) || (identifier.is_a?(Java::OrgTmapiCore::Locator)) identifier_in_use = identifier.map{|x|(x.is_a? Java::OrgTmapiCore::Locator)? x : create_locator(x)} return identifier_in_use.map{|y| yield y} else unless identifier.is_a? Java::OrgTmapiCore::Locator identifier = create_locator(identifier) end return (yield identifier) end end # Identifies the identifier as item identifier, subject locator or # subject identifier and calls create_topic_by_.. accordingly. Creates # new topic if topic does not exist yet. Returns the topic. # # :call-seq: # reroute_for_create(identifier) -> Topic # def reroute_for_create(identifier) case identifier when /^(\^|ii:)\s*(.*)/ #identifiers starts with ^ or ii: create_topic_by_item_identifier(create_locator($2)) when /^(=|sl:)\s*(.*)/ #identifier starts with = or sl: create_topic_by_subject_locator(create_locator($2)) when /^(si:)\s*(.*)/ #identifier starts with si: create_topic_by_subject_identifier(create_locator($2)) else #identifier does not start with a special character create_topic_by_subject_identifier(create_locator(identifier.lstrip))#lstrip: leading whitespace chars removed end end # Identifies the identifier as item identifier, subject locator or # subject identifier and calls get_construct/topic_by_.. accordingly. # Returns the topic. # # :call-seq: # reroute_for_get(identifier) -> Topic # def reroute_for_get(identifier) case identifier when /^(si:|sl:|ii:|=|^)$/ return nil when /^(\^|ii:)\s*(.*)/ #identifiers starts with ^ or ii: get_construct_by_item_identifier(create_locator($2)) when /^(=|sl:)\s*(.+)/ #identifier starts with = or sl: get_topic_by_subject_locator(create_locator($2)) when /^(si:)\s*(.+)/ #identifier starts with si: get_topic_by_subject_identifier(create_locator($2)) else #identifier does not start with a special character get_topic_by_subject_identifier(create_locator(identifier.lstrip)) #lstrip: leading whitespace chars removed end end def proper_base_iri if base_iri[-1] == '/'[-1] return base_iri else return base_iri + '#' end end end