All Downloads are FREE. Search and download functionalities are using the official Maven repository.

simple.client.RPObjectChangeDispatcher Maven / Gradle / Ivy

The newest version!
package simple.client;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import marauroa.common.game.RPObject;
import marauroa.common.game.RPSlot;

/**
 * A dispatcher for RPObjectChangeListeners. This normalizes the tree deltas
 * into individual object deltas.
 *
 * NOTE: The order of dispatch between contained objects and when their
 * container is very specific. Children objects are given a chance to perform
 * creation/updates before their parent is notified it happened to that specific
 * child. For cases of object removal, the parent is notified first, in case the
 * child does destruction/cleanup.
 */
public class RPObjectChangeDispatcher {

    /**
     * The logger.
     */
    private static final Logger LOG
            = Logger.getLogger(RPObjectChangeDispatcher.class.getSimpleName());
    /**
     * The normal listener.
     */
    protected RPObjectChangeListener listener;
    /**
     * The user object listener.
     */
    protected ArrayList userListeners
            = new ArrayList<>();

    /**
     * Create an RPObjectChange event dispatcher.
     *
     * @param listener The normal listener.
     * @param userListener The user object listener.
     */
    public RPObjectChangeDispatcher(final RPObjectChangeListener listener,
            final RPObjectChangeListener userListener) {
        this.listener = listener;
        userListeners.add(userListener);
    }

    //
    // RPObjectChangeDispatcher
    //
    /**
     * Dispatch object added event.
     *
     * @param object The object.
     * @param user If this is the private user object.
     */
    public void dispatchAdded(RPObject object, boolean user) {
        try {
            LOG.log(Level.FINE, "Object({0}) added to client", object.getID());
            fixContainers(object);
            fireAdded(object, user);
        } catch (Exception e) {
            LOG.log(Level.SEVERE, "dispatchAdded failed, object is "
                    + object, e);
        }
    }

    /**
     * Dispatch object removed event.
     *
     * @param object The object.
     * @param user If this is the private user object.
     */
    public void dispatchRemoved(RPObject object, boolean user) {
        try {
            LOG.log(Level.FINE, "Object({0}) removed from client",
                    object.getID());
            fixContainers(object);
            fireRemoved(object, user);
        } catch (Exception e) {
            LOG.log(Level.SEVERE,
                    "dispatchRemovedonDeleted failed, object is " + object, e);
        }
    }

    /**
     * Dispatch object added/changed attribute(s) event.
     *
     * @param object The base object.
     * @param changes The changes.
     * @param user If this is the private user object.
     */
    public void dispatchModifyAdded(final RPObject object,
            final RPObject changes, final boolean user) {
        try {
            LOG.log(Level.FINE, "Object({0}) modified in client",
                    object.getID());
            fixContainers(object);
            fixContainers(changes);
            fireChangedAdded(object, changes, user);
            object.applyDifferences(changes, null);
        } catch (Exception e) {
            LOG.log(Level.FINE, "dispatchModifyAdded failed, object is " + object
                    + ", changes is " + changes, e);
        }
    }

    /**
     * Dispatch object removed attribute(s) event.
     *
     * @param object The base object.
     * @param changes The changes.
     * @param user If this is the private user object.
     */
    public void dispatchModifyRemoved(RPObject object, RPObject changes,
            boolean user) {
        try {
            if (object != null && changes != null) {
                LOG.log(Level.FINE, "Object({0}) modified in client", object.getID());
                LOG.log(Level.FINE, "Original({0}) modified in client", object);

                fixContainers(object);
                fixContainers(changes);
                fireChangedRemoved(object, changes, user);
                object.applyDifferences(null, changes);

                LOG.log(Level.FINE, "Modified({0}) modified in client", object);
                LOG.log(Level.FINE, "Changes({0}) modified in client", changes);
            }
        } catch (Exception e) {
            LOG.log(Level.SEVERE,
                    "dispatchModifyRemoved failed, object is " + object
                    + ", changes is " + changes, e);
        }
    }

