private List<Presence> applyAffiliationChange(@Nonnull final JID target, @Nullable final JID actorUserAddress, @Nullable final String actorNickname, @Nullable final String reason)
    {
        // Update the presence(s) for the new affiliation and inform all occupants
        final List<JID> affectedOccupants = new ArrayList<>();

        // first, determine which actual (user) JIDs are affected by the affiliation change
        if (GroupJID.isGroup(target)) {
            try {
                final Group group = GroupManager.getInstance().getGroup(target);
                // check each occupant to see if they are in the group that was changed
                // if so, calculate a new affiliation (if any) for the occupant(s)
                for (final JID groupMember : group.getAll()) {
                    if (hasOccupant(groupMember)) {
                        affectedOccupants.add(groupMember);
                    }
                }
            } catch (GroupNotFoundException gnfe) {
                Log.error("Error updating group presences for " + target, gnfe);
            }
        } else {
            if (hasOccupant(target)) {
                affectedOccupants.add(target);
            }
        }

        // now update each of the affected occupants with a new role/affiliation
        final List<Presence> updatedPresences = new ArrayList<>();
        // new role/affiliation may be granted via group membership
        for (final JID occupantJID : affectedOccupants) {
            boolean kickMember = false, isOutcast = false;
            final Role newRole;
            final Affiliation newAffiliation;
            if (owners.includes(occupantJID)) {
                newRole = Role.moderator;
                newAffiliation = Affiliation.owner;
            } else if (admins.includes(occupantJID)) {
                newRole = Role.moderator;
                newAffiliation = Affiliation.admin;
            }
            // outcast trumps member when an affiliation is changed
            else if (outcasts.includes(occupantJID)) {
                newAffiliation = Affiliation.outcast;
                newRole = Role.none;
                kickMember = true;
                isOutcast = true;
            } else if (members.includesKey(occupantJID)) {
                newRole = Role.participant;
                newAffiliation = Affiliation.member;
            } else if (isMembersOnly()) {
                newRole = Role.none;
                newAffiliation = Affiliation.none;
                kickMember = true;
            } else {
                newRole = isModerated() ? Role.visitor : Role.participant;
                newAffiliation = Affiliation.none;
            }
            Log.debug("Applying affiliation change for {}. New affiliation: {}", occupantJID, newAffiliation);

            final List<Presence> presences = new ArrayList<>();
            // Get all the roles (i.e. occupants) of this user based on his/her bare JID
            final List<MUCOccupant> occupants;
            try {
                occupants = getOccupantsByBareJID(occupantJID);
                // Collect all the updated presences of these roles
                for (final MUCOccupant occupant : occupants) {
                    // Update the presence with the new affiliation and role
                    occupant.setAffiliation(newAffiliation);
                    occupant.setRole(newRole);

                    // Prepare a new presence to be sent to all the room occupants
                    presences.add(occupant.getPresence());
                }
            } catch (UserNotFoundException e) {
                return presences;
            }

            if (kickMember) {
                // If the room is members-only, remove the user from the room including a status
                // code of 321 to indicate that the user was removed because of an affiliation change
                // a status code of 301 indicates the user was removed as an outcast
                for (final Presence presence : presences) {
                    presence.setType(Presence.Type.unavailable);
                    presence.setStatus(null);

                    final Element x = presence.getChildElement("x", "http://jabber.org/protocol/muc#user");
                    x.addElement("status").addAttribute("code", isOutcast ? "301" : "321");

                    // This removes the kicked occupant from the room. The presenceUpdates returned by this method
                    // (that will be broadcast to all occupants by the caller) won't reach it.
                    kickPresence(presence, actorUserAddress, actorNickname, reason);
                }
            }
            updatedPresences.addAll(presences);
        }
        return updatedPresences;
    }
Updates the internal state of a given occupant after an affiliation change has been applied.
<p>
Evaluate the given JID to determine what the appropriate affiliation should be
after a change has been made. Each affected user will be granted the highest
affiliation they now possess, either explicitly or implicitly via membership
in one or more groups.
<p>
If the JID is a user, the effective affiliation is
applied to each presence corresponding to that user. If the given JID is a group,
each user in the group is evaluated to determine what their new affiliations will
be.
<p>
The returned presence updates will be broadcast to the occupants of the room.

@param target           The JID for the user or group for which the affiliation has been changed
@param actorUserAddress The (real) address of the user that initiated the ban.
@param actorNickname    The actor nickname.
@param reason           An optional reason why the user was kicked.
@return List of presence updates to be delivered to the room's occupants
xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoom.java