/*******************************************************************************
* Copyright (c) 2010-2011 VIVO Harvester Team. For full list of contributors, please see the AUTHORS file provided.
* All rights reserved.
* This program and the accompanying materials are made available under the terms of the new BSD license which accompanies this distribution, and is available at http://www.opensource.org/licenses/bsd-license.html
******************************************************************************/
package org.vivoweb.harvester.util;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Christopher Haines (hainesc@ctrip.ufl.edu)
*/
public class SpecialEntities {
/**
* html encode mapping
*/
private static Map<String, String> htmlEncode = null;
/**
* html decode mapping
*/
private static Map<String, String> htmlDecode = null;
/**
* xml encode mapping
*/
private static Map<String, String> xmlEncode = null;
/**
* xml decode mapping
*/
private static Map<String, String> xmlDecode = null;
/**
* list of accepted characters
*/
private static List<Character> acceptedChars = null;
/**
* Gets the html encode mapping
* @return html encode mapping
*/
private static Map<String, String> getHtmlEncode() {
if(htmlEncode == null) {
htmlEncode = new HashMap<String, String>();
htmlEncode.put(" ", " ");
htmlEncode.put(""", """);
htmlEncode.put("&", "&");
htmlEncode.put("'", "'");
htmlEncode.put("<", "<");
htmlEncode.put(">", ">");
htmlEncode.put(" ", " ");// be carefull with this one (non-breaking white space)
htmlEncode.put("¡", "¡");
htmlEncode.put("¢", "¢");
htmlEncode.put("£", "£");
htmlEncode.put("¤", "¤");
htmlEncode.put("¥", "¥");
htmlEncode.put("¦", "¦");
htmlEncode.put("§", "§");
htmlEncode.put("¨", "¨");
htmlEncode.put("©", "©");
htmlEncode.put("ª", "ª");
htmlEncode.put("«", "«");
htmlEncode.put("¬", "¬");
htmlEncode.put("", "");
htmlEncode.put("®", "®");
htmlEncode.put("¯", "¯");
htmlEncode.put("°", "°");
htmlEncode.put("±", "±");
htmlEncode.put("²", "²");
htmlEncode.put("³", "³");
htmlEncode.put("´", "´");
htmlEncode.put("µ", "µ");
htmlEncode.put("¶", "¶");
htmlEncode.put("·", "·");
htmlEncode.put("¸", "¸");
htmlEncode.put("¹", "¹");
htmlEncode.put("º", "º");
htmlEncode.put("»", "»");
htmlEncode.put("¼", "¼");
htmlEncode.put("½", "½");
htmlEncode.put("¾", "¾");
htmlEncode.put("¿", "¿");
htmlEncode.put("À", "À");
htmlEncode.put("Á", "Á");
htmlEncode.put("Â", "Â");
htmlEncode.put("Ã", "Ã");
htmlEncode.put("Ä", "Ä");
htmlEncode.put("Å", "Å");
htmlEncode.put("Æ", "Æ");
htmlEncode.put("Ç", "Ç");
htmlEncode.put("È", "È");
htmlEncode.put("É", "É");
htmlEncode.put("Ê", "Ê");
htmlEncode.put("Ë", "Ë");
htmlEncode.put("Ì", "Ì");
htmlEncode.put("Í", "Í");
htmlEncode.put("Î", "Î");
htmlEncode.put("Ï", "Ï");
htmlEncode.put("Ð", "Ð");
htmlEncode.put("Ñ", "Ñ");
htmlEncode.put("Ò", "Ò");
htmlEncode.put("Ó", "Ó");
htmlEncode.put("Ô", "Ô");
htmlEncode.put("Õ", "Õ");
htmlEncode.put("Ö", "Ö");
htmlEncode.put("×", "×");
htmlEncode.put("Ø", "Ø");
htmlEncode.put("Ù", "Ù");
htmlEncode.put("Ú", "Ú");
htmlEncode.put("Û", "Û");
htmlEncode.put("Ü", "Ü");
htmlEncode.put("Ý", "Ý");
htmlEncode.put("Þ", "Þ");
htmlEncode.put("ß", "ß");
htmlEncode.put("à", "à");
htmlEncode.put("á", "á");
htmlEncode.put("â", "â");
htmlEncode.put("ã", "ã");
htmlEncode.put("ä", "ä");
htmlEncode.put("å", "å");
htmlEncode.put("æ", "æ");
htmlEncode.put("ç", "ç");
htmlEncode.put("è", "è");
htmlEncode.put("é", "é");
htmlEncode.put("ê", "ê");
htmlEncode.put("ë", "ë");
htmlEncode.put("ì", "ì");
htmlEncode.put("í", "í");
htmlEncode.put("î", "î");
htmlEncode.put("ï", "ï");
htmlEncode.put("ð", "ð");
htmlEncode.put("ñ", "ñ");
htmlEncode.put("ò", "ò");
htmlEncode.put("ó", "ó");
htmlEncode.put("ô", "ô");
htmlEncode.put("õ", "õ");
htmlEncode.put("ö", "ö");
htmlEncode.put("÷", "÷");
htmlEncode.put("ø", "ø");
htmlEncode.put("ù", "ù");
htmlEncode.put("ú", "ú");
htmlEncode.put("û", "û");
htmlEncode.put("ü", "ü");
htmlEncode.put("ý", "ý");
htmlEncode.put("þ", "þ");
htmlEncode.put("ÿ", "ÿ");
htmlEncode.put("Œ", "Œ");
htmlEncode.put("œ", "œ");
htmlEncode.put("Š", "Š");
htmlEncode.put("š", "š");
htmlEncode.put("Ÿ", "Ÿ");
htmlEncode.put("ƒ", "ƒ");
htmlEncode.put("ˆ", "ˆ");
htmlEncode.put("˜", "˜");
htmlEncode.put("Α", "Α");
htmlEncode.put("Β", "Β");
htmlEncode.put("Γ", "Γ");
htmlEncode.put("Δ", "Δ");
htmlEncode.put("Ε", "Ε");
htmlEncode.put("Ζ", "Ζ");
htmlEncode.put("Η", "Η");
htmlEncode.put("Θ", "Θ");
htmlEncode.put("Ι", "Ι");
htmlEncode.put("Κ", "Κ");
htmlEncode.put("Λ", "Λ");
htmlEncode.put("Μ", "Μ");
htmlEncode.put("Ν", "Ν");
htmlEncode.put("Ξ", "Ξ");
htmlEncode.put("Ο", "Ο");
htmlEncode.put("Π", "Π");
htmlEncode.put("Ρ", "Ρ");
htmlEncode.put("Σ", "Σ");
htmlEncode.put("Τ", "Τ");
htmlEncode.put("Υ", "Υ");
htmlEncode.put("Φ", "Φ");
htmlEncode.put("Χ", "Χ");
htmlEncode.put("Ψ", "Ψ");
htmlEncode.put("Ω", "Ω");
htmlEncode.put("α", "α");
htmlEncode.put("β", "β");
htmlEncode.put("γ", "γ");
htmlEncode.put("δ", "δ");
htmlEncode.put("ε", "ε");
htmlEncode.put("ζ", "ζ");
htmlEncode.put("η", "η");
htmlEncode.put("θ", "θ");
htmlEncode.put("ι", "ι");
htmlEncode.put("κ", "κ");
htmlEncode.put("λ", "λ");
htmlEncode.put("μ", "μ");
htmlEncode.put("ν", "ν");
htmlEncode.put("ξ", "ξ");
htmlEncode.put("ο", "ο");
htmlEncode.put("π", "π");
htmlEncode.put("ρ", "ρ");
htmlEncode.put("ς", "ς");
htmlEncode.put("σ", "σ");
htmlEncode.put("τ", "τ");
htmlEncode.put("υ", "υ");
htmlEncode.put("φ", "φ");
htmlEncode.put("χ", "χ");
htmlEncode.put("ψ", "ψ");
htmlEncode.put("ω", "ω");
htmlEncode.put("ϑ", "ϑ");
htmlEncode.put("ϒ", "ϒ");
htmlEncode.put("ϖ", "ϖ");
htmlEncode.put(" ", " ");
htmlEncode.put(" ", " ");
htmlEncode.put(" ", " ");
htmlEncode.put("", "");
htmlEncode.put("", "");
htmlEncode.put("", "");
htmlEncode.put("", "");
htmlEncode.put("–", "–");
htmlEncode.put("—", "—");
htmlEncode.put("‘", "‘");
htmlEncode.put("’", "’");
htmlEncode.put("‚", "‚");
htmlEncode.put("“", "“");
htmlEncode.put("”", "”");
htmlEncode.put("„", "„");
htmlEncode.put("†", "†");
htmlEncode.put("‡", "‡");
htmlEncode.put("•", "•");
htmlEncode.put("…", "…");
htmlEncode.put("‰", "‰");
htmlEncode.put("′", "′");
htmlEncode.put("″", "″");
htmlEncode.put("‹", "‹");
htmlEncode.put("›", "›");
htmlEncode.put("‾", "‾");
htmlEncode.put("⁄", "⁄");
htmlEncode.put("€", "€");
htmlEncode.put("℘", "℘");
htmlEncode.put("ℑ", "ℑ");
htmlEncode.put("ℜ", "ℜ");
htmlEncode.put("™", "™");
htmlEncode.put("ℵ", "ℵ");
htmlEncode.put("←", "←");
htmlEncode.put("↑", "↑");
htmlEncode.put("→", "→");
htmlEncode.put("↓", "↓");
htmlEncode.put("↔", "↔");
htmlEncode.put("↵", "↵");
htmlEncode.put("⇐", "⇐");
htmlEncode.put("⇑", "⇑");
htmlEncode.put("⇒", "⇒");
htmlEncode.put("⇓", "⇓");
htmlEncode.put("⇔", "⇔");
htmlEncode.put("∀", "∀");
htmlEncode.put("∂", "∂");
htmlEncode.put("∃", "∃");
htmlEncode.put("∅", "∅");
htmlEncode.put("∇", "∇");
htmlEncode.put("∈", "∈");
htmlEncode.put("∉", "∉");
htmlEncode.put("∋", "∋");
htmlEncode.put("∏", "∏");
htmlEncode.put("∑", "∑");
htmlEncode.put("−", "−");
htmlEncode.put("∗", "∗");
htmlEncode.put("√", "√");
htmlEncode.put("∝", "∝");
htmlEncode.put("∞", "∞");
htmlEncode.put("∠", "∠");
htmlEncode.put("∧", "∧");
htmlEncode.put("∨", "∨");
htmlEncode.put("∩", "∩");
htmlEncode.put("∪", "∪");
htmlEncode.put("∫", "∫");
htmlEncode.put("∴", "∴");
htmlEncode.put("∼", "∼");
htmlEncode.put("≅", "≅");
htmlEncode.put("≈", "≈");
htmlEncode.put("≠", "≠");
htmlEncode.put("≡", "≡");
htmlEncode.put("≤", "≤");
htmlEncode.put("≥", "≥");
htmlEncode.put("⊂", "⊂");
htmlEncode.put("⊃", "⊃");
htmlEncode.put("⊄", "⊄");
htmlEncode.put("⊆", "⊆");
htmlEncode.put("⊇", "⊇");
htmlEncode.put("⊕", "⊕");
htmlEncode.put("⊗", "⊗");
htmlEncode.put("⊥", "⊥");
htmlEncode.put("⋅", "⋅");
htmlEncode.put("⌈", "⌈");
htmlEncode.put("⌉", "⌉");
htmlEncode.put("⌊", "⌊");
htmlEncode.put("⌋", "⌋");
htmlEncode.put("〈", "〈");
htmlEncode.put("〉", "〉");
htmlEncode.put("◊", "◊");
htmlEncode.put("♠", "♠");
htmlEncode.put("♣", "♣");
htmlEncode.put("♥", "♥");
htmlEncode.put("♦", "♦");
}
return htmlEncode;
}
/**
* Gets the html decode mapping
* @return html decode mapping
*/
private static Map<String, String> getHtmlDecode() {
if(htmlDecode == null) {
htmlDecode = new HashMap<String, String>();
for(String ch : getHtmlEncode().keySet()) {
htmlDecode.put(getHtmlEncode().get(ch), ch);
}
}
return htmlDecode;
}
/**
* Gets the xml encode mapping
* @return xml encode mapping
*/
private static Map<String, String> getXmlEncode() {
if(xmlEncode == null) {
xmlEncode = new HashMap<String, String>();
xmlEncode.put(" ", " ");
xmlEncode.put(""", """);
xmlEncode.put("&", "&");
xmlEncode.put("'", "'");
xmlEncode.put("<", "<");
xmlEncode.put(">", ">");
}
return xmlEncode;
}
/**
* Gets the xml decode mapping
* @return xml decode mapping
*/
private static Map<String, String> getXmlDecode() {
if(xmlDecode == null) {
xmlDecode = new HashMap<String, String>();
for(String ch : getXmlEncode().keySet()) {
xmlDecode.put(getXmlEncode().get(ch), ch);
}
}
return xmlDecode;
}
/**
* Gets the list of accepted characters
* @return the character list
*/
private static List<Character> getAcceptedChars() {
if(acceptedChars == null) {
acceptedChars = new LinkedList<Character>();
acceptedChars.add(Character.valueOf('a'));
acceptedChars.add(Character.valueOf('b'));
acceptedChars.add(Character.valueOf('c'));
acceptedChars.add(Character.valueOf('d'));
acceptedChars.add(Character.valueOf('e'));
acceptedChars.add(Character.valueOf('f'));
acceptedChars.add(Character.valueOf('g'));
acceptedChars.add(Character.valueOf('h'));
acceptedChars.add(Character.valueOf('i'));
acceptedChars.add(Character.valueOf('j'));
acceptedChars.add(Character.valueOf('k'));
acceptedChars.add(Character.valueOf('l'));
acceptedChars.add(Character.valueOf('m'));
acceptedChars.add(Character.valueOf('n'));
acceptedChars.add(Character.valueOf('o'));
acceptedChars.add(Character.valueOf('p'));
acceptedChars.add(Character.valueOf('q'));
acceptedChars.add(Character.valueOf('r'));
acceptedChars.add(Character.valueOf('s'));
acceptedChars.add(Character.valueOf('t'));
acceptedChars.add(Character.valueOf('u'));
acceptedChars.add(Character.valueOf('v'));
acceptedChars.add(Character.valueOf('w'));
acceptedChars.add(Character.valueOf('x'));
acceptedChars.add(Character.valueOf('y'));
acceptedChars.add(Character.valueOf('z'));
acceptedChars.add(Character.valueOf('A'));
acceptedChars.add(Character.valueOf('B'));
acceptedChars.add(Character.valueOf('C'));
acceptedChars.add(Character.valueOf('D'));
acceptedChars.add(Character.valueOf('E'));
acceptedChars.add(Character.valueOf('F'));
acceptedChars.add(Character.valueOf('G'));
acceptedChars.add(Character.valueOf('H'));
acceptedChars.add(Character.valueOf('I'));
acceptedChars.add(Character.valueOf('J'));
acceptedChars.add(Character.valueOf('K'));
acceptedChars.add(Character.valueOf('L'));
acceptedChars.add(Character.valueOf('M'));
acceptedChars.add(Character.valueOf('N'));
acceptedChars.add(Character.valueOf('O'));
acceptedChars.add(Character.valueOf('P'));
acceptedChars.add(Character.valueOf('Q'));
acceptedChars.add(Character.valueOf('R'));
acceptedChars.add(Character.valueOf('S'));
acceptedChars.add(Character.valueOf('T'));
acceptedChars.add(Character.valueOf('U'));
acceptedChars.add(Character.valueOf('V'));
acceptedChars.add(Character.valueOf('W'));
acceptedChars.add(Character.valueOf('X'));
acceptedChars.add(Character.valueOf('Y'));
acceptedChars.add(Character.valueOf('Z'));
acceptedChars.add(Character.valueOf('0'));
acceptedChars.add(Character.valueOf('1'));
acceptedChars.add(Character.valueOf('2'));
acceptedChars.add(Character.valueOf('3'));
acceptedChars.add(Character.valueOf('4'));
acceptedChars.add(Character.valueOf('5'));
acceptedChars.add(Character.valueOf('6'));
acceptedChars.add(Character.valueOf('7'));
acceptedChars.add(Character.valueOf('8'));
acceptedChars.add(Character.valueOf('9'));
acceptedChars.add(Character.valueOf('.'));
acceptedChars.add(Character.valueOf(','));
acceptedChars.add(Character.valueOf(';'));
acceptedChars.add(Character.valueOf(':'));
acceptedChars.add(Character.valueOf('?'));
acceptedChars.add(Character.valueOf('/'));
acceptedChars.add(Character.valueOf('\\'));
acceptedChars.add(Character.valueOf('|'));
acceptedChars.add(Character.valueOf('('));
acceptedChars.add(Character.valueOf(')'));
acceptedChars.add(Character.valueOf('{'));
acceptedChars.add(Character.valueOf('}'));
acceptedChars.add(Character.valueOf('['));
acceptedChars.add(Character.valueOf(']'));
acceptedChars.add(Character.valueOf('~'));
acceptedChars.add(Character.valueOf('`'));
acceptedChars.add(Character.valueOf('!'));
acceptedChars.add(Character.valueOf('@'));
acceptedChars.add(Character.valueOf('#'));
acceptedChars.add(Character.valueOf('$'));
acceptedChars.add(Character.valueOf('%'));
acceptedChars.add(Character.valueOf('^'));
acceptedChars.add(Character.valueOf('*'));
acceptedChars.add(Character.valueOf('-'));
acceptedChars.add(Character.valueOf('_'));
acceptedChars.add(Character.valueOf(' '));
acceptedChars.add(Character.valueOf('+'));
acceptedChars.add(Character.valueOf('='));
acceptedChars.add(Character.valueOf('\t'));
acceptedChars.add(Character.valueOf('\n'));
acceptedChars.add(Character.valueOf('\r'));
acceptedChars.add(Character.valueOf('\f'));
}
return acceptedChars;
}
/**
* Converts all special characters to HTML-entities
* @param s input string
* @param exceptions force these characters to be encoded
* @return html encoded string
*/
public static String htmlEncode(String s, char... exceptions) {
return encode(s, "html", exceptions);
}
/**
* Converts all HTML-special-character-entities to special characters
* @param s input string
* @return html decoded string
*/
public static String htmlDecode(String s) {
return decode(s, "html");
}
/**
* Converts all special characters to XML-entities
* @param s input string
* @param exceptions force these characters to be encoded
* @return xml encoded string
*/
public static String xmlEncode(String s, char... exceptions) {
return encode(s, "xml", exceptions);
}
/**
* Converts all XML-special-character-entities to special characters
* @param s input string
* @return xml decoded string
*/
public static String xmlDecode(String s) {
return decode(s, "xml");
}
/**
* Converts all special characters to encoded-entities
* @param s input string
* @param encodeType the type of encoding
* @param exceptions force these characters to be encoded
* @return encoded string
*/
private static String encode(String s, String encodeType, char... exceptions) {
if(s == null){
return null;
}
Map<String, String> encodeMap;
List<Character> accept = new LinkedList<Character>(getAcceptedChars());
for(char c : exceptions) {
accept.remove(Character.valueOf(c));
}
if(encodeType.equalsIgnoreCase("html")) {
encodeMap = getHtmlEncode();
} else if(encodeType.equalsIgnoreCase("xml")) {
encodeMap = getXmlEncode();
} else {
throw new IllegalArgumentException("encodeType must be xml or html only");
}
StringBuilder b = new StringBuilder(s.length());
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if(accept.contains(Character.valueOf(ch))) {
b.append(ch);
} else {
StringBuilder b2 = new StringBuilder();
if(Character.isISOControl(ch)) {
// ignore
} else if(Character.isWhitespace(ch)) {
b2.append("").append((int)ch).append(";");
} else if(Character.isHighSurrogate(ch)) {
int codePoint;
if((i + 1 < s.length()) && Character.isSurrogatePair(ch, s.charAt(i + 1)) && Character.isDefined(codePoint = (Character.toCodePoint(ch, s.charAt(i + 1))))) {
b2.append("").append(codePoint).append(";");
}
i++;
} else if(Character.isLowSurrogate(ch)) {
i++;
} else if(Character.isDefined(ch)) {
b2.append("").append((int)ch).append(";");
}
if(encodeMap.containsKey(b2.toString())) {
b.append(encodeMap.get(b2.toString()));
} else {
b.append(b2.toString());
}
}
}
return b.toString();
}
/**
* Converts all special-character-entities to special characters
* @param s input string
* @param decodeType the type of decoding
* @return decoded string
*/
private static String decode(String s, String decodeType) {
Map<String, String> decodeMap;
if(decodeType.equalsIgnoreCase("html")) {
decodeMap = getHtmlDecode();
} else if(decodeType.equalsIgnoreCase("xml")) {
decodeMap = getXmlDecode();
} else {
throw new IllegalArgumentException("decodeType must be xml or html only");
}
String str = s;
for(String htmlEntity : decodeMap.keySet()) {
str = str.replaceAll(htmlEntity, decodeMap.get(htmlEntity));
}
Matcher m = Pattern.compile("(.*?);").matcher(str);
Set<String> charCodes = new HashSet<String>();
while(m.find()) {
charCodes.add(m.group(1));
}
for(String ch : charCodes) {
int charCode = Integer.parseInt(ch);
if(Character.isDefined((char)charCode)) {
str = str.replaceAll("" + ch + ";", "" + (char)charCode);
} else if(Character.isDefined(charCode)) {
StringBuilder b2 = new StringBuilder();
for(char c : Character.toChars(charCode)) {
b2.append(c);
}
str = str.replaceAll("" + ch + ";", b2.toString());
}
}
return str;
}
}