KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > help > internal > index > IndexAssembler


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.help.internal.index;
12
13 import java.util.Arrays JavaDoc;
14 import java.util.Comparator JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.help.ITopic;
23 import org.eclipse.help.IUAElement;
24 import org.eclipse.help.internal.HelpPlugin;
25 import org.eclipse.help.internal.Topic;
26 import org.eclipse.help.internal.UAElement;
27 import org.eclipse.help.internal.dynamic.DocumentProcessor;
28 import org.eclipse.help.internal.dynamic.DocumentReader;
29 import org.eclipse.help.internal.dynamic.ExtensionHandler;
30 import org.eclipse.help.internal.dynamic.IncludeHandler;
31 import org.eclipse.help.internal.dynamic.ProcessorHandler;
32 import org.eclipse.help.internal.toc.HrefUtil;
33
34 /*
35  * Assembles individual keyword index contributions into a complete, fully
36  * sorted master index.
37  */

38 public class IndexAssembler {
39
40     private DocumentProcessor processor;
41     private Comparator JavaDoc comparator;
42     private String JavaDoc locale;
43
44     /*
45      * Assembles the given index contributions into a complete, sorted index.
46      * The originals are not modified.
47      */

48     public Index assemble(List JavaDoc contributions, String JavaDoc locale) {
49         this.locale = locale;
50         process(contributions);
51         Index index = merge(contributions);
52         sort(index);
53         return index;
54     }
55     
56     /*
57      * Merge all index contributions into one large index, not sorted.
58      */

59     private Index merge(List JavaDoc contributions) {
60         Index index = new Index();
61         Iterator JavaDoc iter = contributions.iterator();
62         while (iter.hasNext()) {
63             IndexContribution contribution = (IndexContribution)iter.next();
64             mergeChildren(index, (Index)contribution.getIndex());
65         }
66         return index;
67     }
68     
69     /*
70      * Merges the children of nodes a and b, and stores them into a. If the two
71      * contain the same keyword, only one is kept but its children are merged,
72      * recursively. If multiple topics exist with the same href, only the
73      * first one found is kept.
74      */

75     private void mergeChildren(UAElement a, UAElement b) {
76         // create data structures for fast lookup
77
Map JavaDoc entriesByKeyword = new HashMap JavaDoc();
78         Set JavaDoc topicHrefs = new HashSet JavaDoc();
79         IUAElement[] childrenA = a.getChildren();
80         for (int i=0;i<childrenA.length;++i) {
81             UAElement childA = (UAElement)childrenA[i];
82             if (childA instanceof IndexEntry) {
83                 entriesByKeyword.put(childA.getAttribute(IndexEntry.ATTRIBUTE_KEYWORD), childA);
84             }
85             else if (childA instanceof Topic) {
86                 topicHrefs.add(childA.getAttribute(Topic.ATTRIBUTE_HREF));
87             }
88         }
89         
90         // now do the merge
91
IUAElement[] childrenB = b.getChildren();
92         for (int i=0;i<childrenB.length;++i) {
93             UAElement childB = (UAElement)childrenB[i];
94             if (childB instanceof IndexEntry) {
95                 String JavaDoc keyword = childB.getAttribute(IndexEntry.ATTRIBUTE_KEYWORD);
96                 if (entriesByKeyword.containsKey(keyword)) {
97                     // duplicate keyword; merge children
98
mergeChildren((IndexEntry)entriesByKeyword.get(keyword), childB);
99                 }
100                 else {
101                     // wasn't a duplicate
102
a.appendChild(childB);
103                     entriesByKeyword.put(keyword, childB);
104                 }
105             }
106             else if (childB instanceof Topic) {
107                 String JavaDoc href = childB.getAttribute(Topic.ATTRIBUTE_HREF);
108                 if (!topicHrefs.contains(href)) {
109                     // add topic only if href doesn't exist yet
110
a.appendChild(childB);
111                     topicHrefs.add(href);
112                 }
113             }
114         }
115     }
116     
117     private void process(List JavaDoc contributions) {
118         if (processor == null) {
119             DocumentReader reader = new DocumentReader();
120             processor = new DocumentProcessor(new ProcessorHandler[] {
121                 new NormalizeHandler(),
122                 new IncludeHandler(reader, locale),
123                 new ExtensionHandler(reader, locale),
124             });
125         }
126         Iterator JavaDoc iter = contributions.iterator();
127         while (iter.hasNext()) {
128             IndexContribution contribution = (IndexContribution)iter.next();
129             processor.process((Index)contribution.getIndex(), contribution.getId());
130         }
131     }
132
133     /*
134      * Sort the given node's descendants recursively.
135      */

136     private void sort(UAElement element) {
137         if (comparator == null) {
138             comparator = new IndexComparator();
139         }
140         sort(element, comparator);
141     }
142     
143     /*
144      * Sort the given node's descendants recursively using the given
145      * Comparator.
146      */

147     private void sort(UAElement element, Comparator JavaDoc comparator) {
148         // sort children
149
IUAElement[] children = element.getChildren();
150         for (int i=0;i<children.length;++i) {
151             element.removeChild((UAElement)children[i]);
152         }
153         Arrays.sort(children, comparator);
154         for (int i=0;i<children.length;++i) {
155             element.appendChild((UAElement)children[i]);
156         }
157         // sort children's children
158
for (int i=0;i<children.length;++i) {
159             sort((UAElement)children[i], comparator);
160         }
161     }
162     
163     /*
164      * Normalizes topic hrefs, by prepending the plug-in id to form an href.
165      * e.g. "path/myfile.html" -> "/my.plugin/path/myfile.html"
166      */

167     private class NormalizeHandler extends ProcessorHandler {
168         public short handle(UAElement element, String JavaDoc id) {
169             if (element instanceof Topic) {
170                 Topic topic = (Topic)element;
171                 String JavaDoc href = topic.getHref();
172                 if (href != null) {
173                     int index = id.indexOf('/', 1);
174                     if (index != -1) {
175                         String JavaDoc pluginId = id.substring(1, index);
176                         topic.setHref(HrefUtil.normalizeHref(pluginId, href));
177                     }
178                 }
179             }
180             return UNHANDLED;
181         }
182     }
183
184     private static class IndexComparator implements Comparator JavaDoc {
185         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
186             /*
187              * First separate the objects into different groups by type;
188              * topics first, then entries, etc. Then within each
189              * group, sort alphabetically.
190              */

191             int c1 = getCategory((UAElement)o1);
192             int c2 = getCategory((UAElement)o2);
193             if (c1 == c2) {
194                 // same type of object; compare alphabetically
195
String JavaDoc s1 = getLabel((UAElement)o1).toLowerCase();
196                 String JavaDoc s2 = getLabel((UAElement)o2).toLowerCase();
197                 return s1.compareTo(s2);
198             }
199             else {
200                 // different types; compare by type
201
return c1 - c2;
202             }
203         }
204
205         /*
206          * Returns the category of the node. The order is:
207          * 1. topics
208          * 2. entries starting with non-alphanumeric
209          * 3. entries starting with digit
210          * 4. entries starting with alpha
211          * 5. other
212          */

213         private static int getCategory(UAElement element) {
214             if (element instanceof Topic) {
215                 return 0;
216             }
217             else if (element instanceof IndexEntry) {
218                 String JavaDoc keyword = ((IndexEntry)element).getKeyword();
219                 if (keyword != null && keyword.length() > 0) {
220                     char c = keyword.charAt(0);
221                     if (Character.isDigit(c)) {
222                         return 2;
223                     }
224                     else if (Character.isLetter(c)) {
225                         return 3;
226                     }
227                     return 1;
228                 }
229                 return 4;
230             }
231             else {
232                 return 5;
233             }
234         }
235         
236         /*
237          * Returns the string that will be displayed for the given object,
238          * used for sorting.
239          */

240         private static String JavaDoc getLabel(UAElement element) {
241             if (element instanceof Topic) {
242                 Topic topic = (Topic)element;
243                 if (topic.getLabel() == null) {
244                     ITopic topic2 = HelpPlugin.getTocManager().getTopic(topic.getHref());
245                     if (topic2 != null) {
246                         topic.setLabel(topic2.getLabel());
247                     }
248                     else {
249                         String JavaDoc msg = "Unable to look up label for help keyword index topic \"" + topic.getHref() + "\" with missing \"" + Topic.ATTRIBUTE_LABEL + "\" attribute (topic does not exist in table of contents; using href as label)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
250
HelpPlugin.logError(msg);
251                         topic.setLabel(topic.getHref());
252                     }
253                 }
254                 return topic.getLabel();
255             }
256             else if (element instanceof IndexEntry) {
257                 return ((IndexEntry)element).getKeyword();
258             }
259             return null;
260         }
261     };
262 }
263
Popular Tags