/* (C) 2001-2002, DIUF, http://www.unifr.ch/diuf
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program 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 General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package iiuf.xmillum;
import iiuf.dom.DOMUtils;
import iiuf.util.EventListenerList;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.Set;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* FlagManager
*
* This class manages all the data related to flag sets.
*
* @author $Author: ohitz $
* @version $Revision: 1.1 $
*/
public class FlagManager {
public static final String SELECTION = "selection";
public static final String SELECTED = "selected";
/** Contains AllFlagListeners, which get every flag change */
EventListenerList allListeners = new EventListenerList();
/**
* For every flagged element, contains a Set with the names of the
* flags the element is flagged with.
*/
Map flaggedElements = new HashMap();
void removeFlagged(Element element, String name) {
Set flags = (Set) flaggedElements.get(element);
if (flags != null) {
flags.remove(name);
if (flags.isEmpty()) flaggedElements.remove(element);
}
}
void setFlagged(Element element, String name) {
Set flags = (Set) flaggedElements.get(element);
if (flags == null) {
flags = new HashSet();
flaggedElements.put(element, flags);
}
flags.add(name);
}
/**
* Represents a flag type. */
class FlagType {
// Flag name
String name;
// Possible flag values
Map values = new HashMap();
// Objects flagged (key: object, value: flag value)
Map objects = new HashMap();
// Listeners listening for this flag
EventListenerList listeners = new EventListenerList();
// Handler
String handler;
public FlagType(String n, Map v, String h) {
name = n;
values = v;
handler = h;
}
public FlagType(String n, FlagType t, Map v, String h) {
this(n, v, h);
objects = t.objects;
listeners = t.listeners;
}
public Style getStyle(Element e) {
String f = getFlag(e);
if (f == null) {
return null;
} else {
return (Style) values.get(f);
}
}
public synchronized Set getElements(String value) {
Set result = new HashSet();
Iterator i = objects.keySet().iterator();
while (i.hasNext()) {
Element e = (Element) i.next();
if (value.equals(objects.get(e))) {
result.add(e);
}
}
return result;
}
public synchronized void setFlag(Element e, String value, FlagListener source) {
if (value == null) {
removeFlagged(e, name);
} else {
setFlagged(e, name);
}
objects.remove(e);
objects.put(e, value);
EventListener[] ll = listeners.getListeners(FlagListener.class);
for (int i = 0; i < ll.length; i++) {
FlagListener l = (FlagListener) ll[i];
if (l != source) l.setFlag(e, value);
}
ll = allListeners.getListeners(AllFlagListener.class);
for (int i = 0; i < ll.length; i++) {
AllFlagListener l = (AllFlagListener) ll[i];
if (l != source) l.setFlag(e, name, value);
}
}
/**
* Clears all flags of this type and inform the listeners
*
* @param source The listener that is invoking this method (to
* avoid informing himself and create an endless loop)
*/
public synchronized void clearFlags(FlagListener source) {
EventListener[] ll = listeners.getListeners(FlagListener.class);
for (int i = 0; i < ll.length; i++) {
FlagListener l = (FlagListener) ll[i];
if (l != source) {
Iterator o = objects.keySet().iterator();
while (o.hasNext()) {
l.setFlag((Element) o.next(), null);
}
}
}
ll = allListeners.getListeners(AllFlagListener.class);
for (int i = 0; i < ll.length; i++) {
AllFlagListener l = (AllFlagListener) ll[i];
if (l != source) {
Iterator o = objects.keySet().iterator();
while (o.hasNext()) {
l.setFlag((Element) o.next(), name, null);
}
}
}
Iterator o = objects.keySet().iterator();
while (o.hasNext()) {
removeFlagged((Element) o.next(), name);
}
objects.clear();
}
/**
* Get the flag name of an object.
*
* @param e Object to question
* @return Flag name or null if this object is not flagged
*/
public synchronized String getFlag(Element e) {
return (String) objects.get(e);
}
/**
* Adds a listener for this flag type.
*
* @param l FlagListener to add
*/
public synchronized void addListener(FlagListener l) {
listeners.add(FlagListener.class, l, true);
}
/**
* Removes a listener from this flag type.
*
* @param l FlagListener to remove
*/
public synchronized void removeListener(FlagListener l) {
listeners.remove(FlagListener.class, l);
}
}
/**
* Instances of this class are returned to objects which add FlagListener
* so they can manipulate the flag state themselves.
*/
class FlagAccessImpl extends FlagAccess {
FlagType type;
FlagListener listener;
public FlagAccessImpl(FlagListener l, FlagType t) {
listener = l;
type = t;
}
public void clearFlags() {
type.clearFlags(listener);
}
public void setFlag(Element e, String value) {
type.setFlag(e, value, listener);
}
public void toggleFlag(Element e, String value1, String value2) {
String f = type.getFlag(e);
if (value1 != null) {
type.setFlag(e, value1.equals(f) ? value2 : value1, listener);
} else {
type.setFlag(e, value2.equals(f) ? value1 : value2, listener);
}
}
public Set getElements(String value) {
return type.getElements(value);
}
}
// Possible flag types (key: name, value: flag type)
Map flagTypes = new HashMap();
BrowserContext context;
public FlagManager(BrowserContext c) {
context = c;
// Add the "selection" flags
Map values = new HashMap();
values.put(SELECTED, new Style(true));
addFlagType(SELECTION, values);
}
public void setFlags(NodeList f) {
// Add other flags
for (int i = 0; i < f.getLength(); i++) {
Element e = (Element) f.item(i);
String flagName = e.getAttribute("name");
if (flagName == null) {
context.log("Flag definition without a `name' attribute found.");
} else {
String handler = e.getAttribute("handler");
Map values = new HashMap();
NodeList vl = DOMUtils.getChildsByTagName(e, "value");
for (int j = 0; j < vl.getLength(); j++) {
Element v = (Element) vl.item(j);
String vname = v.getAttribute("name");
String vstyle = v.getAttribute("style");
if (vname == null) {
context.log("Flag definition: value without a `name' attribute found.");
} else if (vstyle == null) {
context.log("Flag definition: value without a `style' attribute found.");
} else {
values.put(vname, context.styleRegistry.getStyle(vstyle));
}
}
addFlagType(flagName, values, handler);
}
}
}
public void addFlagType(String type, Map values) {
addFlagType(type, values, null);
}
public void addFlagType(String type, Map values, String handler) {
FlagType t = (FlagType) flagTypes.get(type);
if (t != null) {
t = new FlagType(type, t, values, handler);
} else {
t = new FlagType(type, values, handler);
}
flagTypes.put(type, t);
}
public Set getFlagTypes() {
return flagTypes.keySet();
}
public Set getFlagValues(String type) {
FlagType t = (FlagType) flagTypes.get(type);
if (t != null) {
return t.values.keySet();
} else {
return null;
}
}
public Style[] getStyles(Element e) {
Set types = (Set) flaggedElements.get(e);
if (types == null) {
return new Style[0];
} else {
Style[] styles = new Style[types.size()];
int ii = 0;
Iterator i = types.iterator();
while (i.hasNext()) {
FlagType t = (FlagType) flagTypes.get(i.next());
styles[ii++] = t.getStyle(e);
}
return styles;
}
}
public void setFlag(String type, String value, Element e) {
FlagType t = (FlagType) flagTypes.get(type);
if (t == null) {
context.log("Error in FlagManager.flagObject(): flag type `"+type+"' not found.");
return;
}
t.setFlag(e, value, null);
}
public String getFlag(String type, Element e) {
FlagType t = (FlagType) flagTypes.get(type);
if (t == null) {
context.log("Error in FlagManager.getFlag(): flag type `"+type+"' not found.");
return null;
}
return t.getFlag(e);
}
public void runHandlers() {
Iterator i = flagTypes.keySet().iterator();
while (i.hasNext()) {
FlagType t = (FlagType) flagTypes.get(i.next());
if (t.handler != null && !"".equals(t.handler)) {
Iterator i2 = t.values.keySet().iterator();
while (i2.hasNext()) {
String value = (String) i2.next();
context.actionFactory.handleFlagged(t.handler, value, t.getElements(value));
}
}
}
}
/**
* Adds a flag listener for a specified flag type to the system.
*
* @param type Flag name the listener should listen to
* @param listener Flag listener ready to listen
* @return An object allowed to access the flagging.
*/
public FlagAccess addFlagListener(String type, FlagListener listener) {
FlagType t = (FlagType) flagTypes.get(type);
if (t == null) {
context.log("Error in FlagManager.addFlagListener(): flag type `"+type+"' not found.");
return null;
}
t.addListener(listener);
return new FlagAccessImpl(listener, t);
}
/**
* Removes a flag listener for a specified flag type.
*
* @param type Flag name the listener should be removed from
* @param listener Listener to remove
*/
public void removeFlagListener(String type, FlagListener listener) {
FlagType t = (FlagType) flagTypes.get(type);
if (t == null) {
context.log("Error in FlagManager.removeFlagListener(): flag type `"+type+"' not found.");
return;
}
t.removeListener(listener);
}
/**
* Adds a flag listener for all flag changes
*
* @param listener Flag listener ready to listen
* @return An object allowed to access the flagging.
*/
public void addAllFlagListener(AllFlagListener listener) {
allListeners.add(AllFlagListener.class, listener, true);
}
/**
* Removes a flag listener for all flag changes
*
* @param listener Listener to remove
*/
public void removeAllFlagListener(String type, AllFlagListener listener) {
allListeners.remove(AllFlagListener.class, listener);
}
}