KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > javadoc > search > IndexBuilder


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.javadoc.search;
21
22 import java.io.*;
23 import java.lang.ref.*;
24 import java.text.Collator JavaDoc;
25 import java.util.*;
26 import javax.swing.event.ChangeEvent JavaDoc;
27 import javax.swing.event.ChangeListener JavaDoc;
28 import javax.swing.text.SimpleAttributeSet JavaDoc;
29 import javax.swing.text.html.HTML JavaDoc;
30
31
32 import javax.swing.text.html.parser.*;
33
34 import org.openide.ErrorManager;
35 import org.openide.filesystems.*;
36 import org.openide.util.NbBundle;
37
38 import org.openide.util.RequestProcessor;
39
40
41 /**
42  * Builds index of Javadoc filesystems.
43  * @author Svata Dedic, Jesse Glick
44  */

45 public class IndexBuilder implements Runnable JavaDoc, ChangeListener JavaDoc {
46
47     private static final String JavaDoc[] INDEX_FILE_NAMES = {
48         "overview-summary.html", // NOI18N
49
"index.html", // NOI18N
50
"index.htm", // NOI18N
51
};
52
53     private static IndexBuilder INSTANCE;
54
55     private static RequestProcessor.Task task;
56     
57     private static final ErrorManager err =
58             ErrorManager.getDefault().getInstance("org.netbeans.modules.javadoc.search.IndexBuilder"); // NOI18N;
59

60     private Reference cachedData;
61     
62     private JavadocRegistry jdocRegs;
63
64     /**
65      * WeakMap<FileSystem : info> of information extracted from filesystems.
66      */

67     Map filesystemInfo = Collections.EMPTY_MAP;
68
69     private static class Info {
70         /**
71          * Display name / title of the helpset
72          */

73         String JavaDoc title;
74
75         /**
76          * Name of the index/overview file
77          */

78         String JavaDoc indexFileName;
79     }
80
81     private IndexBuilder() {
82         this.jdocRegs = JavadocRegistry.getDefault();
83         this.jdocRegs.addChangeListener(this);
84         if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
85             err.log("new IndexBuilder");
86         }
87     }
88
89     /**
90      * Get the default index builder instance.
91      * It will start parsing asynch.
92      */

93     public synchronized static IndexBuilder getDefault() {
94         if (INSTANCE != null)
95             return INSTANCE;
96         INSTANCE = new IndexBuilder();
97         scheduleTask();
98         return INSTANCE;
99     }
100     
101     public void run() {
102         cachedData = null;
103         refreshIndex();
104     }
105     
106     public void stateChanged (ChangeEvent JavaDoc event) {
107         scheduleTask ();
108     }
109
110     /**
111      * Get the important information from the index builder.
112      * Waits for parsing to complete first, if necessary.
113      * @return two lists, one of String display names, the other of FileObject indices
114      */

115     public List[] getIndices() {
116         task.waitFinished();
117         if (cachedData != null) {
118             List[] data = (List[])cachedData.get();
119             if (data != null) {
120                 if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
121                     err.log("getIndices (cached)");
122                 }
123                 return data;
124             }
125         }
126         
127         if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
128             err.log("getIndices");
129         }
130         Map m = this.filesystemInfo;
131         Iterator it = m.entrySet().iterator();
132         final Collator JavaDoc c = Collator.getInstance();
133         class Pair implements Comparable JavaDoc {
134             public String JavaDoc display;
135             public FileObject fo;
136             public int compareTo(Object JavaDoc o) {
137                 return c.compare(display, ((Pair)o).display);
138             }
139         }
140         SortedSet pairs = new TreeSet(); // SortedSet<Pair>
141
for (int i = 0; i < m.size(); i++) {
142             Map.Entry e = (Map.Entry)it.next();
143             FileObject f = (FileObject)e.getKey();
144             Info info = (Info)e.getValue();
145             FileObject fo = f.getFileObject(info.indexFileName);
146             if (fo == null)
147                 continue;
148             Pair p = new Pair();
149             p.display = info.title;
150             p.fo = fo;
151             pairs.add(p);
152         }
153         List display = new ArrayList(pairs.size());
154         List fos = new ArrayList(pairs.size());
155         it = pairs.iterator();
156         while (it.hasNext()) {
157             Pair p = (Pair)it.next();
158             display.add(p.display);
159             fos.add(p.fo);
160         }
161         List[] data = new List[] {display, fos};
162         cachedData = new WeakReference(data);
163         return data;
164     }
165
166     private void refreshIndex() {
167         if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
168             err.log("refreshIndex");
169         }
170         Map oldMap;
171         synchronized (this) {
172             oldMap = this.filesystemInfo;
173         }
174         //Enumeration e = FileSystemCapability.DOC.fileSystems();
175
FileObject docRoots[] = jdocRegs.getDocRoots();
176         // XXX needs to be able to listen to result; when it changes, call scheduleTask()
177
Map m = new WeakHashMap();
178 // long startTime = System.nanoTime();
179

