Added some magic on the road toward more elegant support for dynamic languages
like Groovy and JRuby. Created a DynamicEventDispatcher that automatically maps
attribute, element and set events to methods. Say you have a field:
public DSet occupantInfo;
you can create a method in any class:
public void occupantInfoAdded (BodyObject source, OccupantInfo entry);
and then bind that class as a listener using the dynamic event dispatcher:
_myobj.addListener(new DynamicEventDispatcher(object));
I also created a nicer replacement for the MessageHandler system which is
clunky but still way simpler than using a full InvocationService. Basically we
dispatch MessageEvent as if it were a method call.
For example, in AtlantiManager I define:
public void placeTile (BodyObject placer, AtlantiTile tile)
which receives a request by a player to place a tile on their turn. Then in
AtlantiController, I simply call:
_atlobj.manager.invoke("placeTile", tile);
Of course, in JRuby and Groovy, that's going to look like:
_atlobj.manager.placeTile(tile);
which is all part of the fun.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4344 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.samskivert.util.MethodFinder;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Maps distributed object events to methods using reflection.
|
||||
*/
|
||||
public class DynamicEventDispatcher
|
||||
implements AttributeChangeListener, ElementUpdateListener, SetListener
|
||||
{
|
||||
/**
|
||||
* Creates a dynamic event dispatcher on the supplied target.
|
||||
*/
|
||||
public DynamicEventDispatcher (Object target)
|
||||
{
|
||||
_target = target;
|
||||
_finder = new MethodFinder(target.getClass());
|
||||
}
|
||||
|
||||
// from interface AttributeChangeListener
|
||||
public void attributeChanged (AttributeChangedEvent event)
|
||||
{
|
||||
dispatchMethod(event.getName() + "Changed",
|
||||
new Object[] { event.getValue() });
|
||||
}
|
||||
|
||||
// from interface ElementUpdateListener
|
||||
public void elementUpdated (ElementUpdatedEvent event)
|
||||
{
|
||||
dispatchMethod(event.getName() + "Updated",
|
||||
new Object[] { event.getIndex(), event.getValue() });
|
||||
}
|
||||
|
||||
// from interface SetListener
|
||||
public void entryAdded (EntryAddedEvent event)
|
||||
{
|
||||
dispatchMethod(event.getName() + "Added",
|
||||
new Object[] { event.getEntry() });
|
||||
}
|
||||
|
||||
// from interface SetListener
|
||||
public void entryUpdated (EntryUpdatedEvent event)
|
||||
{
|
||||
dispatchMethod(event.getName() + "Updated",
|
||||
new Object[] { event.getEntry() });
|
||||
}
|
||||
|
||||
// from interface SetListener
|
||||
public void entryRemoved (EntryRemovedEvent event)
|
||||
{
|
||||
dispatchMethod(event.getName() + "Removed",
|
||||
new Object[] { event.getKey() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically looks up the method in question on our target and dispatches
|
||||
* an event if it does.
|
||||
*/
|
||||
public void dispatchMethod (String name, Object[] arguments)
|
||||
{
|
||||
// first check the cache
|
||||
Method method = _mcache.get(name);
|
||||
if (method == null) {
|
||||
// if we haven't already determined this method doesn't exist, try
|
||||
// to resolve it
|
||||
if (!_mcache.containsKey(name)) {
|
||||
_mcache.put(name, method = resolveMethod(name, arguments));
|
||||
}
|
||||
}
|
||||
if (method != null) {
|
||||
try {
|
||||
method.invoke(_target, arguments);
|
||||
} catch (Exception e) {
|
||||
log.log(Level.WARNING, "Failed to dispatch event callback " +
|
||||
name + "(" + StringUtil.toString(arguments) + ").", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for a method that matches the supplied signature.
|
||||
*/
|
||||
protected Method resolveMethod (String name, Object[] arguments)
|
||||
{
|
||||
Class clazz = _target.getClass();
|
||||
Class[] ptypes = new Class[arguments.length];
|
||||
for (int ii = 0; ii < arguments.length; ii++) {
|
||||
ptypes[ii] = arguments[ii] == null ?
|
||||
Object.class : arguments[ii].getClass();
|
||||
}
|
||||
try {
|
||||
return _finder.findMethod(name, ptypes);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The object on which we will dynamically dispatch events. */
|
||||
protected Object _target;
|
||||
|
||||
/** Used to look up methods. */
|
||||
protected MethodFinder _finder;
|
||||
|
||||
/** A cache of already resolved methods. */
|
||||
protected HashMap<String,Method> _mcache = new HashMap<String,Method>();
|
||||
}
|
||||
Reference in New Issue
Block a user