package org.archstudio.bna.logics.events;
import static org.archstudio.sysutils.SystemUtils.firstOrNull;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.archstudio.bna.IBNAView;
import org.archstudio.bna.IBNAWorld;
import org.archstudio.bna.ICoordinate;
import org.archstudio.bna.IThing;
import org.archstudio.bna.IThingLogic;
import org.archstudio.bna.constants.MouseType;
import org.archstudio.bna.facets.IHasWorld;
import org.archstudio.bna.logics.AbstractThingLogic;
import org.archstudio.bna.logics.tracking.ThingTypeTrackingLogic;
import org.archstudio.bna.things.utility.WorldThingPeer;
import org.archstudio.bna.ui.IBNAAllEventsListener2;
import org.archstudio.bna.ui.IBNADragAndDropListener;
import org.archstudio.bna.ui.IBNAFocusListener;
import org.archstudio.bna.ui.IBNAMagnifyGestureListener;
import org.archstudio.bna.ui.IBNAMenuListener;
import org.archstudio.bna.ui.IBNAMouseClickListener;
import org.archstudio.bna.ui.IBNAMouseListener;
import org.archstudio.bna.ui.IBNAMouseMoveListener;
import org.archstudio.bna.ui.IBNAMouseTrackListener;
import org.archstudio.bna.ui.IBNAPanGestureListener;
import org.archstudio.bna.ui.IBNARotateGestureListener;
import org.archstudio.bna.ui.IBNASwipeGestureListener;
import org.archstudio.bna.ui.IBNATrackGestureListener;
import org.archstudio.bna.utils.BNAUtils;
import org.archstudio.bna.utils.DefaultCoordinate;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.swt.events.MouseEvent;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/**
* @Deprecated This is replaced by the use of {@link IBNAAllEventsListener2}.
*/
@SuppressWarnings("deprecation")
public class WorldThingExternalEventsLogic extends AbstractThingLogic {
public boolean PROFILE = false;
protected static final LoadingCache<Object, AtomicLong> profileStats = CacheBuilder.newBuilder().weakKeys()
.build(new CacheLoader<Object, AtomicLong>() {
@Override
public AtomicLong load(Object input) {
return new AtomicLong();
}
});
@SuppressWarnings("unchecked")
private static <T> T get(Method method, Object[] args, Class<T> ofType) {
for (Object arg : args) {
if (ofType.isInstance(arg)) {
return (T) arg;
}
}
return null;
}
private static <T> void set(Method method, Object[] args, Class<T> ofType, Object[] newArgs, T value) {
for (int i = 0; i < args.length; i++) {
if (ofType.isInstance(args[i])) {
newArgs[i] = value;
return;
}
}
}
private class ProxyHandler implements IThingLogic, InvocationHandler, IBNAMouseListener, IBNAMouseMoveListener,
IBNAMenuListener {
protected Method method;
protected IHasWorld capturedWorldThing = null;
public ProxyHandler() {
}
@Override
public IBNAWorld getBNAWorld() {
return world;
}
@Override
public void init() {
}
@Override
public void dispose() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
BNAUtils.checkLock();
this.method = method;
try {
Method selfMethod = this.getClass().getMethod(method.getName(), method.getParameterTypes());
return selfMethod.invoke(this, args);
}
catch (NoSuchMethodException | SecurityException e) {
}
IBNAView view = get(method, args, IBNAView.class);
if (view != null) {
for (IThing innerThing : view.getBNAWorld().getBNAModel().getAllThings()) {
propagateEvent(view, innerThing, method, args);
}
}
return null;
}
private void propagateEvent(IBNAView view, IThing innerThing, Method method, Object[] args) {
if (innerThing instanceof IHasWorld) {
IBNAWorld iWorld = ((IHasWorld) innerThing).getWorld();
if (iWorld != null) {
Object[] newArgs = new Object[args.length];
System.arraycopy(args, 0, newArgs, 0, args.length);
WorldThingPeer<?> worldThingPeer = (WorldThingPeer<?>) view.getThingPeer(innerThing);
IBNAView iView = worldThingPeer.getInnerView();
if (iView != null) {
set(method, args, IBNAView.class, newArgs, iView);
ICoordinate location = get(method, args, ICoordinate.class);
if (location != null) {
ICoordinate iLocation = DefaultCoordinate.forLocal(location.getLocalPoint(),
iView.getCoordinateMapper());
set(method, args, ICoordinate.class, newArgs, iLocation);
List<IThing> iThings = iView.getThingsAt(iLocation);
set(method, args, List.class, newArgs, iThings);
}
for (Object o : iWorld.getThingLogicManager().getThingLogics(method.getDeclaringClass())) {
try {
long time = System.nanoTime();
method.invoke(o, newArgs);
if (PROFILE) {
time = System.nanoTime() - time;
profileStats.get(o).addAndGet(time);
}
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
}
}
}
@Override
public void mouseDown(IBNAView view, MouseType type, MouseEvent evt, List<IThing> things, ICoordinate location) {
BNAUtils.checkLock();
capturedWorldThing = firstOrNull(things, IHasWorld.class);
if (capturedWorldThing != null) {
propagateEvent(view, capturedWorldThing, method, new Object[] { view, type, evt, things, location });
}
}
@Override
public void mouseUp(IBNAView view, MouseType type, MouseEvent evt, List<IThing> things, ICoordinate location) {
BNAUtils.checkLock();
if (capturedWorldThing != null) {
propagateEvent(view, capturedWorldThing, method, new Object[] { view, type, evt, things, location });
capturedWorldThing = null;
}
}
@Override
public void mouseMove(IBNAView view, MouseType type, MouseEvent evt, List<IThing> things, ICoordinate location) {
BNAUtils.checkLock();
if (capturedWorldThing != null) {
propagateEvent(view, capturedWorldThing, method, new Object[] { view, type, evt, things, location });
}
else {
for (IThing innerThing : view.getBNAWorld().getBNAModel().getAllThings()) {
if (innerThing instanceof IHasWorld) {
WorldThingPeer<?> worldThingPeer = (WorldThingPeer<?>) view.getThingPeer(innerThing);
if (worldThingPeer.isInThing(location)) {
propagateEvent(view, innerThing, method, new Object[] { view, type, evt, things, location });
}
}
}
}
}
@Override
public void fillMenu(IBNAView view, List<IThing> things, ICoordinate location, IMenuManager menu) {
BNAUtils.checkLock();
IHasWorld worldThing = firstOrNull(things, IHasWorld.class);
if (worldThing != null) {
propagateEvent(view, worldThing, method, new Object[] { view, things, location, menu });
}
}
}
protected final ThingTypeTrackingLogic typeLogic;
protected final IThingLogic proxy;
public WorldThingExternalEventsLogic(IBNAWorld world) {
super(world);
typeLogic = logics.addThingLogic(ThingTypeTrackingLogic.class);
try {
proxy = (IThingLogic) Proxy.newProxyInstance( //
this.getClass().getClassLoader(), //
new Class<?>[] { IThingLogic.class, //
IBNADragAndDropListener.class, //
IBNAFocusListener.class, //
//IBNAKeyListener.class, //
IBNAMagnifyGestureListener.class, //
IBNAMenuListener.class, //
IBNAMouseClickListener.class, //
IBNAMouseListener.class, //
//IBNAMouseMoveListener.class, //
IBNAMouseTrackListener.class, //
IBNAPanGestureListener.class, //
IBNARotateGestureListener.class, //
IBNASwipeGestureListener.class, //
IBNATrackGestureListener.class //
}, new ProxyHandler());
}
catch (IllegalArgumentException | SecurityException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
logics.addThingLogic(proxy);
}
@SuppressWarnings("unchecked")
public <T> T proxy(Class<T> ofType) {
BNAUtils.checkLock();
if (ofType.isInstance(proxy)) {
return (T) proxy;
}
throw new IllegalArgumentException(ofType.toString());
}
@Override
public void dispose() {
BNAUtils.checkLock();
logics.removeThingLogic(proxy);
super.dispose();
}
}