BitField.java

/*
** Module   :BitField.java
** Abstract :BitField, a subclass of BitSet which forbids dynamic set growth
**
** Copyright (c) 2005-2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 SIY 20050217 ADD  @20015  Created initial implementation
** 002 SIY 20050310 CHG  @20354  Organized imports and reordered methods.
** 003 SIY 20050323 CHG  @20468  Fixed formatting.  
** 004 SIY 20050328 CHG  @20574  Fixed missing tags in comments.
** 005 SIY 20050421 ADD  @21007  Added parsing for the LDAP bit string values.
** 006 SIY 20050515 CHG  @21187  Extended and fixed comments.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.directory;

import java.util.BitSet;

/**
 * BitField, a subclass of BitSet which forbids dynamic set growth.
 * <p>
 * The main purpose of this class is to provide Java representation of the
 * <code>Bit Field</code> attribute type.
 * 
 * @author  SIY
 * @version 1.0
 */
public class BitField extends BitSet
{
   /** The "real" width of the bit field. */
   private int width;
   
   /**
    * Construct a BitField of specified width with all bits set 
    * to <code>false</code>.
    *
    * @param   width
    *          Number of bits in field, indexed from <code>0</code> to 
    *          <code>width-1</code>.
    * @throws  NegativeArraySizeException if the specified width is negative.
    */
   public BitField(int width)
   {
      super(width);
      this.width = width;
   }

   /**
    * Parse <code>String</code> like 00011000 and create an instance of
    * <code>BitField</code>.
    * 
    * @param   str
    *          Source string.
    * @return  New <code>BitField</code> instance or <code>null</code> if
    *          source string does not contain properly formatted bit field.
    */
   public static BitField valueOf(String str)
   {
      if (str == null || str.length() == 0)
         return null;
      
      str = str.trim();
      
      if (str.charAt(0) == '\'')
      {
         str = str.substring(1);
         
         int pos = str.indexOf('\'');
         
         if (pos > 0)
            str = str.substring(0, pos);
      }
      
      if (str.length() == 0)
         return null;
      
      char[] data = str.toCharArray();

      BitField fld = new BitField(data.length);
      
      for (int i = 0; i < data.length; i++)
      {
         if (data[i] == '1')
            fld.set(data.length - i - 1);
         else 
            if (data[i] != '0')
               return null;
      }
      
      return fld;
   }
   
   /**
    * Performs a logical AND of this bit field with the argument bit set.
    * 
    * @param   set
    *          A bit set to perform operation with.
    * @throws  IndexOutOfBoundsException
    *          if the specified set is too wide.
    */
   public void and(BitSet set)
   {
      checkBitSet(set);
      super.and(set);
   }

   /**
    * Clears all of the bits whose corresponding bit is set in the specified
    * BitSet.
    * 
    * @param   set
    *          A bit set to perform operation with.
    * @throws  IndexOutOfBoundsException
    *          if the specified set is too wide.
    */
   public void andNot(BitSet set)
   {
      checkBitSet(set);
      super.andNot(set);
   }
   
   /**
    * Set specified bit into <code>false</code>.
    *
    * @param   index 
    *          The index of the bit to clear.
    * @throws  IndexOutOfBoundsException if the specified index is negative
    *          or exceeds BitField width.
    */ 
   public void clear(int index)
   {
      checkIndex(index);
      super.clear(index);
   }

   /**
    * Set specified range of bits into <code>false</code>.
    *
    * @param   from 
    *          The index of the first bit in range.
    * @param   to 
    *          The index of the last bit in range.
    * @throws  IndexOutOfBoundsException if the specified range does not fit
    *          into BitField width.
    */ 
   public void clear(int from, int to)
   {
      //Note: there is no need to check low bound of range,
      //remaining checks are done by base class. 
      checkIndex(to);
      super.clear(from, to);
   }

   /**
    * Set specified bit into opposite value.
    *
    * @param   index 
    *          The index of the bit to change.
    * @throws  IndexOutOfBoundsException if the specified index is negative
    *          or exceeds BitField width.
    */ 
   public void flip(int index)
   {
      checkIndex(index);
      super.flip(index);
   }
   
