module OM::XML::TermXpathGenerator
Public Class Methods
Adds xpath xpath node index predicate to the end of your xpath query Example: add_node_index_predicate
(“//oxns:titleInfo”,0)
=> "//oxns:titleInfo[1]"
lang="finnish"] at add_node_index_predicate("//oxns:titleInfo[
“,0)
=> "//oxns:titleInfo[@lang=\"finnish\"][1]"
# File lib/om/xml/term_xpath_generator.rb, line 240 def self.add_node_index_predicate(xpath_query, array_index_value) modified_query = xpath_query.dup modified_query << "[#{array_index_value + 1}]" end
Adds xpath:position() method call to the end of your xpath query Examples:
add_position_predicate
(“//oxns:titleInfo”,0)
> “//oxns:titleInfo”¶ ↑
lang="finnish"] at add_position_predicate("//oxns:titleInfo[
“,0)
> “//oxns:titleInfo[@lang="finnish" and position()=1]”¶ ↑
# File lib/om/xml/term_xpath_generator.rb, line 253 def self.add_position_predicate(xpath_query, array_index_value) position_function = "position()=#{array_index_value + 1}" self.add_predicate(xpath_query, position_function) end
# File lib/om/xml/term_xpath_generator.rb, line 258 def self.add_predicate(xpath_query, predicate) modified_query = xpath_query.dup # if xpath_query.include?("]") if xpath_query[xpath_query.length-1..xpath_query.length] == "]" modified_query.insert(xpath_query.rindex("]"), " and #{predicate}") else modified_query << "[#{predicate}]" end return modified_query end
# File lib/om/xml/term_xpath_generator.rb, line 229 def self.delimited_list(*args) OM::XML.delimited_list(*args) end
Generate absolute xpath for a Term
@param [OM::XML::Term] term that you want to generate absolute xpath for
Absolute xpaths always begin with “//”. They are generated by relying on the Term's relative xpath and the absolute xpath of its parent node.
# File lib/om/xml/term_xpath_generator.rb, line 62 def self.generate_absolute_xpath(term) relative = generate_relative_xpath(term) if term.parent.nil? return "//#{relative}" else return term.parent.xpath_absolute + "/" + relative end end
# File lib/om/xml/term_xpath_generator.rb, line 71 def self.generate_constrained_xpath(term) if term.namespace_prefix.nil? complete_prefix = "" else complete_prefix = term.namespace_prefix + ":" end absolute = generate_absolute_xpath(term) constraint_predicates = [] arguments_for_contains_function = [] if !term.default_content_path.nil? arguments_for_contains_function << "#{complete_prefix}#{term.default_content_path}" end # If no subelements have been specified to search within, set contains function to search within the current node if arguments_for_contains_function.empty? arguments_for_contains_function << "." end arguments_for_contains_function << "\":::constraint_value:::\"" contains_function = "contains(#{delimited_list(arguments_for_contains_function)})" template = add_predicate(absolute, contains_function) return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }.gsub('"', '\"') end
Generate relative xpath for a term @param [OM::XML::Term] term that you want to generate relative xpath for
In most cases, the resulting xpath will be the Term's path with the appropriate namespace appended to it. If the Term
specifies any attributes, Special Case: attribute Terms If the Term's path is set to {:attribute=>attr_name}, the resulting xpath will points to a node attribute named attr_name ie. a path fo {:attribute=>“lang”} will result in a relative xpath of “@lang” Special Case: xpath functions If the Term's path variable is text(), it will be treated as an xpath function (no namespace) and turned into “text()”
# File lib/om/xml/term_xpath_generator.rb, line 13 def self.generate_relative_xpath(term) template = "" predicates = [] if term.namespace_prefix.nil? complete_prefix = "" else complete_prefix = term.namespace_prefix + ":" end if term.path.kind_of?(Hash) if term.path.has_key?(:attribute) base_path = "@"+term.path[:attribute] else raise "#{term.path} is an invalid path for an OM::XML::Term. You should provide either a string or {:attributes=>XXX}" end else if term.path == "text()" base_path = "#{term.path}[normalize-space(.)]" else unless term.namespace_prefix.nil? template << complete_prefix end base_path = term.path end end template << base_path unless term.attributes.nil? term.attributes.each_pair do |attr_name, attr_value| if attr_value == :none predicates << "not(@#{attr_name})" else predicates << "@#{attr_name}=\"#{attr_value}\"" end end end unless predicates.empty? template << "["+ delimited_list(predicates, " and ")+"]" end return template end
Generate an xpath of the chosen type
for the given Term
. @param [OM::XML::Term] term the term that you want to generate relative xpath for @param [Symbol] type the type of xpath to generate, :relative, :abolute, or :constrained
# File lib/om/xml/term_xpath_generator.rb, line 103 def self.generate_xpath(term, type) case type when :relative self.generate_relative_xpath(term) when :absolute self.generate_absolute_xpath(term) when :constrained self.generate_constrained_xpath(term) end end
Use the given terminology
to generate an xpath with (optional) node indexes for each of the term pointers. Ex. OM::XML::TermXpathGenerator.xpath_with_indexes(my_terminology, {:conference=>0}, {:role=>1}, :text )
will yield an xpath similar to this: '//oxns:name[@type="conference"][1]/oxns:role[2]/oxns:roleTerm[@type="text"]'
@param [OM::XML::Terminology] terminology to generate xpath based on @param [String – OM
term pointer] pointers identifying the node to generate xpath for
# File lib/om/xml/term_xpath_generator.rb, line 119 def self.generate_xpath_with_indexes(terminology, *pointers) if pointers.first.nil? root_term = terminology.root_terms.first if root_term.nil? return "/" else return root_term.xpath end end query_constraints = nil if pointers.length > 1 && pointers.last.kind_of?(Hash) constraints = pointers.pop unless constraints.empty? query_constraints = constraints end end if pointers.length == 1 && pointers.first.instance_of?(String) return xpath_query = pointers.first end # if pointers.first.kind_of?(String) # return pointers.first # end keys = [] xpath = "//" pointers = OM.destringify(pointers) pointers.each_with_index do |pointer, pointer_index| if pointer.kind_of?(Hash) k = pointer.keys.first index = pointer[k] else k = pointer index = nil end keys << k term = terminology.retrieve_term(*keys) # Return nil if there is no term to work with return if term.nil? # If we've encountered a NamedTermProxy, insert path sections corresponding to each entry in its proxy_pointer (rather than just the final term that it points to). # TODO Looks like this only works if the last key is a NamedTermProxy, what if we cross proxies on the way there? if term.kind_of? OM::XML::NamedTermProxy OM.logger.warn "You attempted to call an index value of #{index} on the term \"#{k.inspect}\". However \"#{k.inspect}\" is a proxy so we are ignoring the index. See https://jira.duraspace.org/browse/HYDRA-643" if index && OM.logger current_location = term.parent.nil? ? term.terminology : term.parent relative_path = "" term.proxy_pointer.each_with_index do |proxy_pointer, proxy_pointer_index| begin proxy_term = current_location.retrieve_term(proxy_pointer) if proxy_term.nil? proxy_term = terminology.retrieve_term(proxy_pointer) end proxy_relative_path = proxy_term.xpath_relative if proxy_pointer_index > 0 proxy_relative_path = "/"+proxy_relative_path end relative_path << proxy_relative_path current_location = proxy_term rescue Exception => e raise "There's a problem with the #{term.name} OM::XML::NamedTermProxy, whose proxy pointer is #{term.proxy_pointer}. The #{proxy_pointer} pointer is returning #{proxy_term.inspect}" end end else relative_path = term.xpath_relative unless index.nil? relative_path = add_node_index_predicate(relative_path, index) end end if pointer_index > 0 relative_path = "/"+relative_path end xpath << relative_path end final_term = terminology.retrieve_term(*keys) if query_constraints.kind_of?(Hash) contains_functions = [] query_constraints.each_pair do |k,v| if v.is_a?(Integer) || v == "0" || v.to_i != 0 # lookup sub element xpath = '(' + xpath + '/' + final_term.children[query_constraints.keys.first].xpath_relative + ")[#{v.to_i + 1}]" else if k.instance_of?(Symbol) constraint_path = final_term.children[k].xpath_relative else constraint_path = k end # match for text contains_functions << "#{constraint_path}[text()=\"#{v}\"]" xpath = add_predicate(xpath, delimited_list(contains_functions, " and ") ) end end end # return xpath end