/*
PureMVC Java MultiCore Port by Ima OpenSource <opensource@ima.eu>
Maintained by Anthony Quinault <anthony.quinault@puremvc.org>
PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
Your reuse is governed by the Creative Commons Attribution 3.0 License
*/
package org.puremvc.java.multicore.core.view;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.puremvc.java.multicore.interfaces.IFunction;
import org.puremvc.java.multicore.interfaces.IMediator;
import org.puremvc.java.multicore.interfaces.INotification;
import org.puremvc.java.multicore.interfaces.IObserver;
import org.puremvc.java.multicore.interfaces.IView;
import org.puremvc.java.multicore.patterns.observer.Observer;
/**
* A Multiton <code>IView</code> implementation.
*
* <P>
* In PureMVC, the <code>View</code> class assumes these responsibilities:
* <UL>
* <LI>Maintain a cache of <code>IMediator</code> instances.</LI>
* <LI>Provide methods for registering, retrieving, and removing <code>IMediators</code>.</LI>
* <LI>Notifiying <code>IMediators</code> when they are registered or removed.</LI>
* <LI>Managing the observer lists for each <code>INotification</code> in the application.</LI>
* <LI>Providing a method for attaching <code>IObservers</code> to an <code>INotification</code>'s observer list.</LI>
* <LI>Providing a method for broadcasting an <code>INotification</code>.</LI>
* <LI>Notifying the <code>IObservers</code> of a given <code>INotification</code> when it broadcast.</LI>
* </UL>
*
* @see org.puremvc.java.multicore.patterns.mediator.Mediator Mediator
* @see org.puremvc.java.multicore.patterns.observer.Observer Observer
* @see org.puremvc.java.multicore.patterns.observer.Notification Notification
*/
public class View implements IView {
// Mapping of Mediator names to Mediator instances
// Mapping of Notification names to Observer lists
private HashMap<String,List<IObserver>> observerMap;
private HashMap<String,IMediator> mediatorMap;
/**
* The Multiton Key for this Core.
*/
protected String multitonKey;
protected static Map<String, View> instanceMap = new HashMap<String, View>();
/**
* Constructor.
*
* <P>
* This <code>IView</code> implementation is a Multiton,
* so you should not call the constructor
* directly, but instead call the static Multiton
* Factory method <code>View.getInstance( multitonKey )</code>
*
* @throws Error Error if instance for this Multiton key has already been constructed
*
*/
protected View(String key) {
this.multitonKey = key;
instanceMap.put(multitonKey, this);
this.mediatorMap = new HashMap<String,IMediator>();
this.observerMap = new HashMap<String,List<IObserver>>();
initializeView();
}
/**
* Initialize the Singleton View instance.
*
* <P>
* Called automatically by the constructor, this is your opportunity to
* initialize the Singleton instance in your subclass without overriding
* the constructor.
* </P>
*
*/
protected void initializeView() {
}
/**
* View Singleton Factory method.
*
* @return the Singleton instance of <code>View</code>
*/
public synchronized static View getInstance(String key) {
if(instanceMap.get(key) == null) {
new View(key);
}
return instanceMap.get(key);
}
/**
* Notify the <code>Observers</code> for a particular
* <code>Notification</code>.
*
* <P>
* All previously attached <code>Observers</code> for this
* <code>Notification</code>'s list are notified and are passed a
* reference to the <code>Notification</code> in the order in which they
* were registered.
* </P>
*
* @param note
* the <code>Notification</code> to notify
* <code>Observers</code> of.
*/
public void notifyObservers(INotification note) {
List<IObserver> observers_ref = (List<IObserver>) this.observerMap.get(note.getName());
if (observers_ref != null) {
// Copy observers from reference array to working array,
// since the reference array may change during the
//notification loop
Object[] observers = (Object[])observers_ref.toArray();
// Notify Observers from the working array
for (int i = 0; i < observers.length; i++) {
IObserver observer = (IObserver)observers[i];
observer.notifyObserver(note);
}
}
}
/**
* Remove the observer for a given notifyContext from an observer list for a given Notification name.
* <P>
* @param notificationName which observer list to remove from
* @param notifyContext remove the observer with this object as its notifyContext
*/
public void removeObserver(String notificationName, Object notifyContext) {
// the observer list for the notification under inspection
List<IObserver> observers = observerMap.get(notificationName);
if (observers != null) {
// find the observer for the notifyContext
for(int i=0;i<observers.size();i++) {
Observer observer = (Observer) observers.get(i);
if (observer.compareNotifyContext(notifyContext) == true) {
observers.remove(observer);
}
}
// Also, when a Notification's Observer list length falls to
// zero, delete the notification key from the observer map
if (observers.size() == 0) {
observerMap.remove(notificationName);
}
}
}
/**
* Register an <code>Mediator</code> instance with the <code>View</code>.
*
* <P>
* Registers the <code>Mediator</code> so that it can be retrieved by
* name, and further interrogates the <code>Mediator</code> for its
* <code>Notification</code> interests.
* </P>
* <P>
* If the <code>Mediator</code> returns any <code>Notification</code>
* names to be notified about, an <code>Observer</code> is created
* encapsulating the <code>Mediator</code> instance's
* <code>handleNotification</code> method and registering it as an
* <code>Observer</code> for all <code>Notifications</code> the
* <code>Mediator</code> is interested in.
* </p>
*
* @param mediator
* the name to associate with this <code>IMediator</code>
* instance
*/
public void registerMediator(final IMediator mediator) {
if (!this.mediatorMap.containsKey(mediator.getMediatorName())) {
mediator.initializeNotifier(multitonKey);
// Register the Mediator for retrieval by name
this.mediatorMap.put(mediator.getMediatorName(), mediator);
// Get Notification interests, if any.
String[] noteInterests = mediator.listNotificationInterests();
if (noteInterests.length != 0) {
// Create java style function ref to mediator.handleNotification
IFunction function = new IFunction() {
public void onNotification(INotification notification) {
mediator.handleNotification(notification);
}
};
// Create Observer
Observer observer = new Observer(function, mediator);
// Register Mediator as Observer for its list of Notification
// interests
for (int i = 0; i < noteInterests.length; i++) {
registerObserver(noteInterests[i], observer);
}
}
// alert the mediator that it has been registered
mediator.onRegister();
}
}
/**
* Register an <code>Observer</code> to be notified of
* <code>INotifications</code> with a given name.
*
* @param notificationName
* the name of the <code>Notifications</code> to notify this
* <code>Observer</code> of
* @param observer
* the <code>Observer</code> to register
*/
public void registerObserver(String notificationName, IObserver observer) {
if (this.observerMap.get(notificationName) == null) {
this.observerMap.put(notificationName, new ArrayList<IObserver>());
}
List<IObserver> observers = (List<IObserver>) this.observerMap.get(notificationName);
observers.add(observer);
}
/**
* Remove an <code>Mediator</code> from the <code>View</code>.
*
* @param mediatorName
* name of the <code>Mediator</code> instance to be removed.
*/
public IMediator removeMediator(String mediatorName) {
// Retrieve the named mediator
IMediator mediator = mediatorMap.get(mediatorName);
if(mediator != null) {
// for every notification this mediator is interested in...
String[] interests = mediator.listNotificationInterests();
for (int i=0; i<interests.length; i++) {
// remove the observer linking the mediator
// to the notification interest
removeObserver(interests[i], mediator);
}
// remove the mediator from the map
mediatorMap.remove(mediatorName);
// alert the mediator that it has been removed
mediator.onRemove();
}
return mediator;
}
/**
* Retrieve an <code>Mediator</code> from the <code>View</code>.
*
* @param mediatorName
* the name of the <code>Mediator</code> instance to
* retrieve.
* @return the <code>Mediator</code> instance previously registered with
* the given <code>mediatorName</code>.
*/
public IMediator retrieveMediator(String mediatorName) {
return (IMediator) this.mediatorMap.get(mediatorName);
}
/**
* Check if a Mediator is registered or not
*
* @param mediatorName
* @return whether a Mediator is registered with the given <code>mediatorName</code>.
*/
public boolean hasMediator(String mediatorName) {
return mediatorMap.containsKey(mediatorName);
}
/**
* Remove an IView instance
*
* @param multitonKey of IView instance to remove
*/
public synchronized static void removeView(String key) {
instanceMap.remove(key);
}
}