    /**
     * Dump an object out in an easily readable format. TEMP!! TEST METHOD -
     * USED FOR DEBUGING.
     *
     * Probably should be in a common util class if useful long term.
     *
     * @param object to be dumped
     */
    public static void dumpObject(RPObject object) {
        StringBuilder sbuf = new StringBuilder();

        sbuf.append(object.getRPClass().getName());
        sbuf.append('[');
        buildIDPath(sbuf, object);
        sbuf.append(']');

        LOG.info(sbuf.toString());

        for (String name : object) {
            LOG.log(Level.INFO, "  {0}: {1}",
                    new Object[]{name, object.get(name)});
        }
    }

    /**
     *
     * @param sbuf
     * @param object
     */
    protected static void buildIDPath(final StringBuilder sbuf,
            final RPObject object) {
        RPSlot slot = object.getContainerSlot();

        if (slot != null) {
            buildIDPath(sbuf, object.getContainer());
            sbuf.append(':');
            sbuf.append(slot.getName());
            sbuf.append(':');
        }

        sbuf.append(object.getID().getObjectID());
    }

    /**
     * Fix parent <-> child linkage. on creation.
     *
     * @param object whose slots shall be fixed.
     */
    protected void fixContainers(final RPObject object) {
        for (RPSlot slot : object.slots()) {
            for (RPObject sobject : slot) {
                if (!sobject.isContained()) {
                    LOG.log(Level.FINE, "Fixing container: {0}", slot);
                    sobject.setContainer(object, slot);
                }

                fixContainers(sobject);
            }
        }
    }

    /**
     * Notify listeners that an object was added.
     *
     * @param object The object.
     * @param user If this is the private user object.
     */
    protected void fireAdded(RPObject object, boolean user) {

        /*
         * Call before children have been notified
         */
        listener.onAdded(object);

        if (user) {
            for (RPObjectChangeListener userListener : userListeners) {
                userListener.onAdded(object);
            }
        }

        /*
         * Walk each slot
         */
        for (RPSlot slot : object.slots()) {
            String slotName = slot.getName();

            for (RPObject sobject : slot) {
                fireAdded(object, slotName, sobject, user);
            }
        }
    }

    /**
     * Notify listeners that a slot object was added.
     *
     * @param object The parent object.
     * @param slotName The slot name.
     * @param sobject The slot object.
     * @param user If this is the private user object.
     */
    protected void fireAdded(final RPObject object, final String slotName,
            final RPObject sobject, final boolean user) {
        /*
         * Notify child
         */
        fireAdded(sobject, user);

        /*
         * Call after the child has been notified
         */
        listener.onSlotAdded(object, slotName, sobject);

        if (user) {
            for (RPObjectChangeListener userListener : userListeners) {
                userListener.onSlotAdded(object, slotName, sobject);
            }
        }
    }

    /**
     * Notify listeners that an object added/changed attribute(s). This will
     * cascade down slot trees.
     *
     * @param object The base object.
     * @param changes The changes.
     * @param user If this is the private user object.
     */
    protected void fireChangedAdded(RPObject object, RPObject changes,
            boolean user) {

        /*
         * Walk each slot
         */
        for (RPSlot cslot : changes.slots()) {
            if (cslot.size() != 0) {
                fireChangedAdded(object, cslot, user);
            }
        }

        /*
         * Call after children have been notified
         */
        listener.onChangedAdded(object, changes);

        if (user) {
            for (RPObjectChangeListener userListener : userListeners) {
                userListener.onChangedAdded(object, changes);
            }
        }
    }

    /**
     * Notify listeners that an object slot added/changed attribute(s). This
     * will cascade down object trees.
     *
     * @param object The base object.
     * @param cslot The changes slot.
     * @param user If this is the private user object.
     */
    protected void fireChangedAdded(RPObject object, RPSlot cslot, boolean user) {
        String slotName = cslot.getName();
        RPSlot slot;

        /*
         * Find the original slot entry (if any)
         */
        if (object.hasSlot(slotName)) {
            slot = object.getSlot(slotName);
        } else {
            slot = null;
        }

        /*
         * Walk the changes
         */
        for (RPObject schanges : cslot) {
            RPObject.ID id = schanges.getID();

            if ((slot != null) && slot.has(id)) {
                RPObject sobject = slot.get(id);

                listener.onSlotChangedAdded(object, slotName, sobject, schanges);

                if (user) {
                    for (RPObjectChangeListener userListener : userListeners) {
                        userListener.onSlotChangedAdded(object, slotName, sobject,
                                schanges);
                    }
                }

                fireChangedAdded(sobject, schanges, user);
            } else {
                if (!schanges.isContained()) {
                    LOG.log(Level.WARNING, "!!! Not contained! - {0}", schanges);
                }

                fireAdded(object, slotName, schanges, user);
            }
        }
    }

