Dear Devs,
     Velocity is a great opensource component for weg page render. We deploy
it  on our site that render dynamic web pages more than 1 billion pages
everyday. But velocity have no security protected xss + csrf attack. Every
render reference point need programmer writing code as
"$stringEscapedUtil.escapedHtml($ref)". But the such code will be forgoten
by programmer, especialy a newbie.  So  security can not be handled at every
output.
    Infact, every web page output need to be html encode , about more than
90%. The best solution we shuold do html encode for every output refrence
with default.Some spec Macro directive left 10% content output.   The
attachment is my demo code (Infact we have deploy it in our product
enviroment).  The code implementation is very ugly, but bring us security
sophisticated. Maybe it can bring Velocity Dev Team some idea on web
security.
    Sorry for my code's coment writing in chinese. I think the code is very
simple.  I explain it now:
    1. html/xml encode part, I copy it from apache commons component, and
rewrite it for performace issue and remove encode for non-ascii unicode.
Encode all unicode chars are not wize , cause more large web page and cause
debug problem.
    2. I implement xml/html/javascript/XSS filtter.  I think Xss filter
shuold be optional, because it has many security rules.

  It is ONLY a adive.

Leon Liu
package com.alibaba.china.fasttext.security.velocity;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;

import com.alibaba.china.fasttext.security.xss.Policy;
import com.alibaba.china.fasttext.security.xss.PolicyException;
import com.alibaba.china.fasttext.security.xss.XssXppScanner;

