/* * 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); } } }