   /**
    * Set specified range of bits into opposite value.
    *
    * @param   from
    *          The index of the first bit in range.
    * @param   to
    *          The index of the last bit in range.
    * @throws  IndexOutOfBoundsException if the specified range does not fit
    *          into BitField width.
    */ 
   public void flip(int from, int to)
   {
      //Note: there is no need to check low bound of range,
      //remaining checks are done by base class. 
      checkIndex(to);
      super.flip(from, to);
   }

   /**
    * Get value of the specified bit.
    * 
    * @param   index
    *          The index of the bit to get.
    * @throws  IndexOutOfBoundsException
    *          if the specified index is negative or exceeds BitField width.
    * @return  <code>true</code> if specified bit is set.
    */ 
   public boolean get(int index)
   {
      checkIndex(index);
      return super.get(index);
   }
   
   /**
    * Performs a logical OR of this bit field with the argument bit set.
    *
    * @param   set
    *          A bit set to perform operation with.
    * @throws  IndexOutOfBoundsException if the specified set is too wide.
    */
   public void or(BitSet set)
   {
      checkBitSet(set);
      super.or(set);
   }
   
   /**
    * Set specified bit into <code>true</code>.
    *
    * @param   index
    *          The index of the bit to set.
    * @throws  IndexOutOfBoundsException if the specified index is negative
    *          or exceeds BitField width.
    */ 
   public void set(int index)
   {
      checkIndex(index);
      super.set(index);
   }

   /**
    * Set specified bit into specified value.
    *
    * @param   index
    *          The index of the bit to set
    * @param   val
    *          New bit value.
    * @throws  IndexOutOfBoundsException if the specified index is negative
    *          or exceeds BitField width.
    */ 
   public void set(int index, boolean val)
   {
      checkIndex(index);
      super.set(index, val);
   }
   
   /**
    * Set specified range of bits into <code>true</code>.
    *
    * @param   from 
    *          The index of the first bit in range.
    * @param   to 
    *          The index of the last bit in range.
    * @throws  IndexOutOfBoundsException if the specified range does not fit
    *          into BitField width.
    */ 
   public void set(int from, int to)
   {
      checkIndex(to);
      super.set(from, to);
   }

   /**
    * Set specified range of bits into specified value.
    *
    * @param   from 
    *          The index of the first bit in range.
    * @param   to 
    *          The index of the last bit in range.
    * @param   val
    *          New value for the bits in range.
    * @throws  IndexOutOfBoundsException if the specified range does not fit
    *          into BitField width.
    */ 
   public void set(int from, int to, boolean val)
   {
      checkIndex(to);
      super.set(from, to, val);
   }
   
   /**
    * Return a field width as specified in the constructor. Note that this is
    * not the same value as returned by the parent class.
    * 
    * @return  Bit field width as it was specified in the constructor.
    */
   public int size()
   {
      return width;
   }

   /**
    * Convert bit field into string representation. The result is a string
    * consisting of '0's and '1's from most significant bit (with highest
    * index) to least significant bit.
    * 
    * @return  <code>String</code> on '0's and '1's which represent state of
    *          the bit field.
    */
   public String toString()
   {
      char[] field = new char[size()];

      for (int i = 0; i < field.length; i++)
      {
         field[i] = get(field.length - i - 1) ? '1' : '0';
      }

      return new String(field);
   }
   
   /**
    * Performs a logical XOR of this bit field with the argument bit set.
    * 
    * @param   set
    *          A bit set to perform operation with.
    * @throws  IndexOutOfBoundsException
    *          if the specified set is too wide.
    */
   public void xor(BitSet set)
   {
      checkBitSet(set);
      super.xor(set);
   }
   
   /**
    * Check specified BitSet is narrow enough to be used for operation.
    * 
    * @param   set
    *          BitSet instance to check.
    * @throws  IndexOutOfBoundsException
    *          if specified bit set is too wide.
    */
   private void checkBitSet(BitSet set)
   {
      if (set instanceof BitField)
      {
         if (size() < set.size())
            throw new IndexOutOfBoundsException("Input set is too wide :"
               + set.size());
      }

      //Comparing with old version of size()
      if (super.size() < set.size())
         throw new IndexOutOfBoundsException("Input set is too wide :"
            + set.size());
   }
   
   /**
    * Check specified index to fit into range.
    * 
    * @param   index
    *          index to check.
    * @throws  IndexOutOfBoundsException
    *          if index does not fit into range.
    */
   private void checkIndex(int index)
   {
      if (index >= size())
         throw new IndexOutOfBoundsException("No such bit in field: " + index);
   }
}