module YOWL class Class < YOWL::LabelledDocObject include Comparable attr_reader :resource attr_reader :subClasses attr_reader :associations private def initialize(resource, schema) super(resource, schema) @super_classes = nil @subClasses = nil @associations = nil end public def Class.withUri(resource, schema) if resource.anonymous? warn "WARNING: Ignoring class with uri #{resource.to_s}" return end klass = schema.classes[resource.to_s] if klass return klass end klass = Class.new(resource, schema) schema.classes[resource.to_s] = klass if schema.options.verbose puts "Created class #{klass.short_name}" end return klass end public def short_name() sn = super() if sn[0,7] == "http://" or sn[0,8] == "https://" if sn.include?('#') ns = sn.slice(/.*#/) else ns = sn.slice(/.*\//) end return sn[ns.length..-1] end return sn end public def super_classes() if not @super_classes.nil? return @super_classes end @super_classes = [] @schema.model.query( RDF::Query::Pattern.new(@resource, YOWL::Namespaces::RDFS.subClassOf) ) do |statement| # # Only look at statements like these: # # # # And ignore statements like these: # # # # # # # # if statement.object.uri? superClass = Class.withUri(statement.object, @schema) if superClass if superClass != self @super_classes << superClass end else warn "WARNING: Could not find super class #{statement.object.to_s}" end end end return @super_classes end public def hasSuperClasses? return ! super_classes.empty?() end public def hasSuperClassesInSchema? super_classes.each() do |klass| if @schema.classes.include?(klass.uri) return true end end return false end public def subClasses() if @subClasses return @subClasses end @subClasses = Set.new @schema.model.query( RDF::Query::Pattern.new(nil, YOWL::Namespaces::RDFS.subClassOf, @resource) ) do |statement| subClass = Class.withUri(statement.subject, @schema) if subClass if subClass != self @subClasses << subClass end else warn "WARNING: Could not find sub class of #{short_name} with uri #{statement.subject.to_s}" end end #@subClasses.sort! { |x,y| x <=> y } return @subClasses end public def hasSubClasses? return ! subClasses.empty?() end public # # Return a collection of Associations representing ObjectProperties # where the current class is one of the Domain classes. # def associations() if not @associations.nil? return @associations end @associations = Set.new if @schema.options.verbose puts "Searching for associations of class #{short_name}" end query = RDF::Query.new({ :property => { YOWL::Namespaces::RDFS.domain => @resource, RDF.type => YOWL::Namespaces::OWL.ObjectProperty, YOWL::Namespaces::RDFS.range => :range } }) solutions = query.execute(@schema.model) if @schema.options.verbose puts " - Found #{solutions.count} solutions" end solutions.distinct! if @schema.options.verbose puts " - Found #{solutions.count} distinct solutions" end solutions.each do |solution| property = solution[:property] range = solution[:range] if @schema.options.verbose puts " - Found Association from #{short_name} to #{@schema.prefixedUri(range)}: #{@schema.prefixedUri(property.to_s)}" end rangeClass = Class.withUri(range, @schema) if @schema.options.verbose puts " - Found this class for it: #{rangeClass}" end if rangeClass @associations << Association.new(property, @schema, self, rangeClass) end end if @schema.options.verbose puts " - Returning #{@associations.size} associations for class #{short_name}" end return @associations end public # # Add the current class as a GraphViz node to the given collection of nodes # and to the given graph. Return the collection of nodes. # def addAsGraphvizNode (graph_, nodes_, edges_) name = short_name if @schema.options.verbose puts "- Processing class #{name}" end # # No need to add a node twice # if nodes_.has_key? uri return nodes_, edges_ end node = graph_.add_nodes(escaped_uri) node.URL = "#class_#{short_name}" prefix = nil if name.include?(':') prefix = name.sub(/:\s*(.*)/, "") name = name.sub(/(.*)\s*:/, "") end name = name.split(/(?=[A-Z])/).join(' ') if prefix # # Can't get HTML labels to work # #name = "
#{name}
(#{prefix})
" name = "#{name}\n(#{prefix})" end node.label = name if hasComment? node.tooltip = comment else if hasDefinition? node.tooltip = definition end end nodes_[uri] = node return nodes_, edges_ end public # # Generate a diagram for each class, the "per class diagram" # def perClassDiagramAsSvg #if @schema.options.verbose # puts "Generating SVG Per Class Diagram for #{short_name}" #end g = GraphvizUtility.setDefaults(GraphViz.new(:G, :type => :digraph)) g[:rankdir] = "LR" g.node[:fixedsize] = false nodes = Hash.new edges = Hash.new nodes, edges = addAsGraphvizNode(g, nodes, edges) # # Do the "outbound" associations first # associations.each do |association| nodes, edges = association.rangeClass.addAsGraphvizNode(g, nodes, edges) nodes, edges = association.addAsGraphVizEdge(g, nodes, edges) end # # Then do the "inbound" associations # @schema.classes.values.to_set.each do |klass| klass.associations.each do |association| if self == association.rangeClass nodes, edges = association.rangeClass.addAsGraphvizNode(g, nodes, edges) nodes, edges = association.addAsGraphVizEdge(g, nodes, edges) end end end return GraphvizUtility.embeddableSvg(g) end # # Create the GraphVis Edge for all "is a" (rdf:type) associations from a node # (representing a Class or Individual) to another node (always representing a Class) # def Class.newGraphVizEdge(graph_, domainNode_, rangeNode_, constraint_ = true) options = { :arrowhead => :empty, :arrowsize => 0.5, :dir => :back, :label => "is a", :labeldistance => 2, :penwidth => 0.5, :constraint => constraint_ } if not constraint_ options[:style] = :dashed end return graph_.add_edges( rangeNode_, domainNode_, options ) end end end