    /**
     * Notify listeners that an object removed attribute(s). This will cascade
     * down slot trees.
     *
     * @param object The base object.
     * @param changes The changes.
     * @param user If this is the private user object.
     */
    protected void fireChangedRemoved(RPObject object, RPObject changes,
            boolean user) {

        /*
         * Call before children have been notified
         */
        listener.onChangedRemoved(object, changes);

        if (user) {
            for (RPObjectChangeListener userListener : userListeners) {
                userListener.onChangedRemoved(object, changes);
            }
        }

        /*
         * Walk each slot
         */
        for (RPSlot cslot : changes.slots()) {
            if (cslot.size() != 0) {
                fireChangedRemoved(object, cslot, user);
            }
        }
    }

    /**
     * Notify listeners that an object slot removed attribute(s). This will
     * cascade down object trees.
     *
     * @param object The base object.
     * @param cslot The changes slot.
     * @param user If this is the private user object.
     */
    protected void fireChangedRemoved(RPObject object, RPSlot cslot,
            boolean user) {
        String slotName = cslot.getName();

        /*
         * Find the original slot entry
         */
        RPSlot slot = object.getSlot(slotName);

        /*
         * Walk the changes
         */
        for (RPObject schanges : cslot) {
            RPObject sobject = slot.get(schanges.getID());

            if (sobject == null) {
                LOG.log(Level.FINE, "Unable to find existing: {0}", schanges);
                continue;
            }

            /*
             * Remove attrs vs. object [see applyDifferences()]
             */
            if (schanges.size() > 1) {
                listener.onSlotChangedRemoved(object, slotName, sobject,
                        schanges);

                if (user) {
                    for (RPObjectChangeListener userListener : userListeners) {
                        userListener.onSlotChangedRemoved(object, slotName,
                                sobject, schanges);
                    }
                }

                fireChangedRemoved(sobject, schanges, user);
            } else {
                fireRemoved(object, slotName, sobject, user);
            }
        }
    }

    /**
     * Notify listeners that an object was removed.
     *
     * @param object The object.
     * @param user If this is the private user object.
     */
    protected void fireRemoved(RPObject object, boolean user) {

        /*
         * Walk each slot
         */
        for (RPSlot slot : object.slots()) {
            String slotName = slot.getName();

            for (RPObject sobject : slot) {
                fireRemoved(object, slotName, sobject, user);
            }
        }

        /*
         * Call after children have been notified
         */
        listener.onRemoved(object);

        if (user) {
            for (RPObjectChangeListener userListener : userListeners) {
                userListener.onRemoved(object);
            }
        }
    }

    /**
     * Notify listeners that a slot object was removed.
     *
     * @param object The container object.
     * @param slotName The slot name.
     * @param sobject The slot object.
     * @param user If this is the private user object.
     */
    protected void fireRemoved(final RPObject object, final String slotName,
            final RPObject sobject, final boolean user) {
        /*
         * Call before the child is notified
         */
        listener.onSlotRemoved(object, slotName, sobject);

        if (user) {
            for (RPObjectChangeListener userListener : userListeners) {
                userListener.onSlotRemoved(object, slotName, sobject);
            }
        }

        /*
         * Notify child
         */
        fireRemoved(sobject, user);
    }

    public boolean addRPObjectChangeListener(RPObjectChangeListener listener) {
        return userListeners.add(listener);
    }

    public boolean removeRPObjectChangeListener(RPObjectChangeListener listener) {
        return userListeners.remove(listener);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy