+module Irc\r
+\r
+\r
+ # Due to its Scandinavian origins, IRC has strange case mappings, which\r
+ # consider the characters <tt>{}|^</tt> as the uppercase\r
+ # equivalents of # <tt>[]\~</tt>.\r
+ #\r
+ # This is however not the same on all IRC servers: some use standard ASCII\r
+ # casemapping, other do not consider <tt>^</tt> as the uppercase of\r
+ # <tt>~</tt>\r
+ #\r
+ class Casemap\r
+ @@casemaps = {}\r
+\r
+ # Create a new casemap with name _name_, uppercase characters _upper_ and\r
+ # lowercase characters _lower_\r
+ #\r
+ def initialize(name, upper, lower)\r
+ @key = name.to_sym\r
+ raise "Casemap #{name.inspect} already exists!" if @@casemaps.has_key?(@key)\r
+ @@casemaps[@key] = {\r
+ :upper => upper,\r
+ :lower => lower,\r
+ :casemap => self\r
+ }\r
+ end\r
+\r
+ # Returns the Casemap with the given name\r
+ #\r
+ def Casemap.get(name)\r
+ @@casemaps[name.to_sym][:casemap]\r
+ end\r
+\r
+ # Retrieve the 'uppercase characters' of this Casemap\r
+ #\r
+ def upper\r
+ @@casemaps[@key][:upper]\r
+ end\r
+\r
+ # Retrieve the 'lowercase characters' of this Casemap\r
+ #\r
+ def lower\r
+ @@casemaps[@key][:lower]\r
+ end\r
+\r
+ # Return a Casemap based on the receiver\r
+ #\r
+ def to_irc_casemap\r
+ self\r
+ end\r
+\r
+ # A Casemap is represented by its lower/upper mappings\r
+ #\r
+ def inspect\r
+ "#<#{self.class}:#{'0x%x'% self.object_id}: #{upper.inspect} ~(#{self})~ #{lower.inspect}>"\r
+ end\r
+\r
+ # As a String we return our name\r
+ #\r
+ def to_s\r
+ @key.to_s\r
+ end\r
+\r
+ # Two Casemaps are equal if they have the same upper and lower ranges\r
+ #\r
+ def ==(arg)\r
+ other = arg.to_irc_casemap\r
+ return self.upper == other.upper && self.lower == other.lower\r
+ end\r
+\r
+ # Raise an error if _arg_ and self are not the same Casemap\r
+ #\r
+ def must_be(arg)\r
+ other = arg.to_irc_casemap\r
+ raise "Casemap mismatch (#{self.inspect} != #{other.inspect})" unless self == other\r
+ return true\r
+ end\r
+\r
+ end\r
+\r
+ # The rfc1459 casemap\r
+ #\r
+ class RfcCasemap < Casemap\r
+ include Singleton\r
+\r
+ def initialize\r
+ super('rfc1459', "\x41-\x5e", "\x61-\x7e")\r
+ end\r
+\r
+ end\r
+ RfcCasemap.instance\r
+\r
+ # The strict-rfc1459 Casemap\r
+ #\r
+ class StrictRfcCasemap < Casemap\r
+ include Singleton\r
+\r
+ def initialize\r
+ super('strict-rfc1459', "\x41-\x5d", "\x61-\x7d")\r
+ end\r
+\r
+ end\r
+ StrictRfcCasemap.instance\r
+\r
+ # The ascii Casemap\r
+ #\r
+ class AsciiCasemap < Casemap\r
+ include Singleton\r
+\r
+ def initialize\r
+ super('ascii', "\x41-\x5a", "\x61-\x7a")\r
+ end\r
+\r
+ end\r
+ AsciiCasemap.instance\r
+\r
+\r
+ # This module is included by all classes that are either bound to a server\r
+ # or should have a casemap.\r
+ #\r
+ module ServerOrCasemap\r
+\r
+ attr_reader :server\r
+\r
+ # This method initializes the instance variables @server and @casemap\r
+ # according to the values of the hash keys :server and :casemap in _opts_\r
+ #\r
+ def init_server_or_casemap(opts={})\r
+ @server = opts.fetch(:server, nil)\r
+ raise TypeError, "#{@server} is not a valid Irc::Server" if @server and not @server.kind_of?(Server)\r
+\r
+ @casemap = opts.fetch(:casemap, nil)\r
+ if @server\r
+ if @casemap\r
+ @server.casemap.must_be(@casemap)\r
+ @casemap = nil\r
+ end\r
+ else\r
+ @casemap = (@casemap || 'rfc1459').to_irc_casemap\r
+ end\r
+ end\r
+\r
+ # This is an auxiliary method: it returns true if the receiver fits the\r
+ # server and casemap specified in _opts_, false otherwise.\r
+ #\r
+ def fits_with_server_and_casemap?(opts={})\r
+ srv = opts.fetch(:server, nil)\r
+ cmap = opts.fetch(:casemap, nil)\r
+ cmap = cmap.to_irc_casemap unless cmap.nil?\r
+\r
+ if srv.nil?\r
+ return true if cmap.nil? or cmap == casemap\r
+ else\r
+ return true if srv == @server and (cmap.nil? or cmap == casemap)\r
+ end\r
+ return false\r
+ end\r
+\r
+ # Returns the casemap of the receiver, by looking at the bound\r
+ # @server (if possible) or at the @casemap otherwise\r
+ #\r
+ def casemap\r
+ return @server.casemap if defined?(@server) and @server\r
+ return @casemap\r
+ end\r
+\r
+ # Returns a hash with the current @server and @casemap as values of\r
+ # :server and :casemap\r
+ #\r
+ def server_and_casemap\r
+ h = {}\r
+ h[:server] = @server if defined?(@server) and @server\r
+ h[:casemap] = @casemap if defined?(@casemap) and @casemap\r
+ return h\r
+ end\r
+\r
+ # We allow up/downcasing with a different casemap\r
+ #\r
+ def irc_downcase(cmap=casemap)\r
+ self.to_s.irc_downcase(cmap)\r
+ end\r
+\r
+ # Up/downcasing something that includes this module returns its\r
+ # Up/downcased to_s form\r
+ #\r
+ def downcase\r
+ self.irc_downcase\r
+ end\r
+\r
+ # We allow up/downcasing with a different casemap\r
+ #\r
+ def irc_upcase(cmap=casemap)\r
+ self.to_s.irc_upcase(cmap)\r
+ end\r
+\r
+ # Up/downcasing something that includes this module returns its\r
+ # Up/downcased to_s form\r
+ #\r
+ def upcase\r
+ self.irc_upcase\r
+ end\r
+\r
+ end\r
+\r
+end\r