/*
* Copyright 2007 T-Rank AS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package no.trank.openpipe.step;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import no.trank.openpipe.api.MultiInputOutputFieldPipelineStep;
import no.trank.openpipe.api.PipelineException;
import no.trank.openpipe.api.document.AnnotatedField;
import no.trank.openpipe.api.document.Document;
/**
* This step strips html from input text in four steps:
* <p/>
* <p><ul>
* <li>Html comments are removed.</li>
* <li>Html tags are removed.</li>
* <li>Blocks of whitespace are replaced with a single space character.</li>
* <li>Html entities are decoded.</li>
* </ul>
*
* @version $Revision$
*/
public class StripHtml extends MultiInputOutputFieldPipelineStep {
private static Logger log = LoggerFactory.getLogger(StripHtml.class);
public StripHtml() {
super(true);
}
@Override
protected void process(Document doc, String inputFieldName, List<AnnotatedField> inputFields, String outputFieldName)
throws PipelineException {
List<String> outValues = new ArrayList<String>();
for (AnnotatedField field : inputFields) {
final String text = field.getValue();
String outText = stripComments(text);
outText = stripTags(outText);
outText = trim(outText);
outText = Entities.decodeAll(outText);
log.debug("Field '{}' length: {}; Output field '{}' length: {}",
new Object[]{inputFieldName, text.length(), outputFieldName, outText.length()});
outValues.add(outText);
}
if (outValues.isEmpty()) {
doc.removeField(outputFieldName);
} else {
doc.setFieldValues(outputFieldName, outValues);
}
}
private static String trim(String text) {
text = text.trim();
StringBuilder ret = new StringBuilder(text.length());
boolean ws = false;
for (int i = 0; i < text.length(); ++i) {
final char c = text.charAt(i);
if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
ws = true;
} else {
if (ws) {
ret.append(' ');
ws = false;
}
ret.append(c);
}
}
return ret.toString();
}
private static String stripComments(String text) {
int next = text.indexOf("<!--");
if (next == -1 || text.length() < 8) {
return text;
}
final StringBuilder ret = new StringBuilder(text.length() - 8);
int index = 0;
while (next != -1) {
final int end = text.indexOf("-->", next + 4);
if (end == -1) {
next = -1;
} else {
if (next > index) {
ret.append(text, index, next);
}
index = end + 3;
next = index >= text.length() ? -1 : text.indexOf("<!--", index);
}
}
if (index < text.length()) {
ret.append(text, index, text.length());
}
return ret.toString();
}
private static String stripTags(String text) {
int index = 0;
int next = text.indexOf('<');
if (next == -1 || text.length() < 2) {
return text;
}
final StringBuilder ret = new StringBuilder(text.length() - 2);
while (next != -1) {
char quote = '\0';
int end = -1;
boolean escape = false;
for (int i = next + 1; end == -1 && i < text.length(); ++i) {
char c = text.charAt(i);
if (escape) {
escape = false;
} else if (quote != '\0' && c == '\\') {
escape = true;
} else if (c == quote) {
quote = '\0';
} else if (c == '\'' || c == '"') {
quote = c;
} else if (quote == '\0' && c == '>') {
end = i;
}
}
if (end == -1) {
next = -1;
} else {
if (next > index) {
ret.append(text, index, next);
}
index = end + 1;
next = index >= text.length() ? -1 : text.indexOf('<', index);
}
}
if (index < text.length()) {
ret.append(text, index, text.length());
}
return ret.toString();
}
@Override
public String getRevision() {
return "$Revision$";
}
static class Entities {
static final Map<String, Character> decoder = new HashMap<String, Character>(300);
static String decodeAll(String s) {
int next = s.indexOf('&');
if (next == -1) {
return s;
}
int index = 0;
final StringBuilder ret = new StringBuilder(Math.max(s.length() - 4, 16));
while (next != -1) {
final int end = s.indexOf(';', next);
if (end == -1) {
next = -1;
} else {
if (next > index) {
ret.append(s, index, next);
}
decodeAppend(ret, s, next, end);
index = end + 1;
next = index >= s.length() ? -1 : s.indexOf('&', index);
}
}
if (index < s.length()) {
ret.append(s, index, s.length());
}
return ret.toString();
}
private static void decodeAppend(StringBuilder ret, String entity, int startIdx, int endIdx) {
if (endIdx - startIdx > 2 && entity.charAt(startIdx + 1) == '#') {
final int start;
final int radix;
final char ch = entity.charAt(startIdx + 2);
if (ch == 'X' || ch == 'x') {
start = 3 + startIdx;
radix = 16;
} else {
start = 2 + startIdx;
radix = 10;
}
try {
ret.append((char) Integer.parseInt(entity.substring(start, endIdx), radix));
} catch (NumberFormatException e) {
// Ignoring
}
} else {
final Character c = decoder.get(entity.substring(startIdx, endIdx));
if (c != null) {
ret.append(c.charValue());
}
}
}
static void add(String entity, int value) {
decoder.put(entity, (char) value);
}
static {
add(" ", 32);
add("¡", 161);
add("¢", 162);
add("£", 163);
add("¤", 164);
add("¥", 165);
add("¦", 166);
add("§", 167);
add("¨", 168);
add("©", 169);
add("ª", 170);
add("«", 171);
add("¬", 172);
add("­", 173);
add("®", 174);
add("¯", 175);
add("°", 176);
add("±", 177);
add("²", 178);
add("³", 179);
add("´", 180);
add("µ", 181);
add("¶", 182);
add("·", 183);
add("¸", 184);
add("¹", 185);
add("º", 186);
add("»", 187);
add("¼", 188);
add("½", 189);
add("¾", 190);
add("¿", 191);
add("À", 192);
add("Á", 193);
add("Â", 194);
add("Ã", 195);
add("Ä", 196);
add("Å", 197);
add("Æ", 198);
add("Ç", 199);
add("È", 200);
add("É", 201);
add("Ê", 202);
add("Ë", 203);
add("Ì", 204);
add("Í", 205);
add("Î", 206);
add("Ï", 207);
add("Ð", 208);
add("Ñ", 209);
add("Ò", 210);
add("Ó", 211);
add("Ô", 212);
add("Õ", 213);
add("Ö", 214);
add("×", 215);
add("Ø", 216);
add("Ù", 217);
add("Ú", 218);
add("Û", 219);
add("Ü", 220);
add("Ý", 221);
add("Þ", 222);
add("ß", 223);
add("à", 224);
add("á", 225);
add("â", 226);
add("ã", 227);
add("ä", 228);
add("å", 229);
add("æ", 230);
add("ç", 231);
add("è", 232);
add("é", 233);
add("ê", 234);
add("ë", 235);
add("ì", 236);
add("í", 237);
add("î", 238);
add("ï", 239);
add("ð", 240);
add("ñ", 241);
add("ò", 242);
add("ó", 243);
add("ô", 244);
add("õ", 245);
add("ö", 246);
add("÷", 247);
add("ø", 248);
add("ù", 249);
add("ú", 250);
add("û", 251);
add("ü", 252);
add("ý", 253);
add("þ", 254);
add("ÿ", 255);
add("&fnof", 402);
add("&Alpha", 913);
add("&Beta", 914);
add("&Gamma", 915);
add("&Delta", 916);
add("&Epsilon", 917);
add("&Zeta", 918);
add("&Eta", 919);
add("&Theta", 920);
add("&Iota", 921);
add("&Kappa", 922);
add("&Lambda", 923);
add("&Mu", 924);
add("&Nu", 925);
add("&Xi", 926);
add("&Omicron", 927);
add("&Pi", 928);
add("&Rho", 929);
add("&Sigma", 931);
add("&Tau", 932);
add("&Upsilon", 933);
add("&Phi", 934);
add("&Chi", 935);
add("&Psi", 936);
add("&Omega", 937);
add("&alpha", 945);
add("&beta", 946);
add("&gamma", 947);
add("&delta", 948);
add("&epsilon", 949);
add("&zeta", 950);
add("&eta", 951);
add("&theta", 952);
add("&iota", 953);
add("&kappa", 954);
add("&lambda", 955);
add("&mu", 956);
add("&nu", 957);
add("&xi", 958);
add("&omicron", 959);
add("&pi", 960);
add("&rho", 961);
add("&sigmaf", 962);
add("&sigma", 963);
add("&tau", 964);
add("&upsilon", 965);
add("&phi", 966);
add("&chi", 967);
add("&psi", 968);
add("&omega", 969);
add("&thetasym", 977);
add("&upsih", 978);
add("&piv", 982);
add("&bull", 8226);
add("&hellip", 8230);
add("&prime", 8242);
add("&Prime", 8243);
add("&oline", 8254);
add("&frasl", 8260);
add("&weierp", 8472);
add("&image", 8465);
add("&real", 8476);
add("&trade", 8482);
add("&alefsym", 8501);
add("&larr", 8592);
add("&uarr", 8593);
add("&rarr", 8594);
add("&darr", 8595);
add("&harr", 8596);
add("&crarr", 8629);
add("&lArr", 8656);
add("&uArr", 8657);
add("&rArr", 8658);
add("&dArr", 8659);
add("&hArr", 8660);
add("&forall", 8704);
add("&part", 8706);
add("&exist", 8707);
add("&empty", 8709);
add("&nabla", 8711);
add("&isin", 8712);
add("¬in", 8713);
add("&ni", 8715);
add("&prod", 8719);
add("&sum", 8721);
add("&minus", 8722);
add("&lowast", 8727);
add("&radic", 8730);
add("&prop", 8733);
add("&infin", 8734);
add("&ang", 8736);
add("&and", 8743);
add("&or", 8744);
add("&cap", 8745);
add("&cup", 8746);
add("&int", 8747);
add("&there4", 8756);
add("&sim", 8764);
add("&cong", 8773);
add("&asymp", 8776);
add("&ne", 8800);
add("&equiv", 8801);
add("&le", 8804);
add("&ge", 8805);
add("&sub", 8834);
add("&sup", 8835);
add("&nsub", 8836);
add("&sube", 8838);
add("&supe", 8839);
add("&oplus", 8853);
add("&otimes", 8855);
add("&perp", 8869);
add("&sdot", 8901);
add("&lceil", 8968);
add("&rceil", 8969);
add("&lfloor", 8970);
add("&rfloor", 8971);
add("&lang", 9001);
add("&rang", 9002);
add("&loz", 9674);
add("&spades", 9824);
add("&clubs", 9827);
add("&hearts", 9829);
add("&diams", 9830);
add(""", 34);
add("&", 38);
add("<", 60);
add(">", 62);
add("&OElig", 338);
add("&oelig", 339);
add("&Scaron", 352);
add("&scaron", 353);
add("&Yuml", 376);
add("&circ", 710);
add("&tilde", 732);
add("&ensp", 8194);
add("&emsp", 8195);
add("&thinsp", 8201);
add("&zwnj", 8204);
add("&zwj", 8205);
add("&lrm", 8206);
add("&rlm", 8207);
add("&ndash", 8211);
add("&mdash", 8212);
add("&lsquo", 8216);
add("&rsquo", 8217);
add("&sbquo", 8218);
add("&ldquo", 8220);
add("&rdquo", 8221);
add("&bdquo", 8222);
add("&dagger", 8224);
add("&Dagger", 8225);
add("&permil", 8240);
add("&lsaquo", 8249);
add("&rsaquo", 8250);
add("&euro", 8364);
}
}
}