/** * Copyright 2008 - 2015 The Loon Game Engine Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loon * @author cping * @email:javachenpeng@yahoo.com * @version 0.5 */ package loon.utils.reply; import loon.event.Updateable; public abstract class Bypass { protected static final Cons DISPATCHING = new Cons(null, null); protected Cons _listeners; protected Runs _pendingRuns; public abstract interface GoListener { } public boolean hasConnections() { return _listeners != null; } public synchronized void clearConnections() { if (isDispatching()) { throw new IllegalStateException("System dispatching"); } _listeners = null; } abstract GoListener placeholderListener(); protected synchronized Cons addConnection(GoListener listener) { if (listener == null) throw new NullPointerException("Null listener"); return addCons(new Cons(this, listener)); } protected synchronized Cons addCons(final Cons cons) { if (isDispatching()) { _pendingRuns = append(_pendingRuns, new Runs() { public void action(Object o) { _listeners = Cons.insert(_listeners, cons); connectionAdded(); } }); } else { _listeners = Cons.insert(_listeners, cons); connectionAdded(); } return cons; } protected synchronized void disconnect(final Cons cons) { if (isDispatching()) { _pendingRuns = append(_pendingRuns, new Runs() { public void action(Object o) { _listeners = Cons.remove(_listeners, cons); connectionRemoved(); } }); } else { _listeners = Cons.remove(_listeners, cons); connectionRemoved(); } } protected synchronized void removeConnection(final GoListener listener) { if (isDispatching()) { _pendingRuns = append(_pendingRuns, new Runs() { public void action(Object o){ _listeners = Cons.removeAll(_listeners, listener); connectionRemoved(); } }); } else { _listeners = Cons.removeAll(_listeners, listener); connectionRemoved(); } } protected void checkMutate() { } protected void connectionAdded() { } protected void connectionRemoved() { } protected void notify(final Notifier notifier, final Object a1, final Object a2, final Object a3) { Cons lners; synchronized (this) { if (_listeners == DISPATCHING) { _pendingRuns = append(_pendingRuns, new Runs() { public void action(Object o) { Bypass.this.notify(notifier, a1, a2, a3); } }); return; } lners = _listeners; Cons sentinel = DISPATCHING; _listeners = sentinel; } RuntimeException exn = null; try { for (Cons cons = lners; cons != null; cons = cons.next) { try { notifier.notify(cons.listener(), a1, a2, a3); } catch (RuntimeException ex) { exn = ex; } if (cons.oneShot()){ cons.close(); } } } finally { synchronized (this) { _listeners = lners; } Runs run; while ((run = nextRun()) != null) { try { run.action(this); } catch (RuntimeException ex) { exn = ex; } } } if (exn != null){ throw exn; } } private synchronized Runs nextRun() { Runs run = _pendingRuns; if (run != null){ _pendingRuns = run.next; } return run; } private final boolean isDispatching() { return _listeners == DISPATCHING; } protected static <T> boolean areEqual(T o1, T o2) { return (o1 == o2 || (o1 != null && o1.equals(o2))); } protected static Runs append(Runs head, Runs action) { if (head == null) return action; head.next = append(head.next, action); return head; } protected static abstract class Runs implements Updateable { public Runs next; } protected static abstract class Notifier { public abstract void notify(Object listener, Object a1, Object a2, Object a3); } }