/**
* This file is part of Archiv-Editor.
*
* The software Archiv-Editor serves as a client user interface for working with
* the Person Data Repository. See: pdr.bbaw.de
*
* The software Archiv-Editor was developed at the Berlin-Brandenburg Academy
* of Sciences and Humanities, Jägerstr. 22/23, D-10117 Berlin.
* www.bbaw.de
*
* Copyright (C) 2010-2013 Berlin-Brandenburg Academy
* of Sciences and Humanities
*
* The software Archiv-Editor was developed by @author: Christoph Plutte.
*
* Archiv-Editor 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 3 of the License, or
* (at your option) any later version.
*
* Archiv-Editor 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 Archiv-Editor.
* If not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package org.bbaw.pdr.ae.export.logic;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Vector;
import org.bbaw.pdr.ae.common.AEConstants;
import org.bbaw.pdr.ae.common.CommonActivator;
import org.bbaw.pdr.ae.control.comparator.AspectsByCreatorComparator;
import org.bbaw.pdr.ae.control.comparator.AspectsByCronComparator;
import org.bbaw.pdr.ae.control.comparator.AspectsByRecentChangesComparator;
import org.bbaw.pdr.ae.control.comparator.AspectsByReferenceComparator;
import org.bbaw.pdr.ae.control.comparator.AspectsBySemanticComparator;
import org.bbaw.pdr.ae.control.facade.Facade;
import org.bbaw.pdr.ae.export.pluggable.AeExportCoreProvider;
import org.bbaw.pdr.ae.metamodel.PdrId;
import org.bbaw.pdr.ae.model.Aspect;
import org.bbaw.pdr.ae.model.PdrObject;
import org.bbaw.pdr.ae.model.Person;
import org.bbaw.pdr.ae.model.ReferenceMods;
import org.bbaw.pdr.ae.model.ValidationStm;
import org.bbaw.pdr.ae.model.view.OrderingHead;
import org.bbaw.pdr.ae.view.control.PDRObjectsOrderer;
import org.bbaw.pdr.ae.view.control.PDRObjectsProvider;
import org.bbaw.pdr.ae.view.control.orderer.AspectByYearOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsByMarkupOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsByPersonOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsByPlaceOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsByReferenceOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsByRelationOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsBySemanticOrderer;
import org.bbaw.pdr.ae.view.control.orderer.AspectsByUserOrderer;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
/**
* Represents the set of {@link PdrObject}s currently available at
* the {@link PDRObjectsProvider} instance known by
* {@link AeExportCoreProvider} as a tree.</br>
* As all required methods are implemented, a wrapper can easily be
* written to serve as an {@link ITreeContentProvider}.
*
* @author Jakob Hoeper
*
*/
public class PdrObjectsPreviewStructure implements ITreeContentProvider, ICheckStateProvider {
private ILog log = AEConstants.ILOGGER;
private PDRObjectsProvider provider;
private Facade facade;
private Vector<StructNode> roots;
private HashMap<Object, Vector<StructNode>> nodes;
private String groupedBy;
private int sortedBy;
/**
* <p>Set up an instance of {@link PdrObjectsPreviewStructure} fitted for a tree-ish
* representation of {@link PdrObject} sets and their relations.</p>
* <p>In order to actually populate this structure, {@link #buildTree(boolean)} or
* {@link #buildTree()} have to be called. This can be done whenever a full rebuilding is
* desired. </p>
*/
public PdrObjectsPreviewStructure() {
facade = Facade.getInstanz();
provider = AeExportCoreProvider.getInstance().getPdrObjectsProvider();
// TODO: save somehow what to load and how to group
roots = new Vector<StructNode>();
nodes = new HashMap<Object, Vector<StructNode>>();
sortedBy = 0;
groupedBy = null;
//System.out.println(getClass().getSimpleName()+" set up.");
}
public PdrObjectsPreviewStructure(TreeViewer viewer) {
this();
viewer.setContentProvider(this);
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (oldInput == null || !oldInput.equals(newInput)) {
//System.out.println(this.getClass().getSimpleName()+" input changed");
}
}
/**
* Update entire structure to match contents of central export plugin's
* {@link PDRObjectsProvider} instance.</br>
* (Calls {@link PDRObjectsProvider#getArrangedAspects()} or
* {@link PDRObjectsProvider#getArrangedAspectsByObjects()}, depending on the top
* argument being false or true).
* @param top if true, the {@link PdrObject}s to which the provided aspects
* are related as aspect_of are represented on a top-level layer.
* If false, they will be represented directly above the aspects, but underneath
* the classification structure the provider is configured with.
*/
public void buildTree(boolean top) {
// reset class attributes
groupedBy = groupedBy();
roots = new Vector<StructNode>();
nodes = new HashMap<Object, Vector<StructNode>>();
// translate PDR Objects dependencies into more generic tree structure
// retrieve classified Aspects
//System.out.println(" Retrieve aspects as arranged by PDRObjectsProvider.");
Vector<OrderingHead> groups = top ? provider.getArrangedAspectsByObjects()
: provider.getArrangedAspects();
if (groups != null)
// represent persons/objects at highest level
// this is the current default behaviour
if (top) {
//System.out.println(" Initialize PDR Objects preview tree layout.");
// leave logical layout entirely to treeify(Orderinghead) method
for (OrderingHead personalHead : groups) {
//System.out.println(" Build sub tree for group "+personalHead.getLabel());
this.roots.add(treeify(personalHead));
}
// previously applied layout
} else // represent persons/objects at lowest level of class. hierarchy
for (OrderingHead group : groups) {
StructNode refs = createNode(null, group);
// estimate a parent node that will comprise all objects attached to the
// current OrdernigHead
StructNode head = computeClassNode(group, null);
// contribute Aspects and Persons
HashMap<Person, StructNode> people = new HashMap<Person, StructNode>();
for (Aspect aspect : group.getAspects()) {
PdrObject pobj = getPdrObject(aspect.getOwningObjectId());
StructNode aspectNode=null;
// insert intermediate layer of Persons
if (pobj instanceof Person) {
Person p = (Person)pobj;
if (people.containsKey(p)) {
aspectNode = people.get(p).addChild(aspect);
} else {
StructNode parent = head.addChild(p);
people.put(p, parent);
aspectNode = parent.addChild(aspect);
}
} else
aspectNode = head.addChild(aspect);
// insert References
for (ValidationStm validation : aspect.getValidation().getValidationStms()) {
ReferenceMods mods = facade.getReference(validation.getReference().getSourceId());
if (mods!=null) {
aspectNode.addChild(mods);
refs.addChild(mods);
}
}
}
}
//System.out.println(this.getClass().getSimpleName()+" input changed.");
//System.out.println(" Number of root nodes: "+roots.size());
//System.out.println(" Number of nodes total: "+nodes.size());
}
/**
* Builds tree structure expressing the results of
* {@link PDRObjectsProvider#getArrangedAspectsByObjects()}, meaning
* that the classification the provider has been configured with
* using {@link PDRObjectsProvider#setOrderer(PDRObjectsOrderer)}
* will be nested within top level classes representing whatever
* objects (most likely {@link Person}s) own the comprised {@link Aspect}s.
*/
public void buildTree() {
this.buildTree(true);
}
/**
* Build a tree representing the given {@link OrderingHead} and its underlying aspects structure.
* <p>Note: nested layers of OrderingHeads are ignored. Only OrderingHeads directly beneath the
* given head are handled and henceforth expected to contain only aspects as children.
* </p>
* <p>This method pretty much expects {@link OrderingHead} root nodes as input, preferably those
* whose {@link OrderingHead#getValue()} fields are set to a {@link Person}'s {@link PdrId}.
* An input like that is provided by {@link PDRObjectsProvider#getArrangedAspectsByObjects()}.
* </p>
* @param head
* @return
*/
private StructNode treeify(OrderingHead head) {
// try to find Person object represented by top level node
Object rootContent = head;
PdrId id = new PdrId(head.getValue());
if (id.isValid())
rootContent = Facade.getInstanz().getPerson(id);
if (rootContent == null)
rootContent = head;
// encapsulate top level node in internal tree node
StructNode root = this.createNode(null, rootContent);
// assume exactly one intermediate layer of ordering head nodes between
// top level and aspects level. Nodes on this layer can be expanded.
//System.out.println(" Traverse subcategories:");
for (OrderingHead cat : head.getSubCategories()) {
//System.out.println(" "+cat.getLabel());
// place tree node container for classification ordering heads
// according to depth indicated by '::' delimiters in ordering head value fields
StructNode catNode = this.computeClassNode(cat, root);
// attach aspects to ordering head nodes, references to aspect nodes
//System.out.println(" append aspects and references");
for (Aspect aspect : cat.getAspects()) {
StructNode aspectNode = catNode.addChild(aspect);
for (ValidationStm validation : aspect.getValidation().getValidationStms()) {
ReferenceMods mods = facade.getReference(validation.getReference().getSourceId());
if (mods!=null)
aspectNode.addChild(mods);
}
}
}
// TODO: or whatever PdrObject this ordered head is representing...
root.setType(root.getType()+"."+head.getValue().replace(' ', '_').toLowerCase());
return root;
}
/**
* <p>Identifies the {@link StructNode} representing an {@link OrderingHead} within the
* specified subtree. A node is considered an ordering head's counterpart if
* {@link StructNode#getType()} of the former matches the category identified by
* {@link OrderingHead#getValue()} of
* the latter. An ordering head value field might list multiple categories, using
* the delimiter '::', in which case the ordering head will be represented by
* multiple nodes, each one standing for a single category and being a child of the previous one.</br>
* If no matching node can be identified for an ordering head, it will be created at
* the appropriate position.</p>
* <p><b>Call only for nodes representing {@link OrderingHead}s.</b></p>
* @param group {@link OrderingHead} object that we desire to map into this
* {@link PdrObjectsPreviewStructure}.
* @param parent Node amongst whose descendants the given ordering head is to be
* placed. If null, iteration starts at root level.
* @return newly created descendant of the given node, so that
* the subclass structure stored in the available {@link OrderingHead#getValue()}
* is properly expressed
*/
private StructNode computeClassNode(OrderingHead group, StructNode parent) {
StructNode node = parent;
//System.out.println("______________\n"+group.getValue()+" - "+group.getLabel());
// consider segments of ordering head value string delimited by '::' being nested aspects classes
String[] keys = group.getValue().split("::");
// semantic labels
Vector<String> semPath = new Vector<String>();
String labelProvider = null;
// TODO: wahrscheinlich auch fuer place u.ae.
/*if (this.getClassifier().endsWith(".semantic"))
labelProvider = AeExportCoreProvider.PRIMARY_SEMANTIC_PROVIDER;
else */if (this.getClassifier().endsWith(".markup"))
labelProvider = AeExportCoreProvider.PRIMARY_TAGGING_PROVIDER;
// determine on which level iteration starts
Vector<StructNode> candidates = (parent != null) ? parent.getChildren() : this.roots;
// along the path specified by the given markup configuration, stay
// on the matching branch as long as possible before creating
// required sub nodes
String type = "";
StructNode next;
for (String key : keys) {
semPath.add(key);
type += (type.length()>0 ? "." : "") + key.toLowerCase(); //TODO replace special chars
next = null;
// see if we can stay on branch..
for (StructNode cand : candidates)
if (cand.getType().contains(type))
next = cand;
// in case we lost branch:
if (next == null) {
// as in 'category', not related to feline mammals in any way..
OrderingHead catHead = new OrderingHead(type);
if (labelProvider != null)
catHead.setLabel(
AeExportCoreProvider.getAnnotationLabel(semPath, labelProvider));
else
catHead.setLabel(group.getLabel());
if (node == null) {
next = createNode(null, catHead);
if (parent==null) this.roots.add(next);
} else
next = node.addChild(catHead);
}
// walk down tree
node = next;
candidates = node.getChildren();
}
//System.out.println(" Estimated category node: "+node.getLabel()+", "+node.getRootCategory());
if (node == null)
log.log(new Status(IStatus.WARNING, CommonActivator.PLUGIN_ID,
"COMPUTATION OF CLASS NODE FAILED FOR "+group.getLabel()+
" under parent "+parent.getLabel()));
// FIXME: make sure that when returning, the deepest nested node is the passed group instance!
// FIXME: otherwise, making the PDRObjectsProvider keep its OH instances on comparator update
// FIXME: is useless for us.
// FIXME: we need the very instance passed to here as group parameter to be able to identify known
// FIXME: OH objects/aspects on re-sort
return node;
}
/**
* Instantiates a new {@link StructNode} instance which
* references the StructNode passed as the parent parameter
* as its parent node and
* represents the passed Object.<br>
* @param parent
* @param content
* @return
*/
public StructNode createNode(StructNode parent, Object content) {
StructNode node = new StructNode(parent, content, this);
// register node
// FIXME: since in the new layout, ordering heads appear multiple times
// FIXME: at different positions, rather register them under their
// FIXME: getRootCategory()!
if (!nodes.containsKey(content))
nodes.put(content, new Vector<StructNode>());
nodes.get(content).add(node);
return node;
}
public StructNode createRoot(Object content) {
StructNode node = new StructNode(content, this);
// register node
// FIXME: since in the new layout, ordering heads appear multiple times
// FIXME: at different positions, rather register them under their
// FIXME: getRootCategory()!
if (!nodes.containsKey(content))
nodes.put(content, new Vector<StructNode>());
nodes.get(content).add(node);
roots.add(node);
return node;
}
/**
* <p>Returns a string identifier for the condition at which
* aspects of the export plugin's current {@link PDRObjectsProvider}
* are put
* together into groups. Calls {@link #groupedBy()} to do this.
* Might be out of date?</p>
* <p>The returning identifier corresponds to the currently active
* {@link PDRObjectsProvider#getOrderer()}, which might be one of
* {@link AspectsByMarkupOrderer}, {@link AspectsByPersonOrderer},
* {@link AspectsByPlaceOrderer}, {@link AspectsByReferenceOrderer},
* {@link AspectsByRelationOrderer}, {@link AspectsBySemanticOrderer},
* {@link AspectsByUserOrderer}, or any other class implementing
* {@link PDRObjectsOrderer}.
* @return String identifier looking like <code>grouped.[condition]</code>
* @see {@link #groupedBy()}, {@link AeExportCoreProvider}
*/
public String getClassifier() {
if (this.groupedBy == null)
this.groupedBy = groupedBy();
return this.groupedBy;
}
/**
* Detects criteria that causes Aspects to be grouped together
* in the way it can be seen in the return set of
* {@link PDRObjectsProvider#getArrangedAspects()} or
* {@link PDRObjectsProvider#getArrangedAspectsByObjects()}. </br>
* In other words, name the ObjectProvider's current
* {@link PDRObjectsOrderer} implementation.
* @return String identifier for the currently active
* {@link PDRObjectsOrderer} implementation in our
* PDRObjectsProvider. Possible
* return values are:
* <ul><li>grouped.person</li>
* <li>grouped.year</li>
* <li>grouped.markup</li>
* <li>grouped.place</li>
* <li>grouped.reference</li>
* <li>grouped.relation</li>
* <li>grouped.semantic</li>
* <li>grouped.user</li></ul>
*/
public String groupedBy() {
PDRObjectsOrderer agg = provider.getOrderer();
String res = "grouped.";
if (agg == null)
return res+"none";
if (agg instanceof AspectsByPersonOrderer)
return res+"person";
if (agg instanceof AspectByYearOrderer)
return res+"year";
if (agg instanceof AspectsByMarkupOrderer)
return res+"markup";
if (agg instanceof AspectsByPlaceOrderer)
return res+"place";
if (agg instanceof AspectsByReferenceOrderer)
return res+"reference";
if (agg instanceof AspectsByRelationOrderer)
return res+"relation";
if (agg instanceof AspectsBySemanticOrderer)
return res+"semantic";
if (agg instanceof AspectsByUserOrderer)
return res+"user";
return "";
}
/**
* Return root-level nodes.
* @return
*/
@Override
public StructNode[] getElements(Object obj) {
return roots.toArray(new StructNode[roots.size()]);
}
/**
* Return root-level nodes.
* @return
*/
public Vector<StructNode> getElements() {
return roots;
}
/**
* Returns all children of the node being passed as parentElement,
* assuming it is a {@link StructNode} instance.
* @param parentElement a {@link StructNode} object
* @return Array of StructNode
*/
public StructNode[] getChildren(Object parentElement) {
if (parentElement instanceof StructNode) {
Vector<StructNode> res = ((StructNode)parentElement).getChildren();
if (res != null)
return res.toArray(new StructNode[res.size()]);
return null;
}
return null;
}
/**
* Returns the parent of a {@link StructNode}
* @param element StructNode
* @return StructNode which references the passed entity as its child.
*/
public StructNode getParent(Object element) {
if (element instanceof StructNode)
return ((StructNode)element).getParent();
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof StructNode)
return ((StructNode)element).hasChildren();
return false;
}
/**
* Returns a {@link Vector} with those nodes that are registered
* as containing the given object.
* @param content an Object to find nodes for, e.g. a {@link PdrObject} instance
* @return Vector of nodes, or empty Vector if object is unknown
*/
public Vector<StructNode> getNodesFor(Object obj) {
Vector<StructNode> matches = new Vector<StructNode>();
if (obj instanceof OrderingHead) {
// FIXME: since in the new layout, orderingheads appear multiple times
// FIXME: at different positions, rather register them under their
// FIXME: getRootCategory()!
StructNode dummy = new StructNode(null, obj, this);
for (Map.Entry<Object, Vector<StructNode>> chapter : nodes.entrySet())
if (chapter.getKey() instanceof OrderingHead)
for (StructNode node : chapter.getValue())
if (node.getType().equals(dummy.getType()))
matches.add(node);
}
if (nodes.containsKey(obj))
matches.addAll(nodes.get(obj));
return matches;
}
/**
*
* @param id
* @return
*/
public PdrObject getPdrObject(PdrId id){
//TODO: makes no sense. Change it into a lookup function for the pdr objects
//stored in this structure instead.
PdrObject obj;
obj = Facade.getInstanz().getPdrObject(id);
return obj;
}
/**
* returns the object stored by the given node.
*/
public Object getContent(Object node){
if (node instanceof StructNode)
return ((StructNode)node).getContent();
return null;
}
/**
* Updates the sorting criteria of this structure, equips the responsible
* {@link PDRObjectsProvider} with the corresponding {@link Aspect}-based
* {@link Comparator} and re-builds the internal structure tree based on
* the updated contents of the provider.
* @param compId
* @return
*/
public int sortBy(int compId) {
//it is assumed that compId <= 5 anyway. See caller
compId = compId & 7;
Comparator<Aspect> comp = null;
boolean asc;
if ((sortedBy & 7) != compId) {
sortedBy = compId; // overwrite sorting criterion on change
} else // if same base comparator:
sortedBy ^= 8; // keep sorting, but switch ascending flag
asc = ((sortedBy >> 3) == 0); // descending order only if 8-bit is set
switch (sortedBy & 7) {
case 0: Collections.sort(this.roots, asc ? null : Collections.reverseOrder()); break;
case 1: comp = new AspectsByCronComparator(asc); break;
case 2: comp = new AspectsBySemanticComparator(
AeExportCoreProvider.PRIMARY_SEMANTIC_PROVIDER, asc);break;
case 3: comp = new AspectsByRecentChangesComparator(asc);break;
case 4: comp = new AspectsByReferenceComparator(asc);break;
case 5: comp = new AspectsByCreatorComparator(asc);break;
}
log.log(new Status(IStatus.INFO, CommonActivator.PLUGIN_ID,
"Sorting criteria: "+sortedBy+", ascending: "+asc));
if (comp != null) {
provider.setComparator(comp);
// TODO: experiment: rather than rebuilding the entire thing, we just sort
// the structnodes children lists
//buildTree();
Vector<StructNode> nodes = new Vector<StructNode>();
NodeComparator ncomp = new NodeComparator(comp);
nodes = this.getExpandableNodes();
log.log(new Status(IStatus.INFO, CommonActivator.PLUGIN_ID,
"Nodes to sort: "+nodes.size()));
for (StructNode n : nodes)
n.sort(ncomp);
log.log(new Status(IStatus.INFO, CommonActivator.PLUGIN_ID,
" > "+comp.getClass().getName()+"\n"+
(asc ? "ascending" : "descending")));
}
return sortedBy;
}
/**
* If a {@link StructNode} instance is given, its selection flag will be
* overwritten with the given boolean value by calling {@link StructNode#setSelected(boolean)},
* which will take care of selection state consistency within the affected subtree.
* <p>
* <strike>If the node's selection flag status is actually being changed by this call, the
* return value will be true, if not, false.</strike>
* </p>
* Returns a list of those {@link StructNode} instances whose selection flags are being
* changed during the procedure.
* @param obj {@link StructNode} object
* @param state
* @return true if operation has any effect
* @see StructNode#setSelected(boolean)
*/
public HashSet<StructNode> setSelected(Object obj, boolean state) {
HashSet<StructNode> res;
if (obj instanceof StructNode) {
StructNode node = (StructNode)obj;
res = node.setSelected(state);
//System.out.println(" changed check state of "+node.getLabel());
} else
res = new HashSet<StructNode>();
return res;
}
/**
* Sets selection flags of every single node to false.
*/
public void deselectAll() {
for (StructNode root : this.roots)
root.setSelected(false);
}
/**
* Populates an Array of those {@link StructNode} objects that are:
* <ol><li>flagged as selected</li>
* <li>descendants of selected nodes only</li>
* <li>no ancestors of any selected nodes themselves</li>
* </ol>
* If none of the root nodes are
* selected, the result will be an empty Array.
* @return {@link StructNode}[]
*/
public StructNode[] getSelected() {
Vector<StructNode> selection = new Vector<StructNode>();
for (StructNode root : roots)
selection.addAll(root.getSelectedDescendants());
return selection.toArray(new StructNode[selection.size()]);
}
/**
* Returns all expanded paths, represented by those nodes that are expanded
* themselves, but don't know any further expanded ancestors.
* @return
*/
public StructNode[] getExpandedNodes() {
Vector<StructNode> expansion = new Vector<StructNode>();
for (StructNode root : roots)
expansion.addAll(root.getExpandedDescendants());
return expansion.toArray(new StructNode[expansion.size()]);
}
public void expand(StructNode node) {
node.setExpanded(true);
}
public void collapse(StructNode node) {
node.setExpanded(false);
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
/**
* Generates a copy of the structure's contents
* that will only contain those objects that are currently marked as
* selected in this {@link PdrObjectsPreviewStructure}.
* @return List of {@link OrderingHead}s that hold the ids of {@link Person} objects
* and whose children, which are OrderingHead instances as well, represent groups of
* {@link Aspect}s and the {@link ReferenceMods} validations involved.
*/
public Vector<OrderingHead> getSelectionHeads() {
Vector<OrderingHead> res = new Vector<OrderingHead>();
// create ordering heads for all person objects represented on root level
log.log(new Status(IStatus.INFO, CommonActivator.PLUGIN_ID,
"BUILD OrderingHead CONTAINERS FOR EXPORT CONTENTS"));
for (StructNode root : roots) {
Person p = (Person)root.getContent();
OrderingHead head = new OrderingHead(p.getPdrId().toString());
// attach lower levels to new head instance
for (StructNode cat : root.getChildren())
for (OrderingHead node : collapseSubCategories(cat))
head.addSubCategory(node);
// return ordering head only if it is not empty
if (head.hasSubCategories())
res.add(head);
}
return res;
}
public Vector<StructNode> getExpandableNodes() {
HashSet<StructNode> res = new HashSet<StructNode>();
for (Vector<StructNode> nds : this.nodes.values())
res.addAll(nds);
return new Vector<StructNode>(res);
}
/**
* When given a {@link StructNode} whose content is an {@link OrderingHead}, return a list
* of OrderingHeads that represent any nested categories that might unfold under said node.
*/
private Vector<OrderingHead> collapseSubCategories(StructNode catNode) {
Vector<OrderingHead> res = new Vector<OrderingHead>();
if (!catNode.isSelected())
return res;
String value = catNode.getRootCategory();
//System.out.println(" OrderingHead contains: "+value);
OrderingHead head = new OrderingHead(value);
head.setLabel(catNode.getLabel());
// test for any sub categories amongst children
for (StructNode chld : catNode.getChildren())
if (chld.isSelected())
if (chld.getContent() instanceof OrderingHead)
res.addAll(collapseSubCategories(chld));
else if (chld.getContent() instanceof Aspect) {
head.addAspect((Aspect)chld.getContent());
for (StructNode refNode : chld.getChildren())
if (refNode.getContent() instanceof ReferenceMods)
head.addReference((ReferenceMods)refNode.getContent());
}
// if no sub categories are expected under this node, return node itself
if (res.size()<1)
res.add(head);
return res;
}
/**
* Returns a new {@link PdrObjectsPreviewStructure} instance, containing
* copies of those {@link StructNode} trees which represent or of which descendants
* represent {@link PdrObject} referenced to by the given selection.
* @param selection an array of {@link PdrObject} instances
* @return
*/
public PdrObjectsPreviewStructure subSet(PdrObject[] selection) {
PdrObjectsPreviewStructure subset = new PdrObjectsPreviewStructure();
subset.groupedBy = this.groupedBy;
subset.sortedBy = this.sortedBy;
LinkedHashSet<StructNode> rootNodes = new LinkedHashSet<StructNode>();
// process selection of pdr objects
for (PdrObject pdrObj : selection) {
Vector<StructNode> there = subset.getNodesFor(pdrObj);
if (there.isEmpty()) {
Vector<StructNode> here = getNodesFor(pdrObj);
// find nodes containing specified pdr object
for (StructNode repr : here) {
StructNode copy = repr.copyIntoStructure(subset);
/*if (!subset.nodes.containsKey(pdrObj))
subset.nodes.put(pdrObj, new Vector<StructNode>());
subset.nodes.get(pdrObj).add(copy);*/
rootNodes.add(copy.getRoot());
}
}
}
subset.roots = new Vector<StructNode>(rootNodes);
return subset;
}
@Override
public boolean isChecked(Object element) {
if (element instanceof StructNode) {
StructNode node = (StructNode)element;
return node.isSelected();
}
return false;
}
@Override
public boolean isGrayed(Object element) {
// TODO Auto-generated method stub
return false;
}
}