/*
* This file is part of aion-emu <aion-emu.com>.
*
* aion-emu is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* aion-emu 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with aion-emu. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aionemu.commons.callbacks;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.log4j.Logger;
/**
* Class that implements helper methods for callbacks.<br>
* All enhanced objects are delegating main part of their logic to this class
*
* @author SoulKeeper
*/
public class CallbackHelper
{
/**
* Logger
*/
private static final Logger log = Logger.getLogger(CallbackHelper.class);
/**
* Private empty constructor to prevent initialization
*/
private CallbackHelper()
{
}
/**
* Adds callback to the list.<br>
* Sorting is done while adding to avoid extra calls.
*
* @param callback
* what to add
* @param object
* add callback to which objec
*/
@SuppressWarnings( { "unchecked" })
public static void addCallback(Callback callback, EnhancedObject object)
{
try
{
Map<Class<? extends Callback>, List<Callback>> cbMap = object.getCallbacks();
object.getCallbackLock().lock();
List<Callback> list = cbMap.get(callback.getBaseClass());
if(list == null)
{
list = new CopyOnWriteArrayList<Callback>();
cbMap.put(callback.getBaseClass(), list);
}
int callbackPriority = getCallbackPriority(callback);
// hand-made sorting, if needed to insert to the middle
for(int i = 0, n = list.size(); i < n; i++)
{
Callback c = list.get(i);
int cPrio = getCallbackPriority(c);
if(callbackPriority < cPrio)
{
list.add(i, callback);
return;
}
}
// add last
list.add(callback);
}
finally
{
object.getCallbackLock().unlock();
}
}
/**
* Removes callback from the list
*
* @param callback
* what to remove
* @param object
* remove callback from which object
*/
@SuppressWarnings("unchecked")
public static void removeCallback(Callback callback, EnhancedObject object)
{
try
{
object.getCallbackLock().lock();
Map<Class<? extends Callback>, List<Callback>> cbMap = object.getCallbacks();
List<Callback> list = cbMap.get(callback.getBaseClass());
if(list == null || !list.remove(callback))
{
// noinspection ThrowableInstanceNeverThrown
log.error("Attempt to remove callback that doesn't exists", new RuntimeException());
return;
}
if(list.isEmpty())
{
cbMap.remove(callback.getBaseClass());
}
}
finally
{
object.getCallbackLock().unlock();
}
}
/**
* This method call callbacks before actual method invocation takes place
*
* @param obj
* object that callbacks are invoked for
* @param callbackClass
* base callback class
* @param args
* args of method
* @return {@link Callback#beforeCall(Object, Object[])}
*/
@SuppressWarnings( { "unchecked" })
public static CallbackResult<?> beforeCall(EnhancedObject obj, Class callbackClass, Object... args)
{
List<Callback> list = obj.getCallbacks().get(callbackClass);
if(list == null || list.isEmpty())
{
return CallbackResult.newContinue();
}
CallbackResult<?> cr = null;
for(Callback c : list)
{
try
{
cr = c.beforeCall(obj, args);
if(cr.isBlockingCallbacks())
{
break;
}
}
catch(Throwable t)
{
log.error("Uncaught exception in callback", t);
}
}
return cr == null ? CallbackResult.newContinue() : cr;
}
/**
* This method invokes callbacks after method invocation
*
* @param obj
* object that invokes this method
* @param callbackClass
* superclass of callback
* @param args
* method args
* @param result
* method invokation result
* @return {@link Callback#afterCall(Object, Object[], Object)}
*/
@SuppressWarnings("unchecked")
public static CallbackResult<?> afterCall(EnhancedObject obj, Class callbackClass, Object[] args, Object result)
{
List<Callback> list = obj.getCallbacks().get(callbackClass);
if(list == null || list.isEmpty())
{
return CallbackResult.newContinue();
}
CallbackResult<?> cr = null;
for(Callback c : list)
{
try
{
cr = c.afterCall(obj, args, result);
if(cr.isBlockingCallbacks())
{
break;
}
}
catch(Throwable t)
{
log.error("Uncaught exception in callback", t);
}
}
return cr == null ? CallbackResult.newContinue() : cr;
}
/**
* Returns priority of callback.<br>
* Method checks if callback is instance of {@link CallbackPriority}, and returns
*
* <pre>
* {@link CallbackPriority#DEFAULT_PRIORITY} - {@link CallbackPriority#getPriority()}
* </pre>
*
* .<br>
* If callback is not instance of CallbackPriority then it returns {@link CallbackPriority#DEFAULT_PRIORITY}
*
* @param callback
* priority to get from
* @return priority of callback
*/
@SuppressWarnings("unchecked")
private static int getCallbackPriority(Callback callback)
{
if(callback instanceof CallbackPriority)
{
CallbackPriority instancePriority = (CallbackPriority) callback;
return CallbackPriority.DEFAULT_PRIORITY - instancePriority.getPriority();
}
else
{
return CallbackPriority.DEFAULT_PRIORITY;
}
}
}