#!/usr/bin/ruby # # linkage.rb - this file contains the class declarations for the # LinkParser::Linkage class. # # == Synopsis # # # == Rcsid # # $Id: linkage.rb,v 1.12 2003/08/10 21:56:40 stillflame Exp $ # # == Authors # # Martin Chase # #:include: COPYRIGHT # #--- # # Please see the file COPYRIGHT for licensing details. # require "linkparser/word" require "linkparser/connection" #require "linkparser/log" class LinkParser class Linkage def initialize( words=[], connections=[] ) @connections = connections @words = words end # the connections of this linkage attr_reader :connections # the words in this linkage attr_reader :words # Combine two linkages together. def combine(other) Linkage::new( @words.superimpose(other.words), @connections + other.connections ) end alias :+ :combine # add new connections to the list of connections def add_connections(*connections) @connections += connections self end alias :addConnections :add_connections ### Inserts into this linkage the connection and words found in the ### supplied arguments. def insert(connection, left, right, left_word, right_word) @connections << connection if @words[left] @words[left].combine!(left_word) else @words[left] = left_word end if @words[right] @words[right].combine!(right_word) else @words[right] = right_word end self end # returns whether or not the linkage is valid def valid? (@connections.length + 1) >= @words.length end # returns whether the linkage is connected def connected? @words.length == @words.compact.length end # Hash on the only important part - the connections. def hash @connections.sort.hash end # Equality based only on the "important" parts. def eql?(o) @connections.sort.eql?(o.connections.sort) end # string display def inspect result = @words.collect {|word| word.to_s}.join(" ") + " --- " + @connections.join(" ") #:TODO: ? end # pretty string display def to_s rows = [] # the diagram rows.unshift(@words.join(' ').split('')) # insert the words pos = 0 starts = @words.map {|word| # the starting position of each word start = pos pos += word.to_s.length + 1 start } offsets = @words.map {|word| word.to_s.length / 2} centers = (starts.zip(offsets)).map {|c| c[0]+c[1]} # where to attach connections to each word pos = 0 spaces = [] # where there are spaces between words @words.each {|word| spaces << ((pos - 1)..(pos - 1)) if pos.nonzero? pos += word.to_s.length + 1 } rows.unshift(Array.new(rows[0].length, " ")) centers.each {|i| rows[0][i] = "|"} conns = @connections.dup until conns.empty? row = Array.new(rows[0].length, " ") these = [] conns.delete_if {|conn| conn.length == (rows.length - 1) and these << conn} #:TODO: loops infinitely - need to use counter instead of rows.length #next if these.empty? these.each {|conn| first = centers[conn.lword.position] last = centers[conn.rword.position] span = (first..last) dist = span.to_a.length disp = "+" + conn.to_s + "+" # minimum display string if disp.length > dist #:TODO: *ack* # i will need to expand the spaces inbetween the words, # as well as between the connection arcs, and then # update the centers and spaces array. this could be # done one space at a time, cycling through the possible # word gaps. space_affect = (spaces.index(spaces.find {|r| first <= r.first}))..(spaces.length - 1) cent_affect = (centers.index(centers.find {|i| first <= i}))..(centers.length - 1) until dist >= disp.length #:?: dist += 1 # centers.map! {|i| # cent_affect.member?(centers.index(i)) ? i + 1 : i # } # first = centers[conn.lword.position] # last = centers[conn.rword.position] # span = (first..last) # dist = span.to_a.length end elsif disp.length < dist side = true until disp.length >= dist if side disp[0] = "+-" else disp[-1] = "-+" end side = ! side end end first.upto(last) do |i| row[i] = disp.slice!(0).chr end } centers.each_with_index {|i,w| row[i] = "|" if row[i] == " " and conns.find {|conn| [conn.rword, conn.lword].include?(@words[w])} } rows.unshift(row) end return (rows.map {|row| row.join('')}).join("\n") end # string conversion def to_str @words.join(' ') end end # class Linkage end # class LinkParser