# File lib/activeldap/base.rb, line 779
    def write
      @@logger.debug("stub: write called")
      # Validate against the objectClass requirements
      validate

      # Put all changes into one change entry to ensure
      # automatic rollback upon failure.
      entry = []


      # Expand subtypes to real ldap_data entries
      # We can't reuse @ldap_data because an exception would leave
      # an object in an unknown state
      @@logger.debug("#write: dup'ing @ldap_data")
      ldap_data = Marshal.load(Marshal.dump(@ldap_data))
      @@logger.debug("#write: dup finished @ldap_data")
      @@logger.debug("#write: expanding subtypes in @ldap_data")
      ldap_data.keys.each do |key|
        ldap_data[key].each do |value|
          if value.class == Hash
            suffix, real_value = extract_subtypes(value)
            if ldap_data.has_key? key + suffix
              ldap_data[key + suffix].push(real_value)
            else
              ldap_data[key + suffix] = real_value
            end
            ldap_data[key].delete(value)
          end
        end
      end
      @@logger.debug('#write: subtypes expanded for @ldap_data')

      # Expand subtypes to real data entries, but leave @data alone
      @@logger.debug('#write: duping @data')
      data = Marshal.load(Marshal.dump(@data))
      @@logger.debug('#write: finished duping @data')

      @@logger.debug('#write: removing disallowed attributes from @data')
      bad_attrs = @data.keys - (@must+@may)
      bad_attrs.each do |removeme|
        data.delete(removeme) 
      end
      @@logger.debug('#write: finished removing disallowed attributes from @data')


      @@logger.debug('#write: expanding subtypes for @data')
      data.keys.each do |key|
        data[key].each do |value|
          if value.class == Hash
            suffix, real_value = extract_subtypes(value)
            if data.has_key? key + suffix
              data[key + suffix].push(real_value)
            else
              data[key + suffix] = real_value
            end
            data[key].delete(value)
          end
        end
      end
      @@logger.debug('#write: subtypes expanded for @data')

      if @exists
        # Cycle through all attrs to determine action
        action = {}

        replaceable = []
        # Now that all the subtypes will be treated as unique attributes
        # we can see what's changed and add anything that is brand-spankin'
        # new.
        @@logger.debug('#write: traversing ldap_data determining replaces and deletes')
        ldap_data.each do |pair|
          suffix = ''
          binary = 0

          name, *suffix_a = pair[0].split(/;/)
          suffix = ';'+ suffix_a.join(';') if suffix_a.size > 0
          name = @attr_methods[name]
          name = pair[0].split(/;/)[0] if name.nil? # for objectClass, or removed vals
          value = data[name+suffix]
          # If it doesn't exist, don't freak out.
          value = [] if value.nil?

          # Detect subtypes and account for them
          binary = LDAP::LDAP_MOD_BVALUES if Base.schema.binary? name

          replaceable.push(name+suffix)
          if pair[1] != value
            # Create mod entries
            if not value.empty?
              # Ditched delete then replace because attribs with no equality match rules
              # will fails
              @@logger.debug("updating attribute of existing entry:  #{name+suffix}: #{value.inspect}")
              entry.push(LDAP.mod(LDAP::LDAP_MOD_REPLACE|binary, name + suffix, value))
            else
              # Since some types do not have equality matching rules, delete doesn't work
              # Replacing with nothing is equivalent.
              @@logger.debug("removing attribute from existing entry:  #{name+suffix}")
              entry.push(LDAP.mod(LDAP::LDAP_MOD_REPLACE|binary, name + suffix, []))
            end
          end
        end
        @@logger.debug('#write: finished traversing ldap_data')
        @@logger.debug('#write: traversing data determining adds')
        data.each do |pair|
          suffix = ''
          binary = 0

          name, *suffix_a = pair[0].split(/;/)
          suffix = ';' + suffix_a.join(';') if suffix_a.size > 0
          name = @attr_methods[name]
          name = pair[0].split(/;/)[0] if name.nil? # for obj class or removed vals
          value = pair[1]
          # Make sure to change this to an Array if there was mistake earlier.
          value = [] if value.nil?

          if not replaceable.member? name+suffix
            # Detect subtypes and account for them
            binary = LDAP::LDAP_MOD_BVALUES if Base.schema.binary? name
            @@logger.debug("adding attribute to existing entry:  #{name+suffix}: #{value.inspect}")
            # REPLACE will function like ADD, but doesn't hit EQUALITY problems
            # TODO: Added equality(attr) to Schema2
            entry.push(LDAP.mod(LDAP::LDAP_MOD_REPLACE|binary, name + suffix, value)) unless value.empty?
          end
        end
        @@logger.debug('#write: traversing data complete')
        Base.connection(WriteError.new(
                        "Failed to modify: '#{entry}'")) do |conn|
          @@logger.debug("#write: modifying #{@dn}")
          conn.modify(@dn, entry)
          @@logger.debug('#write: modify successful')
        end
      else # add everything!
        @@logger.debug('#write: adding all attribute value pairs')
        @@logger.debug("#write: adding #{@attr_methods[dnattr()].inspect} = #{data[@attr_methods[dnattr()]].inspect}")
        entry.push(LDAP.mod(LDAP::LDAP_MOD_ADD, @attr_methods[dnattr()], 
          data[@attr_methods[dnattr()]]))
        @@logger.debug("#write: adding objectClass = #{data[@attr_methods['objectClass']].inspect}")
        entry.push(LDAP.mod(LDAP::LDAP_MOD_ADD, 'objectClass', 
          data[@attr_methods['objectClass']]))
        data.each do |pair|
          if pair[1].size > 0  and pair[0] != 'objectClass' and pair[0] != @attr_methods[dnattr()]
            # Detect subtypes and account for them
            if Base.schema.binary? pair[0].split(/;/)[0]
              binary = LDAP::LDAP_MOD_BVALUES 
            else
              binary = 0
            end
            @@logger.debug("adding attribute to new entry:  #{pair[0].inspect}: #{pair[1].inspect}")
            entry.push(LDAP.mod(LDAP::LDAP_MOD_ADD|binary, pair[0], pair[1]))
          end
        end
        Base.connection(WriteError.new(
                        "Failed to add: '#{entry}'")) do |conn|
          @@logger.debug("#write: adding #{@dn}")
          conn.add(@dn, entry)
          @@logger.debug("#write: add successful")
          @exists = true
        end
      end
      @@logger.debug("#write: resetting @ldap_data to a dup of @data")
      @ldap_data = Marshal.load(Marshal.dump(data))
      # Delete items disallowed by objectclasses. 
      # They should have been removed from ldap.
      @@logger.debug('#write: removing attributes from @ldap_data not sent in data')
      bad_attrs.each do |removeme|
        @ldap_data.delete(removeme) 
      end
      @@logger.debug('#write: @ldap_data reset complete')
      @@logger.debug('stub: write exitted')
    end