/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.help.internal.search; import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.Hashtable; import javax.xml.parsers.*; import org.eclipse.core.runtime.*; import org.eclipse.help.IHelpResource; import org.eclipse.help.internal.base.*; import org.eclipse.help.internal.base.util.ProxyUtil; import org.eclipse.help.internal.entityresolver.LocalEntityResolver; import org.eclipse.help.search.ISearchEngine; import org.eclipse.help.search.ISearchEngineResult; import org.eclipse.help.search.ISearchEngineResultCollector; import org.eclipse.help.search.ISearchScope; import org.w3c.dom.*; import org.xml.sax.*; /** * This implementation of <code>ISearchEngine</code> interface performs search * by running a query on the remote InfoCenter and presenting the results * locally. Instances of this engine type are required to supply the URL to the * InfoCenter. * * <p> * This class is made public in order to be instantiated and parametrized * directly in the extentsions. Clients are required to supply the required URL * as a parameter <code>url</code>. * * <p> * This class is not expected to be subclassed or otherwise accessed * programmatically. * * @since 3.1 */ public final class InfoCenter implements ISearchEngine { private Hashtable<String, IHelpResource> tocs; public static class Scope implements ISearchScope { String url; boolean searchSelected; String[] tocs; public Scope(String url, boolean searchSelected, String[] tocs) { this.url = url; this.searchSelected = searchSelected; this.tocs = tocs; } } private class InfoCenterResult implements ISearchEngineResult { private IHelpResource category; private Element node; private String baseURL; public InfoCenterResult(String baseURL, Element node) { this.baseURL = baseURL; this.node = node; createCategory(node); } private void createCategory(Element node) { final String href = node.getAttribute("toc"); //$NON-NLS-1$ final String label = node.getAttribute("toclabel"); //$NON-NLS-1$ if (href != null && label != null) { category = tocs.get(href); if (category == null) { category = new IHelpResource() { @Override public String getLabel() { return label; } @Override public String getHref() { return href; } }; tocs.put(href, category); } } } @Override public String getLabel() { return node.getAttribute("label"); //$NON-NLS-1$ } @Override public String getDescription() { return null; } @Override public IHelpResource getCategory() { return category; } @Override public String getHref() { return node.getAttribute("href"); //$NON-NLS-1$ } @Override public float getScore() { String value = node.getAttribute("score"); //$NON-NLS-1$ if (value != null) return Float.parseFloat(value); return (float) 0.0; } @Override public boolean getForceExternalWindow() { return false; } @Override public String toAbsoluteHref(String href, boolean frames) { String url = baseURL; if (!url.endsWith("/")) //$NON-NLS-1$ url = url + "/"; //$NON-NLS-1$ if (frames) { return url + "topic" + href; //$NON-NLS-1$ } if (url.indexOf('?')> 0) { return url + "topic" + href + "&noframes=true"; //$NON-NLS-1$ //$NON-NLS-2$ } return url + "topic" + href + "?noframes=true"; //$NON-NLS-1$ //$NON-NLS-2$ } } /** * The default constructor. */ public InfoCenter() { tocs = new Hashtable<>(); } /* * (non-Javadoc) * * @see ISearchEngine#run(String, ISearchScope, * ISearchEngineResultCollector, IProgressMonitor) */ @Override public void run(String query, ISearchScope scope, ISearchEngineResultCollector collector, IProgressMonitor monitor) throws CoreException { URL url = createURL(query, (Scope) scope); if (url == null) return; InputStream is = null; tocs.clear(); try { URLConnection connection = ProxyUtil.getConnection(url); SubMonitor subMonitor = SubMonitor.convert(monitor, HelpBaseResources.InfoCenter_connecting, 5); is = connection.getInputStream(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { subMonitor.worked(1); load(((Scope) scope).url, reader, collector, subMonitor.split(4)); } } catch (FileNotFoundException e) { reportError(HelpBaseResources.InfoCenter_fileNotFound, e, collector); } catch (IOException e) { reportError(HelpBaseResources.InfoCenter_io, e, collector); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } private void reportError(String message, IOException e, ISearchEngineResultCollector collector) { Status status = new Status(IStatus.ERROR, HelpBasePlugin.PLUGIN_ID, IStatus.OK, message, e); collector.error(status); } private void load(String baseURL, Reader r, ISearchEngineResultCollector collector, IProgressMonitor monitor) { Document document = null; try { DocumentBuilder parser = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); parser.setEntityResolver(new LocalEntityResolver()); if (monitor.isCanceled()) return; SubMonitor subMonitor = SubMonitor.convert(monitor, 5); subMonitor.subTask(HelpBaseResources.InfoCenter_searching); document = parser.parse(new InputSource(r)); if (monitor.isCanceled()) return; // Strip out any comments first Node root = document.getFirstChild(); while (root.getNodeType() == Node.COMMENT_NODE) { document.removeChild(root); root = document.getFirstChild(); if (monitor.isCanceled()) return; } subMonitor.worked(1); load(baseURL, document, (Element) root, collector, subMonitor.split(4)); } catch (ParserConfigurationException e) { // ignore } catch (IOException e) { // ignore } catch (SAXException e) { // ignore } } private void load(String baseURL, Document doc, Element root, ISearchEngineResultCollector collector, IProgressMonitor monitor) { NodeList topics = root.getElementsByTagName("hit"); //$NON-NLS-1$ ISearchEngineResult[] results = new ISearchEngineResult[topics .getLength()]; monitor.subTask(HelpBaseResources.InfoCenter_processing); monitor.beginTask("", results.length); //$NON-NLS-1$ for (int i = 0; i < topics.getLength(); i++) { Element el = (Element) topics.item(i); if (monitor.isCanceled()) break; results[i] = new InfoCenterResult(baseURL, el); monitor.worked(1); } collector.accept(results); } private URL createURL(String query, Scope scope) { if (scope.url == null) { return null; } StringBuffer buf = new StringBuffer(); buf.append(scope.url); if (!scope.url.endsWith("/")) //$NON-NLS-1$ buf.append("/search?phrase="); //$NON-NLS-1$ else buf.append("search?phrase="); //$NON-NLS-1$ try { buf.append(URLEncoder.encode(query, "UTF-8")); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { buf.append(query); } buf.append("&locale="); //$NON-NLS-1$ buf.append(Platform.getNL()); if (scope.searchSelected && scope.tocs != null) { buf.append("&scopedSearch=true"); //$NON-NLS-1$ for (int i = 0; i < scope.tocs.length; i++) { String toc; try { toc = URLEncoder.encode(scope.tocs[i], "UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { toc = scope.tocs[i]; } buf.append("&scope="); //$NON-NLS-1$ buf.append(toc); } } try { return new URL(buf.toString()); } catch (MalformedURLException e) { return null; } } }