/**
 * <pre>
 * 1.htmlÊä³ö, ʲô¶¼²»Ö´ÐУ¬ °´Ô­Ê¼¸ñʽÊä³ö¡£ Ëû²¢²»ÊÇÕæÕýµÄ²»Ö´ÐÐÈκαä
 * »¯£¬ ÒòΪËû»áÖ´ÐÐxssµÄ¹ýÂ˶¯×÷¡£Ò»¸ö·Ç³£¸´Ôӵݲȫ´¦Àí¹ý³Ì£¬ Èç¹û²»ÊÇÊä
 * ³öHTML£¬ ÇëÎðʹÓᣠÕâ¸ö±íʾ½«»áÏûºÄ´óÁ¿µÄCPU´¦Àí¡£
 * #ZHTML($html)
 * 2.xml±àÂëÊä³ö£¬ ½«»áÖ´ÐÐ xml encodeÊä³ö
 * #ZXML($xml)
 * 3.js±àÂëÊä³ö , ½«»áÖ´ÐÐjavascript encodeÊä³ö
 * #ZJS($js)
 * 4.´¿Îı¾±àÂëÊä³ö, ½«»áÖ´ÐÐhtml encode
 * $pureText
 * 5. ÒѾ­±»ÒµÎñ´úÂëescaped¹ýµÄÊä³ö
 * Õâ¸öĿǰûÓÐʲôºÃµÄ°ì·¨£¬ Ö»ÄÜÐÞ¸ÄÒµÎñ´úÂ룬 È¥³ýÕâЩescapedµÄµØ·½¡£
 * ʹÓÃfind . |grep java$ |xargs grep -i StringEscapeUtil.escapeHtml À´Ñ°ÕÒ
 * ³ÌÐòÖгöÏֵģ¬ ÎÒÕÒÁËÏÂexodusÀïµÄ´úÂ룬 ÓÐ21¸öµØ·½ÊÇÕâô×öµÄ¡£ ÎÒÏëÕâ¸ö
 * ÐÞ¸ÄÆðÀ´µÄÄѶÈÊDZȽϵ͵ġ£
 * 6. ʹÓÃ$stringEscapeUtil.escapeHtml($text)Êä³öµÄµØ·½
 * ÓÉÓÚ´úÂëÐÞ¸ÄÆðÀ´±È½ÏÂé·³£¬ ÎÒÃÇʹÓÃÒ»¸öºêÀ´´¦ÀíÕâ¸öÎÊÌâ, Õâ¸öʹÓÃÒ»¸öͳ
 * Ò»Ìæ»»¹¤×÷¡£°Ñ$stringEscapeUtil.escapeHtml Ìæ»»³É
 * #ZPURETEXT($text)
 * 7. URLµÄÊä³ö, Ò²ÐíÎÒÃǾͽ«À´Òª×öURLµÄ°²È«¼ì²é£¬ Òò´Ë£¬ ÇëÎðʹÓÃÔÚ·ÇURL³ö
 * Ïֵĵط½ ËäÈ»ËûʲôתÒå¶¼²»×ö¡£
 * Ò»°ãÇé¿öÏ£¬ ÎÒÃǵÄURLÊä³öʹÓÃURIBoker¶ÔÏó£¬ µ«ÊÇ£¬ ÎÒ×¢Òâµ½ÁË£¬ Óв¿·Ö
 * URLʹÓÃÁËURIBoker.render() »òÕߣ¬ URIBoker.toString() ÕâôÊä³ö£¬ Õâ¸ö»á
 * Ôì³ÉURL±»html encode. Õâ¸öÊǺÜÎÞÄεġ£
 * Èç¹û·¢ÉúÕâ¸öÇé¿ö£¬ Çë¼ÓÉÏ
 * #ZURL($url), Õâ¸öºêָʾÁ˲»ÒªÊ¹ÓÃhtml encode.
 * 8. ΪÁ˱ÜÃâ·¢ÉúÆçÒ壬 #ZHTMLËäÈ»ÊDz»×ªÒ壬 µ«ÊÇÎÒÃÇÃ÷ȷ˵µÄÊÇHTML£¬ µ«
 * ÊÇ£¬ Èç¹ûÕæÓв»ÐèҪתÒåµÄÇé¿ö³öÏÖ£¬ ÎÒÃÇ¿ÉÒÔʹÓÃ
 * #ZLITERAL($text)
 * tips: literal - ÕÕ×ÖÃæµÄ;Ô­ÒåµÄ.
 * 
 * ¼ÓÒ»¸öZÊÇΪÁ˾¡Á¿±ÜÃâºÍÓ¦Óö¨ÒåµÄºê³öÏÖ³åÍ»£¬ ûÓбðµÄº¬Òå. µ«ÊÇÎÒÃÇÈÔÈ»
 * Óм¸¸öµØ·½ÐèÒªÎÒÃÇ×¢ÒâµÄµØ·½£º
 * 
 * 1£®À©Õ¹ÊµÏÖµÄÊDZàÂëÊä³öString¶ÔÏó£¬ Èç¹ûÄãÊä³öµÄ²»ÊÇString¶ÔÏó£¬ ÕâЩºê
 * ²Ù×÷½«»áʧЧ£¬ Õâ¸öÊÇΪÁËÌá¸ßϵͳµÄ±àÂëÐÔÄÜ£¬²ÉÈ¡µÄ̬¶È¡£ Èç¹ûÒ»¸ö¶ÔÏóû
 * ÓÐÖØÐÂʵÏÖtoString()µÄ£¬ Õâ¸ö¶ÔÏóµÄÊä³öÊDz»¿ÉÄÜÓÐhtml±àÂëµÄÎÊÌâµÄ¡£ÒòΪ
 * Object.toStringʵÏÖµÄÊǶÔÏóµØÖ·£¬ ÁíÍâʵÏÖÁËtoStringµÄ·½·¨£¬ ÎÒÃǹÃÇÒÈÏ
 * ΪÊǰ²È«£¬ Õâ¸öËäÈ»ÑϸñÒâÒåÉÏÊDz»ÕýÈ·µÄ£¬µ«ÊÇ£¬ ÎÒÃǵÄϵͳµÄÈ·»ù±¾ÊÇÕâÑù
 * ¹¤×÷µÄ¡£
 * 2£®ÈçºÎ±ÜÃâÒ»¸öÊý¾Ý±»¶à´Îencode¡£
 * Ê×ÏÈÎÒÃǼÙÉè $text=&quot;s&amp;&quot;
 * a.$stringUtil.getXXX($text)
 * Õâ¸öÐÎʽÊä³öÊÇ s&amp;
 * b.$stringUtil.getXXX(&quot;$text&quot;)
 * Õâ¸öÐÎʽÊä³öÊÇ s&amp;amp;
 * Ϊʲô»áÕâÑùÄØ£¬&quot;&quot;ÊǸæËßvelocity, ÏÈÊä³ö£¬ ºó°Ñ²ÎÊýË͸ø
 * #stringUtil.getXXX. ÕâÊÇÒ»¸ö·Ç³£ÐèҪעÒâµÄµØ·½£¬Õâ¸öµØ·½µ¼ÖÂÁË´óÁ¿µÄµØ·½
 * ÐèÒªÐ޸ĵĵط½¡£ Ò²ÊÇĿǰ×îÎÞÄεĵط½¡£Õâ¸ö
 * Õâ¸ö»áÔì³ÉһЩ±äÐεÄÌØÊâÇé¿ö£¬ ±ÈÈç
 * $stringUtil.equals(&quot;$text&quot;£¬ &quot;&amp;&quot;)
 * ÕâÑùÇé¿ö¾ÍÊÇ£¬ &quot;$text&quot;, Õâ¸öÒѾ­·¢Éúhtlml encode, È»¶ø &quot;&amp;&quot;Êdz£Á¿£¬ ³£Á¿
 * ÊÇÎÞ·¨±»×ªÒåµÄ£¬Òò´ËÕâ¸ö¾ÍÊÇ·¢ÉúÑÏÖØµÄbug.
 * ½â¾öµÄ°ì·¨ÊÇÕâôд£º
 * $stringUtil.equals($text£¬ &quot;&amp;&quot;)
 * c.$stringUtil.getXXX(&quot;the pro: $text&quot;)
 * Õâ¸öÇé¿ö£¬ ĿǰÎÒÃǵÄϵͳҲÊDZȽ϶࣬ ³£Á¿ÓëÊä³öµÄ»ìºÏ. ½â¾öÕâ¸öÎÊÌâµÄ°ì
 * ·¨ÊÇ£ºHTML_XSS_FILTER
 * $stringUtil.getXXX(&quot;the pro: #ZLITERAL($text)&quot;)
 * 
 * d.$control.setTemplate($text)
 * Õâ¸öϵͳ»á×Ô¶¯´¦Àí
 * e.$stringUtil.escapeHtml($text)
 * Õâ¸öÉÏÃæµÄµÚ6Ìõ×÷³öÁ˽âÊÍ
 * f.$screen_placeholder
 * Õâ¸öϵͳ»á×Ô¶¯´¦Àí
 * 
 * </pre>
 * 
 * @author leon
 */
