Files
event/manager.go
2019-04-01 20:38:16 +08:00

389 lines
8.8 KiB
Go

package event
import (
"regexp"
"strings"
)
// Wildcard event name
const Wildcard = "*"
// regex for check good event name.
var goodNameReg = regexp.MustCompile(`^[a-zA-Z][\w-.*]*$`)
// M is short name fo map[string]...
type M map[string]interface{}
// ManagerFace event manager interface
type ManagerFace interface {
// events:
// add event
AddEvent(Event)
// listeners:
// add listeners
On(name string, listener Listener, priority ...int)
// fire event
Fire(name string, params M) (error, Event)
}
// Manager event manager definition. for manage events and listeners
type Manager struct {
name string
// pool sync.Pool
// is an sample for new BasicEvent
sample *BasicEvent
// storage user custom Event instance. you can pre-define some Event instances.
events map[string]Event
// storage all event name and ListenerQueue map
listeners map[string]*ListenerQueue
// storage all event names by listened
listenedNames map[string]int
}
// NewManager create event manager
func NewManager(name string) *Manager {
em := &Manager{
name: name,
sample: &BasicEvent{},
events: make(map[string]Event),
// listeners
listeners: make(map[string]*ListenerQueue),
listenedNames: make(map[string]int),
}
return em
}
/*************************************************************
* Listener Manage: - register listener
*************************************************************/
// AddListener alias of the method On()
func (em *Manager) AddListener(name string, listener Listener, priority ...int) {
em.On(name, listener, priority...)
}
// On register a event handler/listener. can setting priority.
// Usage:
// On("evt0", listener)
// On("evt0", listener, High)
func (em *Manager) On(name string, listener Listener, priority ...int) {
pv := Normal
if len(priority) > 0 {
pv = priority[0]
}
em.addListenerItem(name, &ListenerItem{pv, listener})
}
// AddSubscriber add events by subscriber interface.
// you can register multi event listeners in a struct func.
// more usage please see README or test.
func (em *Manager) AddSubscriber(sbr Subscriber) {
for name, listener := range sbr.SubscribedEvents() {
switch lt := listener.(type) {
case Listener:
em.On(name, lt)
// case ListenerFunc:
// em.On(name, lt)
case ListenerItem:
em.addListenerItem(name, &lt)
default:
panic("event: the value must be an Listener or ListenerItem instance")
}
}
}
func (em *Manager) addListenerItem(name string, li *ListenerItem) {
if name != Wildcard {
name = goodName(name)
}
if li.Listener == nil {
panic("event: the event '" + name + "' listener cannot be empty")
}
// exists, append it.
if lq, ok := em.listeners[name]; ok {
lq.Push(li)
} else { // first add.
em.listenedNames[name] = 1
em.listeners[name] = (&ListenerQueue{}).Push(li)
}
}
/*************************************************************
* Listener Manage: - trigger event
*************************************************************/
// MustFire fire event by name. will panic on error
func (em *Manager) MustFire(name string, params M) Event {
err, e := em.Fire(name, params)
if err != nil {
panic(err)
}
return e
}
// Trigger alias of the method Fire()
func (em *Manager) Trigger(name string, params M) (error, Event) {
return em.Fire(name, params)
}
// Fire trigger event by name
func (em *Manager) Fire(name string, params M) (err error, e Event) {
name = goodName(name)
// not found listeners
if !em.HasListeners(name) {
return
}
// call listeners use defined Event
if e, ok := em.events[name]; ok {
if params != nil {
e.SetData(params)
}
err = em.FireEvent(e)
return err, e
}
// create a basic event instance
e = em.newBasicEvent(name, params)
// call listeners handle event
err = em.FireEvent(e)
return
}
// AsyncFire async fire event by 'go' keywords
func (em *Manager) AsyncFire(e Event) {
go func(e Event) {
_ = em.FireEvent(e)
}(e)
}
// AwaitFire async fire event by 'go' keywords, but will wait return result
func (em *Manager) AwaitFire(e Event) (err error) {
ch := make(chan error)
go func(e Event) {
err := em.FireEvent(e)
ch <- err
}(e)
err = <-ch
return
}
// FireBatch fire multi event at once.
// Usage:
// FireBatch("name1", "name2", &MyEvent{})
func (em *Manager) FireBatch(es ...interface{}) (ers []error) {
var err error
for _, e := range es {
if name, ok := e.(string); ok {
err, _ = em.Fire(name, nil)
} else if evt, ok := e.(Event); ok {
err = em.FireEvent(evt)
} // ignore invalid param.
if err != nil {
ers = append(ers, err)
}
}
return
}
// FireEvent fire event by given Event instance
func (em *Manager) FireEvent(e Event) (err error) {
// ensure aborted is false.
e.Abort(false)
name := e.Name()
// find matched listeners
lq, ok := em.listeners[name]
if ok {
// sort by priority before call.
for _, li := range lq.Sort().Items() {
err = li.Listener.Handle(e)
if err != nil || e.IsAborted() {
return
}
}
}
// has group listeners. "app.*" "app.db.*"
// eg: "app.run" will trigger listeners on the "app.*"
pos := strings.LastIndexByte(name, '.')
if pos > 0 && pos < len(name) {
groupName := name[:pos+1] + Wildcard // "app.*"
if lq, ok := em.listeners[groupName]; ok {
for _, li := range lq.Sort().Items() {
err = li.Listener.Handle(e)
if err != nil || e.IsAborted() {
return
}
}
}
}
// has wildcard event listeners
if lq, ok := em.listeners[Wildcard]; ok {
for _, li := range lq.Sort().Items() {
err = li.Listener.Handle(e)
if err != nil || e.IsAborted() {
break
}
}
}
return
}
/*************************************************************
* Event Manage
*************************************************************/
// AddEvent add a defined event instance to manager.
func (em *Manager) AddEvent(e Event) {
name := goodName(e.Name())
em.events[name] = e
}
// GetEvent get a defined event instance by name
func (em *Manager) GetEvent(name string) (e Event, ok bool) {
e, ok = em.events[name]
return
}
// HasEvent has event check
func (em *Manager) HasEvent(name string) bool {
_, ok := em.events[name]
return ok
}
// RemoveEvent delete Event by name
func (em *Manager) RemoveEvent(name string) {
if _, ok := em.events[name]; ok {
delete(em.events, name)
}
}
// RemoveEvents remove all registered events
func (em *Manager) RemoveEvents() {
em.events = map[string]Event{}
}
/*************************************************************
* Helper Methods
*************************************************************/
// newBasicEvent create new BasicEvent by clone em.sample
func (em *Manager) newBasicEvent(name string, data M) *BasicEvent {
var cp = *em.sample
cp.SetName(name)
cp.SetData(data)
return &cp
}
// HasListeners has listeners for the event name.
func (em *Manager) HasListeners(name string) bool {
_, ok := em.listenedNames[name]
return ok
}
// Listeners get all listeners
func (em *Manager) Listeners() map[string]*ListenerQueue {
return em.listeners
}
// ListenersByName get listeners by given event name
func (em *Manager) ListenersByName(name string) *ListenerQueue {
return em.listeners[name]
}
// ListenersCount get listeners number for the event name.
func (em *Manager) ListenersCount(name string) int {
if lq, ok := em.listeners[name]; ok {
return lq.Len()
}
return 0
}
// ListenedNames get listened event names
func (em *Manager) ListenedNames() map[string]int {
return em.listenedNames
}
// RemoveListener remove a given listener, you can limit event name.
// Usage:
// RemoveListener("", listener)
// RemoveListener("name", listener) // limit event name.
func (em *Manager) RemoveListener(name string, listener Listener) {
if name != "" {
if lq, ok := em.listeners[name]; ok {
lq.Remove(listener)
// delete from manager
if lq.IsEmpty() {
delete(em.listeners, name)
delete(em.listenedNames, name)
}
}
return
}
// name is empty. find all listener and remove matched.
for name, lq := range em.listeners {
lq.Remove(listener)
// delete from manager
if lq.IsEmpty() {
delete(em.listeners, name)
delete(em.listenedNames, name)
}
}
}
// RemoveListeners remove listeners by given name
func (em *Manager) RemoveListeners(name string) {
_, ok := em.listenedNames[name]
if ok {
em.listeners[name].Clear()
// delete from manager
delete(em.listeners, name)
delete(em.listenedNames, name)
}
}
// Clear all data
func (em *Manager) Clear() {
// clear all listeners
for _, lq := range em.listeners {
lq.Clear()
}
// reset all
em.name = ""
em.events = make(map[string]Event)
em.listeners = make(map[string]*ListenerQueue)
em.listenedNames = make(map[string]int)
}
func goodName(name string) string {
name = strings.TrimSpace(name)
if name == "" {
panic("event: the event name cannot be empty")
}
if !goodNameReg.MatchString(name) {
panic(`event: the event name is invalid, must match regex '^[a-zA-Z][\w-.]*$'`)
}
return name
}