package org.nexml.model.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.nexml.model.NexmlWritable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* An ordered set with named subsets.
*
* @param <T> the type we're storing.
*/
abstract class SetManager<T> extends AnnotatableImpl {
/**
* Protected constructors that take a DOM document object but not
* an element object are used for generating new element nodes in
* a NeXML document. On calling such constructors, a new element
* is created, which can be retrieved using getElement(). After this
* step, the Impl class that called this constructor would still
* need to attach the element in the proper location (typically
* as a child element of the class that called the constructor).
* (A work-around for no multiple inheritance: we need to pass this stuff up
* the type hierarchy to {@code NexmlWritableImpl}.)
*
* @param document this {@code NexmlWritable}'s root DOM {@code Document}.
*/
protected SetManager(Document document) {
super(document);
}
/** {@code mThings} is an ordered set - i.e. no duplicates. */
private final List<T> mOrderedSet = new ArrayList<T>();
/** Named subsets of {@code mThings}. */
private final Map<String, Set<T>> mSubsets = new HashMap<String, Set<T>>();
/**
* Protected constructors are intended for recursive parsing, i.e.
* starting from the root element (which maps onto DocumentImpl) we
* traverse the element tree such that for every child element that maps
* onto an Impl class the containing class calls that child's protected
* constructor, passes in the element of the child. From there the
* child takes over, populates itself and calls the protected
* constructors of its children. These should probably be protected
* because there is all sorts of opportunity for outsiders to call
* these in the wrong context, passing in the wrong elements etc.
* @param document the containing DOM document object. Every {@code NexmlWritable}
* class needs a reference to this {@code Document} so that it can create DOM element
* objects
* @param element the equivalent NeXML {@code Element} (e.g. for OTUsImpl, it's
* the <otus/> element)
* @author rvosa
*/
protected SetManager(Document document, Element element) {
super(document, element);
}
/**
* Add {@code thing} to this set.
*
* @param thing that which we're adding.
*/
protected void addThing(T thing) {
if (!mOrderedSet.contains(thing)) {
mOrderedSet.add(thing);
}
}
/**
* Get all member of this set, in order.
*
* @return all member of this set, in order.
*/
protected List<T> getThings() {
return mOrderedSet;
}
/**
* Remove {@code thing} from this set.
*
* @param thing to be removed.
*/
protected void removeThing(T thing) {
for (Set<T> set : mSubsets.values()) {
set.remove(thing);
}
mOrderedSet.remove(thing);
}
/**
* Add {@code thing} to the subset named {@code subsetName}.
*
* @param setName see description.
* @param thing see description.
*/
protected void addToSubset(String subsetName, T thing) {
if (!mOrderedSet.contains(thing)) {
throw new IllegalArgumentException(
"thing is not already in this SetManager.");
}
createSubset(subsetName);
mSubsets.get(subsetName).add(thing);
}
protected T getThingById(String id) {
if ( null == id ) {
return null;
}
for ( T thing : mOrderedSet ) {
if ( thing instanceof NexmlWritable ) {
if ( id.equals(((NexmlWritable)thing).getId()) ) {
return thing;
}
}
}
return null;
}
protected void addToSet(String setName, T thing) {
createSubset(setName);
mSubsets.get(setName).add(thing);
String classNames = ((NexmlWritableImpl)thing).getElement().getAttribute("class");
if ( classNames.equals("") ) {
classNames = setName;
}
else {
classNames += " " + setName;
}
((NexmlWritableImpl)thing).getElement().setAttribute("class", classNames);
}
/**
* Create subset named {@subsetName}.
*
* @param subsetName the name of the subset we're creating.
*/
protected void createSubset(String subsetName) {
if (!mSubsets.containsKey(subsetName)) {
mSubsets.put(subsetName, new HashSet<T>());
NodeList childNodes = getElement().getChildNodes();
Element classElement = getDocument().createElement("class");
classElement.setAttribute("id", subsetName);
NON_META: for ( int i = 0; i < childNodes.getLength(); i++ ) {
String nodeName = childNodes.item(i).getNodeName();
if ( null != nodeName && ! "meta".equals(nodeName) ) {
getElement().insertBefore(classElement, childNodes.item(i));
break NON_META;
}
}
}
}
/**
* Get the subset named {@subsetName}.
*
* @param subsetName see description.
*/
protected List<T> getSubset(String subsetName) {
List<T> list = new ArrayList<T>();
for (T thing : mOrderedSet) {
if (mSubsets.get(subsetName).contains(thing)) {
list.add(thing);
}
}
return Collections.unmodifiableList(list);
}
/**
* Get the names of the subsets.
*
* @return the names of the subset.
*/
public Set<String> getSubsetNames() {
return mSubsets.keySet();
}
}