# encoding: UTF-8 =begin Copyright GodObject Team , 2012-2016 This file is part of BitSet. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. =end module GodObject module BitSet # A Configuration defines the digits of a BitSet. Additionally it can hold # information on how to represent the digits in a String representation. class Configuration # @return [String] the default String representation for enabled digits UNNAMED_ENABLED = '1'.freeze # @return [String] the default String representation for disabled digits UNNAMED_DISABLED = '0'.freeze # @return [String] the default String representation for disabled digits # which have a custom enabled representation NAMED_DISABLED = '-'.freeze class << self # @overload build(configuration) # Returns an existing instance of GodObject::BitSet::Configuration. # @param [GodObject::BitSet::Configuration] configuration an already # existing Configuration # @return [GodObject::BitSet::Configuration] the same Configuration object # # @overload build(digits) # Returns a new Configuration object with given attributes. # @param [Array] digits a list of digit names # @return [GodObject::PosixMode::Mode] a new Configuration object # # @overload build(enabled_representations_by_digits) # Returns a new Configuration object with given attributes. # @param [Hash String>] enabled_representations_by_digits # digit names mapped to their enabled character representations # @return [GodObject::PosixMode::Mode] a new Configuration object # # @overload build(representations_by_digits) # Returns a new Configuration object with given attributes. # @param [Hash Array>] representations_by_digits # digit names mapped to their enabled and disabled character # representations # @return [GodObject::PosixMode::Mode] a new Configuration object def build(configuration) if configuration.is_a?(Configuration) configuration else new(configuration) end end end # @return [Range] the Range in which an Integer representation of a # BitSet with this Configuration can be. attr_reader :valid_range # Initializes a new BitSet::Configuration # # @overload initialize(digits) # @param [Array] digits a list of digit names # # @overload initialize(enabled_representations_by_digits) # @param [Hash String>] enabled_representations_by_digits # digit names mapped to their enabled character representations # # @overload initialize(representations_by_digits) # @param [Hash Array>] representations_by_digits # digit names mapped to their enabled and disabled character # representations def initialize(configuration) @digits = {} @enabled = {} @disabled = {} configuration.each do |digit, display| digit = digit.to_sym @digits[digit] = nil case when display.respond_to?(:all?) && display.all?{|s| s.respond_to?(:to_str) && s.to_str.length == 1} @enabled[digit] = display.first.to_str.dup.freeze @disabled[digit] = display.last.to_str.dup.freeze when display.respond_to?(:to_str) && display.to_str.length == 1 @enabled[digit] = display.to_str.dup.freeze @disabled[digit] = NAMED_DISABLED when display.nil? @enabled[digit] = UNNAMED_ENABLED @disabled[digit] = UNNAMED_DISABLED else raise ArgumentError, 'Invalid configuration' end end raise ArgumentError, 'At least one digit must be configured' if digits.count < 1 @digits.keys.reverse.each_with_index{|digit, index| @digits[digit] = 2 ** index } @valid_range = Range.new(0, (@digits.values.first * 2) - 1).freeze @unique_characters = !@enabled.values.dup.uniq! end # Answers if all digits have unique enabled representations. # # @return [true, false] true if all digits have unique enabled # representations, false otherwise def unique_characters? @unique_characters end # @attribute digits [readonly] # @return [Array] an ordered list of all configured digit names def digits @digits.keys end # @param [Integer, Array] state either the octal state of the # BitSet or a list of enabled digits # @return [GodObject::BitSet] a new BitSet object with the current # configuration def new(*state) BitSet.new(*state, self) end # @return [Integer] the Integer representation of a BitSet where # only the given digit is enabled. def binary_position(digit) @digits[find_digit(digit)] end # @param [Symbol, Integer] digit the name or index of the digit # @return [String] the String representation for the given digit when # it is disabled def disabled_character(digit) @disabled[find_digit(digit)] end # @param [Symbol, Integer] digit the name or index of the digit # @return [String] the String representation for the given digit when # it is enabled def enabled_character(digit) @enabled[find_digit(digit)] end # @param [Symbol, Integer] index_or_digit the name or index of the digit # @return [Symbol] the digit's name def find_digit(index_or_digit) case when index_or_digit.respond_to?(:to_sym) digit = index_or_digit.to_sym raise ArgumentError, "Invalid digit name (#{index_or_digit})" unless @digits.keys.include?(digit) else digit = @digits.keys[index_or_digit.to_int] end raise ArgumentError, "Invalid index or digit (#{index_or_digit})" unless digit digit end # Answers if another object is equal. # # Equality is defined as having the same ordered list of digits. # # @param [Object] other an object to be checked for equality # @return [true, false] true if the object is considered equal, false # otherwise def ==(other) digits == other.digits rescue NoMethodError false end # Answers if another object is equal and of the same type family. # # @see GodObject::Configuration#== # @param [Object] other an object to be checked for equality # @return [true, false] true if the object is considered equal and of # the same type familiy, false otherwise def eql?(other) self == other && other.kind_of?(self.class) end # @return (see Hash#hash) identity hash for hash table usage def hash @digits.hash end end end end