blob: e1b5f09d1340cb7c35559f2efa67704df16df69a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.help.internal.webapp.servlet;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.servlet.http.*;
import org.eclipse.core.runtime.Platform;
import org.eclipse.help.internal.base.HelpBasePlugin;
import org.eclipse.help.internal.webapp.data.*;
import org.eclipse.help.webapp.IFilter;
public class HighlightFilter implements IFilter {
private static final String HIGHLIGHT_ON = "highlight-on"; //$NON-NLS-1$
private static final String scriptPart1 = "\n<script type=\"text/javascript\">\n<!--\nvar keywords = new Array ("; //$NON-NLS-1$
private static final String scriptPart2 = ");\nvar pluginDefault = "; //$NON-NLS-1$
private static final String scriptPart3 = ";\n-->\n</script>\n<script type=\"text/javascript\" src=\""; //$NON-NLS-1$
private static final String scriptPart5 = "advanced/highlight.js\"></script>\n"; //$NON-NLS-1$
private static final String sheetRefPart1 = "<link id=\"highlightStyle\" rel=\"STYLESHEET\" href=\""; //$NON-NLS-1$
private static final String sheetRefPart3 = "advanced/highlight.css\" charset=\"ISO-8859-1\" type=\"text/css\"></link>\n"; //$NON-NLS-1$
@Override
public OutputStream filter(HttpServletRequest req, OutputStream out) {
String uri = req.getRequestURI();
if (uri == null) {
return out;
}
if (!(UrlUtil.isIE(req) || UrlUtil.isMozilla(req))) {
return out;
}
Collection<String> keywords = getWords(req);
if (keywords.size() == 0) {
return out;
}
keywords = removeWildCards(keywords);
keywords = encodeKeyWords(keywords);
byte[] script = createJScript(req, keywords);
if (script == null) {
return out;
}
return new FilterHTMLHeadOutputStream(out, script);
}
/**
* Creates Java Script that does highlighting
*
* @param keywords
* @return byte[]
*/
private byte[] createJScript(HttpServletRequest req, Collection<String> keywords) {
StringBuilder buf = new StringBuilder(scriptPart1);
StringBuilder buf2 = new StringBuilder(sheetRefPart1);
// append comma separated list of keywords
Iterator<String> it = keywords.iterator();
if (!it.hasNext())
return null;
String keyword = it.next();
buf.append("\"").append(keyword).append("\""); //$NON-NLS-1$ //$NON-NLS-2$
while (it.hasNext()) {
keyword = it.next();
buf.append(", \"").append(keyword).append("\""); //$NON-NLS-1$ //$NON-NLS-2$
}
buf.append(scriptPart2);
buf.append(Platform.getPreferencesService().getBoolean
(HelpBasePlugin.PLUGIN_ID, HIGHLIGHT_ON, false, null));
buf.append(scriptPart3);
// append "../" to get to the webapp
String path = FilterUtils.getRelativePathPrefix(req);
buf.append(path);
buf2.append(path);
buf.append(scriptPart5);
buf.append(buf2.toString());
buf.append(sheetRefPart3);
return buf.toString().getBytes(StandardCharsets.US_ASCII);
}
/**
* Extracts keywords from query that contains keywords dobule quoted and
* separated by space
*
* @return Collection of String
*/
private Collection<String> getWords(HttpServletRequest req) {
// Collect words to hash set to eliminate duplcates
Collection<String> tokens = new ArrayList<>();
String searchWord = req.getParameter("resultof"); //$NON-NLS-1$
if (searchWord == null) {
return tokens;
}
//Divide along quotation marks
StringTokenizer qTokenizer = new StringTokenizer(searchWord.trim(),
"\"", true); //$NON-NLS-1$
boolean withinQuotation = false;
String quotedString = ""; //$NON-NLS-1$
while (qTokenizer.hasMoreTokens()) {
String curToken = qTokenizer.nextToken();
if (curToken.equals("\"")) { //$NON-NLS-1$
if (!withinQuotation) {
//beginning of quoted string
quotedString = ""; //$NON-NLS-1$
} else {
//end of quoted string
tokens.add(quotedString);
}
withinQuotation = !withinQuotation;
continue;
}
if (withinQuotation) {
tokens.add(curToken);
}
}
return tokens;
}
/**
* Encodes strings inside collection for embedding in HTML source
*
* @return Collection of String
*/
private Collection<String> encodeKeyWords(Collection<String> col) {
if (col == null)
return col;
Collection<String> result = new ArrayList<>();
for (String word : col) {
int l = word.length();
if (l < 1)
continue;
result.add(UrlUtil.JavaScriptEncode(word));
}
return result;
}
/**
* Removes wildcard characters from words, by splitting words around wild
* cards
*
* @return Collection of String
*/
private Collection<String> removeWildCards(Collection<String> col) {
if (col == null)
return col;
// Split words into parts: before "*" and after "*"
Collection<String> resultPass1 = new ArrayList<>();
for (String word : col) {
int index;
while ((index = word.indexOf("*")) >= 0) { //$NON-NLS-1$
if (index > 0)
resultPass1.add(word.substring(0, index));
if (word.length() > index)
word = word.substring(index + 1);
}
if (word.length() > 0)
resultPass1.add(word);
}
// Split words into parts: before "?" and after "?"
Collection<String> resultPass2 = new ArrayList<>();
for (String word : resultPass1) {
int index;
while ((index = word.indexOf("?")) >= 0) { //$NON-NLS-1$
if (index > 0)
resultPass2.add(word.substring(0, index));
if (word.length() > index)
word = word.substring(index + 1);
}
if (word.length() > 0)
resultPass2.add(word);
}
return resultPass2;
}
}