1 11 package org.eclipse.help.search; 12 13 import java.io.File ; 14 import java.io.FileInputStream ; 15 import java.io.IOException ; 16 import java.io.InputStream ; 17 import java.io.InputStreamReader ; 18 import java.net.MalformedURLException ; 19 import java.net.URL ; 20 import java.util.ArrayList ; 21 import java.util.Collection ; 22 import java.util.Dictionary ; 23 import java.util.HashSet ; 24 import java.util.Iterator ; 25 import java.util.Locale ; 26 import java.util.Properties ; 27 import java.util.Set ; 28 import java.util.jar.Attributes ; 29 import java.util.jar.Manifest ; 30 31 import javax.xml.parsers.DocumentBuilder ; 32 import javax.xml.parsers.DocumentBuilderFactory ; 33 34 import org.eclipse.core.runtime.CoreException; 35 import org.eclipse.core.runtime.IProgressMonitor; 36 import org.eclipse.core.runtime.IStatus; 37 import org.eclipse.core.runtime.MultiStatus; 38 import org.eclipse.core.runtime.OperationCanceledException; 39 import org.eclipse.core.runtime.Platform; 40 import org.eclipse.core.runtime.Status; 41 import org.eclipse.core.runtime.SubProgressMonitor; 42 import org.eclipse.help.internal.base.HelpBasePlugin; 43 import org.eclipse.help.internal.base.HelpBaseResources; 44 import org.eclipse.help.internal.search.AnalyzerDescriptor; 45 import org.eclipse.help.internal.search.PluginVersionInfo; 46 import org.eclipse.help.internal.search.SearchIndex; 47 import org.eclipse.osgi.util.ManifestElement; 48 import org.eclipse.osgi.util.NLS; 49 import org.osgi.framework.Constants; 50 import org.osgi.framework.Version; 51 import org.w3c.dom.Document ; 52 import org.w3c.dom.Element ; 53 import org.w3c.dom.NamedNodeMap ; 54 import org.w3c.dom.Node ; 55 import org.w3c.dom.NodeList ; 56 import org.xml.sax.InputSource ; 57 58 89 90 public class HelpIndexBuilder { 91 private static final String POINT_TOC = "org.eclipse.help.toc"; 93 private static final String EL_TOC = "toc"; 95 private static final String EL_INDEX = "index"; 97 private File manifest; 98 99 private String indexPath; 100 101 private File destination; 102 103 private ArrayList tocFiles = new ArrayList (); 104 105 private ArrayList localeDirs = new ArrayList (); 106 107 private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory 108 .newInstance(); 109 110 private DocumentBuilder parser; 111 112 private static Locale [] legalLocales = Locale.getAvailableLocales(); 113 private static HashSet legalLanguages = null; 114 private static HashSet legalCountries = null; 115 116 class PluginIdentifier { 117 String id; 118 119 Version version; 120 121 public PluginIdentifier(String id, String version) { 122 this.id = id; 123 this.version = new Version(version); 124 } 125 } 126 127 class LocaleDir { 128 String locale; 129 String relativePath; 130 ArrayList dirs = new ArrayList (); 131 132 public LocaleDir(String locale, String relativePath) { 133 this.locale = locale; 134 this.relativePath = relativePath; 135 } 136 137 public File findFile(String file) { 138 for (int i=0; i<dirs.size(); i++) { 139 File dir = (File )dirs.get(i); 140 File absoluteFile = new File (dir, file); 141 if (absoluteFile.exists()) 142 return absoluteFile; 143 } 144 return null; 145 } 146 public URL findURL(String href) { 147 File file = findFile(href); 148 if (file!=null) { 149 try { 150 return file.toURL(); 151 } 152 catch (MalformedURLException e) { 153 } 154 } 155 return null; 156 } 157 public void addDirectory(File directory) { 158 dirs.add(directory); 159 } 160 } 161 162 class IndexerPluginVersionInfo extends PluginVersionInfo { 163 private static final long serialVersionUID = 1L; 164 165 public IndexerPluginVersionInfo(PluginIdentifier id, 166 PluginIdentifier fid, File dir) { 167 super(SearchIndex.INDEXED_CONTRIBUTION_INFO_FILE, null, dir, false); 168 createInfo(id, fid); 169 } 170 171 protected void createTable(Collection docBundleIds) { 172 } 174 175 protected void createInfo(PluginIdentifier id, PluginIdentifier fid) { 176 StringBuffer buffer = new StringBuffer (); 180 appendBundleInformation(buffer, id.id, id.version.toString()); 181 if (fid != null) 182 appendBundleInformation(buffer, fid.id, fid.version.toString()); 183 184 this.put(id.id, buffer.toString()); 185 } 186 } 187 188 class TocFile { 189 String href; 190 boolean primary; 191 String extraDir; 192 193 public TocFile(String href, boolean primary, String extraDir) { 194 this.href = href; 195 this.primary = primary; 196 this.extraDir = extraDir; 197 } 198 } 199 200 203 public HelpIndexBuilder() { 204 } 205 206 214 public File getManifest() { 215 return manifest; 216 } 217 218 227 public void setManifest(File manifest) { 228 if (manifest.getName().equalsIgnoreCase("MANIFEST.MF")) { File parent = manifest.getParentFile(); 230 if (parent.getName().equalsIgnoreCase("META-INF")) { File project = parent.getParentFile(); 232 manifest = new File (project, "plugin.xml"); if (!manifest.exists()) 234 manifest=null; 235 } 236 } 237 this.manifest = manifest; 238 } 239 240 245 public File getDestination() { 246 return destination; 247 } 248 249 256 public void setDestination(File destination) { 257 this.destination = destination; 258 } 259 260 272 273 public void execute(IProgressMonitor monitor) throws CoreException { 274 reset(); 275 if (manifest == null || destination == null) 276 return; 277 Document doc = readXMLFile(manifest); 278 if (doc == null) 279 return; 280 281 PluginIdentifier pid = getPluginID(manifest.getParentFile(), doc); 282 PluginIdentifier fid = null; 283 284 if (!manifest.getParentFile().equals(destination)) { 285 File fragmentFile = new File (destination, "fragment.xml"); Document fdoc=null; 288 if (fragmentFile.exists()) 289 fdoc = readXMLFile(fragmentFile); 290 fid = getPluginID(destination, fdoc); 291 fdoc=null; 292 } 293 294 Element[] extensions = getTocExtensions(doc); 295 for (int i = 0; i < extensions.length; i++) { 296 processExtension(extensions[i]); 297 } 298 if (indexPath == null) { 299 throwCoreException(HelpBaseResources.HelpIndexBuilder_noDestinationPath, null); 300 } 301 doc = null; 303 computeLocaleDirs(fid!=null); 305 306 monitor.beginTask(HelpBaseResources.HelpIndexBuilder_buildingIndex, localeDirs.size()); 307 MultiStatus multiStatus = null; 308 309 for (int i=0; i<localeDirs.size(); i++) { 310 LocaleDir localeDir = (LocaleDir) localeDirs.get(i); 312 MultiStatus localeStatus = processLocaleDir(pid, fid, 313 localeDir, new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); 314 if (localeStatus != null) { 315 if (multiStatus == null) 316 multiStatus = localeStatus; 317 else 318 multiStatus.addAll(localeStatus); 319 } 320 } 321 monitor.done(); 322 if (multiStatus != null) 323 throw new CoreException(multiStatus); 324 } 325 326 329 private void processExtension(Element extensionNode) { 330 NodeList children = extensionNode.getElementsByTagName(EL_TOC); 331 for (int i = 0; i < children.getLength(); i++) { 332 Node node = children.item(i); 333 String file = getAttribute(node, "file"); String primary = getAttribute(node, "primary"); String extradir = getAttribute(node, "extradir"); addTocFile(file, primary, extradir); 337 } 338 children = extensionNode.getElementsByTagName(EL_INDEX); 339 if (children.getLength() == 1) { 340 Node node = children.item(0); 341 indexPath = getAttribute(node, "path"); } 343 } 344 345 private void addTocFile(String file, String primary, String extradir) { 346 boolean isPrimary = primary != null && primary.equalsIgnoreCase("true"); tocFiles.add(new TocFile(file, isPrimary, extradir)); 348 } 349 350 355 private void computeLocaleDirs(boolean fragment) { 356 if (!fragment) { 357 LocaleDir dir = new LocaleDir(null, "/"); dir.addDirectory(destination); 359 localeDirs.add(dir); 360 } 361 File ws = new File (destination, "ws"); computeSystem(ws, Platform.knownWSValues()); 363 File os = new File (destination, "os"); computeSystem(os, Platform.knownOSValues()); 365 366 File nl = new File (destination, "nl"); if (!nl.exists() || !nl.isDirectory()) 368 return; 369 File [] languages = nl.listFiles(); 370 HashSet locales = new HashSet (); 371 for (int i=0; i<languages.length; i++) { 372 File language = languages[i]; 373 if (!language.isDirectory()) 374 continue; 375 if (!isValidLanguage(language.getName())) 376 continue; 377 File [] countries = language.listFiles(); 378 for (int j=0; j<countries.length; j++) { 379 File country = countries[j]; 380 String locale; 381 boolean hasCountry = false; 382 if (country.isDirectory() && isValidCountry(country.getName())) 383 hasCountry = true; 384 if (hasCountry) 385 locale = language.getName()+"_"+country.getName(); else 387 locale = language.getName(); 388 if (isValidLocale(locale) && !locales.contains(locale)) { 389 String relativePath; 390 if (hasCountry) 391 relativePath = "/nl/"+language.getName()+"/"+country.getName(); else 393 relativePath = "/nl/"+language.getName(); LocaleDir dir = new LocaleDir(locale, relativePath); 395 if (hasCountry) 396 dir.addDirectory(country); 397 dir.addDirectory(language); 398 dir.addDirectory(destination); 399 localeDirs.add(dir); 400 locales.add(locale); 401 } 402 } 403 } 404 } 405 406 private void computeSystem(File systemRoot, String [] values) { 407 if (systemRoot.exists() && systemRoot.isDirectory()) { 408 File [] files = systemRoot.listFiles(); 410 for (int i=0; i<files.length; i++) { 411 File sdir = files[i]; 412 if (!sdir.isDirectory()) 413 continue; 414 String sname = sdir.getName(); 415 for (int j=0; j<values.length; j++) { 416 if (values[j].equals(sname)) { 417 String relativePath="/"+systemRoot.getName()+"/"+sname; LocaleDir dir = new LocaleDir(sname, relativePath); 420 dir.addDirectory(sdir); 421 dir.addDirectory(destination); 422 localeDirs.add(dir); 423 break; 424 } 425 } 426 } 427 } 428 } 429 430 433 private boolean isValidLocale(String locale) { 434 for (int i=0; i<legalLocales.length; i++) { 435 Locale legalLocale = legalLocales[i]; 436 if (legalLocale.toString().equals(locale)) 437 return true; 438 } 439 return false; 440 } 441 442 private boolean isValidLanguage(String language) { 443 if (legalLanguages==null) { 444 legalLanguages = new HashSet (); 445 String [] choices = Locale.getISOLanguages(); 446 for (int i=0; i<choices.length; i++) { 447 legalLanguages.add(choices[i]); 448 } 449 } 450 return legalLanguages.contains(language); 451 } 452 453 private boolean isValidCountry(String country) { 454 if (legalCountries==null) { 455 legalCountries = new HashSet (); 456 String [] choices = Locale.getISOCountries(); 457 for (int i=0; i<choices.length; i++) { 458 legalCountries.add(choices[i]); 459 } 460 } 461 return legalCountries.contains(country); 462 } 463 464 468 469 private MultiStatus processLocaleDir(PluginIdentifier id, 470 PluginIdentifier fid, LocaleDir localeDir, IProgressMonitor monitor) 471 throws CoreException { 472 String message = NLS.bind(HelpBaseResources.HelpIndexBuilder_indexFor, ((File )localeDir.dirs.get(0)).getName()); 474 monitor.beginTask(message, 5); 475 File directory = (File )localeDir.dirs.get(0); 476 File indexDirectory = new File (directory, indexPath); 477 prepareDirectory(indexDirectory); 478 Collection docs = collectDocs(localeDir); 479 MultiStatus status = null; 480 if (docs.size()>0) { 481 String locale = localeDir.locale!=null?localeDir.locale:Platform.getNL(); 482 SearchIndex index = new SearchIndex(indexDirectory, locale, 483 new AnalyzerDescriptor(locale), null, localeDir.relativePath); 484 IndexerPluginVersionInfo docPlugins = new IndexerPluginVersionInfo(id, 485 fid, indexDirectory); 486 index.setDocPlugins(docPlugins); 487 status = createIndex(id.id, fid!=null, localeDir, index, docs, 488 new SubProgressMonitor(monitor, 5, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); 489 index.deleteLockFile(); 490 } 491 monitor.setTaskName(""); monitor.done(); 493 return status; 494 } 495 496 500 501 private Collection collectDocs(LocaleDir localeDir) 502 throws CoreException { 503 HashSet docs = new HashSet (); 504 for (int i = 0; i < tocFiles.size(); i++) { 505 TocFile tocFile = (TocFile) tocFiles.get(i); 506 collectDocs(docs, getTocFile(localeDir, tocFile.href)); 507 if (tocFile.extraDir!=null) { 508 } 511 } 512 return docs; 513 } 514 515 520 521 private File getTocFile(LocaleDir localeDir, String href) { 522 File file = localeDir.findFile(href); 524 if (file!=null) 525 return file; 526 File pdir = manifest.getParentFile(); 528 return new File (pdir, href); 529 } 530 531 534 private void collectDocs(Set docs, File tocFile) 535 throws CoreException { 536 if (!tocFile.exists()) 537 return; 538 Document doc = readXMLFile(tocFile); 539 add(doc.getDocumentElement(), docs); 540 } 541 542 545 private void add(Element topic, Set hrefs) { 546 String href = getAttribute(topic, "href"); if (topic.getTagName().equals("toc")) { href = getAttribute(topic, "topic"); } 551 if (href != null 552 && !href.equals("") && !href.startsWith("http://") && !href.startsWith("https://")) { href = SearchIndex.getIndexableHref(href); 554 if (href != null) 555 hrefs.add(href); 556 } 557 NodeList subtopics = topic.getElementsByTagName("topic"); for (int i = 0; i < subtopics.getLength(); i++) { 559 Element subtopic = (Element) subtopics.item(i); 560 href = getAttribute(subtopic, "href"); if (href != null && !href.equals("") && !href.startsWith("http://") && !href.startsWith("https://")) { href = SearchIndex.getIndexableHref(href); 563 if (href != null) 564 hrefs.add(href); 565 } 566 } 567 } 568 569 574 575 private MultiStatus createIndex(String pluginId, boolean fragment, LocaleDir localeDir, 576 SearchIndex index, Collection addedDocs, IProgressMonitor monitor) 577 throws CoreException { 578 monitor.beginTask(HelpBaseResources.UpdatingIndex, addedDocs.size()); 579 if (!index.beginAddBatch(true)) { 580 throwCoreException(HelpBaseResources.HelpIndexBuilder_error, null); 581 } 582 checkCancelled(monitor); 583 MultiStatus multiStatus = null; 584 585 for (Iterator it = addedDocs.iterator(); it.hasNext();) { 586 String href = (String ) it.next(); 587 URL url = localeDir.findURL(href); 588 if (url != null) { 589 IStatus status = index 590 .addDocument(getName(pluginId, href), url); 591 if (status.getCode() != IStatus.OK) { 592 if (multiStatus == null) 593 multiStatus = createMultiStatus(); 594 multiStatus.add(status); 595 } 596 } 597 else { 598 String locale = localeDir.locale!=null?localeDir.locale:Platform.getNL(); 600 String message = NLS.bind(HelpBaseResources.HelpIndexBuilder_cannotFindDoc, locale, href); 601 IStatus status = new Status(IStatus.WARNING, pluginId, IStatus.OK, message, null); 602 if (multiStatus == null) 603 multiStatus = createMultiStatus(); 604 multiStatus.add(status); 605 } 606 checkCancelled(monitor); 607 monitor.worked(1); 608 } 609 monitor.subTask(HelpBaseResources.Writing_index); 610 if (!index.endAddBatch(true, true)) { 611 IStatus status = new Status(IStatus.ERROR, HelpBasePlugin.PLUGIN_ID, 612 IStatus.OK, HelpBaseResources.HelpIndexBuilder_errorWriting, null); 613 if (multiStatus==null) 614 multiStatus = createMultiStatus(); 615 multiStatus.add(status); 616 } 617 monitor.done(); 618 return multiStatus; 619 } 620 621 private MultiStatus createMultiStatus() { 622 return new MultiStatus( 623 HelpBasePlugin.PLUGIN_ID, 624 IStatus.OK, 625 HelpBaseResources.HelpIndexBuilder_incompleteIndex, 626 null); 627 } 628 629 private void checkCancelled(IProgressMonitor pm) 630 throws OperationCanceledException { 631 if (pm.isCanceled()) 632 throw new OperationCanceledException(); 633 } 634 635 private String getName(String pluginId, String href) { 636 int i = href.indexOf('?'); 638 if (i != -1) 639 href = href.substring(0, i); 640 return "/" + pluginId + "/" + href; } 642 643 649 650 private void prepareDirectory(File indexDirectory) throws CoreException { 651 if (indexDirectory.exists()) { 652 File [] files = indexDirectory.listFiles(); 653 for (int i = 0; i < files.length; i++) { 654 File file = files[i]; 655 boolean result = file.delete(); 656 if (!result) 657 throwCoreException( 658 HelpBaseResources.HelpIndexBuilder_cannotScrub, null); 659 } 660 } else { 661 boolean result = indexDirectory.mkdirs(); 662 if (!result) 663 throwCoreException(HelpBaseResources.HelpIndexBuilder_cannotCreateDest, 664 null); 665 } 666 } 667 668 private void reset() { 669 localeDirs.clear(); 670 tocFiles.clear(); 671 indexPath = null; 672 } 673 674 private PluginIdentifier getPluginID(File dir, Document doc) throws CoreException { 675 String id = null; 676 String version = null; 677 if (doc != null) { 678 Node root = doc.getDocumentElement(); 679 id = getAttribute(root, "id"); version = getAttribute(root, "version"); if (id != null && version != null) 682 return new PluginIdentifier(id, version); 683 } 684 File OSGiFile = new File (dir, 686 "META-INF/MANIFEST.MF"); 688 if (OSGiFile.exists()) { 689 try { 690 Manifest OSGiManifest = new Manifest (new FileInputStream ( 691 OSGiFile)); 692 Dictionary headers = manifestToProperties(OSGiManifest 693 .getMainAttributes()); 694 String value = headers.get(Constants.BUNDLE_SYMBOLICNAME) 695 .toString(); 696 if (value == null) 697 return null; 698 ManifestElement[] elements = ManifestElement.parseHeader( 699 Constants.BUNDLE_SYMBOLICNAME, value); 700 if (elements.length > 0) 701 id = elements[0].getValue(); 702 value = headers.get(Constants.BUNDLE_VERSION).toString(); 703 if (value == null) 704 return null; 705 elements = ManifestElement.parseHeader( 706 Constants.BUNDLE_VERSION, value); 707 if (elements.length > 0) 708 version = elements[0].getValue(); 709 if (id != null && version != null) 710 return new PluginIdentifier(id, version); 711 } catch (Exception e1) { 712 throwCoreException(HelpBaseResources.HelpIndexBuilder_errorExtractingId, e1); 713 } 714 } 715 return null; 716 } 717 718 private String getAttribute(Node node, String name) { 719 NamedNodeMap atts = node.getAttributes(); 720 if (atts != null) { 721 Node att = atts.getNamedItem(name); 722 if (att != null) 723 return att.getNodeValue(); 724 } 725 return null; 726 } 727 728 private Document readXMLFile(File file) throws CoreException { 729 InputStream stream = null; 730 Document d = null; 731 try { 732 stream = new FileInputStream (file); 733 InputStreamReader reader = new InputStreamReader (stream, "utf-8"); InputSource inputSource = new InputSource (reader); 735 inputSource.setSystemId(manifest.toString()); 736 737 if (parser == null) 738 parser = documentBuilderFactory.newDocumentBuilder(); 739 d = parser.parse(inputSource); 740 } catch (Exception e) { 741 String message = NLS.bind(HelpBaseResources.HelpIndexBuilder_errorParsing, file.getName()); 742 throwCoreException(message, e); 743 } finally { 744 if (stream != null) { 745 try { 746 stream.close(); 747 } catch (IOException e) { 748 } 749 stream = null; 750 } 751 } 752 return d; 753 } 754 755 private Element[] getTocExtensions(Document doc) { 756 ArrayList list = new ArrayList (); 757 NodeList children = doc.getElementsByTagName("extension"); for (int i = 0; i < children.getLength(); i++) { 760 Node child = children.item(i); 761 String point = getAttribute(child, "point"); if (point.equals(POINT_TOC)) 763 list.add(child); 764 } 765 return (Element[]) list.toArray(new Element[list.size()]); 766 } 767 768 private Properties manifestToProperties(Attributes d) { 769 Iterator iter = d.keySet().iterator(); 770 Properties result = new Properties (); 771 while (iter.hasNext()) { 772 Attributes.Name key = (Attributes.Name ) iter.next(); 773 result.put(key.toString(), d.get(key)); 774 } 775 return result; 776 } 777 778 private void throwCoreException(String message, Throwable t) 779 throws CoreException { 780 IStatus status = new Status(IStatus.ERROR, HelpBasePlugin.PLUGIN_ID, 781 IStatus.OK, message, t); 782 throw new CoreException(status); 783 } 784 } | Popular Tags |