blob: 86ee5535292444cfabd187b4f5b2c0b53390d8c1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Oakland Software (Francis Upton) <francisu@ieee.org> - bug 219273
*******************************************************************************/
package org.eclipse.ui.internal.registry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.ui.internal.WorkbenchPlugin;
/**
* The CategorizedPageRegistryReader is the abstract super class
* of registry readers for page that have categorization.
*/
public abstract class CategorizedPageRegistryReader extends RegistryReader {
public static final String ATT_CATEGORY = "category"; //$NON-NLS-1$
static final String PREFERENCE_SEPARATOR = "/"; //$NON-NLS-1$
List topLevelNodes;
/**
* Internal class used to sort all the preference page nodes
* based on the category.
*/
abstract class CategoryNode {
/**
* Comment for <code>reader</code>
*/
private final CategorizedPageRegistryReader reader;
//private WorkbenchPreferenceNode node;
private String flatCategory;
/**
* Default constructor
*/
public CategoryNode(CategorizedPageRegistryReader reader) {
this.reader = reader;
}
/**
* Return the flatten category
*/
public String getFlatCategory() {
if (flatCategory == null) {
initialize();
if (flatCategory == null) {
flatCategory = getLabelText();
}
}
return flatCategory;
}
/**
* Get the label text for this node.
* @return String
*/
abstract String getLabelText();
/*
* Initialize the flat category to include the parents'
* category names and the current node's label
*/
private void initialize() {
String category = reader.getCategory(getNode());
if (category == null) {
return;
}
StringBuffer sb = new StringBuffer();
StringTokenizer stok = new StringTokenizer(category, PREFERENCE_SEPARATOR);
Object immediateParent = null;
while (stok.hasMoreTokens()) {
String pathID = stok.nextToken();
immediateParent = this.reader.findNode(pathID);
if (immediateParent == null) {
return;
}
if (sb.length() > 0) {
sb.append(PREFERENCE_SEPARATOR);
}
sb.append(getLabelText(immediateParent));
}
if (sb.length() > 0) {
sb.append(PREFERENCE_SEPARATOR);
}
sb.append(getLabelText());
flatCategory = sb.toString();
}
/**
* Return the label text for the passed element.
* @param element
* @return String
*/
abstract String getLabelText(Object element);
/**
* Get the node the receiver represents.
* @return Object
*/
abstract Object getNode();
}
/**
* Create a new instance of the receiver.
*/
public CategorizedPageRegistryReader() {
super();
}
/**
* Process the preference page nodes.
*/
void processNodes() {
topLevelNodes = new ArrayList();
//root nodes (which contain subnodes)
//Add root nodes to the contributions vector
StringTokenizer tokenizer;
String currentToken;
CategoryNode[] nodes = createCategoryNodes(getNodes());
// flag to indicate that some work was done in the inner loop over the nodes
boolean workDone;
do {
//reset the flag
workDone = false;
List deferred = new ArrayList();
for (int i = 0; i < nodes.length; i++) {
// Iterate through all the nodes
CategoryNode categoryNode = nodes[i];
Object node = categoryNode.getNode();
String category = getCategory(node);
if (category == null) {
topLevelNodes.add(node);
continue;
}
// has category
tokenizer = new StringTokenizer(category, PREFERENCE_SEPARATOR);
Object parent = null;
while (tokenizer.hasMoreElements()) {
currentToken = tokenizer.nextToken();
Object child = null;
if (parent == null) {
child = findNode(currentToken);
} else {
child = findNode(parent, currentToken);
}
if (child == null) {
parent = null;
break;
}
parent = child;
}
if (parent != null) {
//we've done some work - the number of nodes to process has decreased
workDone = true;
add(parent, node);
} else {
// we haven't done any work - the parent for this node has not been found.
deferred.add(categoryNode);
}
}
// reset the nodes to all that have yet to find their proper parent
nodes = (CategoryNode[]) deferred.toArray(new CategoryNode[deferred
.size()]);
} while (nodes.length > 0 && workDone); // loop while we still have nodes to work on and the list is shrinking
// log anything left over.
for (int i = 0; i < nodes.length; i++) {
CategoryNode categoryNode = nodes[i];
// Could not find the parent - log
WorkbenchPlugin
.log("Invalid preference page path: " + categoryNode.getFlatCategory()); //$NON-NLS-1$
topLevelNodes.add(categoryNode.getNode());
}
}
/**
* Get the category for the node if there is one. If there
* isn't return <code>null</code>.
* @param node
* @return String or <code>null</code>.
*/
abstract String getCategory(Object node);
/**
* Add the node to the parent.
* @param parent
* @param node
*/
abstract void add(Object parent, Object node);
/**
* Get the nodes for the receiver.
* @return Collection of Object
*/
abstract Collection getNodes();
/**
* Sort the nodes based on full category + name. Category used for sorting
* is created by substituting node IDs with labels of the referenced
* nodes. workbench node is excluded from sorting because it always
* appears first in the dialog.
*/
CategoryNode[] createCategoryNodes(Collection nodesToCategorize) {
//sort by categories
List nodes = new ArrayList();
Iterator nodesIterator = nodesToCategorize.iterator();
while (nodesIterator.hasNext()) {
nodes.add(createCategoryNode(this, nodesIterator.next()));
}
return (CategoryNode[]) nodes.toArray(new CategoryNode[nodes.size()]);
}
/**
* Create a node for categorization from the reader
* and the supplied object.
* @param reader
* @param object
* @return CategoryNode
*/
abstract CategoryNode createCategoryNode(CategorizedPageRegistryReader reader, Object object);
/**
* Searches for the top-level node with the given id.
* @param id
* @return Object of the type being categorized or
* <code>null</code>
*/
abstract Object findNode(String id);
/**
* Find the node with the given parent with the id
* of currentToken.
* @param parent
* @param currentToken
* @return
*/
abstract Object findNode(Object parent, String currentToken);
}