/* * The tracker package defines a set of video/image analysis tools * built on the Open Source Physics framework by Wolfgang Christian. * * Copyright (c) 2019 Douglas Brown * * Tracker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Tracker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tracker; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at <http://www.gnu.org/copyleft/gpl.html> * * For additional Tracker information and documentation, please see * <http://physlets.org/tracker/>. */ package org.opensourcephysics.cabrillo.tracker; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.io.FileWriter; import java.net.URL; import java.util.ArrayList; import java.util.Map; import java.util.TreeMap; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.text.Document; import org.opensourcephysics.controls.XML; import org.opensourcephysics.controls.XMLControl; import org.opensourcephysics.controls.XMLControlElement; import org.opensourcephysics.display.OSPRuntime; import org.opensourcephysics.tools.FontSizer; import org.opensourcephysics.tools.LaunchNode; import org.opensourcephysics.tools.LaunchPanel; import org.opensourcephysics.tools.Launcher; import org.opensourcephysics.tools.ResourceLoader; import org.opensourcephysics.tools.Launcher.HTMLPane; /** * A class to search Tracker Help * * @author Douglas Brown */ public class HelpFinder { // map of pagekey to map of anchor to text lines private static Map<String, Map<String, ArrayList<String>>> pages = new TreeMap<String, Map<String, ArrayList<String>>>(); // map of pagekey to page title private static Map<String, String> pageNames = new TreeMap<String, String>(); // map of anchor to section title private static Map<String, String> anchorNames = new TreeMap<String, String>(); // map of pagekey to html path private static Map<String, String> pagePaths = new TreeMap<String, String>(); // initial context phrase extent before/after the search phrase private static int contextPhraseLength = 120; // maximum trim taken from context phrase to render more readable private static int contextPhraseTrim = 15; // minimum length of search phrase to trigger search private static int minimumSearchPhraseLength = 3; // color shown for search terms not found private static Color _RED = new Color(255, 160, 180); // help launcher elements private static Launcher helpLauncher; private static LaunchPanel searchResultsTab; private static LaunchNode rootNode; // GUI components private static JTextField searchField = new JTextField(12); private static JLabel searchLabel; private static JButton clearSearchButton; private static String css; static { initialize(); } /** * Searches for a phrase and returns a list of results. * Each result is a String[] {page title & subtitle, context phrase, keywords}. * * @param searchPhrase the phrase to search for * @param termsFound a list to which terms found are added * @return list of results */ protected static ArrayList<String[]> search(String searchPhrase, ArrayList<String> termsFound) { // search with entire phrase ArrayList<String[]> results = search(searchPhrase); if (results.size()>0) { termsFound.add(searchPhrase); return results; } // search with individual terms String[] terms = searchPhrase.split(" "); //$NON-NLS-1$ // no use continuing if only one term in the search phrase if (terms.length<2) return results; ArrayList<ArrayList<String[]>> allResults = new ArrayList<ArrayList<String[]>>(); ArrayList<String[]> termResults = new ArrayList<String[]>(); // get results for each term independently for (String term: terms) { termResults = search(term); allResults.add(termResults); } // go through the last set of results and look for keywords that are found in all (AND mode) ArrayList<String> contexts = new ArrayList<String>(); outer: for (String[] next: termResults) { contexts.clear(); String keyword = next[2]; for (ArrayList<String[]> nextResults: allResults) { boolean found = false; inner: for (String[] result: nextResults) { if (keyword.equals(result[2])) { found = true; contexts.add(result[1]); break inner; } } if (!found) continue outer; } // keyword was found in all so merge contexts and add to results String context = getMergedContext(contexts); results.add(new String[] {next[0], context, next[2]}); } if (results.size()>0) { for (String term: terms) { termsFound.add(term); } } return results; } private static String getMergedContext(ArrayList<String> contexts) { ArrayList<String> phrases = new ArrayList<String>(); ArrayList<String> cleanStarts = new ArrayList<String>(); ArrayList<String> cleanEnds = new ArrayList<String>(); for (String next: contexts) { if (!next.startsWith("...")) { //$NON-NLS-1$ cleanStarts.add(next.substring(0, Math.min(14, next.length()))); } if (!next.endsWith("...")) { //$NON-NLS-1$ cleanEnds.add(next.substring(Math.max(0, next.length()-14))); } next = next.replaceAll("\\.\\.\\.", ""); //$NON-NLS-1$ //$NON-NLS-2$ if (phrases.isEmpty()) { phrases.add(next); continue; } boolean merged = false; // check each phrase to see if one is substring of other ArrayList<String> testing = new ArrayList<String>(phrases); for (String context: testing) { if (context.contains(next)) { merged = true; break; } if (next.contains(context)) { phrases.remove(context); phrases.add(next); merged = true; break; } } if (!merged && next.length()>15) { String toMatch = next.substring(0, 15); for (String context: testing) { int n = context.indexOf(toMatch); if (n>0) { phrases.remove(context); context = context.substring(0, n) + next; phrases.add(context); merged = true; break; } } if (!merged) { int len = next.length(); toMatch = next.substring(len-15, len); for (String context: testing) { int n = context.indexOf(toMatch); if (n>0) { phrases.remove(context); context = next+context.substring(n+15); phrases.add(context); merged = true; break; } } } } if (!merged) { phrases.add(next); } } // assemble final context String context = ""; //$NON-NLS-1$ boolean addEllipsis = true; for (String next: phrases) { if (!addEllipsis) context += "\" | \""; //$NON-NLS-1$ addEllipsis = true; for (String cleanStart: cleanStarts) { if (next.startsWith(cleanStart)) addEllipsis = false; } if (addEllipsis) context += "..."; //$NON-NLS-1$ context += next; addEllipsis = true; for (String cleanEnd: cleanEnds) { if (next.endsWith(cleanEnd)) addEllipsis = false; } } if (addEllipsis) context += "..."; //$NON-NLS-1$ return context.trim(); } protected static Component[] getNavComponentsFor(Launcher launcher) { helpLauncher = launcher; searchLabel.setText(TrackerRes.getString("HelpFinder.Label.SearchFor.Text")+":"); //$NON-NLS-1$ //$NON-NLS-2$ searchLabel.setToolTipText(TrackerRes.getString("HelpFinder.Label.SearchFor.Tooltip")); //$NON-NLS-1$ searchField.setToolTipText(TrackerRes.getString("HelpFinder.Label.SearchFor.Tooltip")); //$NON-NLS-1$ clearSearchButton.setText(TrackerRes.getString("HelpFinder.Button.ClearResults.Text")); //$NON-NLS-1$ clearSearchButton.setToolTipText(TrackerRes.getString("HelpFinder.Button.ClearResults.Tooltip")); //$NON-NLS-1$ return new Component[] {searchLabel, searchField, Box.createHorizontalStrut(4), clearSearchButton, TToolBar.getSeparator()}; } /** * Searches for a phrase and returns a list of results. * Each result is a String[] {page title & subtitle, context phrase, keywords}. * * @param searchPhrase the phrase to search for * @return list of results */ private static ArrayList<String[]> search(String searchPhrase) { searchPhrase = searchPhrase.toLowerCase(); ArrayList<String> keywordsFound = new ArrayList<String>(); ArrayList<String[]> results = new ArrayList<String[]>(); // search page titles for (String pageKey: pageNames.keySet()) { String pageTitle = pageNames.get(pageKey); if (pageTitle.toLowerCase().contains(searchPhrase)) { // found pagekey, so get the anchor and context inner: for (String anchor: anchorNames.keySet()) { String sectionTitle = anchorNames.get(anchor); if (sectionTitle!=null && sectionTitle.equals(pageTitle)) { // found the anchor so get the context: 1st line of text, if any Map<String, ArrayList<String>> anchors = pages.get(pageKey); ArrayList<String> lines = anchors.get(anchor); String line = (lines!=null && lines.size()>0)? lines.get(0): pageTitle; String context = getContextPhrase(line, searchPhrase); String keyword = pageKey+"#"+anchor; //$NON-NLS-1$ String fullPath = pagePaths.get(pageKey)+"#"+anchor; //$NON-NLS-1$ String[] result = new String[] {pageTitle, context, fullPath}; results.add(result); keywordsFound.add(keyword); break inner; } } } } // search section titles for (String anchor: anchorNames.keySet()) { String section = anchorNames.get(anchor); if (section!=null && section.toLowerCase().contains(searchPhrase)) { // found the anchor, so get the pagekey and context for (String pageKey: pages.keySet()) { Map<String, ArrayList<String>> anchors = pages.get(pageKey); for (String next: anchors.keySet()) { if (next.equals(anchor)) { // found the pageKey for the anchor String keyword = pageKey+"#"+anchor; //$NON-NLS-1$ if (!keywordsFound.contains(keyword)) { // get the context: 1st line of text, if any String pageName = pageNames.get(pageKey); ArrayList<String> lines = anchors.get(anchor); String line = (lines!=null && lines.size()>0)? lines.get(0): pageName; String context = getContextPhrase(line, searchPhrase); String name = pageName+": "+section; //$NON-NLS-1$ String fullPath = pagePaths.get(pageKey)+"#"+anchor; //$NON-NLS-1$ String[] result = new String[] {name, context, fullPath}; results.add(result); keywordsFound.add(keyword); } } } } } } // search pages for (String pageKey: pages.keySet()) { Map<String, ArrayList<String>> anchors = pages.get(pageKey); for (String anchor: anchors.keySet()) { String keyword = pageKey+"#"+anchor; //$NON-NLS-1$ ArrayList<String> lines = anchors.get(anchor); for (String line: lines) { if (!keywordsFound.contains(keyword)) { int n = line.toLowerCase().indexOf(searchPhrase); if (n>-1) { String phrase = getContextPhrase(line, searchPhrase); String name = pageNames.get(pageKey); String section = anchorNames.get(anchor); if (section!=null && !section.equals(name)) { name += ": "+section; //$NON-NLS-1$ } String fullPath = pagePaths.get(pageKey)+"#"+anchor; //$NON-NLS-1$ String[] result = new String[] {name, phrase, fullPath}; results.add(result); keywordsFound.add(keyword); } } } } } return results; } private static void initialize() { // initialize the maps URL url = Tracker.class.getResource("resources/help/tracker_topics.xml"); //$NON-NLS-1$ String xml = ResourceLoader.getString(url.toExternalForm()); XMLControl control = new XMLControlElement(xml); ArrayList<LaunchNode> children = (ArrayList<LaunchNode>)control.getObject("child_nodes"); //$NON-NLS-1$ for (LaunchNode next: children) { String pagekey = next.getKeywords(); String name = next.getName(); pageNames.put(pagekey, name); String path = next.getDisplayTab(0).getURL().toString(); pagePaths.put(pagekey, path); if (css==null) { css = XML.getDirectoryPath(path)+"/help.css"; //$NON-NLS-1$ } String html = ResourceLoader.getString(path); Map<String, ArrayList<String>> map = getAnchors(html); pages.put(pagekey, map); } // create the search field, label and button searchField = new JTextField(20) { @Override public Dimension getMaximumSize() { return new Dimension((int)(100*FontSizer.getFactor()), clearSearchButton.getPreferredSize().height); } }; searchField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_ENTER) { String searchPhrase = stripExtraSpace(searchField.getText(), " "); //$NON-NLS-1$ if (searchPhrase.length()>=minimumSearchPhraseLength) { ArrayList<String> found = new ArrayList<String>(); ArrayList<String[]> results = search(searchPhrase, found); if (results.size()==0) { searchField.setBackground(_RED); return; } searchField.setBackground(Color.white); // write results in HTML file File file = writeResultsFile(searchPhrase, results, found); if (file==null) return; // create and display results node LaunchNode node = new LaunchNode("\""+searchPhrase+"\""); //$NON-NLS-1$ //$NON-NLS-2$ node.addDisplayTab(null, file.getAbsolutePath(), null); node.getDisplayTab(0).getURL(); // so display tab url is not null displayResultsNode(node); } } else { searchField.setBackground(Color.yellow); } } }); searchLabel = new JLabel(); searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4)); clearSearchButton = new JButton(); clearSearchButton.setAction(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { helpLauncher.setSelectedTab(searchResultsTab); helpLauncher.removeSelectedTab(); searchField.setText(null); clearSearchButton.setEnabled(false); searchResultsTab = null; rootNode = null; } }); clearSearchButton.setEnabled(false); } private static void displayResultsNode(LaunchNode node) { if (rootNode==null) { rootNode = new LaunchNode(TrackerRes.getString("HelpFinder.SearchResults")); //$NON-NLS-1$ } if (searchResultsTab!=null) { helpLauncher.setSelectedTab(searchResultsTab); helpLauncher.removeSelectedTab(); } rootNode.add(node); ArrayList<LaunchNode> children = new ArrayList<LaunchNode>(); for (int i=0; i<rootNode.getChildCount(); i++) { children.add((LaunchNode)rootNode.getChildAt(i)); } File file = writeSummaryFile(children); if (rootNode.getDisplayTabCount()>0) { rootNode.removeDisplayTab(0); } rootNode.addDisplayTab(null, file.getAbsolutePath(), null); rootNode.getDisplayTab(0).getURL(); // so display tab url is not null helpLauncher.addTab(rootNode); // also selects the tab searchResultsTab = helpLauncher.getSelectedTab(); if (FontSizer.getLevel()>0) { String newValue = "help"+FontSizer.getLevel()+".css"; //$NON-NLS-1$ //$NON-NLS-2$ searchResultsTab.getHTMLSubstitutionMap().put("help.css", newValue); //$NON-NLS-1$ helpLauncher.setFontLevel(FontSizer.getLevel()); for (int i=0; i<helpLauncher.getHTMLTabCount(); i++) { HTMLPane pane = helpLauncher.getHTMLTab(i); pane.editorPane.getDocument().putProperty(Document.StreamDescriptionProperty, null); } } searchResultsTab.setSelectedNode(node); clearSearchButton.setEnabled(true); } private static Map<String, ArrayList<String>> getAnchors(String html) { // clean the html html = clean(html); Map<String, ArrayList<String>> anchorMap = new TreeMap<String, ArrayList<String>>(); String[] sections = html.split("<h1|<h3"); //$NON-NLS-1$ for (int i=1; i< sections.length; i++) { // find section-identifying ID anchor String[] anchorSplit = sections[i].split("<a name=\""); //$NON-NLS-1$ if (anchorSplit.length<2) continue; String anchor = anchorSplit[1].substring(0, anchorSplit[1].indexOf("\"")); //$NON-NLS-1$ // find anchor name // strip end anchor tag, if any anchorSplit[1] = stripTag(anchorSplit[1], "</a"); //$NON-NLS-1$ String name = null; int m = anchorSplit[1].indexOf(">"); //$NON-NLS-1$ int n = anchorSplit[1].indexOf("</h3>"); //$NON-NLS-1$ int p = anchorSplit[1].indexOf("</h1>"); //$NON-NLS-1$ if (m>0 && n>0 && p<0) { name = anchorSplit[1].substring(m+1, n); } else if (m>0 && n<0 && p>0) { name = anchorSplit[1].substring(m+1, p); } // strip section numbering try { Integer.parseInt(name.substring(0, 1)); name = name.substring(name.indexOf(" ")+1, name.length()); //$NON-NLS-1$ } catch (Exception e) { } anchorNames.put(anchor, name); // after finding the ID anchor, strip any other anchor tags sections[i] = stripTag(sections[i], "<a href"); //$NON-NLS-1$ sections[i] = stripTag(sections[i], "</a"); //$NON-NLS-1$ ArrayList<String> lines = new ArrayList<String>(); String[] split = sections[i].split("<p>"); //$NON-NLS-1$ for (int j=1; j< split.length; j++) { n = split[j].indexOf("</p>"); //$NON-NLS-1$ if (n>-1) { split[j] = split[j].substring(0, n).trim(); if ("".equals(split[j])) continue; //$NON-NLS-1$ } else { // flag HTML code with nested lists that cause missing </p> after substitutions System.out.println("no ending </p> in "+split[j]); //$NON-NLS-1$ System.out.println("found in section: "+sections[i]); //$NON-NLS-1$ } // eliminate any extra spaces split[j] = stripExtraSpace(split[j], " "); //$NON-NLS-1$ lines.add(split[j]); } anchorMap.put(anchor, lines); } return anchorMap; } private static String clean(String text) { text = text.replaceAll("<h5>", "<p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("</h5>", "</p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("<blockquote>", "<p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("</blockquote>", "</p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("<li>", "<p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("</li>", "</p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("<p align=\"center\">", "<p>"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll(">", ">"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("<", "<"); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll("<br>", " "); //$NON-NLS-1$ //$NON-NLS-2$ text = text.replaceAll(""", "\""); //$NON-NLS-1$ //$NON-NLS-2$ text = stripTag(text, "<img"); //$NON-NLS-1$ text = stripTag(text, "<b"); //$NON-NLS-1$ text = stripTag(text, "</b"); //$NON-NLS-1$ text = stripTag(text, "<em"); //$NON-NLS-1$ text = stripTag(text, "</em"); //$NON-NLS-1$ text = stripTag(text, "<strong"); //$NON-NLS-1$ text = stripTag(text, "</strong"); //$NON-NLS-1$ text = stripTag(text, "<ol"); //$NON-NLS-1$ text = stripTag(text, "</ol"); //$NON-NLS-1$ text = stripTag(text, "<ul"); //$NON-NLS-1$ text = stripTag(text, "</ul"); //$NON-NLS-1$ return text; } private static String getContextPhrase(String line, String searchPhrase) { int n = line.toLowerCase().indexOf(searchPhrase); if (n>-1) { int start = Math.max(0, n-contextPhraseLength); int end = Math.min(line.length(), n+searchPhrase.length()+contextPhraseLength); String phrase = line.substring(start, end); start = phrase.toLowerCase().indexOf(searchPhrase); end = start+searchPhrase.length(); n = phrase.indexOf(" "); //$NON-NLS-1$ if (n>-1 && n<start-contextPhraseLength+contextPhraseTrim) { phrase = "..."+phrase.substring(n+1); //$NON-NLS-1$ } start = phrase.toLowerCase().indexOf(searchPhrase); end = start+searchPhrase.length(); n = phrase.lastIndexOf(" "); //$NON-NLS-1$ if (n>end+contextPhraseLength-contextPhraseTrim) { phrase = phrase.substring(0, n)+"..."; //$NON-NLS-1$ } return phrase; } else { String phrase = line.substring(0, Math.min(line.length(), 2*contextPhraseLength+contextPhraseTrim)); n = phrase.lastIndexOf(" "); //$NON-NLS-1$ if (n>contextPhraseLength) { phrase = phrase.substring(0, n)+"..."; //$NON-NLS-1$ } return phrase; } } private static String stripTag(String text, String tag) { int i = text.indexOf(tag); while (i>0) { String later = text.substring(i); text = text.substring(0, i); int j = later.indexOf(">"); //$NON-NLS-1$ if (j<0) break; later = later.substring(j+1); text += later; i = text.indexOf(tag); } return text; } private static String stripExtraSpace(String text, String space) { text = text.trim(); String extraSpace = " "+space; //$NON-NLS-1$ int n = text.indexOf(extraSpace); while (n>-1) { text = text.replaceAll(extraSpace, space); n = text.indexOf(extraSpace); } return text; } /** * Writes an HTML results file from scratch, using the current field text. */ private static File writeResultsFile(String searchPhrase, ArrayList<String[]> results, ArrayList<String> termsFound) { // get HTML code first String htmlCode = getResultsHTMLCode(searchPhrase, results, termsFound); // use hashcode for unique filename String fileName = "search"+results.hashCode()+".tmp"; //$NON-NLS-1$ //$NON-NLS-2$ // write temporary HTML file in default OSPRuntime path ArrayList<String> paths = OSPRuntime.getDefaultSearchPaths(); for (String path: paths) { // attempt to write html file File target = new File(path, fileName); target = writeFile(htmlCode, target); if (target!=null) { target.deleteOnExit(); return target; } } String pathlist = ""; //$NON-NLS-1$ for (String path: paths) { pathlist += "\n"+path+","; //$NON-NLS-1$ //$NON-NLS-2$ } pathlist = pathlist.substring(0, pathlist.length()-1); // if all attempts failed, inform user and return null JOptionPane.showMessageDialog(searchField.getTopLevelAncestor(), TrackerRes.getString("HelpFinder.Dialog.UnableToWrite.Text")+pathlist, //$NON-NLS-1$ TrackerRes.getString("HelpFinder.Dialog.UnableToWrite.Title"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); return null; } /** * Writes an HTML summary file from scratch, using the current field text. */ private static File writeSummaryFile(ArrayList<LaunchNode> resultNodes) { // get HTML code first String htmlCode = getSummaryHTMLCode(resultNodes); String fileName = "search_summary.tmp"; //$NON-NLS-1$ // write temporary HTML file in default OSPRuntime path ArrayList<String> paths = OSPRuntime.getDefaultSearchPaths(); for (String path: paths) { // attempt to write html file File target = new File(path, fileName); target = writeFile(htmlCode, target); if (target!=null) { target.deleteOnExit(); return target; } } return null; } /** * Writes a text file. * * @param text the text * @param target the File to write * @return the written File, or null if failed */ private static File writeFile(String text, File target) { try { FileWriter fout = new FileWriter(target); fout.write(text); fout.close(); return target; } catch(Exception ex) {} return null; } /** * Gets the html code for a resource with specified properties. * Note this code is for display in the LibraryBrowser, and has no stylesheet of its own. * * @param title the name of the resource * @param description a description of the resource * @param authors authors * @param contact author contact information or institution * * @return the html code */ private static String getResultsHTMLCode(String searchPhrase, ArrayList<String[]> searchResults, ArrayList<String> termsToHighlight) { StringBuffer buffer = new StringBuffer(); buffer.append ( "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); //$NON-NLS-1$ buffer.append( "\n <html>"); //$NON-NLS-1$ buffer.append( "\n <head>"); //$NON-NLS-1$ buffer.append( "\n"+getStyleSheetCode()); //$NON-NLS-1$ buffer.append( "\n <meta http-equiv=\"content-type\" content=\"text/html;charset=iso-8859-1\">"); //$NON-NLS-1$ buffer.append( "\n </head>\n"); //$NON-NLS-1$ buffer.append( "\n <body>"); //$NON-NLS-1$ buffer.append( "\n <h2>"+TrackerRes.getString("HelpFinder.ResultsFor") //$NON-NLS-1$//$NON-NLS-2$ +" \""+searchPhrase+"\"</h2>"); //$NON-NLS-1$ //$NON-NLS-2$ for (String[] next: searchResults) { buffer.append(getResultsHTMLBody(next, termsToHighlight)); } buffer.append( "\n </body>"); //$NON-NLS-1$ buffer.append( "\n </html>"); //$NON-NLS-1$ return buffer.toString(); } /** * Gets the html code for a resource with specified properties. * Note this code is for display in the LibraryBrowser, and has no stylesheet of its own. * * @param title the name of the resource * @param description a description of the resource * @param authors authors * @param contact author contact information or institution * * @return the html code */ private static String getSummaryHTMLCode(ArrayList<LaunchNode> resultNodes) { StringBuffer buffer = new StringBuffer(); buffer.append ( "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); //$NON-NLS-1$ buffer.append( "\n <html>"); //$NON-NLS-1$ buffer.append( "\n <head>"); //$NON-NLS-1$ buffer.append( "\n"+getStyleSheetCode()); //$NON-NLS-1$ buffer.append( "\n <meta http-equiv=\"content-type\" content=\"text/html;charset=iso-8859-1\">"); //$NON-NLS-1$ buffer.append( "\n </head>\n"); //$NON-NLS-1$ buffer.append( "\n <body>"); //$NON-NLS-1$ buffer.append( "\n <h2>"+TrackerRes.getString("HelpFinder.ResultsFor")+":</h2>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ buffer.append(getSummaryHTMLBody(resultNodes)); buffer.append( "\n </body>"); //$NON-NLS-1$ buffer.append( "\n </html>"); //$NON-NLS-1$ return buffer.toString(); } /** * Gets html <body> code for a search result. * * @param title the name of the resource * @param description a description of the resource * @param authors authors * @param contact author contact information or institution * * @return the html path */ private static String getResultsHTMLBody(String[] result, ArrayList<String> highlightTerms) { StringBuffer buffer = new StringBuffer(); buffer.append("\n<p><a href=\""+result[2]+"\"><strong>" //$NON-NLS-1$//$NON-NLS-2$ +result[0]+"</strong></a>"); //$NON-NLS-1$ String context = result[1]; for (String term: highlightTerms) { context = addHighlights(context, term); } buffer.append(" \""+context+"\"</p>"); //$NON-NLS-1$ //$NON-NLS-2$ return buffer.toString(); } /** * Gets html <body> code for a search result. * * @param title the name of the resource * @param description a description of the resource * @param authors authors * @param contact author contact information or institution * * @return the html path */ private static String getSummaryHTMLBody(ArrayList<LaunchNode> resultNodes) { StringBuffer buffer = new StringBuffer(); buffer.append("<blockquote>"); //$NON-NLS-1$ for (LaunchNode next: resultNodes) { buffer.append("<h4><a href=\""+next.getDisplayTab(0).getURL()+"\">" //$NON-NLS-1$//$NON-NLS-2$ +next+"</a></h4>"); //$NON-NLS-1$ } buffer.append("</blockquote>"); //$NON-NLS-1$ return buffer.toString(); } /** * Returns the html code for a stylesheet. * * @return the style sheet code */ private static String getStyleSheetCode() { return "<link href=\""+css+"\" rel=\"stylesheet\" type=\"text/css\">"; //$NON-NLS-1$ //$NON-NLS-2$ } /** * Returns the html code for a stylesheet. * * @return the style sheet code */ private static String addHighlights(String text, String highlight) { highlight = highlight.toLowerCase(); StringBuffer output = new StringBuffer(); int n = text.toLowerCase().indexOf(highlight); while (n>-1) { output.append(text.substring(0, n)); String term = text.substring(n, n+highlight.length()); text = text.substring(n+highlight.length()); output.append("<strong>"+term+"</strong>"); //$NON-NLS-1$ //$NON-NLS-2$ n = text.toLowerCase().indexOf(highlight); } output.append(text); return output.toString(); } }