parent
b384d7a70a
commit
4d2fc55a29
@ -1,16 +0,0 @@ |
||||
package mightypork.gamecore.eventbus; |
||||
|
||||
|
||||
/** |
||||
* Access to an {@link EventBus} instance |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface BusAccess { |
||||
|
||||
/** |
||||
* @return event bus |
||||
*/ |
||||
EventBus getEventBus(); |
||||
|
||||
} |
@ -1,121 +0,0 @@ |
||||
package mightypork.gamecore.eventbus; |
||||
|
||||
|
||||
import mightypork.gamecore.eventbus.event_flags.DelayedEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.NonConsumableEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.SingleReceiverEvent; |
||||
|
||||
|
||||
/** |
||||
* <p> |
||||
* Event that can be handled by HANDLER, subscribing to the event bus. |
||||
* </p> |
||||
* <p> |
||||
* Can be annotated as {@link SingleReceiverEvent} to be delivered once only, |
||||
* and {@link DelayedEvent} or {@link DirectEvent} to specify default sending |
||||
* mode. When marked as {@link NotLoggedEvent}, it will not appear in detailed |
||||
* bus logging (useful for very frequent events, such as UpdateEvent). |
||||
* </p> |
||||
* <p> |
||||
* Events annotated as {@link NonConsumableEvent} will throw an exception upon |
||||
* an attempt to consume them. |
||||
* </p> |
||||
* <p> |
||||
* Default sending mode (if not changed by annotations) is <i>queued</i> with |
||||
* zero delay. |
||||
* </p> |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
* @param <HANDLER> handler type |
||||
*/ |
||||
public abstract class BusEvent<HANDLER> { |
||||
|
||||
private boolean consumed; |
||||
private boolean served; |
||||
|
||||
|
||||
/** |
||||
* Ask handler to handle this message. |
||||
* |
||||
* @param handler handler instance |
||||
*/ |
||||
protected abstract void handleBy(HANDLER handler); |
||||
|
||||
|
||||
/** |
||||
* Consume the event, so no other clients will receive it. |
||||
* |
||||
* @throws UnsupportedOperationException if the {@link NonConsumableEvent} |
||||
* annotation is present. |
||||
*/ |
||||
public final void consume() |
||||
{ |
||||
if (consumed) throw new IllegalStateException("Already consumed."); |
||||
|
||||
if (getClass().isAnnotationPresent(NonConsumableEvent.class)) { |
||||
throw new UnsupportedOperationException("Not consumable."); |
||||
} |
||||
|
||||
consumed = true; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Deliver to a handler using the handleBy method. |
||||
* |
||||
* @param handler handler instance |
||||
*/ |
||||
final void deliverTo(HANDLER handler) |
||||
{ |
||||
handleBy(handler); |
||||
|
||||
if (!served) { |
||||
if (getClass().isAnnotationPresent(SingleReceiverEvent.class)) { |
||||
consumed = true; |
||||
} |
||||
|
||||
served = true; |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Check if the event is consumed. Consumed event is not served to other |
||||
* clients. |
||||
* |
||||
* @return true if consumed |
||||
*/ |
||||
public final boolean isConsumed() |
||||
{ |
||||
return consumed; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @return true if the event was served to at least 1 client |
||||
*/ |
||||
final boolean wasServed() |
||||
{ |
||||
return served; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Clear "served" and "consumed" flags before dispatching. |
||||
*/ |
||||
final void clearFlags() |
||||
{ |
||||
served = false; |
||||
consumed = false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Called after all clients have received the event. |
||||
*/ |
||||
public void onDispatchComplete(EventBus bus) |
||||
{ |
||||
} |
||||
} |
@ -1,399 +0,0 @@ |
||||
package mightypork.gamecore.eventbus; |
||||
|
||||
|
||||
import java.lang.reflect.ParameterizedType; |
||||
import java.lang.reflect.Type; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.DelayQueue; |
||||
import java.util.concurrent.Delayed; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import mightypork.gamecore.eventbus.clients.DelegatingClient; |
||||
import mightypork.gamecore.eventbus.event_flags.DelayedEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; |
||||
import mightypork.gamecore.eventbus.events.Destroyable; |
||||
import mightypork.gamecore.logging.Log; |
||||
import mightypork.gamecore.util.Utils; |
||||
|
||||
|
||||
/** |
||||
* An event bus, accommodating multiple EventChannels.<br> |
||||
* Channel will be created when an event of type is first encountered. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
final public class EventBus implements Destroyable, BusAccess { |
||||
|
||||
/** |
||||
* Queued event holder |
||||
*/ |
||||
private class DelayQueueEntry implements Delayed { |
||||
|
||||
private final long due; |
||||
private final BusEvent<?> evt; |
||||
|
||||
|
||||
public DelayQueueEntry(double seconds, BusEvent<?> event) |
||||
{ |
||||
super(); |
||||
this.due = System.currentTimeMillis() + (long) (seconds * 1000); |
||||
this.evt = event; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public int compareTo(Delayed o) |
||||
{ |
||||
return Long.valueOf(getDelay(TimeUnit.MILLISECONDS)).compareTo(o.getDelay(TimeUnit.MILLISECONDS)); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public long getDelay(TimeUnit unit) |
||||
{ |
||||
return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
|
||||
public BusEvent<?> getEvent() |
||||
{ |
||||
return evt; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Thread handling queued events |
||||
*/ |
||||
private class QueuePollingThread extends Thread { |
||||
|
||||
public volatile boolean stopped = false; |
||||
|
||||
|
||||
public QueuePollingThread() |
||||
{ |
||||
super("Queue Polling Thread"); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void run() |
||||
{ |
||||
DelayQueueEntry evt; |
||||
|
||||
while (!stopped) { |
||||
evt = null; |
||||
|
||||
try { |
||||
evt = sendQueue.take(); |
||||
} catch (final InterruptedException ignored) { |
||||
//
|
||||
} |
||||
|
||||
if (evt != null) { |
||||
try { |
||||
dispatch(evt.getEvent()); |
||||
} catch (final Throwable t) { |
||||
Log.w(logMark + "Error while dispatching event: ", t); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
static final String logMark = "(bus) "; |
||||
|
||||
|
||||
private static Class<?> getEventListenerClass(BusEvent<?> event) |
||||
{ |
||||
// BEHOLD, MAGIC!
|
||||
|
||||
final Type evtc = event.getClass().getGenericSuperclass(); |
||||
|
||||
if (evtc instanceof ParameterizedType) { |
||||
if (((ParameterizedType) evtc).getRawType() == BusEvent.class) { |
||||
final Type[] types = ((ParameterizedType) evtc).getActualTypeArguments(); |
||||
for (final Type genericType : types) { |
||||
return (Class<?>) genericType; |
||||
} |
||||
} |
||||
} |
||||
|
||||
throw new RuntimeException("Could not detect event listener type."); |
||||
} |
||||
|
||||
/** Log detailed messages (debug) */ |
||||
public boolean detailedLogging = false; |
||||
|
||||
/** Queue polling thread */ |
||||
private final QueuePollingThread busThread; |
||||
|
||||
/** Registered clients */ |
||||
private final Set<Object> clients = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>()); |
||||
|
||||
/** Whether the bus was destroyed */ |
||||
private boolean dead = false; |
||||
|
||||
/** Message channels */ |
||||
private final Set<EventChannel<?, ?>> channels = Collections.newSetFromMap(new ConcurrentHashMap<EventChannel<?, ?>, Boolean>()); |
||||
|
||||
/** Messages queued for delivery */ |
||||
private final DelayQueue<DelayQueueEntry> sendQueue = new DelayQueue<>(); |
||||
|
||||
|
||||
/** |
||||
* Make a new bus and start it's queue thread. |
||||
*/ |
||||
public EventBus() |
||||
{ |
||||
busThread = new QueuePollingThread(); |
||||
busThread.setDaemon(true); |
||||
busThread.start(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Halt bus thread and reject any future events. |
||||
*/ |
||||
@Override |
||||
public void destroy() |
||||
{ |
||||
assertLive(); |
||||
|
||||
busThread.stopped = true; |
||||
dead = true; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Send based on annotation |
||||
* |
||||
* @param event event |
||||
*/ |
||||
public void send(BusEvent<?> event) |
||||
{ |
||||
assertLive(); |
||||
|
||||
final DelayedEvent adelay = Utils.getAnnotation(event, DelayedEvent.class); |
||||
if (adelay != null) { |
||||
sendDelayed(event, adelay.delay()); |
||||
return; |
||||
} |
||||
|
||||
if (Utils.hasAnnotation(event, DirectEvent.class)) { |
||||
sendDirect(event); |
||||
return; |
||||
} |
||||
|
||||
sendQueued(event); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Add event to a queue |
||||
* |
||||
* @param event event |
||||
*/ |
||||
public void sendQueued(BusEvent<?> event) |
||||
{ |
||||
assertLive(); |
||||
|
||||
sendDelayed(event, 0); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Add event to a queue, scheduled for given time. |
||||
* |
||||
* @param event event |
||||
* @param delay delay before event is dispatched |
||||
*/ |
||||
public void sendDelayed(BusEvent<?> event, double delay) |
||||
{ |
||||
assertLive(); |
||||
|
||||
final DelayQueueEntry dm = new DelayQueueEntry(delay, event); |
||||
|
||||
if (shallLog(event)) { |
||||
Log.f3(logMark + "Qu [" + Log.str(event) + "]" + (delay == 0 ? "" : (", delay: " + delay + "s"))); |
||||
} |
||||
|
||||
sendQueue.add(dm); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Send immediately.<br> |
||||
* Should be used for real-time events that require immediate response, such |
||||
* as timing events. |
||||
* |
||||
* @param event event |
||||
*/ |
||||
public void sendDirect(BusEvent<?> event) |
||||
{ |
||||
assertLive(); |
||||
|
||||
if (shallLog(event)) Log.f3(logMark + "Di [" + Log.str(event) + "]"); |
||||
|
||||
dispatch(event); |
||||
} |
||||
|
||||
|
||||
public void sendDirectToChildren(DelegatingClient delegatingClient, BusEvent<?> event) |
||||
{ |
||||
assertLive(); |
||||
|
||||
if (shallLog(event)) Log.f3(logMark + "Di->sub [" + Log.str(event) + "]"); |
||||
|
||||
doDispatch(delegatingClient.getChildClients(), event); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Connect a client to the bus. The client will be connected to all current |
||||
* and future channels, until removed from the bus. |
||||
* |
||||
* @param client the client |
||||
*/ |
||||
public void subscribe(Object client) |
||||
{ |
||||
assertLive(); |
||||
|
||||
if (client == null) return; |
||||
|
||||
clients.add(client); |
||||
|
||||
if (detailedLogging) Log.f3(logMark + "Client joined: " + Log.str(client)); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Disconnect a client from the bus. |
||||
* |
||||
* @param client the client |
||||
*/ |
||||
public void unsubscribe(Object client) |
||||
{ |
||||
assertLive(); |
||||
|
||||
clients.remove(client); |
||||
|
||||
if (detailedLogging) Log.f3(logMark + "Client left: " + Log.str(client)); |
||||
} |
||||
|
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private boolean addChannelForEvent(BusEvent<?> event) |
||||
{ |
||||
try { |
||||
if (detailedLogging) { |
||||
Log.f3(logMark + "Setting up channel for new event type: " + Log.str(event.getClass())); |
||||
} |
||||
|
||||
final Class<?> listener = getEventListenerClass(event); |
||||
final EventChannel<?, ?> ch = EventChannel.create(event.getClass(), listener); |
||||
|
||||
if (ch.canBroadcast(event)) { |
||||
|
||||
channels.add(ch); |
||||
//channels.flush();
|
||||
|
||||
if (detailedLogging) { |
||||
Log.f3(logMark + "Created new channel: " + Log.str(event.getClass()) + " -> " + Log.str(listener)); |
||||
} |
||||
|
||||
return true; |
||||
|
||||
} else { |
||||
Log.w(logMark + "Could not create channel for event " + Log.str(event.getClass())); |
||||
} |
||||
|
||||
} catch (final Throwable t) { |
||||
Log.w(logMark + "Error while trying to add channel for event.", t); |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Make sure the bus is not destroyed. |
||||
* |
||||
* @throws IllegalStateException if the bus is dead. |
||||
*/ |
||||
private void assertLive() throws IllegalStateException |
||||
{ |
||||
if (dead) throw new IllegalStateException("EventBus is dead."); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Send immediately.<br> |
||||
* Should be used for real-time events that require immediate response, such |
||||
* as timing events. |
||||
* |
||||
* @param event event |
||||
*/ |
||||
private synchronized void dispatch(BusEvent<?> event) |
||||
{ |
||||
assertLive(); |
||||
|
||||
doDispatch(clients, event); |
||||
event.onDispatchComplete(this); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Send to a set of clients |
||||
* |
||||
* @param clients clients |
||||
* @param event event |
||||
*/ |
||||
private synchronized void doDispatch(Collection<?> clients, BusEvent<?> event) |
||||
{ |
||||
boolean accepted = false; |
||||
|
||||
event.clearFlags(); |
||||
|
||||
for (int i = 0; i < 2; i++) { // two tries.
|
||||
|
||||
for (final EventChannel<?, ?> b : channels) { |
||||
if (b.canBroadcast(event)) { |
||||
accepted = true; |
||||
b.broadcast(event, clients); |
||||
} |
||||
|
||||
if (event.isConsumed()) break; |
||||
} |
||||
|
||||
if (!accepted) if (addChannelForEvent(event)) continue; |
||||
|
||||
break; |
||||
} |
||||
|
||||
if (!accepted) Log.e(logMark + "Not accepted by any channel: " + Log.str(event)); |
||||
if (!event.wasServed() && shallLog(event)) Log.w(logMark + "Not delivered: " + Log.str(event)); |
||||
} |
||||
|
||||
|
||||
private boolean shallLog(BusEvent<?> event) |
||||
{ |
||||
if (!detailedLogging) return false; |
||||
if (Utils.hasAnnotation(event, NotLoggedEvent.class)) return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public EventBus getEventBus() |
||||
{ |
||||
return this; // just for compatibility use-case
|
||||
} |
||||
|
||||
} |
@ -1,207 +0,0 @@ |
||||
package mightypork.gamecore.eventbus; |
||||
|
||||
|
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
|
||||
import mightypork.gamecore.eventbus.clients.DelegatingClient; |
||||
import mightypork.gamecore.eventbus.clients.ToggleableClient; |
||||
import mightypork.gamecore.eventbus.event_flags.NonRejectableEvent; |
||||
import mightypork.gamecore.logging.Log; |
||||
import mightypork.gamecore.util.Utils; |
||||
|
||||
|
||||
/** |
||||
* Event delivery channel, module of {@link EventBus} |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
* @param <EVENT> event type |
||||
* @param <CLIENT> client (subscriber) type |
||||
*/ |
||||
class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> { |
||||
|
||||
private final Class<CLIENT> clientClass; |
||||
private final Class<EVENT> eventClass; |
||||
|
||||
|
||||
/** |
||||
* Create a channel |
||||
* |
||||
* @param eventClass event class
|
||||
* @param clientClass client class
|
||||
*/ |
||||
public EventChannel(Class<EVENT> eventClass, Class<CLIENT> clientClass) |
||||
{ |
||||
|
||||
if (eventClass == null || clientClass == null) { |
||||
throw new NullPointerException("Null Event or Client class."); |
||||
} |
||||
|
||||
this.clientClass = clientClass; |
||||
this.eventClass = eventClass; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Try to broadcast a event.<br> |
||||
* If event is of wrong type, <code>false</code> is returned. |
||||
* |
||||
* @param event a event to be sent |
||||
* @param clients collection of clients |
||||
*/ |
||||
public void broadcast(BusEvent<?> event, Collection<?> clients) |
||||
{ |
||||
if (!canBroadcast(event)) return; |
||||
|
||||
doBroadcast(eventClass.cast(event), clients, new HashSet<>()); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Send the event |
||||
* |
||||
* @param event sent event |
||||
* @param clients subscribing clients |
||||
* @param processed clients already processed |
||||
*/ |
||||
private void doBroadcast(final EVENT event, final Collection<?> clients, final Collection<Object> processed) |
||||
{ |
||||
for (final Object client : clients) { |
||||
|
||||
// exclude obvious non-clients
|
||||
if (!isClientValid(client)) { |
||||
continue; |
||||
} |
||||
|
||||
// avoid executing more times
|
||||
if (processed.contains(client)) { |
||||
Log.w(EventBus.logMark + "Client already served: " + Log.str(client)); |
||||
continue; |
||||
} |
||||
processed.add(client); |
||||
|
||||
final boolean must_deliver = Utils.hasAnnotation(event, NonRejectableEvent.class); |
||||
|
||||
// opt-out
|
||||
if (client instanceof ToggleableClient) { |
||||
if (!must_deliver && !((ToggleableClient) client).isListening()) continue; |
||||
} |
||||
|
||||
sendTo(client, event); |
||||
|
||||
if (event.isConsumed()) return; |
||||
|
||||
// pass on to delegated clients
|
||||
if (client instanceof DelegatingClient) { |
||||
if (must_deliver || ((DelegatingClient) client).doesDelegate()) { |
||||
|
||||
final Collection<?> children = ((DelegatingClient) client).getChildClients(); |
||||
|
||||
if (children != null && children.size() > 0) { |
||||
doBroadcast(event, children, processed); |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Send an event to a client. |
||||
* |
||||
* @param client target client |
||||
* @param event event to send |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
private void sendTo(Object client, EVENT event) |
||||
{ |
||||
if (isClientOfChannelType(client)) { |
||||
((BusEvent<CLIENT>) event).deliverTo((CLIENT) client); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Check if the given event can be broadcasted by this channel |
||||
* |
||||
* @param event event object |
||||
* @return can be broadcasted |
||||
*/ |
||||
public boolean canBroadcast(BusEvent<?> event) |
||||
{ |
||||
return event != null && eventClass.isInstance(event); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Create an instance for given types |
||||
* |
||||
* @param eventClass event class
|
||||
* @param clientClass client class
|
||||
* @return the broadcaster |
||||
*/ |
||||
public static <F_EVENT extends BusEvent<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> eventClass, Class<F_CLIENT> clientClass) |
||||
{ |
||||
return new EventChannel<>(eventClass, clientClass); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Check if client is of channel type |
||||
* |
||||
* @param client client |
||||
* @return is of type |
||||
*/ |
||||
private boolean isClientOfChannelType(Object client) |
||||
{ |
||||
return clientClass.isInstance(client); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Check if the channel is compatible with given |
||||
* |
||||
* @param client client |
||||
* @return is supported |
||||
*/ |
||||
public boolean isClientValid(Object client) |
||||
{ |
||||
return isClientOfChannelType(client) || (client instanceof DelegatingClient); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public int hashCode() |
||||
{ |
||||
final int prime = 13; |
||||
int result = 1; |
||||
result = prime * result + ((clientClass == null) ? 0 : clientClass.hashCode()); |
||||
result = prime * result + ((eventClass == null) ? 0 : eventClass.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean equals(Object obj) |
||||
{ |
||||
if (this == obj) return true; |
||||
if (obj == null) return false; |
||||
if (!(obj instanceof EventChannel)) return false; |
||||
final EventChannel<?, ?> other = (EventChannel<?, ?>) obj; |
||||
if (clientClass == null) { |
||||
if (other.clientClass != null) return false; |
||||
} else if (!clientClass.equals(other.clientClass)) return false; |
||||
if (eventClass == null) { |
||||
if (other.eventClass != null) return false; |
||||
} else if (!eventClass.equals(other.eventClass)) return false; |
||||
return true; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String toString() |
||||
{ |
||||
return "{ " + Log.str(eventClass) + " => " + Log.str(clientClass) + " }"; |
||||
} |
||||
} |
@ -1,115 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
import java.util.Collection; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
|
||||
import mightypork.gamecore.eventbus.BusAccess; |
||||
import mightypork.gamecore.eventbus.EventBus; |
||||
|
||||
|
||||
/** |
||||
* Client that can be attached to the {@link EventBus}, or added as a child |
||||
* client to another {@link DelegatingClient} |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public abstract class BusNode implements BusAccess, ClientHub { |
||||
|
||||
private final BusAccess busAccess; |
||||
|
||||
private final Set<Object> clients = new LinkedHashSet<>(); |
||||
private boolean listening = true; |
||||
private boolean delegating = true; |
||||
|
||||
|
||||
/** |
||||
* @param busAccess access to bus |
||||
*/ |
||||
public BusNode(BusAccess busAccess) |
||||
{ |
||||
this.busAccess = busAccess; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Collection<Object> getChildClients() |
||||
{ |
||||
return clients; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean doesDelegate() |
||||
{ |
||||
return delegating; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean isListening() |
||||
{ |
||||
return listening; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Add a child subscriber to the {@link EventBus}.<br> |
||||
* |
||||
* @param client |
||||
*/ |
||||
@Override |
||||
public void addChildClient(Object client) |
||||
{ |
||||
if (client instanceof RootBusNode) { |
||||
throw new IllegalArgumentException("Cannot nest RootBusNode."); |
||||
} |
||||
|
||||
clients.add(client); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Remove a child subscriber |
||||
* |
||||
* @param client subscriber to remove |
||||
*/ |
||||
@Override |
||||
public void removeChildClient(Object client) |
||||
{ |
||||
if (client != null) { |
||||
clients.remove(client); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Set whether events should be received. |
||||
* |
||||
* @param listening receive events |
||||
*/ |
||||
public void setListening(boolean listening) |
||||
{ |
||||
this.listening = listening; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Set whether events should be passed on to child nodes |
||||
* |
||||
* @param delegating |
||||
*/ |
||||
public void setDelegating(boolean delegating) |
||||
{ |
||||
this.delegating = delegating; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public EventBus getEventBus() |
||||
{ |
||||
return busAccess.getEventBus(); |
||||
} |
||||
|
||||
} |
@ -1,42 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
import java.util.Collection; |
||||
|
||||
import mightypork.gamecore.eventbus.EventBus; |
||||
|
||||
|
||||
/** |
||||
* Common methods for client hubs (ie delegating vlient implementations) |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface ClientHub extends DelegatingClient, ToggleableClient { |
||||
|
||||
@Override |
||||
public boolean doesDelegate(); |
||||
|
||||
|
||||
@Override |
||||
public Collection<Object> getChildClients(); |
||||
|
||||
|
||||
@Override |
||||
public boolean isListening(); |
||||
|
||||
|
||||
/** |
||||
* Add a child subscriber to the {@link EventBus}.<br> |
||||
* |
||||
* @param client |
||||
*/ |
||||
public void addChildClient(Object client); |
||||
|
||||
|
||||
/** |
||||
* Remove a child subscriber |
||||
* |
||||
* @param client subscriber to remove |
||||
*/ |
||||
void removeChildClient(Object client); |
||||
} |
@ -1,21 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
import java.util.ArrayList; |
||||
|
||||
|
||||
/** |
||||
* Array-list with varargs constructor |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public class ClientList extends ArrayList<Object> { |
||||
|
||||
public ClientList(Object... clients) |
||||
{ |
||||
for (final Object c : clients) { |
||||
super.add(c); |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,27 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
import java.util.Collection; |
||||
|
||||
|
||||
/** |
||||
* Client containing child clients. According to the contract, if the collection |
||||
* of clients is ordered, the clients will be served in that order. In any case, |
||||
* the {@link DelegatingClient} itself will be served beforehand. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface DelegatingClient { |
||||
|
||||
/** |
||||
* @return collection of child clients. Can not be null. |
||||
*/ |
||||
public Collection<?> getChildClients(); |
||||
|
||||
|
||||
/** |
||||
* @return true if delegating is active |
||||
*/ |
||||
public boolean doesDelegate(); |
||||
|
||||
} |
@ -1,51 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
import java.util.Collection; |
||||
|
||||
import mightypork.gamecore.gui.Enableable; |
||||
|
||||
|
||||
/** |
||||
* Basic delegating client |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public class DelegatingList extends ClientList implements DelegatingClient, Enableable { |
||||
|
||||
private boolean enabled = true; |
||||
|
||||
|
||||
public DelegatingList(Object... clients) |
||||
{ |
||||
super(clients); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Collection<?> getChildClients() |
||||
{ |
||||
return this; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean doesDelegate() |
||||
{ |
||||
return isEnabled(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void setEnabled(boolean yes) |
||||
{ |
||||
enabled = yes; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean isEnabled() |
||||
{ |
||||
return enabled; |
||||
} |
||||
} |
@ -1,45 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
import mightypork.gamecore.eventbus.BusAccess; |
||||
import mightypork.gamecore.eventbus.events.Destroyable; |
||||
import mightypork.gamecore.util.annot.DefaultImpl; |
||||
|
||||
|
||||
/** |
||||
* Bus node that should be directly attached to the bus. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public abstract class RootBusNode extends BusNode implements Destroyable { |
||||
|
||||
/** |
||||
* @param busAccess access to bus |
||||
*/ |
||||
public RootBusNode(BusAccess busAccess) |
||||
{ |
||||
super(busAccess); |
||||
|
||||
getEventBus().subscribe(this); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public final void destroy() |
||||
{ |
||||
deinit(); |
||||
|
||||
getEventBus().unsubscribe(this); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Deinitialize the subsystem<br> |
||||
* (called during destruction) |
||||
*/ |
||||
@DefaultImpl |
||||
protected void deinit() |
||||
{ |
||||
} |
||||
|
||||
} |
@ -1,16 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.clients; |
||||
|
||||
|
||||
/** |
||||
* Client that can toggle receiving messages. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface ToggleableClient { |
||||
|
||||
/** |
||||
* @return true if the client wants to receive messages |
||||
*/ |
||||
public boolean isListening(); |
||||
|
||||
} |
@ -1,22 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.event_flags; |
||||
|
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
|
||||
/** |
||||
* Event that should be queued with given delay (default: 0); |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Inherited |
||||
@Documented |
||||
public @interface DelayedEvent { |
||||
|
||||
/** |
||||
* @return event dispatch delay [seconds] |
||||
*/ |
||||
double delay() default 0; |
||||
} |
@ -1,16 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.event_flags; |
||||
|
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
|
||||
/** |
||||
* Event that should not be queued. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Inherited |
||||
@Documented |
||||
public @interface DirectEvent {} |
@ -1,21 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.event_flags; |
||||
|
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
|
||||
/** |
||||
* Event that cannot be consumed |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Target(ElementType.TYPE) |
||||
public @interface NonConsumableEvent { |
||||
|
||||
} |
@ -1,21 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.event_flags; |
||||
|
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
|
||||
/** |
||||
* Event that is forcibly delivered to all clients (bypass Toggleable etc) |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Target(ElementType.TYPE) |
||||
public @interface NonRejectableEvent { |
||||
|
||||
} |
@ -1,17 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.event_flags; |
||||
|
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
|
||||
/** |
||||
* Event that's not worth logging, unless there was an error with it.<br> |
||||
* Useful for common events that would otherwise clutter the log. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Inherited |
||||
@Documented |
||||
public @interface NotLoggedEvent {} |
@ -1,16 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.event_flags; |
||||
|
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
|
||||
/** |
||||
* Handled only by the first client, then discarded. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Inherited |
||||
@Documented |
||||
public @interface SingleReceiverEvent {} |
@ -1,24 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.events; |
||||
|
||||
|
||||
import mightypork.gamecore.eventbus.BusEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.NonConsumableEvent; |
||||
|
||||
|
||||
/** |
||||
* Invoke destroy() method of all subscribers. Used to deinit a system. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@DirectEvent |
||||
@NonConsumableEvent |
||||
public class DestroyEvent extends BusEvent<Destroyable> { |
||||
|
||||
@Override |
||||
public void handleBy(Destroyable handler) |
||||
{ |
||||
handler.destroy(); |
||||
} |
||||
|
||||
} |
@ -1,15 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.events; |
||||
|
||||
|
||||
/** |
||||
* Object that can be destroyed (free resources etc) |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface Destroyable { |
||||
|
||||
/** |
||||
* Destroy this object |
||||
*/ |
||||
public void destroy(); |
||||
} |
@ -1,38 +0,0 @@ |
||||
package mightypork.gamecore.eventbus.events; |
||||
|
||||
|
||||
import mightypork.gamecore.eventbus.BusEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.NonConsumableEvent; |
||||
import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; |
||||
import mightypork.gamecore.util.math.timing.Updateable; |
||||
|
||||
|
||||
/** |
||||
* Delta timing update event. Not logged. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@NotLoggedEvent |
||||
@DirectEvent |
||||
@NonConsumableEvent |
||||
public class UpdateEvent extends BusEvent<Updateable> { |
||||
|
||||
private final double deltaTime; |
||||
|
||||
|
||||
/** |
||||
* @param deltaTime time since last update (sec) |
||||
*/ |
||||
public UpdateEvent(double deltaTime) |
||||
{ |
||||
this.deltaTime = deltaTime; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void handleBy(Updateable handler) |
||||
{ |
||||
handler.update(deltaTime); |
||||
} |
||||
} |
@ -1,25 +0,0 @@ |
||||
package mightypork.gamecore.gui; |
||||
|
||||
|
||||
/** |
||||
* Can be enabled or disabled.<br> |
||||
* Implementations should take appropriate action (ie. stop listening to events, |
||||
* updating etc.) |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface Enableable { |
||||
|
||||
/** |
||||
* Change enabled state |
||||
* |
||||
* @param yes enabled |
||||
*/ |
||||
public void setEnabled(boolean yes); |
||||
|
||||
|
||||
/** |
||||
* @return true if enabled |
||||
*/ |
||||
public boolean isEnabled(); |
||||
} |
@ -1,385 +0,0 @@ |
||||
package mightypork.gamecore.logging; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.io.PrintWriter; |
||||
import java.io.StringWriter; |
||||
import java.util.HashMap; |
||||
import java.util.logging.Level; |
||||
|
||||
import mightypork.gamecore.logging.monitors.LogMonitor; |
||||
import mightypork.gamecore.logging.monitors.LogToSysoutMonitor; |
||||
import mightypork.gamecore.logging.writers.ArchivingLog; |
||||
import mightypork.gamecore.logging.writers.LogWriter; |
||||
import mightypork.gamecore.logging.writers.SimpleLog; |
||||
import mightypork.gamecore.util.annot.FactoryMethod; |
||||
import mightypork.gamecore.util.strings.StringUtils; |
||||
|
||||
|
||||
/** |
||||
* A log. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public class Log { |
||||
|
||||
private static LogWriter main = null; |
||||
private static boolean enabled = true; |
||||
private static final LogToSysoutMonitor sysoMonitor = new LogToSysoutMonitor(); |
||||
private static final long start_ms = System.currentTimeMillis(); |
||||
|
||||
private static HashMap<String, SimpleLog> logs = new HashMap<>(); |
||||
|
||||
|
||||
/** |
||||
* Create a logger. If another with the name already exists, it'll be |
||||
* retrieved instead of creating a new one. |
||||
* |
||||
* @param logName log name (used for filename, should be application-unique) |
||||
* @param logFile log file; old logs will be kept here too. |
||||
* @param oldLogsCount number of old logs to keep, -1 infinite, 0 none. |
||||
* @return the created Log instance |
||||
*/ |
||||
@FactoryMethod |
||||
public static synchronized LogWriter create(String logName, File logFile, int oldLogsCount) |
||||
{ |
||||
if (logs.containsKey(logName)) return logs.get(logName); |
||||
|
||||
final ArchivingLog log = new ArchivingLog(logName, logFile, oldLogsCount); |
||||
log.init(); |
||||
|
||||
logs.put(logName, log); |
||||
|
||||
return log; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Create a logger. If another with the name already exists, it'll be |
||||
* retrieved instead of creating a new one. |
||||
* |
||||
* @param logName log name (used for filename, must be application-unique) |
||||
* @param logFile log file; old logs will be kept here too. |
||||
* @return the created Log instance |
||||
*/ |
||||
@FactoryMethod |
||||
public static synchronized LogWriter create(String logName, File logFile) |
||||
{ |
||||
if (logs.containsKey(logName)) return logs.get(logName); |
||||
|
||||
final SimpleLog log = new SimpleLog(logName, logFile); |
||||
log.init(); |
||||
|
||||
logs.put(logName, log); |
||||
|
||||
return log; |
||||
} |
||||
|
||||
|
||||
public static void setMainLogger(LogWriter log) |
||||
{ |
||||
main = log; |
||||
} |
||||
|
||||
|
||||
public static void addMonitor(LogMonitor mon) |
||||
{ |
||||
assertInited(); |
||||
|
||||
main.addMonitor(mon); |
||||
} |
||||
|
||||
|
||||
public static void removeMonitor(LogMonitor mon) |
||||
{ |
||||
assertInited(); |
||||
|
||||
main.removeMonitor(mon); |
||||
} |
||||
|
||||
|
||||
private static void assertInited() |
||||
{ |
||||
if (main == null) throw new IllegalStateException("Main logger not initialized."); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log a message |
||||
* |
||||
* @param level message level |
||||
* @param msg message text |
||||
*/ |
||||
public static void log(Level level, String msg) |
||||
{ |
||||
if (enabled) { |
||||
sysoMonitor.onMessageLogged(level, formatMessage(level, msg, null, start_ms)); |
||||
|
||||
if (main != null) { |
||||
main.log(level, msg); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log a message |
||||
* |
||||
* @param level message level |
||||
* @param msg message text |
||||
* @param t thrown exception |
||||
*/ |
||||
public static void log(Level level, String msg, Throwable t) |
||||
{ |
||||
if (enabled) { |
||||
sysoMonitor.onMessageLogged(level, formatMessage(level, msg, t, start_ms)); |
||||
|
||||
if (main != null) { |
||||
main.log(level, msg, t); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log FINE message |
||||
* |
||||
* @param msg message |
||||
*/ |
||||
public static void f1(String msg) |
||||
{ |
||||
log(Level.FINE, msg); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log FINER message |
||||
* |
||||
* @param msg message |
||||
*/ |
||||
public static void f2(String msg) |
||||
{ |
||||
log(Level.FINER, msg); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log FINEST message |
||||
* |
||||
* @param msg message |
||||
*/ |
||||
public static void f3(String msg) |
||||
{ |
||||
log(Level.FINEST, msg); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log INFO message |
||||
* |
||||
* @param msg message |
||||
*/ |
||||
public static void i(String msg) |
||||
{ |
||||
log(Level.INFO, msg); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log WARNING message (less severe than ERROR) |
||||
* |
||||
* @param msg message |
||||
*/ |
||||
public static void w(String msg) |
||||
{ |
||||
log(Level.WARNING, msg); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log ERROR message |
||||
* |
||||
* @param msg message |
||||
*/ |
||||
public static void e(String msg) |
||||
{ |
||||
log(Level.SEVERE, msg); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log warning message with exception |
||||
* |
||||
* @param msg message |
||||
* @param thrown thrown exception |
||||
*/ |
||||
public static void w(String msg, Throwable thrown) |
||||
{ |
||||
log(Level.WARNING, msg, thrown); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log exception thrown as warning |
||||
* |
||||
* @param thrown thrown exception |
||||
*/ |
||||
public static void w(Throwable thrown) |
||||
{ |
||||
log(Level.WARNING, null, thrown); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log error message |
||||
* |
||||
* @param msg message |
||||
* @param thrown thrown exception |
||||
*/ |
||||
public static void e(String msg, Throwable thrown) |
||||
{ |
||||
log(Level.SEVERE, msg, thrown); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log exception thrown as error |
||||
* |
||||
* @param thrown thrown exception |
||||
*/ |
||||
public static void e(Throwable thrown) |
||||
{ |
||||
log(Level.SEVERE, null, thrown); |
||||
} |
||||
|
||||
|
||||
public static void enable(boolean flag) |
||||
{ |
||||
enabled = flag; |
||||
} |
||||
|
||||
|
||||
public static void setSysoutLevel(Level level) |
||||
{ |
||||
sysoMonitor.setLevel(level); |
||||
} |
||||
|
||||
|
||||
public static void setLevel(Level level) |
||||
{ |
||||
assertInited(); |
||||
|
||||
main.setLevel(level); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Get stack trace from throwable |
||||
* |
||||
* @param t |
||||
* @return trace |
||||
*/ |
||||
public static String getStackTrace(Throwable t) |
||||
{ |
||||
final StringWriter sw = new StringWriter(); |
||||
final PrintWriter pw = new PrintWriter(sw, true); |
||||
t.printStackTrace(pw); |
||||
pw.flush(); |
||||
sw.flush(); |
||||
return sw.toString(); |
||||
} |
||||
|
||||
|
||||
public static String formatMessage(Level level, String message, Throwable throwable, long start_ms) |
||||
{ |
||||
if (message == null) message = ""; |
||||
|
||||
final String nl = System.getProperty("line.separator"); |
||||
|
||||
if (message.length() > 0) { |
||||
if (message.equals("\n")) { |
||||
return nl; |
||||
} |
||||
|
||||
if (message.charAt(0) == '\n') { |
||||
message = nl + message.substring(1); |
||||
} |
||||
} |
||||
|
||||
final long time_ms = (System.currentTimeMillis() - start_ms); |
||||
final double time_s = time_ms / 1000D; |
||||
final String time = String.format("%6.2f ", time_s); |
||||
final String time_blank = StringUtils.repeat(" ", time.length()); |
||||
|
||||
String prefix = "[ ? ]"; |
||||
|
||||
if (level == Level.FINE) { |
||||
prefix = "[ # ] "; |
||||
} else if (level == Level.FINER) { |
||||
prefix = "[ - ] "; |
||||
} else if (level == Level.FINEST) { |
||||
prefix = "[ ] "; |
||||
} else if (level == Level.INFO) { |
||||
prefix = "[ i ] "; |
||||
} else if (level == Level.SEVERE) { |
||||
prefix = "[!E!] "; |
||||
} else if (level == Level.WARNING) { |
||||
prefix = "[!W!] "; |
||||
} |
||||
|
||||
message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl; |
||||
|
||||
if (throwable != null) { |
||||
message += getStackTrace(throwable); |
||||
} |
||||
|
||||
return message; |
||||
} |
||||
|
||||
|
||||
public static String str(Object o) |
||||
{ |
||||
|
||||
if (o == null) return "<null>"; |
||||
|
||||
boolean hasToString = false; |
||||
|
||||
try { |
||||
hasToString = (o.getClass().getMethod("toString").getDeclaringClass() != Object.class); |
||||
} catch (final Throwable t) { |
||||
// oh well..
|
||||
} |
||||
|
||||
if (hasToString) { |
||||
return o.toString(); |
||||
} else { |
||||
|
||||
return str(o.getClass()); |
||||
} |
||||
} |
||||
|
||||
|
||||
public static String str(Class<?> cls) |
||||
{ |
||||
final LogAlias ln = cls.getAnnotation(LogAlias.class); |
||||
if (ln != null) { |
||||
return ln.name(); |
||||
} |
||||
|
||||
String name = cls.getName(); |
||||
|
||||
String sep = ""; |
||||
|
||||
if (name.contains("$")) { |
||||
name = name.substring(name.lastIndexOf("$") + 1); |
||||
sep = "$"; |
||||
} else { |
||||
name = name.substring(name.lastIndexOf(".") + 1); |
||||
sep = "."; |
||||
} |
||||
|
||||
final Class<?> enclosing = cls.getEnclosingClass(); |
||||
|
||||
return (enclosing == null ? "" : str(enclosing) + sep) + name; |
||||
} |
||||
} |
@ -1,21 +0,0 @@ |
||||
package mightypork.gamecore.logging; |
||||
|
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.Inherited; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
|
||||
|
||||
/** |
||||
* Specify pretty name to be used when logging (eg. <code>Log.str()</code>) |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Inherited |
||||
@Documented |
||||
public @interface LogAlias { |
||||
|
||||
String name(); |
||||
} |
@ -1,39 +0,0 @@ |
||||
package mightypork.gamecore.logging.monitors; |
||||
|
||||
|
||||
import java.util.logging.Level; |
||||
|
||||
|
||||
public abstract class BaseLogMonitor implements LogMonitor { |
||||
|
||||
private boolean enabled = true; |
||||
private Level accepted = Level.ALL; |
||||
|
||||
|
||||
@Override |
||||
public void onMessageLogged(Level level, String message) |
||||
{ |
||||
if (!enabled) return; |
||||
if (accepted.intValue() > level.intValue()) return; |
||||
|
||||
logMessage(level, message); |
||||
} |
||||
|
||||
|
||||
protected abstract void logMessage(Level level, String message); |
||||
|
||||
|
||||
@Override |
||||
public void setLevel(Level level) |
||||
{ |
||||
this.accepted = level; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void enable(boolean flag) |
||||
{ |
||||
this.enabled = flag; |
||||
} |
||||
|
||||
} |
@ -1,17 +0,0 @@ |
||||
package mightypork.gamecore.logging.monitors; |
||||
|
||||
|
||||
import java.util.logging.Level; |
||||
|
||||
|
||||
public interface LogMonitor { |
||||
|
||||
void onMessageLogged(Level level, String message); |
||||
|
||||
|
||||
void setLevel(Level level); |
||||
|
||||
|
||||
void enable(boolean flag); |
||||
|
||||
} |
@ -1,19 +0,0 @@ |
||||
package mightypork.gamecore.logging.monitors; |
||||
|
||||
|
||||
import java.util.logging.Level; |
||||
|
||||
|
||||
public class LogToSysoutMonitor extends BaseLogMonitor { |
||||
|
||||
@Override |
||||
protected void logMessage(Level level, String message) |
||||
{ |
||||
if (level == Level.SEVERE || level == Level.WARNING) { |
||||
System.err.print(message); |
||||
} else { |
||||
System.out.print(message); |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,129 +0,0 @@ |
||||
package mightypork.gamecore.logging.writers; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.io.FileFilter; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Collections; |
||||
import java.util.Comparator; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
|
||||
import mightypork.gamecore.util.files.FileUtils; |
||||
|
||||
|
||||
/** |
||||
* Logger that cleans directory & archives old logs |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
* @copy (c) 2014 |
||||
*/ |
||||
public class ArchivingLog extends SimpleLog { |
||||
|
||||
/** Number of old logs to keep */ |
||||
private final int logs_to_keep; |
||||
|
||||
|
||||
/** |
||||
* Log |
||||
* |
||||
* @param name log name |
||||
* @param file log file (in log directory) |
||||
* @param oldLogCount number of old log files to keep: -1 all, 0 none. |
||||
*/ |
||||
public ArchivingLog(String name, File file, int oldLogCount) |
||||
{ |
||||
super(name, file); |
||||
this.logs_to_keep = oldLogCount; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Log, not keeping 5 last log files (default); |
||||
* |
||||
* @param name log name |
||||
* @param file log file (in log directory) |
||||
*/ |
||||
public ArchivingLog(String name, File file) |
||||
{ |
||||
super(name, file); |
||||
this.logs_to_keep = 5; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void init() |
||||
{ |
||||
cleanLoggingDirectory(); |
||||
|
||||
super.init(); |
||||
} |
||||
|
||||
|
||||
private void cleanLoggingDirectory() |
||||
{ |
||||
if (logs_to_keep == 0) return; // overwrite
|
||||
|
||||
final File log_file = getFile(); |
||||
final File log_dir = log_file.getParentFile(); |
||||
final String fname = FileUtils.getBasename(log_file.toString()); |
||||
|
||||
// move old file
|
||||
for (final File f : FileUtils.listDirectory(log_dir)) { |
||||
if (!f.isFile()) continue; |
||||
if (f.equals(getFile())) { |
||||
|
||||
final Date d = new Date(f.lastModified()); |
||||
final String fbase = fname + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d); |
||||
final String suff = "." + getSuffix(); |
||||
String cntStr = ""; |
||||
File f2; |
||||
|
||||
for (int cnt = 0; (f2 = new File(log_dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {} |
||||
|
||||
if (!f.renameTo(f2)) throw new RuntimeException("Could not move log file."); |
||||
} |
||||
} |
||||
|
||||
if (logs_to_keep == -1) return; // keep all
|
||||
|
||||
final List<File> oldLogs = FileUtils.listDirectory(log_dir, new FileFilter() { |
||||
|
||||
@Override |
||||
public boolean accept(File f) |
||||
{ |
||||
if (f.isDirectory()) return false; |
||||
if (!f.getName().endsWith(getSuffix())) return false; |
||||
if (!f.getName().startsWith(fname)) return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
}); |
||||
|
||||
Collections.sort(oldLogs, new Comparator<File>() { |
||||
|
||||
@Override |
||||
public int compare(File o1, File o2) |
||||
{ |
||||
return o1.getName().compareTo(o2.getName()); |
||||
} |
||||
}); |
||||
|
||||
// playing with fireee
|
||||
for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) { |
||||
if (!oldLogs.get(i).delete()) { |
||||
throw new RuntimeException("Could not delete old log file."); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @return log filename suffix |
||||
*/ |
||||
private String getSuffix() |
||||
{ |
||||
return FileUtils.getExtension(getFile()); |
||||
} |
||||
} |
@ -1,72 +0,0 @@ |
||||
package mightypork.gamecore.logging.writers; |
||||
|
||||
|
||||
import java.util.logging.Level; |
||||
|
||||
import mightypork.gamecore.logging.monitors.LogMonitor; |
||||
|
||||
|
||||
/** |
||||
* Log interface
|
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public interface LogWriter { |
||||
|
||||
/** |
||||
* Prepare logs for logging |
||||
*/ |
||||
void init(); |
||||
|
||||
|
||||
/** |
||||
* Add log monitor |
||||
* |
||||
* @param mon monitor |
||||
*/ |
||||
void addMonitor(LogMonitor mon); |
||||
|
||||
|
||||
/** |
||||
* Remove a monitor |
||||
* |
||||
* @param removed monitor to remove |
||||
*/ |
||||
void removeMonitor(LogMonitor removed); |
||||
|
||||
|
||||
/** |
||||
* Set logging level |
||||
* |
||||
* @param level |
||||
*/ |
||||
void setLevel(Level level); |
||||
|
||||
|
||||
/** |
||||
* Enable logging. |
||||
* |
||||
* @param flag do enable logging |
||||
*/ |
||||
void enable(boolean flag); |
||||
|
||||
|
||||
/** |
||||
* Log a message |
||||
* |
||||
* @param level message level |
||||
* @param msg message text |
||||
*/ |
||||
void log(Level level, String msg); |
||||
|
||||
|
||||
/** |
||||
* Log a message |
||||
* |
||||
* @param level message level |
||||
* @param msg message text |
||||
* @param t thrown exception |
||||
*/ |
||||
void log(Level level, String msg, Throwable t); |
||||
|
||||
} |
@ -1,166 +0,0 @@ |
||||
package mightypork.gamecore.logging.writers; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
import java.util.HashSet; |
||||
import java.util.logging.FileHandler; |
||||
import java.util.logging.Formatter; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.LogRecord; |
||||
import java.util.logging.Logger; |
||||
|
||||
import mightypork.gamecore.logging.Log; |
||||
import mightypork.gamecore.logging.monitors.LogMonitor; |
||||
|
||||
|
||||
/** |
||||
* Basic logger |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public class SimpleLog implements LogWriter { |
||||
|
||||
/** |
||||
* Log file formatter. |
||||
*/ |
||||
class LogFormatter extends Formatter { |
||||
|
||||
@Override |
||||
public String format(LogRecord record) |
||||
{ |
||||
return Log.formatMessage(record.getLevel(), record.getMessage(), record.getThrown(), started_ms); |
||||
} |
||||
} |
||||
|
||||
/** Log file */ |
||||
private final File file; |
||||
|
||||
/** Log name */ |
||||
private final String name; |
||||
|
||||
/** Logger instance. */ |
||||
private Logger logger; |
||||
|
||||
private boolean enabled = true; |
||||
private final HashSet<LogMonitor> monitors = new HashSet<>(); |
||||
private final long started_ms; |
||||
|
||||
|
||||
public SimpleLog(String name, File file) |
||||
{ |
||||
this.name = name; |
||||
this.file = file; |
||||
this.started_ms = System.currentTimeMillis(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void init() |
||||
{ |
||||
logger = Logger.getLogger(getName()); |
||||
|
||||
FileHandler handler = null; |
||||
try { |
||||
handler = new FileHandler(getFile().getPath()); |
||||
} catch (final Throwable t) { |
||||
throw new RuntimeException("Failed to init log.", t); |
||||
} |
||||
|
||||
handler.setFormatter(new LogFormatter()); |
||||
logger.addHandler(handler); |
||||
logger.setUseParentHandlers(false); |
||||
logger.setLevel(Level.ALL); |
||||
|
||||
printHeader(); |
||||
} |
||||
|
||||
|
||||
protected void printHeader() |
||||
{ |
||||
final String stamp = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); |
||||
log(Level.INFO, "Logger \"" + getName() + "\" initialized.\n" + stamp); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Add log monitor |
||||
* |
||||
* @param mon monitor |
||||
*/ |
||||
@Override |
||||
public synchronized void addMonitor(LogMonitor mon) |
||||
{ |
||||
monitors.add(mon); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Remove a monitor |
||||
* |
||||
* @param removed monitor to remove |
||||
*/ |
||||
@Override |
||||
public synchronized void removeMonitor(LogMonitor removed) |
||||
{ |
||||
monitors.remove(removed); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void setLevel(Level level) |
||||
{ |
||||
logger.setLevel(level); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void enable(boolean flag) |
||||
{ |
||||
enabled = flag; |
||||
} |
||||
|
||||
|
||||
public File getFile() |
||||
{ |
||||
return file; |
||||
} |
||||
|
||||
|
||||
public String getName() |
||||
{ |
||||
return name; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void log(Level level, String msg) |
||||
{ |
||||
if (enabled) { |
||||
logger.log(level, msg); |
||||
|
||||
final String fmt = Log.formatMessage(level, msg, null, started_ms); |
||||
|
||||
for (final LogMonitor mon : monitors) { |
||||
mon.onMessageLogged(level, fmt); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void log(Level level, String msg, Throwable t) |
||||
{ |
||||
if (enabled) { |
||||
logger.log(level, msg, t); |
||||
|
||||
final String fmt = Log.formatMessage(level, msg, t, started_ms); |
||||
|
||||
for (final LogMonitor mon : monitors) { |
||||
mon.onMessageLogged(level, fmt); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,32 +0,0 @@ |
||||
package mightypork.gamecore.resources; |
||||
|
||||
|
||||
import mightypork.gamecore.util.math.Calc; |
||||
|
||||
|
||||
public class Profiler { |
||||
|
||||
public static long begin() |
||||
{ |
||||
return System.currentTimeMillis(); |
||||
} |
||||
|
||||
|
||||
public static double end(long begun) |
||||
{ |
||||
return endLong(begun) / 1000D; |
||||
} |
||||
|
||||
|
||||
public static long endLong(long begun) |
||||
{ |
||||
return System.currentTimeMillis() - begun; |
||||
} |
||||
|
||||
|
||||
public static String endStr(long begun) |
||||
{ |
||||
return Calc.toString(end(begun)) + " s"; |
||||
} |
||||
|
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue