/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml.common.eventhandler;
import java.util.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
public class ListenerList
{
private static ImmutableList<ListenerList> allLists = ImmutableList.of();
private static int maxSize = 0;
@Nullable
private ListenerList parent;
private ListenerListInst[] lists = new ListenerListInst[0];
public ListenerList()
{
this(null);
}
public ListenerList(@Nullable ListenerList parent)
{
// parent needs to be set before resize !
this.parent = parent;
extendMasterList(this);
resizeLists(maxSize);
}
private synchronized static void extendMasterList(ListenerList inst)
{
ImmutableList.Builder<ListenerList> builder = ImmutableList.builder();
builder.addAll(allLists);
builder.add(inst);
allLists = builder.build();
}
public static void resize(int max)
{
if (max <= maxSize)
{
return;
}
for (ListenerList list : allLists)
{
list.resizeLists(max);
}
maxSize = max;
}
public void resizeLists(int max)
{
if (parent != null)
{
parent.resizeLists(max);
}
if (lists.length >= max)
{
return;
}
ListenerListInst[] newList = new ListenerListInst[max];
int x = 0;
for (; x < lists.length; x++)
{
newList[x] = lists[x];
}
for(; x < max; x++)
{
if (parent != null)
{
newList[x] = new ListenerListInst(parent.getInstance(x));
}
else
{
newList[x] = new ListenerListInst();
}
}
lists = newList;
}
public static void clearBusID(int id)
{
for (ListenerList list : allLists)
{
list.lists[id].dispose();
}
}
protected ListenerListInst getInstance(int id)
{
return lists[id];
}
public IEventListener[] getListeners(int id)
{
return lists[id].getListeners();
}
public void register(int id, EventPriority priority, IEventListener listener)
{
lists[id].register(priority, listener);
}
public void unregister(int id, IEventListener listener)
{
lists[id].unregister(listener);
}
public static void unregisterAll(int id, IEventListener listener)
{
for (ListenerList list : allLists)
{
list.unregister(id, listener);
}
}
private class ListenerListInst
{
private boolean rebuild = true;
private IEventListener[] listeners;
private ArrayList<ArrayList<IEventListener>> priorities;
private ListenerListInst parent;
private List<ListenerListInst> children;
private ListenerListInst()
{
int count = EventPriority.values().length;
priorities = new ArrayList<ArrayList<IEventListener>>(count);
for (int x = 0; x < count; x++)
{
priorities.add(new ArrayList<IEventListener>());
}
}
public void dispose()
{
for (ArrayList<IEventListener> listeners : priorities)
{
listeners.clear();
}
priorities.clear();
parent = null;
listeners = null;
if (children != null)
children.clear();
}
private ListenerListInst(ListenerListInst parent)
{
this();
this.parent = parent;
this.parent.addChild(this);
}
/**
* Returns a ArrayList containing all listeners for this event,
* and all parent events for the specified priority.
*
* The list is returned with the listeners for the children events first.
*
* @param priority The Priority to get
* @return ArrayList containing listeners
*/
public ArrayList<IEventListener> getListeners(EventPriority priority)
{
ArrayList<IEventListener> ret = new ArrayList<IEventListener>(priorities.get(priority.ordinal()));
if (parent != null)
{
ret.addAll(parent.getListeners(priority));
}
return ret;
}
/**
* Returns a full list of all listeners for all priority levels.
* Including all parent listeners.
*
* List is returned in proper priority order.
*
* Automatically rebuilds the internal Array cache if its information is out of date.
*
* @return Array containing listeners
*/
public IEventListener[] getListeners()
{
if (shouldRebuild()) buildCache();
return listeners;
}
protected boolean shouldRebuild()
{
return rebuild;// || (parent != null && parent.shouldRebuild());
}
protected void forceRebuild()
{
this.rebuild = true;
if (this.children != null)
{
for (ListenerListInst child : this.children)
child.forceRebuild();
}
}
private void addChild(ListenerListInst child)
{
if (this.children == null)
this.children = Lists.newArrayList();
this.children.add(child);
}
/**
* Rebuild the local Array of listeners, returns early if there is no work to do.
*/
private void buildCache()
{
if(parent != null && parent.shouldRebuild())
{
parent.buildCache();
}
ArrayList<IEventListener> ret = new ArrayList<IEventListener>();
for (EventPriority value : EventPriority.values())
{
List<IEventListener> listeners = getListeners(value);
if (listeners.size() > 0)
{
ret.add(value); //Add the priority to notify the event of it's current phase.
ret.addAll(listeners);
}
}
listeners = ret.toArray(new IEventListener[ret.size()]);
rebuild = false;
}
public void register(EventPriority priority, IEventListener listener)
{
priorities.get(priority.ordinal()).add(listener);
this.forceRebuild();
}
public void unregister(IEventListener listener)
{
for(ArrayList<IEventListener> list : priorities)
{
if (list.remove(listener))
{
this.forceRebuild();
}
}
}
}
}