180         for ( int ifCount = 0; ifCount < docRoots.length; ifCount++ ) {
181             FileObject fo = docRoots[ifCount];
182             Info oldInfo = (Info)oldMap.get(fo);
183             if (oldInfo != null) {
184                 // No need to reparse.
185
m.put(fo, oldInfo);
186                 continue;
187             }
188             
189             FileObject index = null;
190             for (int i = 0; i < INDEX_FILE_NAMES.length; i++) {
191                 if ((index = fo.getFileObject(INDEX_FILE_NAMES[i])) != null) {
192                     break;
193                 }
194             }
195             if (index == null || index.getName().equals("index")) { // NOI18N
196
// For single-package doc sets, overview-summary.html is not present,
197
// and index.html is less suitable (it is framed). Look for a package
198
// summary.
199
// [PENDING] Display name is not ideal, e.g. "org.openide.windows (NetBeans Input/Output API)"
200
// where simply "NetBeans Input/Output API" is preferable... but standard title filter
201
// regexps are not so powerful (to avoid matching e.g. "Servlets (Main Documentation)").
202
FileObject packageList = fo.getFileObject("package-list"); // NOI18N
203
if (packageList != null) {
204                     try {
205                         InputStream is = packageList.getInputStream();
206                         try {
207                             BufferedReader r = new BufferedReader(new InputStreamReader(is));
208                             String JavaDoc line = r.readLine();
209                             if (line != null && r.readLine() == null) {
210                                 // Good, exactly one line as expected. A package name.
211
String JavaDoc resName = line.replace('.', '/') + "/package-summary.html"; // NOI18N
212
FileObject pindex = fo.getFileObject(resName);
213                                 if (pindex != null) {
214                                     index = pindex;
215                                 }
216                                 // else fall back to index.html if available
217
}
218                         } finally {
219                             is.close();
220                         }
221                     } catch (IOException ioe) {
222                             // Oh well, skip this one.
223
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
224                     }
225                 }
226             }
227             if (index != null) {
228                 // Try to find a title.
229
String JavaDoc title = parseTitle(index);
230                 if (title != null) {
231                     JavadocSearchType st = jdocRegs.findSearchType( fo );
232                     if (st == null)
233                         continue;
234                     title = st.getOverviewTitleBase(title);
235                 }
236                 if ("".equals(title)) { // NOI18N
237
title = NbBundle.getMessage(IndexBuilder.class,
238                             "FMT_NoOverviewTitle", new Object JavaDoc[] { index.getPath(), // NOI18N
239
fo.getName(),
240                                                                    fo.getName() });
241                 }
242                 Info info = new Info();
243                 info.title = title == null ? fo.getName() : title;
244                 info.indexFileName = FileUtil.getRelativePath(fo, index);
245                 m.put(fo, info);
246             }
247             synchronized (this) {
248                 this.filesystemInfo = m;
249             }
250         }
251
252 // long elapsedTime = System.nanoTime() - startTime;
253
// System.out.println("\nElapsed time[nano]: " + elapsedTime);
254
}
255     
256     /**
257      * Attempt to find the title of an HTML file object.
258      * May return null if there is no title tag, or "" if it is empty.
259      */

