--- xwiki-platform-core/src/main/java/org/xwiki/observation/ObservationManager.java	Fri Jul 20 09:03:25 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/ObservationManager.java	Fri Jul 20 09:03:25 CEST 2007
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation;
+
+import org.xwiki.observation.event.Event;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class ObservationManager
+{
+    /**
+     * Registered listeners.
+     * The Map key is the Event class name and the values is a List with (Event, Listener) pairs.
+     */
+    private Map listeners = new HashMap();
+
+    /**
+     * Add a listener.
+     *
+     * @param event the event to register the listener against
+     * @param eventListener the listener to register
+     */
+    public void addListener(Event event, EventListener eventListener)
+    {
+        // Check if this is a new Event not already registered
+        List eventListeners = (List) this.listeners.get(event.getClass().getName());
+        if (eventListeners == null) {
+            eventListeners = new ArrayList();
+            this.listeners.put(event.getClass().getName(),  eventListeners);
+        }
+
+        // Check to see if the event/listener pair is already there
+        for (Iterator it = eventListeners.iterator(); it.hasNext(); ) {
+            WeakReference reference = (WeakReference) it.next();
+            Object[] pairs = (Object[]) reference.get();
+            if (pairs[1] == eventListener) {
+                return;
+            }
+        }
+
+        // Add the event/listener pair
+        eventListeners.add(new WeakReference(new Object[] {event, eventListener}));
+    }
+
+    /**
+     * Remove a listener from a specific event.
+     *
+     * @param event the event to remove the listener from.
+     * @param eventListener the listener to remove.
+     */
+    public void removeListener(Event event, EventListener eventListener)
+    {
+        if (event == null) {
+            removeListener(eventListener);
+            return;
+        }
+
+        // Find the event key for the specified event
+        List eventListeners = (List) this.listeners.get(event.getClass().getName());
+        if (eventListeners == null) {
+            return;
+        }
+
+        // Remove the listener
+        for (Iterator it = eventListeners.iterator(); it.hasNext();) {
+            WeakReference reference = (WeakReference) it.next();
+            Object[] pairs = (Object[]) reference.get();
+            if (pairs[1] == eventListener) {
+                it.remove();
+                break;
+            }
+        }
+
+        // Remove the event as such if this was the last listener for this event
+        if (eventListeners.size() == 0) {
+            this.listeners.remove(event);
+        }
+    }
+
+    /**
+     * Remove a listener from all events it is registered by. Convenient way of cleaning up an
+     * listener object being destroyed.
+     *
+     * @param eventListener the listener to remove.
+     */
+    public void removeListener(EventListener eventListener)
+    {
+        // Loop over all registered events and remove the specified listener
+        // Loop over a copy in case the removeListener() call wants to
+        // remove the entire event from the map.
+        List listeners = new ArrayList(this.listeners.keySet());
+        for (Iterator it = listeners.iterator(); it.hasNext();) {
+            Object[] pairs = (Object[]) it.next();
+            if (pairs[1] == eventListener) {
+                removeListener((Event) pairs[0], eventListener);
+            }
+        }
+    }
+
+    /**
+     * Call the registered listeners. The definition of <em>source</em> and <em>data</em> is
+     * purely up to the communicating classes.
+     *
+     * @param event the event to pass to the registered listeners
+     * @param source the source of the event (or null)
+     * @param data the additional data related to the event (or null)
+     */
+    public void notify(Event event, Object source, Object data)
+    {
+        // Find all event/listeners pairs for this event
+        List eventListeners = (List) this.listeners.get(event.getClass().getName());
+        if (eventListeners == null) {
+            return;
+        }
+
+        // Loop over a copy of the list in case it is altered by a listener
+        List copy = new ArrayList(eventListeners);
+        for (Iterator it = copy.iterator(); it.hasNext();) {
+            WeakReference reference = (WeakReference) it.next();
+            Object[] pairs = (Object[]) reference.get();
+            if (pairs[1] == null) {
+                it.remove();
+            } else {
+                Event storedEvent = (Event) pairs[0];
+                if (storedEvent.matches(event)) {
+                    EventListener eventListener = (EventListener) pairs[1];
+                    eventListener.onEvent(event, source, data);
+                }
+            }
+        }
+    }
+
+    /**
+     * Convenience front-end where the additional data parameter is null.
+     *
+     * @param event the event to pass to the registered listeners
+     * @param source the source of the event (or null)
+     * @see #notify(org.xwiki.observation.event.Event, Object, Object)
+     */
+    public void notify(Event event, Object source)
+    {
+        notify(event, source, null);
+    }
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/EventListener.java	Tue Jul 17 12:30:55 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/EventListener.java	Tue Jul 17 12:30:55 CEST 2007
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation;
+
+import org.xwiki.observation.event.Event;
+
+public interface EventListener
+{
+    void onEvent(Event event, Object source, Object data);
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/filter/RegexEventFilter.java	Fri Jul 20 09:59:42 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/filter/RegexEventFilter.java	Fri Jul 20 09:59:42 CEST 2007
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event.filter;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class RegexEventFilter
+{
+    private String filter;
+    private Pattern pattern;
+
+    public RegexEventFilter(String filter)
+    {
+        this.filter = filter;
+        this.pattern = Pattern.compile(filter);
+    }
+
+    public String getFilter()
+    {
+        return this.filter;
+    }
+
+    public boolean matches(EventFilter eventFilter)
+    {
+        Matcher matcher = this.pattern.matcher(eventFilter.getFilter());
+        return matcher.matches();
+    }
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/filter/EventFilter.java	Fri Jul 20 08:57:21 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/filter/EventFilter.java	Fri Jul 20 08:57:21 CEST 2007
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event.filter;
+
+public interface EventFilter
+{
+    String getFilter();
+    boolean matches(EventFilter eventFilter);
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/AbstractDocumentEvent.java	Fri Jul 20 09:01:53 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/AbstractDocumentEvent.java	Fri Jul 20 09:01:53 CEST 2007
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event;
+
+import org.xwiki.observation.event.filter.EventFilter;
+import org.xwiki.observation.event.filter.FixedNameEventFilter;
+
+public abstract class AbstractDocumentEvent implements Event
+{
+    private EventFilter eventFilter;
+    
+    public AbstractDocumentEvent(String documentName)
+    {
+        this.eventFilter = new FixedNameEventFilter(documentName);
+    }
+
+    public AbstractDocumentEvent(EventFilter eventFilter)
+    {
+        this.eventFilter = eventFilter;
+    }
+
+    public EventFilter getEventFilter()
+    {
+        return this.eventFilter;
+    }
+
+    public boolean matches(Object otherEvent)
+    {
+        boolean isMatching = false;
+        if (AbstractDocumentEvent.class.isAssignableFrom(otherEvent.getClass())) {
+            AbstractDocumentEvent event = (AbstractDocumentEvent) otherEvent;
+            isMatching = getEventFilter().matches(event.getEventFilter());
+        }
+        return isMatching;
+    }
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/Event.java	Fri Jul 20 08:49:54 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/Event.java	Fri Jul 20 08:49:54 CEST 2007
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event;
+
+public interface Event
+{
+    boolean matches(Object otherEvent);
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/filter/FixedNameEventFilter.java	Fri Jul 20 08:58:44 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/filter/FixedNameEventFilter.java	Fri Jul 20 08:58:44 CEST 2007
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event.filter;
+
+public class FixedNameEventFilter implements EventFilter
+{
+    private String filter;
+
+    public FixedNameEventFilter(String filter)
+    {
+        this.filter = filter;
+    }
+
+    public String getFilter()
+    {
+        return this.filter;
+    }
+
+    public boolean matches(EventFilter eventFilter)
+    {
+        return (getFilter().equals(eventFilter.getFilter()));
+    }
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/DocumentSaveEvent.java	Tue Jul 17 13:51:28 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/DocumentSaveEvent.java	Tue Jul 17 13:51:28 CEST 2007
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event;
+
+import org.xwiki.observation.event.filter.EventFilter;
+
+public class DocumentSaveEvent extends AbstractDocumentEvent
+{
+    public DocumentSaveEvent(EventFilter eventFilter)
+    {
+        super(eventFilter);
+    }
+
+    public DocumentSaveEvent(String documentName)
+    {
+        super(documentName);
+    }
+}
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/DocumentDeleteEvent.java	Tue Jul 17 13:51:09 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/DocumentDeleteEvent.java	Tue Jul 17 13:51:09 CEST 2007
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event;
+
+import org.xwiki.observation.event.filter.EventFilter;
+
+public class DocumentDeleteEvent extends AbstractDocumentEvent
+{
+    public DocumentDeleteEvent(EventFilter eventFilter)
+    {
+        super(eventFilter);
+    }
+
+    public DocumentDeleteEvent(String documentName)
+    {
+        super(documentName);
+    }
+}
\ No newline at end of file
--- xwiki-platform-core/src/main/java/org/xwiki/observation/event/ActionExecutionEvent.java	Fri Jul 20 09:01:48 CEST 2007
+++ xwiki-platform-core/src/main/java/org/xwiki/observation/event/ActionExecutionEvent.java	Fri Jul 20 09:01:48 CEST 2007
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation.event;
+
+/**
+ * @todo use the enumerated Action class when it's implemented...
+ */
+public class ActionExecutionEvent implements Event
+{
+    private String actionName;
+
+    public ActionExecutionEvent(String actionName)
+    {
+        this.actionName = actionName;
+    }
+
+    public String getActionName()
+    {
+        return this.actionName;
+    }
+
+    public int hashCode()
+    {
+        return getActionName().hashCode();
+    }
+
+    public boolean equals(Object object)
+    {
+        return getActionName().equals(object);
+    }
+
+    public boolean matches(Object otherEvent)
+    {
+        boolean isMatching = false;
+        if (ActionExecutionEvent.class.isAssignableFrom(otherEvent.getClass())) {
+            ActionExecutionEvent actionEvent = (ActionExecutionEvent) otherEvent;
+            isMatching = getActionName().equals(actionEvent.getActionName());
+        }
+        return isMatching;
+    }
+}
--- xwiki-platform-core/src/test/java/org/xwiki/observation/ObservationManagerTest.java	Fri Jul 20 10:00:20 CEST 2007
+++ xwiki-platform-core/src/test/java/org/xwiki/observation/ObservationManagerTest.java	Fri Jul 20 10:00:20 CEST 2007
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007, XpertNet SARL, and individual contributors as indicated
+ * by the contributors.txt.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+package org.xwiki.observation;
+
+import junit.framework.TestCase;
+import org.xwiki.observation.event.DocumentSaveEvent;
+import org.xwiki.observation.event.Event;
+
+public class ObservationManagerTest extends TestCase
+{
+    public class TestListener implements EventListener
+    {
+        public boolean hasListenerBeenCalled = false;
+
+        public void onEvent(Event event, Object source, Object data)
+        {
+            assertEquals(DocumentSaveEvent.class.getName(), event.getClass().getName());
+            assertEquals("some source", (String) source);
+            assertEquals("some data", (String) data);
+            this.hasListenerBeenCalled = true;
+        }
+    }
+
+    public void testNotify()
+    {
+        ObservationManager manager = new ObservationManager();
+        TestListener listener = new TestListener();
+        manager.addListener(new DocumentSaveEvent("SomeDocument"), listener);
+
+        manager.notify(new DocumentSaveEvent("SomeDocument"), "some source", "some data");
+        assertTrue("Listener has not been called", listener.hasListenerBeenCalled);
+    }
+
+}
