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