260     private String JavaDoc parseTitle(FileObject html) {
261         String JavaDoc title = null;
262         try {
263             // #71979: html parser used again to fix encoding issues.
264
// I have measured no difference if the parser or plain file reading
265
// is used (#32551).
266
// In case the parser is stopped as soon as it finds the title it is
267
// even faster than the previous fix.
268
InputStream is = new BufferedInputStream(html.getInputStream(), 1024);
269             SimpleTitleParser tp = new SimpleTitleParser(is);
270             try {
271                 tp.parse();
272                 title = tp.getTitle();
273             } finally {
274                 is.close();
275             }
276         } catch (IOException ioe) {
277             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
278         }
279         if (title == null) { // fallback
280
title = FileUtil.getFileDisplayName(html);
281         }
282         return title;
283     }
284
285     private synchronized static void scheduleTask() {
286         if (task == null) {
287             task = new RequestProcessor("Javadoc Index Builder").create(getDefault()); // NOI18N
288
}
289         // Give it a small delay to avoid restarting too many times e.g. during
290
// project switch:
291
task.schedule(100);
292     }
293
294     static final class SimpleTitleParser {
295
296         private char cc;
297         private InputStream is;
298         private String JavaDoc charset;
299         private String JavaDoc title;
300         private int state = CONTINUE;
301
302         private static final int CONTINUE = 0;
303         private static final int EXIT = 0;
304
305         SimpleTitleParser(InputStream is) {
306             this.is = is;
307         }
308
309         public String JavaDoc getTitle() {
310             return title;
311         }
312
313         public void parse() throws IOException {
314             readNext();
315             while (state == CONTINUE) {
316                 switch (cc) {
317                     case '<' : // start of tags
318
handleOpenBrace();
319                         break;
320                     case (char) -1 : // EOF
321
return;
322                     default:
323                         readNext();
324                 }
325             }
326         }
327
328         private void readNext() throws IOException {
329             cc = (char) is.read();
330         }
331
332         private void handleOpenBrace() throws IOException {
333             StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
334             while (true) {
335                 readNext();
336                 switch (cc) {
337                     case '>': // end of tag
338
String JavaDoc tag = sb.toString().toLowerCase();
339                         if (tag.startsWith("body")) { // NOI18N
340
state = EXIT;
341                             return; // exit parsing, no title
342
} else if (tag.startsWith("meta")) { // NOI18N
343
handleMetaTag(tag);
344                             return;
345                         } else if (tag.startsWith("title")) { // NOI18N
346
handleTitleTag();
347                             return;
348
349                         }
350                         return;
351                     case (char) -1: // EOF
352
return;
353                     case ' ':
354                         if (sb.length() == 0) // ignore leading spaces
355
break;
356                     default:
357                         sb.append(cc);
358                 }
359             }
360
361         }
362
363         private void handleMetaTag(String JavaDoc txt) {
364             // parse something like
365
// <META http-equiv="Content-Type" content="text/html; charset=euc-jp">
366
// see http://www.w3.org/TR/REC-html32#meta
367
String JavaDoc name = ""; // NOI18N
368
String JavaDoc value = ""; // NOI18N
369

370             char tc;
371             char[] txts = txt.toCharArray();
372             int offset = 5; // skip "meta "
373
int start = offset;
374             int state = 0;
375             while (offset < txts.length) {
376                 tc = txt.charAt(offset);
377                 if (tc == '=' && state == 0) { // end of name
378
name = String.valueOf(txts, start, offset++ - start).trim();
379                     state = 1;
380                 } else if (state == 1 && (tc == '"' || tc == '\'')) { // start of value
381
start = ++offset;
382                     state = 2;
383                 } else if (state == 2 && (tc == '"' || tc == '\'')) { // end of value
384
value = String.valueOf(txts, start, offset++ - start);
385                     if ("content".equals(name)) { // NOI18N
386
break;
387                     }
388                     name = ""; // NOI18N
389
state = 0;
390                     start = offset;
391                 } else {
392                     ++offset;
393                 }
394
395             }
396
397             StringTokenizer tk = new StringTokenizer(value, ";"); // NOI18N
398
while (tk.hasMoreTokens()) {
399                 String JavaDoc str = tk.nextToken().trim();
400                 if (str.startsWith("charset")) { //NOI18N
401
str = str.substring(7).trim();
402                     if (str.charAt(0) == '=') {
403                         this.charset = str.substring(1).trim();
404                         return;
405                     }
406                 }
407             }
408         }
409
410         private void handleTitleTag() throws IOException {
411             byte[] buf = new byte[200];
412             int offset = 0;
413             while (true) {
414                 readNext();
415                 switch (cc) {
416                     case (char) -1: // EOF
417
return;
418                     case '>': // </title>
419
if ("</title".equals(new String JavaDoc(buf, offset - 7, 7).toLowerCase())) {
420                             // title is ready
421
// XXX maybe we should also resolve entities like &gt;
422
state = EXIT;
423                             if (charset == null) {
424                                 title = new String JavaDoc(buf, 0, offset - 7).trim();
425                             } else {
426                                 title = new String JavaDoc(buf, 0, offset - 7, charset).trim();
427                             }
428                             return;
429                         }
430                     default:
431                         cc = (cc == '\n' || cc == '\r')? ' ': cc;
432                         if (offset == buf.length) {
433                             buf = enlarge(buf);
434                         }
435                         buf[offset++] = (byte) cc;
436
437                 }
438             }
439         }
440
441         private static byte[] enlarge(byte[] b) {
442             byte[] b2 = new byte[b.length + 200];
443             for (int i = 0; i < b.length; i++) {
444                 b2[i] = b[i];
445             }
446             return b2;
447         }
448     }
449
450 }
451
Popular Tags