001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.registry;
011:
012: import com.ibm.icu.text.Collator;
013: import java.util.ArrayList;
014: import java.util.Collection;
015: import java.util.Collections;
016: import java.util.Comparator;
017: import java.util.Iterator;
018: import java.util.List;
019: import java.util.StringTokenizer;
020:
021: import org.eclipse.ui.internal.WorkbenchPlugin;
022:
023: /**
024: * The CategorizedPageRegistryReader is the abstract super class
025: * of registry readers for page that have categorization.
026: */
027: public abstract class CategorizedPageRegistryReader extends
028: RegistryReader {
029:
030: public static final String ATT_CATEGORY = "category"; //$NON-NLS-1$
031:
032: static final String PREFERENCE_SEPARATOR = "/"; //$NON-NLS-1$
033:
034: List topLevelNodes;
035:
036: private static final Comparator comparer = new Comparator() {
037: private Collator collator = Collator.getInstance();
038:
039: public int compare(Object arg0, Object arg1) {
040: String s1 = ((CategoryNode) arg0).getFlatCategory();
041: String s2 = ((CategoryNode) arg1).getFlatCategory();
042: return collator.compare(s1, s2);
043: }
044: };
045:
046: /**
047: * Internal class used to sort all the preference page nodes
048: * based on the category.
049: */
050: abstract class CategoryNode {
051: /**
052: * Comment for <code>reader</code>
053: */
054: private final CategorizedPageRegistryReader reader;
055:
056: //private WorkbenchPreferenceNode node;
057:
058: private String flatCategory;
059:
060: /**
061: * Default constructor
062: */
063: public CategoryNode(CategorizedPageRegistryReader reader) {
064: this .reader = reader;
065: }
066:
067: /**
068: * Return the flatten category
069: */
070: public String getFlatCategory() {
071: if (flatCategory == null) {
072: initialize();
073: if (flatCategory == null) {
074: flatCategory = getLabelText();
075: }
076: }
077: return flatCategory;
078: }
079:
080: /**
081: * Get the label text for this node.
082: * @return String
083: */
084: abstract String getLabelText();
085:
086: /*
087: * Initialize the flat category to include the parents'
088: * category names and the current node's label
089: */
090: private void initialize() {
091: String category = reader.getCategory(getNode());
092: if (category == null) {
093: return;
094: }
095:
096: StringBuffer sb = new StringBuffer();
097: StringTokenizer stok = new StringTokenizer(category,
098: PREFERENCE_SEPARATOR);
099: Object immediateParent = null;
100: while (stok.hasMoreTokens()) {
101: String pathID = stok.nextToken();
102: immediateParent = this .reader.findNode(pathID);
103: if (immediateParent == null) {
104: return;
105: }
106: if (sb.length() > 0) {
107: sb.append(PREFERENCE_SEPARATOR);
108: }
109: sb.append(getLabelText(immediateParent));
110: }
111:
112: if (sb.length() > 0) {
113: sb.append(PREFERENCE_SEPARATOR);
114: }
115: sb.append(getLabelText());
116: flatCategory = sb.toString();
117: }
118:
119: /**
120: * Return the label text for the passed element.
121: * @param element
122: * @return String
123: */
124: abstract String getLabelText(Object element);
125:
126: /**
127: * Get the node the receiver represents.
128: * @return Object
129: */
130: abstract Object getNode();
131: }
132:
133: /**
134: * Create a new instance of the receiver.
135: */
136: public CategorizedPageRegistryReader() {
137: super ();
138: }
139:
140: /**
141: * Process the preference page nodes.
142: */
143: void processNodes() {
144: topLevelNodes = new ArrayList();
145: //root nodes (which contain subnodes)
146:
147: //Add root nodes to the contributions vector
148: StringTokenizer tokenizer;
149: String currentToken;
150:
151: // Make the advisor's favorite the first category
152: Object favorite = null;
153: String favoriteId = getFavoriteNodeId();
154: if (favoriteId != null) {
155: favorite = findNode(favoriteId);
156: }
157: if (favorite != null) {
158: topLevelNodes.add(favorite);
159: }
160:
161: // Sort nodes based on flattened display path composed of
162: // actual labels of nodes referenced in category attribute.
163: Object[] sortedNodes = sortByCategories(getNodes());
164: for (int i = 0; i < sortedNodes.length; i++) {
165: //Iterate through all the nodes
166: CategoryNode categoryNode = (CategoryNode) sortedNodes[i];
167: Object node = categoryNode.getNode();
168: if (node == favorite) {
169: // skip it - favorite already at the top of the list
170: continue;
171: }
172: String category = getCategory(node);
173: if (category == null) {
174: topLevelNodes.add(node);
175: continue;
176: }
177: // has category
178: tokenizer = new StringTokenizer(category,
179: PREFERENCE_SEPARATOR);
180: Object parent = null;
181: while (tokenizer.hasMoreElements()) {
182: currentToken = tokenizer.nextToken();
183: Object child = null;
184: if (parent == null) {
185: child = findNode(currentToken);
186: } else {
187: child = findNode(parent, currentToken);
188: }
189: if (child == null) {
190: parent = null;
191: break;
192: } else {
193: parent = child;
194: }
195: }
196: if (parent != null) {
197: add(parent, node);
198: } else {
199: //Could not find the parent - log
200: WorkbenchPlugin
201: .log("Invalid preference page path: " + categoryNode.getFlatCategory()); //$NON-NLS-1$
202: topLevelNodes.add(node);
203: }
204: }
205: }
206:
207: /**
208: * Get the category for the node if there is one. If there
209: * isn't return <code>null</code>.
210: * @param node
211: * @return String or <code>null</code>.
212: */
213: abstract String getCategory(Object node);
214:
215: /**
216: * Add the node to the parent.
217: * @param parent
218: * @param node
219: */
220: abstract void add(Object parent, Object node);
221:
222: /**
223: * Get the nodes for the receiver.
224: * @return Collection of Object
225: */
226: abstract Collection getNodes();
227:
228: /**
229: * Return the id of the favorite node or <code>null</code>
230: * if there isn't one.
231: * @return String
232: */
233: abstract String getFavoriteNodeId();
234:
235: /**
236: * Sort the nodes based on full category + name. Category used for sorting
237: * is created by substituting node IDs with labels of the referenced
238: * nodes. workbench node is excluded from sorting because it always
239: * appears first in the dialog.
240: */
241: Object[] sortByCategories(Collection nodesToCategorize) {
242: //sort by categories
243: List nodes = new ArrayList();
244:
245: Iterator nodesIterator = nodesToCategorize.iterator();
246: while (nodesIterator.hasNext()) {
247: nodes.add(createCategoryNode(this , nodesIterator.next()));
248: }
249:
250: Collections.sort(nodes, comparer);
251: return nodes.toArray();
252: }
253:
254: /**
255: * Create a node for categorization from the reader
256: * and the supplied object.
257: * @param reader
258: * @param object
259: * @return CategoryNode
260: */
261: abstract CategoryNode createCategoryNode(
262: CategorizedPageRegistryReader reader, Object object);
263:
264: /**
265: * Searches for the top-level node with the given id.
266: * @param id
267: * @return Object of the type being categorized or
268: * <code>null</code>
269: */
270: abstract Object findNode(String id);
271:
272: /**
273: * Find the node with the given parent with the id
274: * of currentToken.
275: * @param parent
276: * @param currentToken
277: * @return
278: */
279: abstract Object findNode(Object parent, String currentToken);
280:
281: }
|