/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* 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 net.bull.javamelody.swing.print;
import java.util.Arrays;
/**
* Encodage de chaines au format HTML.
*
* @author Emeric Vernat
*/
final class MHtmlEncoder {
/**
* Liste des caracteres speciaux HTML triée. Remplie dans le init static
*/
private static final char[] TO_REPLACE;
/**
* Liste des chaines remplacantes triée dans le même ordre que to_replace. Remplie dans le init static
*/
private static final char[][] REPLACE_BY;
/**
* Initialisation de la classe. 1) tri de la liste de caracteres speciaux 2) generation des tables de remplacement
*
* @formatter:off
*/
static {
// Mapping des caractères spéciaux vers leur code HTML
// Respecter l'espace entre le caractere et son code.
final String[] htmlCharacters = { "€ €", "Œ ", "œ ", // latin-15
// pas de nbsp (insécable à l'impression) " ",
"\n <br/>", "\r ", "\t ",
"< <", "& &", "Æ Æ", "À À", "Ä Ä", "É É",
"Ë Ë", "Ì Ì", "Ó Ó", "Ø Ø", "Þ Þ", "Ù Ù",
"á á", "à à", "ä ä", "ê ê", "ë ë", "ì ì",
"ó ó", "ø ø", "ß ß", "û û", "ý ý", "¤ ¤",
"§ §", "ª ª", " ", "° °", "³ ³", "¶ ¶", "¹ ¹",
"¼ ¼", "¿ ¿", "× ×", "÷ ÷", "¢ ¢", "> >",
"\" "", "' '", "` '", "Á Á", "Å Å", "Ç Ç",
"Ê Ê", "Í Í", "Ï Ï", "Ô Ô", "Õ Õ", "Ú Ú",
"Ü Ü", "â â", "å å", "ç ç", "è è", "í í",
"ï ï", "ô ô", "õ õ", "þ þ", "ù ù", "ÿ ÿ",
"¡ ¡", "¥ ¥", "¨ ¨", "« «", "® ®", "± ±",
"´ ´", "· ·", "º º", "½ ½", "Â Â", "Ã Ã",
"Ð Ð", "È È", "Î Î", "Ñ Ñ", "Ò Ò", "Ö Ö",
"Û Û", "Ý Ý", "æ æ", "ã ã", "é é", "ð ð",
"î î", "ñ ñ", "ò ò", "ö ö", "ú ú", "ü ü",
"£ £", "¦ ¦", "© ©", "¬ ¬", "¯ ¯", "² ²",
"µ µ", "¸ ¸", "» »", "¾ ¾",
"‘ ‘", "’ ’", "‚ ‚", "“ “", "” ”", "„ „",
"† †", "‡ ‡", "‰ ‰", "‹ ‹", "› ›", "™ ™",
"– –", "— —", };
// tri de la table des caracteres HTML
Arrays.sort(htmlCharacters);
final int length = htmlCharacters.length;
TO_REPLACE = new char[length];
REPLACE_BY = new char[length][];
for (int i = 0; i < length; i++) {
// premier caractere dans la table des carateres a remplacer
TO_REPLACE[i] = htmlCharacters[i].charAt(0);
// à partir du troisième caractere :
// dans la table des codes remplacants
REPLACE_BY[i] = htmlCharacters[i].substring(2).toCharArray();
}
}
/**
* Constructeur.
*/
private MHtmlEncoder() {
super();
}
/**
* Retourne une chaine encodée.
*
* @return String Chaine encodée
* @param s
* String Chaine à encoder
*/
public static String encodeString(final String s) {
if (s != null) {
final StringBuilder sb = new StringBuilder(s.length() + s.length() / 4);
encodeString(sb, s);
return sb.toString();
}
return "";
}
/**
* Append une chaine à un StringBuilder apres l'avoir encodée. Plus la chaine à encoder est longue, plus les gains de perfs sont sensibles.
*
* @param sb
* String StringBuilder à appender.
* @param s
* String Chaine à encoder et à ajouter à <CODE>sb</CODE>
*/
public static void encodeString(final StringBuilder sb, final String s) {
if (s == null) {
return;
}
final int len = s.length();
// réserve un peu plus de place dans le StringBuilder
sb.ensureCapacity(sb.length() + len + len / 4);
int i;
int index;
char c;
for (i = 0; i < len; i++) {
c = s.charAt(i);
// petite optimisation (qui represente 90% des cas...)
if (isSimpleLetterOrDigit(c)) {
sb.append(c);
} else {
// cherche dans le tableau des caractères
index = Arrays.binarySearch(TO_REPLACE, c);
if (index >= 0) {
// si trouvé, append la chaîne remplaçante
sb.append(REPLACE_BY[index]);
} else if (c < '\u0020' || c > '\u007e') {
// si c'est un caractère bizarre non reconnu, on code son numéro décimal (en charset iso-8859-1)
sb.append("").append(Integer.toString(c)).append(';');
} else {
// sinon append le caractère sans le modifier
sb.append(c); // nécessite un charset système genre windows-1252
}
}
}
}
private static boolean isSimpleLetterOrDigit(char c) {
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9';
}
}