public class SecurityReferenceInsertionEventHandler extends RootReferenceFilter implements ReferenceInsertionEventHandler {

    private static final Log logger = LogFactory.getLog(SecurityReferenceInsertionEventHandler.class);
    private XssXppScanner    scanner;

    public SecurityReferenceInsertionEventHandler(){
        try {
            scanner = new XssXppScanner(Policy.getLoosePolicyInstance());
        } catch (PolicyException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see ReferenceAction
     */
    public Object referenceInsert(String reference, Object value) {
        if (value instanceof String) {
            ReferenceAction action = getReferenceAction(reference);
            String retValue = (String) value;
            switch (action) {
                case HTML_XSS_FILTER:
                    retValue = scanner.scan(retValue);
                    break;

                case JAVASCRIPT_ENCODE:
                    FastEntities.escapeJavaScript(retValue);
                    break;
                case XML_ENCODE:
                    retValue = XML.escape(retValue);
                    break;
                case LITERAL:
                case URL_FILTER:// do nothing
                    break;
                case HTML_ENCODE:
                    retValue = HTML40.escape(retValue);
                    break;
                case PURE_TEXT_HTML_ENCODE:
                    retValue = HTML40.escape(retValue);
                    break;
                case NOT_DEFINED:
                default:
                    // ÔÚĬÈϵÄÇé¿öÏ£¬¡¡ÈÕÖ¾¼Ç¼£¬¡¡¿ÉÄÜÊÇhtmlµÄÊä³ö
                    if (logger.isDebugEnabled()) {
                        if (retValue.indexOf('<') > -1 && retValue.indexOf('>') > -1) {
                            logger.debug("reference=" + reference + " maybe is a html content");
                        }
                    }
                    retValue = HTML40.escape(retValue);
                    break;
            }
            return retValue;
        }
        return value;
    }
}

/**
 * <pre>
 * HTML_XSS_FILTER: ʹÓÃxss¹ýÂ˹¤¾ßÊä³ö
 * HTML_ENCODE: °´html encodeÊä³ö
 * JAVASCRIPT_ENCODE: °´js encode Êä³ö
 * XML_ENCODE: °´xml encodeÊä³ö
 * LITERAL: °´Ô­À´µÄÊä³ö
 * URL_FILTER: ÔÝʱûÓÐ×öÈκαàÂëÐÐΪ 
 * PURE_TEXT_HTML_ENCODE: ¼æÈÝÔ­À´µÄϵͳʹÓ㬠ΪÁ˱ÜÃâ·¢Éú¶þ´Î±àÂë
 * NOT_DEFINED: 䶨ÒåµÄ£¬ ʹÓÃhtml encode
 * </pre>
 */
enum ReferenceAction {
    HTML_XSS_FILTER, HTML_ENCODE, JAVASCRIPT_ENCODE, XML_ENCODE, LITERAL, URL_FILTER, PURE_TEXT_HTML_ENCODE,
    NOT_DEFINED
}

/**
 * <pre>
 * #########html xss filter#############
 * #macro(SHTML $SHTML_)$SHTML_#end
 * #########xml encode #############
 * #macro(SXML $SXML_)$SXML_#end
 * #########javascript encode #############
 * #macro(SJS $SJS_)$SJS_#end
 * #########pure text html encode, it is a compatibility purpose, shun double escaped#############
 * #macro(SPURETEXT $SPURETEXT_)$SPURETEXT_#end
 * #########pure text as literal #############
 * #macro(SLITERAL $SLITERAL_)$SLITERAL_#end
 * #########url output #############
 * #macro(SURL $SURL_)$SURL_#end
 *</pre>
 */
class RootReferenceFilter extends FastEntities {

    static Map<String, ReferenceAction> filter = new HashMap<String, ReferenceAction>();

    static {
        // system define
        filter.put("$screen_placeholder", ReferenceAction.LITERAL);

        // encode part
        filter.put("$!{security_h_t_m_l_z}", ReferenceAction.HTML_XSS_FILTER);
        filter.put("$!{security_x_m_l_z}", ReferenceAction.XML_ENCODE);
        filter.put("$!{security_j_s_z}", ReferenceAction.JAVASCRIPT_ENCODE);
        filter.put("$!{security_p_u_r_e_t_e_x_t_z}", ReferenceAction.HTML_ENCODE);
        filter.put("$!{security_l_i_t_e_r_a_l_z}", ReferenceAction.LITERAL);
        filter.put("$!{security_u_r_l_z}", ReferenceAction.URL_FILTER);
    }

    public static ReferenceAction getReferenceAction(String ref) {
        ReferenceAction action = filter.get(ref);
        if (action == null) {
            action = ReferenceAction.NOT_DEFINED;
        }
        return action;
    }

}

/**
 * ¿ìËÙhtml/js/xml ±àÂëʵÏÖ
 */
class FastEntities extends FastDetectChar {

    private static final String[][]  BASIC_ARRAY     = { { "&quot;", "34" }, { "&amp;", "38" }, { "&lt;", "60" },
            { "&gt;", "62" },                       };
    private static final String[][]  APOS_ARRAY      = { { "&apos;", "39" }, };
    private static final String[][]  ISO8859_1_ARRAY = { { "&nbsp;", "160" }, { "&iexcl;", "161" },
            { "&cent;", "162" }, { "&pound;", "163" }, { "&curren;", "164" }, { "&yen;", "165" },
            { "&brvbar;", "166" }, { "&sect;", "167" }, { "&uml;", "168" }, { "&copy;", "169" }, { "&ordf;", "170" },
            { "&laquo;", "171" }, { "&not;", "172" }, { "&shy;", "173" }, { "&reg;", "174" }, { "&macr;", "175" },
            { "&deg;", "176" }, { "&plusmn;", "177" }, { "&sup2;", "178" }, { "&sup3;", "179" }, { "&acute;", "180" },
            { "&micro;", "181" }, { "&para;", "182" }, { "&middot;", "183" }, { "&cedil;", "184" },
            { "&sup1;", "185" }, { "&ordm;", "186" }, { "&raquo;", "187" }, { "&frac14;", "188" },
            { "&frac12;", "189" }, { "&frac34;", "190" }, { "&iquest;", "191" }, { "&Agrave;", "192" },
            { "&Aacute;", "193" }, { "&Acirc;", "194" }, { "&Atilde;", "195" }, { "&Auml;", "196" },
            { "&Aring;", "197" }, { "&AElig;", "198" }, { "&Ccedil;", "199" }, { "&Egrave;", "200" },
            { "&Eacute;", "201" }, { "&Ecirc;", "202" }, { "&Euml;", "203" }, { "&Igrave;", "204" },
            { "&Iacute;", "205" }, { "&Icirc;", "206" }, { "&Iuml;", "207" }, { "&ETH;", "208" },
            { "&Ntilde;", "209" }, { "&Ograve;", "210" }, { "&Oacute;", "211" }, { "&Ocirc;", "212" },
            { "&Otilde;", "213" }, { "&Ouml;", "214" }, { "&times;", "215" }, { "&Oslash;", "216" },
            { "&Ugrave;", "217" }, { "&Uacute;", "218" }, { "&Ucirc;", "219" }, { "&Uuml;", "220" },
            { "&Yacute;", "221" }, { "&THORN;", "222" }, { "&szlig;", "223" }, { "&agrave;", "224" },
            { "&aacute;", "225" }, { "&acirc;", "226" }, { "&atilde;", "227" }, { "&auml;", "228" },
            { "&aring;", "229" }, { "&aelig;", "230" }, { "&ccedil;", "231" }, { "&egrave;", "232" },
            { "&eacute;", "233" }, { "&ecirc;", "234" }, { "&euml;", "235" }, { "&igrave;", "236" },
            { "&iacute;", "237" }, { "&icirc;", "238" }, { "&iuml;", "239" }, { "&eth;", "240" },
            { "&ntilde;", "241" }, { "&ograve;", "242" }, { "&oacute;", "243" }, { "&ocirc;", "244" },
            { "&otilde;", "245" }, { "&ouml;", "246" }, { "&divide;", "247" }, { "&oslash;", "248" },
            { "&ugrave;", "249" }, { "&uacute;", "250" }, { "&ucirc;", "251" }, { "&uuml;", "252" },
            { "&yacute;", "253" }, { "&thorn;", "254" }, { "&yuml;", "255" }, };
    private static final String[][]  HTML40_ARRAY    = { { "&fnof;", "402" }, { "&Alpha;", "913" },
            { "&Beta;", "914" }, { "&Gamma;", "915" }, { "&Delta;", "916" }, { "&Epsilon;", "917" },
            { "&Zeta;", "918" }, { "&Eta;", "919" }, { "&Theta;", "920" }, { "&Iota;", "921" }, { "&Kappa;", "922" },
            { "&Lambda;", "923" }, { "&Mu;", "924" }, { "&Nu;", "925" }, { "&Xi;", "926" }, { "&Omicron;", "927" },
            { "&Pi;", "928" }, { "&Rho;", "929" },

            { "&Sigma;", "931" }, { "&Tau;", "932" }, { "&Upsilon;", "933" }, { "&Phi;", "934" }, { "&Chi;", "935" },
            { "&Psi;", "936" }, { "&Omega;", "937" }, { "&alpha;", "945" }, { "&beta;", "946" }, { "&gamma;", "947" },
            { "&delta;", "948" }, { "&epsilon;", "949" }, { "&zeta;", "950" }, { "&eta;", "951" },
            { "&theta;", "952" }, { "&iota;", "953" }, { "&kappa;", "954" }, { "&lambda;", "955" }, { "&mu;", "956" },
            { "&nu;", "957" }, { "&xi;", "958" }, { "&omicron;", "959" }, { "&pi;", "960" }, { "&rho;", "961" },
            { "&sigmaf;", "962" }, { "&sigma;", "963" }, { "&tau;", "964" }, { "&upsilon;", "965" },
            { "&phi;", "966" }, { "&chi;", "967" }, { "&psi;", "968" }, { "&omega;", "969" }, { "&thetasym;", "977" },
            { "&upsih;", "978" }, { "&piv;", "982" }, { "&bull;", "8226" }, { "&hellip;", "8230" },
            { "&prime;", "8242" }, { "&Prime;", "8243" }, { "&oline;", "8254" }, { "&frasl;", "8260" },
            { "&weierp;", "8472" }, { "&image;", "8465" }, { "&real;", "8476" }, { "&trade;", "8482" },
            { "&alefsym;", "8501" }, { "&larr;", "8592" }, { "&uarr;", "8593" }, { "&rarr;", "8594" },
            { "&darr;", "8595" }, { "&harr;", "8596" }, { "&crarr;", "8629" }, { "&lArr;", "8656" },
            { "&uArr;", "8657" }, { "&rArr;", "8658" }, { "&dArr;", "8659" }, { "&hArr;", "8660" },
            { "&forall;", "8704" }, { "&part;", "8706" }, { "&exist;", "8707" }, { "&empty;", "8709" },
            { "&nabla;", "8711" }, { "&isin;", "8712" }, { "&notin;", "8713" }, { "&ni;", "8715" },
            { "&prod;", "8719" }, { "&sum;", "8721" }, { "&minus;", "8722" }, { "&lowast;", "8727" },
            { "&radic;", "8730" }, { "&prop;", "8733" }, { "&infin;", "8734" }, { "&ang;", "8736" },
            { "&and;", "8743" }, { "&or;", "8744" }, { "&cap;", "8745" }, { "&cup;", "8746" }, { "&int;", "8747" },
            { "&there4;", "8756" }, { "&sim;", "8764" }, { "&cong;", "8773" }, { "&asymp;", "8776" },
            { "&ne;", "8800" }, { "&equiv;", "8801" }, { "&le;", "8804" }, { "&ge;", "8805" }, { "&sub;", "8834" },
            { "&sup;", "8835" }, { "&sube;", "8838" }, { "&supe;", "8839" }, { "&oplus;", "8853" },
            { "&otimes;", "8855" }, { "&perp;", "8869" }, { "&sdot;", "8901" }, { "&lceil;", "8968" },
            { "&rceil;", "8969" }, { "&lfloor;", "8970" }, { "&rfloor;", "8971" }, { "&lang;", "9001" },
            { "&rang;", "9002" }, { "&loz;", "9674" }, { "&spades;", "9824" }, { "&clubs;", "9827" },
            { "&hearts;", "9829" }, { "&diams;", "9830" }, { "&OElig;", "338" }, { "&oelig;", "339" },
            { "&Scaron;", "352" }, { "&scaron;", "353" }, { "&Yuml;", "376" }, { "&circ;", "710" },
            { "&tilde;", "732" }, { "&ensp;", "8194" }, { "&emsp;", "8195" }, { "&thinsp;", "8201" },
            { "&zwnj;", "8204" }, { "&zwj;", "8205" }, { "&lrm;", "8206" }, { "&rlm;", "8207" }, { "&ndash;", "8211" },
            { "&mdash;", "8212" }, { "&lsquo;", "8216" }, { "&rsquo;", "8217" }, { "&sbquo;", "8218" },
            { "&ldquo;", "8220" }, { "&rdquo;", "8221" }, { "&bdquo;", "8222" }, { "&dagger;", "8224" },
            { "&Dagger;", "8225" }, { "&permil;", "8240" }, { "&lsaquo;", "8249" }, { "&rsaquo;", "8250" },
            { "&euro;", "8364" },                   };
    public static final FastEntities XML;
    public static final FastEntities HTML40;

    static {
        XML = new FastEntities();
        XML.addEntities(BASIC_ARRAY);
        XML.addEntities(APOS_ARRAY);
    }

    static {
        HTML40 = new FastEntities();
        HTML40.addEntities(BASIC_ARRAY);
        HTML40.addEntities(ISO8859_1_ARRAY);
        HTML40.addEntities(HTML40_ARRAY);
    }

    public void addEntities(String[][] entityArray) {
        for (int i = 0; i < entityArray.length; ++i) {
            char c = (char) Integer.parseInt(entityArray[i][1]);
            addEntity(c, entityArray[i][0]);
            this.addTransferChar(c);
        }
    }

    /**
     * <pre>
     * ×öÁ˼¸¸öÓÐËٶȵĴ¦Àí, ÔںܶàÇé¿öÏ£¬¿ÉÄÜÌá¸ß£±£°±¶ÒÔÉϵÄÐÔÄÜ£¬¡¡ÒÔ¼°½ÚÔ¼²»ÉÙÄÚ´æÊ¹Óá£
     * 1.·µ»Ø½á¹û¸ù¾Ýʱºò·¢ÉúÁ˱àÂë½øÐиĽø£¬¡¡Èç¹ûûÓз¢Éú±àÂ룬ÄÇôֱ½Ó·µ»ØÔ­À´µÄstr.
     * 2.¿ìËÙ¼ì²éÊÇ·ñÒª½øÐÐתÂë
     * 3.ʹÓÿìËÙ²é±í£¬¡¡¶Ô0-255µÄ×Ö·û½øÐпìËÙ²éÕÒ
     * </pre>
     * 
     * @param str
     * @return
     */
    public String escape(String str) {
        if (str == null) {
            return null;
        }
        StringBuilder buffer = null;
        int len = str.length();
        char ch;
        char[] entityName;
        for (int i = 0; i < len; i++) {
            ch = str.charAt(i);
            entityName = getEntity(ch);
            if (entityName == null) {
                if (buffer != null) {
                    buffer.append(ch);
                }
            } else {
                if (buffer == null) {
                    buffer = new StringBuilder(str.length() << 1);
                    buffer.append(str, 0, i);
                }
                buffer.append(entityName);
            }
        }
        if (buffer != null) {
            return buffer.toString();
        } else {
            return str;
        }
    }

    public static String escapeJavaScript(String str) {

        if (str == null) {
            return null;
        }
        int length = str.length();
        Writer out = new StringWriter(length << 1);
        try {
            for (int i = 0; i < length; i++) {
                char ch = str.charAt(i);

                if (ch < 32) {
                    switch (ch) {
                        case '\b':

                            out.write('\\');

                            out.write('b');
                            break;

                        case '\n':
                            out.write('\\');
                            out.write('n');
                            break;

                        case '\t':
                            out.write('\\');
                            out.write('t');
                            break;

                        case '\f':
                            out.write('\\');
                            out.write('f');
                            break;

                        case '\r':
                            out.write('\\');
                            out.write('r');
                            break;

                        default:
                            if (ch > 0xf) {
                                out.write("\\u00" + Integer.toHexString(ch).toUpperCase());
                            } else {
                                out.write("\\u000" + Integer.toHexString(ch).toUpperCase());
                            }

                            break;
                    }

                } else {
                    switch (ch) {
                        case '\'':
                            out.write('\\');
                            out.write('\'');
                            break;
                        case '"':
                            out.write('\\');
                            out.write('"');
                            break;
                        case '\\':
                            out.write('\\');
                            out.write('\\');
                            break;
                        default:
                            out.write(ch);
                            break;
                    }
                }
            }
        } catch (IOException e) {
            // impossible
        }
        return out.toString();
    }

}

class FastDetectChar {

    private Map<Character, char[]> map               = new HashMap<Character, char[]>();
    private int                    LOOKUP_TABLE_SIZE = 256;
    private char[][]               lookupTable       = new char[LOOKUP_TABLE_SIZE][];
    BitSet                         maskSet           = new BitSet(1 << 16);

    public void addEntity(char charValue, String entiryName) {
        if (charValue > -1 && charValue < LOOKUP_TABLE_SIZE) {
            lookupTable[charValue] = entiryName.toCharArray();
        }
        map.put(charValue, entiryName.toCharArray());
    }

    public char[] getEntity(char charValue) {
        if (charValue < LOOKUP_TABLE_SIZE) {
            return lookupTable[charValue];
        }
        return (maskSet.get(charValue) ? map.get(charValue) : null);
    }

    /**
     * Ôö¼ÓÒ»¸öÌøÔ½×Ö·û
     * 
     * @param c
     */
    public void addTransferChar(char c) {
        maskSet.set(c);
    }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to