1 19 20 package org.netbeans.modules.javadoc.search; 21 22 import java.io.*; 23 import java.lang.ref.*; 24 import java.text.Collator ; 25 import java.util.*; 26 import javax.swing.event.ChangeEvent ; 27 import javax.swing.event.ChangeListener ; 28 import javax.swing.text.SimpleAttributeSet ; 29 import javax.swing.text.html.HTML ; 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 45 public class IndexBuilder implements Runnable , ChangeListener { 46 47 private static final String [] INDEX_FILE_NAMES = { 48 "overview-summary.html", "index.html", "index.htm", }; 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"); 60 private Reference cachedData; 61 62 private JavadocRegistry jdocRegs; 63 64 67 Map filesystemInfo = Collections.EMPTY_MAP; 68 69 private static class Info { 70 73 String title; 74 75 78 String 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 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 event) { 107 scheduleTask (); 108 } 109 110 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 c = Collator.getInstance(); 133 class Pair implements Comparable { 134 public String display; 135 public FileObject fo; 136 public int compareTo(Object o) { 137 return c.compare(display, ((Pair)o).display); 138 } 139 } 140 SortedSet pairs = new TreeSet(); 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 FileObject docRoots[] = jdocRegs.getDocRoots(); 176 Map m = new WeakHashMap(); 178 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 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")) { FileObject packageList = fo.getFileObject("package-list"); if (packageList != null) { 204 try { 205 InputStream is = packageList.getInputStream(); 206 try { 207 BufferedReader r = new BufferedReader(new InputStreamReader(is)); 208 String line = r.readLine(); 209 if (line != null && r.readLine() == null) { 210 String resName = line.replace('.', '/') + "/package-summary.html"; FileObject pindex = fo.getFileObject(resName); 213 if (pindex != null) { 214 index = pindex; 215 } 216 } 218 } finally { 219 is.close(); 220 } 221 } catch (IOException ioe) { 222 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe); 224 } 225 } 226 } 227 if (index != null) { 228 String 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)) { title = NbBundle.getMessage(IndexBuilder.class, 238 "FMT_NoOverviewTitle", new Object [] { index.getPath(), 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 } 255 256 260 private String parseTitle(FileObject html) { 261 String title = null; 262 try { 263 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) { 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()); } 289 task.schedule(100); 292 } 293 294 static final class SimpleTitleParser { 295 296 private char cc; 297 private InputStream is; 298 private String charset; 299 private String 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 getTitle() { 310 return title; 311 } 312 313 public void parse() throws IOException { 314 readNext(); 315 while (state == CONTINUE) { 316 switch (cc) { 317 case '<' : handleOpenBrace(); 319 break; 320 case (char) -1 : 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 sb = new StringBuilder (); 334 while (true) { 335 readNext(); 336 switch (cc) { 337 case '>': String tag = sb.toString().toLowerCase(); 339 if (tag.startsWith("body")) { state = EXIT; 341 return; } else if (tag.startsWith("meta")) { handleMetaTag(tag); 344 return; 345 } else if (tag.startsWith("title")) { handleTitleTag(); 347 return; 348 349 } 350 return; 351 case (char) -1: return; 353 case ' ': 354 if (sb.length() == 0) break; 356 default: 357 sb.append(cc); 358 } 359 } 360 361 } 362 363 private void handleMetaTag(String txt) { 364 String name = ""; String value = ""; 370 char tc; 371 char[] txts = txt.toCharArray(); 372 int offset = 5; int start = offset; 374 int state = 0; 375 while (offset < txts.length) { 376 tc = txt.charAt(offset); 377 if (tc == '=' && state == 0) { name = String.valueOf(txts, start, offset++ - start).trim(); 379 state = 1; 380 } else if (state == 1 && (tc == '"' || tc == '\'')) { start = ++offset; 382 state = 2; 383 } else if (state == 2 && (tc == '"' || tc == '\'')) { value = String.valueOf(txts, start, offset++ - start); 385 if ("content".equals(name)) { break; 387 } 388 name = ""; state = 0; 390 start = offset; 391 } else { 392 ++offset; 393 } 394 395 } 396 397 StringTokenizer tk = new StringTokenizer(value, ";"); while (tk.hasMoreTokens()) { 399 String str = tk.nextToken().trim(); 400 if (str.startsWith("charset")) { 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: return; 418 case '>': if ("</title".equals(new String (buf, offset - 7, 7).toLowerCase())) { 420 state = EXIT; 423 if (charset == null) { 424 title = new String (buf, 0, offset - 7).trim(); 425 } else { 426 title = new String (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 |