18b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira/*
28b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * Copyright (C) 2006 Google Inc.
38b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira *
48b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
58b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * you may not use this file except in compliance with the License.
68b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * You may obtain a copy of the License at
78b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira *
88b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * http://www.apache.org/licenses/LICENSE-2.0
98b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira *
108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * Unless required by applicable law or agreed to in writing, software
118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * See the License for the specific language governing permissions and
148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * limitations under the License.
158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira */
1630e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangpackage com.android.mail.lib.base;
178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1830e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport static com.android.mail.lib.base.Preconditions.checkNotNull;
198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport java.io.IOException;
218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira/**
238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * Utility functions for dealing with {@code CharEscaper}s, and some commonly
248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * used {@code CharEscaper} instances.
258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira *
268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * @author sven@google.com (Sven Mawson)
278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira * @author laurence@google.com (Laurence Gonsalves)
288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira */
298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereirapublic final class CharEscapers {
308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private CharEscapers() {}
318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // TODO(matevossian): To implementors of escapers --
338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  //                    For each xxxEscaper method, please add links to external
348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  //                    reference pages that we consider authoritative for what
358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  //                    that escaper should exactly be doing.
368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Performs no escaping.
398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper NULL_ESCAPER = new CharEscaper() {
418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      @Override
428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    public String escape(String string) {
438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        checkNotNull(string);
448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return string;
458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      @Override
488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      public Appendable escape(final Appendable out) {
498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        checkNotNull(out);
508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        // we can't simply return out because the CharEscaper contract says that
528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        // the returned Appendable will throw a NullPointerException if asked to
538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        // append null.
548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return new Appendable() {
558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            @Override public Appendable append(CharSequence csq) throws IOException {
568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              checkNotNull(csq);
578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              out.append(csq);
588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              return this;
598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            }
608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            @Override public Appendable append(CharSequence csq, int start, int end)
628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira                throws IOException {
638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              checkNotNull(csq);
648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              out.append(csq, start, end);
658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              return this;
668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            }
678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            @Override public Appendable append(char c) throws IOException {
698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              out.append(c);
708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              return this;
718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            }
728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          };
738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      @Override
768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      protected char[] escape(char c) {
778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return null;
788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    };
808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} that does no escaping.
838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper nullEscaper() {
858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return NULL_ESCAPER;
868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters in a
908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string so it can safely be included in an XML document in either element
918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * content or attribute values.
928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b></p>: silently removes null-characters and control
948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * characters, as there is no way to represent them in XML.
958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper xmlEscaper() {
978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return XML_ESCAPER;
988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
1018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes special characters from a string so it can safely be included in an
1028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * XML document in either element content or attribute values.  Also removes
1038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * null-characters and control characters, as there is no way to represent
1048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * them in XML.
1058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
1068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper XML_ESCAPER = newBasicXmlEscapeBuilder()
1078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('"', "&quot;")
1088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\'', "&apos;")
1098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .toEscaper();
1108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
1128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters in a
1138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string so it can safely be included in an XML document in element content.
1148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
1158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b></p>: double and single quotes are not escaped, so it is not
1168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * safe to use this escaper to escape attribute values. Use the
1178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@link #xmlEscaper()} escaper to escape attribute values or if you are
1188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * unsure. Also silently removes non-whitespace control characters, as there
1198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * is no way to represent them in XML.
1208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
1218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper xmlContentEscaper() {
1228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return XML_CONTENT_ESCAPER;
1238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
1248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
1268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes special characters from a string so it can safely be included in an
1278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * XML document in element content.  Note that quotes are <em>not</em>
1288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * escaped, so <em>this is not safe for use in attribute values</em>. Use
1298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@link #XML_ESCAPER} for attribute values, or if you are unsure.  Also
1308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * removes non-whitespace control characters, as there is no way to represent
1318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * them in XML.
1328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
1338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper XML_CONTENT_ESCAPER =
1348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      newBasicXmlEscapeBuilder().toEscaper();
1358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
1378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters in a
1388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string so it can safely be included in an HTML document in either element
1398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * content or attribute values.
1408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
1418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b></p>: alters non-ASCII and control characters.
1428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
1438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * The entity list was taken from:
1448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <a href="http://www.w3.org/TR/html4/sgml/entities.html">here</a>
1458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
1468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper htmlEscaper() {
1478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return HtmlEscaperHolder.HTML_ESCAPER;
1488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
1498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
1518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * A lazy initialization holder for HTML_ESCAPER.
1528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
1538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static class HtmlEscaperHolder {
1548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    private static final CharEscaper HTML_ESCAPER
1558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        = new HtmlCharEscaper(new CharEscaperBuilder()
1568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('"',      "&quot;")
1578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\'',     "&#39;")
1588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('&',      "&amp;")
1598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('<',      "&lt;")
1608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('>',      "&gt;")
1618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A0', "&nbsp;")
1628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A1', "&iexcl;")
1638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A2', "&cent;")
1648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A3', "&pound;")
1658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A4', "&curren;")
1668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A5', "&yen;")
1678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A6', "&brvbar;")
1688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A7', "&sect;")
1698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A8', "&uml;")
1708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00A9', "&copy;")
1718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00AA', "&ordf;")
1728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00AB', "&laquo;")
1738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00AC', "&not;")
1748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00AD', "&shy;")
1758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00AE', "&reg;")
1768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00AF', "&macr;")
1778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B0', "&deg;")
1788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B1', "&plusmn;")
1798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B2', "&sup2;")
1808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B3', "&sup3;")
1818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B4', "&acute;")
1828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B5', "&micro;")
1838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B6', "&para;")
1848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B7', "&middot;")
1858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B8', "&cedil;")
1868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00B9', "&sup1;")
1878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00BA', "&ordm;")
1888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00BB', "&raquo;")
1898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00BC', "&frac14;")
1908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00BD', "&frac12;")
1918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00BE', "&frac34;")
1928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00BF', "&iquest;")
1938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C0', "&Agrave;")
1948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C1', "&Aacute;")
1958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C2', "&Acirc;")
1968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C3', "&Atilde;")
1978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C4', "&Auml;")
1988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C5', "&Aring;")
1998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C6', "&AElig;")
2008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C7', "&Ccedil;")
2018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C8', "&Egrave;")
2028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00C9', "&Eacute;")
2038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00CA', "&Ecirc;")
2048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00CB', "&Euml;")
2058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00CC', "&Igrave;")
2068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00CD', "&Iacute;")
2078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00CE', "&Icirc;")
2088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00CF', "&Iuml;")
2098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D0', "&ETH;")
2108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D1', "&Ntilde;")
2118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D2', "&Ograve;")
2128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D3', "&Oacute;")
2138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D4', "&Ocirc;")
2148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D5', "&Otilde;")
2158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D6', "&Ouml;")
2168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D7', "&times;")
2178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D8', "&Oslash;")
2188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00D9', "&Ugrave;")
2198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00DA', "&Uacute;")
2208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00DB', "&Ucirc;")
2218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00DC', "&Uuml;")
2228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00DD', "&Yacute;")
2238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00DE', "&THORN;")
2248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00DF', "&szlig;")
2258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E0', "&agrave;")
2268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E1', "&aacute;")
2278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E2', "&acirc;")
2288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E3', "&atilde;")
2298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E4', "&auml;")
2308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E5', "&aring;")
2318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E6', "&aelig;")
2328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E7', "&ccedil;")
2338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E8', "&egrave;")
2348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00E9', "&eacute;")
2358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00EA', "&ecirc;")
2368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00EB', "&euml;")
2378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00EC', "&igrave;")
2388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00ED', "&iacute;")
2398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00EE', "&icirc;")
2408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00EF', "&iuml;")
2418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F0', "&eth;")
2428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F1', "&ntilde;")
2438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F2', "&ograve;")
2448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F3', "&oacute;")
2458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F4', "&ocirc;")
2468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F5', "&otilde;")
2478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F6', "&ouml;")
2488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F7', "&divide;")
2498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F8', "&oslash;")
2508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00F9', "&ugrave;")
2518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00FA', "&uacute;")
2528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00FB', "&ucirc;")
2538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00FC', "&uuml;")
2548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00FD', "&yacute;")
2558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00FE', "&thorn;")
2568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u00FF', "&yuml;")
2578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0152', "&OElig;")
2588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0153', "&oelig;")
2598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0160', "&Scaron;")
2608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0161', "&scaron;")
2618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0178', "&Yuml;")
2628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0192', "&fnof;")
2638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u02C6', "&circ;")
2648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u02DC', "&tilde;")
2658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0391', "&Alpha;")
2668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0392', "&Beta;")
2678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0393', "&Gamma;")
2688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0394', "&Delta;")
2698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0395', "&Epsilon;")
2708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0396', "&Zeta;")
2718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0397', "&Eta;")
2728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0398', "&Theta;")
2738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u0399', "&Iota;")
2748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u039A', "&Kappa;")
2758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u039B', "&Lambda;")
2768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u039C', "&Mu;")
2778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u039D', "&Nu;")
2788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u039E', "&Xi;")
2798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u039F', "&Omicron;")
2808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A0', "&Pi;")
2818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A1', "&Rho;")
2828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A3', "&Sigma;")
2838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A4', "&Tau;")
2848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A5', "&Upsilon;")
2858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A6', "&Phi;")
2868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A7', "&Chi;")
2878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A8', "&Psi;")
2888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03A9', "&Omega;")
2898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B1', "&alpha;")
2908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B2', "&beta;")
2918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B3', "&gamma;")
2928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B4', "&delta;")
2938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B5', "&epsilon;")
2948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B6', "&zeta;")
2958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B7', "&eta;")
2968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B8', "&theta;")
2978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03B9', "&iota;")
2988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03BA', "&kappa;")
2998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03BB', "&lambda;")
3008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03BC', "&mu;")
3018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03BD', "&nu;")
3028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03BE', "&xi;")
3038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03BF', "&omicron;")
3048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C0', "&pi;")
3058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C1', "&rho;")
3068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C2', "&sigmaf;")
3078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C3', "&sigma;")
3088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C4', "&tau;")
3098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C5', "&upsilon;")
3108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C6', "&phi;")
3118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C7', "&chi;")
3128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C8', "&psi;")
3138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03C9', "&omega;")
3148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03D1', "&thetasym;")
3158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03D2', "&upsih;")
3168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u03D6', "&piv;")
3178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2002', "&ensp;")
3188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2003', "&emsp;")
3198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2009', "&thinsp;")
3208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u200C', "&zwnj;")
3218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u200D', "&zwj;")
3228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u200E', "&lrm;")
3238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u200F', "&rlm;")
3248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2013', "&ndash;")
3258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2014', "&mdash;")
3268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2018', "&lsquo;")
3278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2019', "&rsquo;")
3288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u201A', "&sbquo;")
3298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u201C', "&ldquo;")
3308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u201D', "&rdquo;")
3318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u201E', "&bdquo;")
3328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2020', "&dagger;")
3338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2021', "&Dagger;")
3348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2022', "&bull;")
3358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2026', "&hellip;")
3368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2030', "&permil;")
3378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2032', "&prime;")
3388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2033', "&Prime;")
3398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2039', "&lsaquo;")
3408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u203A', "&rsaquo;")
3418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u203E', "&oline;")
3428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2044', "&frasl;")
3438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u20AC', "&euro;")
3448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2111', "&image;")
3458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2118', "&weierp;")
3468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u211C', "&real;")
3478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2122', "&trade;")
3488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2135', "&alefsym;")
3498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2190', "&larr;")
3508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2191', "&uarr;")
3518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2192', "&rarr;")
3528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2193', "&darr;")
3538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2194', "&harr;")
3548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u21B5', "&crarr;")
3558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u21D0', "&lArr;")
3568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u21D1', "&uArr;")
3578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u21D2', "&rArr;")
3588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u21D3', "&dArr;")
3598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u21D4', "&hArr;")
3608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2200', "&forall;")
3618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2202', "&part;")
3628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2203', "&exist;")
3638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2205', "&empty;")
3648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2207', "&nabla;")
3658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2208', "&isin;")
3668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2209', "&notin;")
3678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u220B', "&ni;")
3688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u220F', "&prod;")
3698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2211', "&sum;")
3708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2212', "&minus;")
3718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2217', "&lowast;")
3728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u221A', "&radic;")
3738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u221D', "&prop;")
3748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u221E', "&infin;")
3758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2220', "&ang;")
3768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2227', "&and;")
3778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2228', "&or;")
3788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2229', "&cap;")
3798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u222A', "&cup;")
3808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u222B', "&int;")
3818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2234', "&there4;")
3828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u223C', "&sim;")
3838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2245', "&cong;")
3848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2248', "&asymp;")
3858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2260', "&ne;")
3868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2261', "&equiv;")
3878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2264', "&le;")
3888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2265', "&ge;")
3898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2282', "&sub;")
3908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2283', "&sup;")
3918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2284', "&nsub;")
3928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2286', "&sube;")
3938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2287', "&supe;")
3948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2295', "&oplus;")
3958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2297', "&otimes;")
3968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u22A5', "&perp;")
3978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u22C5', "&sdot;")
3988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2308', "&lceil;")
3998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2309', "&rceil;")
4008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u230A', "&lfloor;")
4018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u230B', "&rfloor;")
4028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2329', "&lang;")
4038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u232A', "&rang;")
4048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u25CA', "&loz;")
4058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2660', "&spades;")
4068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2663', "&clubs;")
4078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2665', "&hearts;")
4088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .addEscape('\u2666', "&diams;")
4098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            .toArray());
4108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
4118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
4128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
4138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters in a
4148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string so it can safely be included in an HTML document in either element
4158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * content or attribute values.
4168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b></p>: does not alter non-ASCII and control characters.
4188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
4198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper asciiHtmlEscaper() {
4208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return ASCII_HTML_ESCAPER;
4218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
4228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
4238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
4248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes special characters from a string so it can safely be included in an
4258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * HTML document in either element content or attribute values. Does
4268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <em>not</em> alter non-ASCII characters or control characters.
4278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
4288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper ASCII_HTML_ESCAPER = new CharEscaperBuilder()
4298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('"', "&quot;")
4308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\'', "&#39;")
4318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('&', "&amp;")
4328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('<', "&lt;")
4338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('>', "&gt;")
4348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .toEscaper();
4358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
4368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
4378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns an {@link Escaper} instance that escapes Java chars so they can be
4388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * safely included in URIs. For details on escaping URIs, see section 2.4 of
4398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
4408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>When encoding a String, the following rules apply:
4428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
4438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
4448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     through "9" remain the same.
4458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The special characters ".", "-", "*", and "_" remain the same.
4468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The space character " " is converted into a plus sign "+".
4478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>All other characters are converted into one or more bytes using UTF-8
4488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     encoding and each byte is then represented by the 3-character string
4498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     "%XY", where "XY" is the two-digit, uppercase, hexadecimal
4508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     representation of the byte value.
4518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
4528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase
4548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * hexadecimal sequences. From <a href="http://www.ietf.org/rfc/rfc3986.txt">
4558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 3986</a>:<br>
4568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <i>"URI producers and normalizers should use uppercase hexadecimal digits
4578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * for all percent-encodings."</i>
4588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>This escaper has identical behavior to (but is potentially much faster
4608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * than):
4618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
4628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>{@link com.google.httputil.FastURLEncoder#encode(String)}
4638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>{@link com.google.httputil.FastURLEncoder#encode(String,String)}
4648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     with the encoding name "UTF-8"
4658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>{@link java.net.URLEncoder#encode(String, String)}
4668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     with the encoding name "UTF-8"
4678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * </ul>
4688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>This method is equivalent to {@code uriEscaper(true)}.
4708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
4718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static Escaper uriEscaper() {
4728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return uriEscaper(true);
4738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
4748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
4758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
4768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns an {@link Escaper} instance that escapes Java chars so they can be
4778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * safely included in URI path segments. For details on escaping URIs, see
4788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * section 2.4 of <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
4798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>When encoding a String, the following rules apply:
4818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
4828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
4838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     through "9" remain the same.
4848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The unreserved characters ".", "-", "~", and "_" remain the same.
4858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The general delimiters "@" and ":" remain the same.
4868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The subdelimiters "!", "$", "&amp;", "'", "(", ")", "*", ",", ";",
4878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     and "=" remain the same.
4888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The space character " " is converted into %20.
4898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>All other characters are converted into one or more bytes using UTF-8
4908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     encoding and each byte is then represented by the 3-character string
4918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     "%XY", where "XY" is the two-digit, uppercase, hexadecimal
4928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     representation of the byte value.
4938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * </ul>
4948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
4958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase
4968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * hexadecimal sequences. From <a href="http://www.ietf.org/rfc/rfc3986.txt">
4978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 3986</a>:<br>
4988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <i>"URI producers and normalizers should use uppercase hexadecimal digits
4998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * for all percent-encodings."</i>
5008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
5018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static Escaper uriPathEscaper() {
5028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return URI_PATH_ESCAPER;
5038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
5048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
5058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
5068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns an {@link Escaper} instance that escapes Java chars so they can be
5078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * safely included in URI query string segments. When the query string
5088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * consists of a sequence of name=value pairs separated by &amp;, the names
5098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * and values should be individually encoded. If you escape an entire query
5108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string in one pass with this escaper, then the "=" and "&amp;" characters
5118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * used as separators will also be escaped.
5128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>This escaper is also suitable for escaping fragment identifiers.
5148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>For details on escaping URIs, see
5168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * section 2.4 of <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
5178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>When encoding a String, the following rules apply:
5198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
5208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
5218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     through "9" remain the same.
5228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The unreserved characters ".", "-", "~", and "_" remain the same.
5238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The general delimiters "@" and ":" remain the same.
5248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The path delimiters "/" and "?" remain the same.
5258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The subdelimiters "!", "$", "'", "(", ")", "*", ",", and ";",
5268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     remain the same.
5278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The space character " " is converted into %20.
5288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The equals sign "=" is converted into %3D.
5298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The ampersand "&amp;" is converted into %26.
5308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>All other characters are converted into one or more bytes using UTF-8
5318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     encoding and each byte is then represented by the 3-character string
5328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     "%XY", where "XY" is the two-digit, uppercase, hexadecimal
5338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     representation of the byte value.
5348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * </ul>
5358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase
5378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * hexadecimal sequences. From <a href="http://www.ietf.org/rfc/rfc3986.txt">
5388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 3986</a>:<br>
5398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <i>"URI producers and normalizers should use uppercase hexadecimal digits
5408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * for all percent-encodings."</i>
5418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>This method is equivalent to {@code uriQueryStringEscaper(false)}.
5438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
5448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static Escaper uriQueryStringEscaper() {
5458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return uriQueryStringEscaper(false);
5468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
5478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
5488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
5498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link Escaper} instance that escapes Java characters so they can
5508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * be safely included in URIs. For details on escaping URIs, see section 2.4
5518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
5528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>When encoding a String, the following rules apply:
5548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
5558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
5568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     through "9" remain the same.
5578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The special characters ".", "-", "*", and "_" remain the same.
5588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>If {@code plusForSpace} was specified, the space character " " is
5598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     converted into a plus sign "+". Otherwise it is converted into "%20".
5608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>All other characters are converted into one or more bytes using UTF-8
5618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     encoding and each byte is then represented by the 3-character string
5628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     "%XY", where "XY" is the two-digit, uppercase, hexadecimal
5638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     representation of the byte value.
5648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * </ul>
5658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase
5678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * hexadecimal sequences. From <a href="http://www.ietf.org/rfc/rfc3986.txt">
5688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 3986</a>:<br>
5698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <i>"URI producers and normalizers should use uppercase hexadecimal digits
5708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * for all percent-encodings."</i>
5718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @param plusForSpace if {@code true} space is escaped to {@code +} otherwise
5738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        it is escaped to {@code %20}. Although common, the escaping of
5748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        spaces as plus signs has a very ambiguous status in the relevant
5758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        specifications. You should prefer {@code %20} unless you are doing
5768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        exact character-by-character comparisons of URLs and backwards
5778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        compatibility requires you to use plus signs.
5788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @see #uriEscaper()
5808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
5818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static Escaper uriEscaper(boolean plusForSpace) {
5828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return plusForSpace ? URI_ESCAPER : URI_ESCAPER_NO_PLUS;
5838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
5848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
5858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
5868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns an {@link Escaper} instance that escapes Java chars so they can be
5878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * safely included in URI query string segments. When the query string
5888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * consists of a sequence of name=value pairs separated by &amp;, the names
5898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * and values should be individually encoded. If you escape an entire query
5908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string in one pass with this escaper, then the "=" and "&amp;" characters
5918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * used as separators will also be escaped.
5928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>This escaper is also suitable for escaping fragment identifiers.
5948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>For details on escaping URIs, see
5968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * section 2.4 of <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
5978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
5988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>When encoding a String, the following rules apply:
5998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
6008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
6018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     through "9" remain the same.
6028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The unreserved characters ".", "-", "~", and "_" remain the same.
6038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The general delimiters "@" and ":" remain the same.
6048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The path delimiters "/" and "?" remain the same.
6058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The subdelimiters "!", "$", "'", "(", ")", "*", ",", and ";",
6068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     remain the same.
6078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>If {@code plusForSpace} was specified, the space character " " is
6088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     converted into a plus sign "+". Otherwise it is converted into "%20".
6098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The equals sign "=" is converted into %3D.
6108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The ampersand "&amp;" is converted into %26.
6118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>All other characters are converted into one or more bytes using UTF-8
6128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     encoding and each byte is then represented by the 3-character string
6138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     "%XY", where "XY" is the two-digit, uppercase, hexadecimal
6148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     representation of the byte value.
6158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * </ul>
6168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase
6188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * hexadecimal sequences. From <a href="http://www.ietf.org/rfc/rfc3986.txt">
6198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 3986</a>:<br>
6208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <i>"URI producers and normalizers should use uppercase hexadecimal digits
6218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * for all percent-encodings."</i>
6228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @param plusForSpace if {@code true} space is escaped to {@code +} otherwise
6248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        it is escaped to {@code %20}. Although common, the escaping of
6258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        spaces as plus signs has a very ambiguous status in the relevant
6268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        specifications. You should prefer {@code %20} unless you are doing
6278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        exact character-by-character comparisons of URLs and backwards
6288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *        compatibility requires you to use plus signs.
6298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @see #uriQueryStringEscaper()
6318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
6328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static Escaper uriQueryStringEscaper(boolean plusForSpace) {
6338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return plusForSpace ?
6348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira           URI_QUERY_STRING_ESCAPER_WITH_PLUS : URI_QUERY_STRING_ESCAPER;
6358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
6368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final Escaper URI_ESCAPER =
6388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, true);
6398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final Escaper URI_ESCAPER_NO_PLUS =
6418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, false);
6428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final Escaper URI_PATH_ESCAPER =
6448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      new PercentEscaper(PercentEscaper.SAFEPATHCHARS_URLENCODER, false);
6458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final Escaper URI_QUERY_STRING_ESCAPER =
6478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER, false);
6488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final Escaper URI_QUERY_STRING_ESCAPER_WITH_PLUS =
6508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER, true);
6518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
6538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link Escaper} instance that escapes Java characters in a manner
6548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * compatible with the C++ webutil/url URL class (the {@code kGoogle1Escape}
6558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * set).
6568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>When encoding a String, the following rules apply:
6588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <ul>
6598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
6608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * through "9" remain the same.
6618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The special characters "!", "(", ")", "*", "-", ".", "_", "~", ",", "/"
6628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * and ":" remain the same.
6638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>The space character " " is converted into a plus sign "+".
6648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <li>All other characters are converted into one or more bytes using UTF-8
6658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     encoding and each byte is then represented by the 3-character string
6668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     "%XY", where "XY" is the two-digit, uppercase, hexadecimal
6678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     representation of the byte value.
6688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * </ul>
6698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase
6718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * hexadecimal sequences. From <a href="http://www.ietf.org/rfc/rfc3986.txt">
6728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 3986</a>:<br>
6738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <i>"URI producers and normalizers should use uppercase hexadecimal digits
6748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * for all percent-encodings."</i>
6758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b>: This escaper is a special case and is <em>not
6778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * compliant</em> with <a href="http://www.ietf.org/rfc/rfc2396.txt">
6788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * RFC 2396</a>. Specifically it will not escape "/", ":" and ",". This is
6798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * only provided for certain limited use cases and you should favor using
6808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@link #uriEscaper()} whenever possible.
6818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
6828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static Escaper cppUriEscaper() {
6838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return CPP_URI_ESCAPER;
6848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
6858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // Based on comments from FastURLEncoder:
6878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // These octets mimic the ones escaped by the C++ webutil/url URL class --
6888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // the kGoogle1Escape set.
6898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // To produce the same escaping as C++, use this set with the plusForSpace
6908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // option.
6918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  // WARNING: Contrary to RFC 2396 ",", "/" and ":" are listed as safe here.
6928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final Escaper CPP_URI_ESCAPER =
6938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      new PercentEscaper("!()*-._~,/:", true);
6948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
6968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters in a
6978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string so it can safely be included in a Java string literal.
6988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
6998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p><b>Note</b></p>: does not escape single quotes, so use the escaper
7008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * returned by {@link #javaCharEscaper()} if you are generating char
7018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * literals or if you are unsure.
7028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper javaStringEscaper() {
7048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return JAVA_STRING_ESCAPER;
7058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
7068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
7088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes special characters from a string so it can safely be included in a
7098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Java string literal. Does <em>not</em> escape single-quotes, so use
7108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * JAVA_CHAR_ESCAPE if you are generating char literals, or if you are unsure.
7118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
7128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>Note that non-ASCII characters will be octal or Unicode escaped.
7138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper JAVA_STRING_ESCAPER
7158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      = new JavaCharEscaper(new CharEscaperBuilder()
7168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\b', "\\b")
7178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\f', "\\f")
7188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\n', "\\n")
7198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\r', "\\r")
7208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\t', "\\t")
7218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\"', "\\\"")
7228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\\', "\\\\")
7238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .toArray());
7248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
7268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters in a
7278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * string so it can safely be included in a Java char or string literal. The
7288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * behavior of this escaper is the same as that of the
7298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@link #javaStringEscaper()}, except it also escapes single quotes.
7308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper javaCharEscaper() {
7328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return JAVA_CHAR_ESCAPER;
7338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
7348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
7368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes special characters from a string so it can safely be included in a
7378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Java char literal or string literal.
7388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
7398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>Note that non-ASCII characters will be octal or Unicode escaped.
7408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
7418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>This is the same as {@link #JAVA_STRING_ESCAPER}, except that it escapes
7428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * single quotes.
7438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper JAVA_CHAR_ESCAPER
7458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      = new JavaCharEscaper(new CharEscaperBuilder()
7468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\b', "\\b")
7478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\f', "\\f")
7488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\n', "\\n")
7498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\r', "\\r")
7508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\t', "\\t")
7518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\'', "\\'")
7528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\"', "\\\"")
7538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\\', "\\\\")
7548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .toArray());
7558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
7578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that replaces non-ASCII characters
7588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * in a string with their Unicode escape sequences ({@code \\uxxxx} where
7598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@code xxxx} is a hex number). Existing escape sequences won't be affected.
7608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper javaStringUnicodeEscaper() {
7628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return JAVA_STRING_UNICODE_ESCAPER;
7638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
7648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
7668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes each non-ASCII character in with its Unicode escape sequence
7678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@code \\uxxxx} where {@code xxxx} is a hex number. Existing escape
7688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * sequences won't be affected.
7698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper JAVA_STRING_UNICODE_ESCAPER
7718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      = new CharEscaper() {
7728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          @Override protected char[] escape(char c) {
7738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            if (c <= 127) {
7748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira              return null;
7758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            }
7768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            char[] r = new char[6];
7788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            r[5] = HEX_DIGITS[c & 15];
7798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            c >>>= 4;
7808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            r[4] = HEX_DIGITS[c & 15];
7818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            c >>>= 4;
7828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            r[3] = HEX_DIGITS[c & 15];
7838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            c >>>= 4;
7848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            r[2] = HEX_DIGITS[c & 15];
7858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            r[1] = 'u';
7868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            r[0] = '\\';
7878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            return r;
7888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          }
7898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        };
7908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
7918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
7928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes special characters from
7938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * a string so it can safely be included in a Python string literal. Does not
7948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * have any special handling for non-ASCII characters.
7958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
7968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper pythonEscaper() {
7978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return PYTHON_ESCAPER;
7988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
7998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
8018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escapes special characters in a string so it can safely be included in a
8028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Python string literal. Does not have any special handling for non-ASCII
8038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * characters.
8048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
8058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper PYTHON_ESCAPER = new CharEscaperBuilder()
8068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // TODO(laurence): perhaps this should escape non-ASCII characters?
8078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\n', "\\n")
8088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\r', "\\r")
8098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\t', "\\t")
8108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\\', "\\\\")
8118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\"', "\\\"")
8128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .addEscape('\'', "\\\'")
8138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      .toEscaper();
8148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
8168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a {@link CharEscaper} instance that escapes non-ASCII characters in
8178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * a string so it can safely be included in a Javascript string literal.
8188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Non-ASCII characters are replaced with their ASCII javascript escape
8198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * sequences (e.g., \\uhhhh or \xhh).
8208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
8218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper javascriptEscaper() {
8228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return JAVASCRIPT_ESCAPER;
8238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
8248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
8268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * {@code CharEscaper} to escape javascript strings. Turns all non-ASCII
8278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * characters into ASCII javascript escape sequences (e.g., \\uhhhh or \xhh).
8288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
8298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final CharEscaper JAVASCRIPT_ESCAPER
8308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      = new JavascriptCharEscaper(new CharEscaperBuilder()
8318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\'', "\\x27")
8328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('"',  "\\x22")
8338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('<',  "\\x3c")
8348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('=',  "\\x3d")
8358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('>',  "\\x3e")
8368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('&',  "\\x26")
8378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\b', "\\b")
8388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\t', "\\t")
8398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\n', "\\n")
8408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\f', "\\f")
8418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\r', "\\r")
8428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .addEscape('\\', "\\\\")
8438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          .toArray());
8448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static CharEscaperBuilder newBasicXmlEscapeBuilder() {
8468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return new CharEscaperBuilder()
8478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        .addEscape('&', "&amp;")
8488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        .addEscape('<', "&lt;")
8498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        .addEscape('>', "&gt;")
8508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        .addEscapes(new char[] {
8518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            '\000', '\001', '\002', '\003', '\004',
8528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            '\005', '\006', '\007', '\010', '\013',
8538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            '\014', '\016', '\017', '\020', '\021',
8548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            '\022', '\023', '\024', '\025', '\026',
8558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            '\027', '\030', '\031', '\032', '\033',
8568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            '\034', '\035', '\036', '\037'}, "");
8578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
8588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
8608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Returns a composite {@link CharEscaper} instance that tries to escape
8618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * characters using a primary {@code CharEscaper} first and falls back to a
8628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * secondary one if there is no escaping.
8638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
8648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * <p>The returned escaper will attempt to escape each character using the
8658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * primary escaper, and if the primary escaper has no escaping for that
8668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * character, it will use the secondary escaper. If the secondary escaper has
8678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * no escaping for a character either, the original character will be used.
8688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * If the primary escaper has an escape for a character, the secondary escaper
8698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * will not be used at all for that character; the escaped output of the
8708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * primary is not run through the secondary. For a case where you would like
8718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * to first escape with one escaper, and then with another, it is recommended
8728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * that you call each escaper in order.
8738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *
8748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @param primary The primary {@code CharEscaper} to use
8758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @param secondary The secondary {@code CharEscaper} to use if the first one
8768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   *     has no escaping rule for a character
8778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * @throws NullPointerException if any of the arguments is null
8788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
8798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  public static CharEscaper fallThrough(CharEscaper primary,
8808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      CharEscaper secondary) {
8818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    checkNotNull(primary);
8828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    checkNotNull(secondary);
8838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    return new FallThroughCharEscaper(primary, secondary);
8848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
8858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
8878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * A fast {@link CharEscaper} that uses an array of replacement characters and
8888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * a range of safe characters. It overrides {@link #escape(String)} to improve
8898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * performance. Rough benchmarking shows that this almost doubles the speed
8908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * when processing strings that do not require escaping (providing the escape
8918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * test itself is efficient).
8928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
8938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static abstract class FastCharEscaper extends CharEscaper {
8948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
8958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    protected final char[][] replacements;
8968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    protected final int replacementLength;
8978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    protected final char safeMin;
8988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    protected final char safeMax;
8998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    public FastCharEscaper(char[][] replacements, char safeMin, char safeMax) {
9018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      this.replacements = replacements;
9028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      this.replacementLength = replacements.length;
9038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      this.safeMin = safeMin;
9048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      this.safeMax = safeMax;
9058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
9068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    /** Overridden for performance (see {@link FastCharEscaper}). */
9088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    @Override public String escape(String s) {
9098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      int slen = s.length();
9108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      for (int index = 0; index < slen; index++) {
9118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        char c = s.charAt(index);
9128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        if ((c < replacementLength && replacements[c] != null)
9138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira            || c < safeMin || c > safeMax) {
9148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          return escapeSlow(s, index);
9158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        }
9168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
9178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      return s;
9188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
9198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
9208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
9228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escaper for Java character escaping, contains both an array and a
9238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * backup function.  We're not overriding the array decorator because we
9248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * want to keep this as fast as possible, so no calls to super.escape first.
9258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
9268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static class JavaCharEscaper extends FastCharEscaper {
9278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    public JavaCharEscaper(char[][] replacements) {
9298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      super(replacements, ' ', '~');
9308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
9318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    @Override protected char[] escape(char c) {
9338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // First check if our array has a valid escaping.
9348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c < replacementLength) {
9358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        char[] r = replacements[c];
9368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        if (r != null) {
9378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          return r;
9388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        }
9398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
9408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // This range is un-escaped.
9428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (safeMin <= c && c <= safeMax) {
9438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return null;
9448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
9458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c <= 0xFF) {
9478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        // Convert c to an octal-escaped string.
9488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        // Equivalent to String.format("\\%03o", (int)c);
9498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        char[] r = new char[4];
9508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[0] = '\\';
9518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[3] = HEX_DIGITS[c & 7];
9528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        c >>>= 3;
9538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[2] = HEX_DIGITS[c & 7];
9548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        c >>>= 3;
9558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[1] = HEX_DIGITS[c & 7];
9568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return r;
9578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
9588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // Convert c to a hex-escaped string.
9608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // Equivalent to String.format("\\u%04x", (int)c);
9618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      char[] r = new char[6];
9628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[0] = '\\';
9638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[1] = 'u';
9648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[5] = HEX_DIGITS[c & 15];
9658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      c >>>= 4;
9668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[4] = HEX_DIGITS[c & 15];
9678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      c >>>= 4;
9688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[3] = HEX_DIGITS[c & 15];
9698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      c >>>= 4;
9708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[2] = HEX_DIGITS[c & 15];
9718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      return r;
9728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
9738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
9748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
9768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escaper for javascript character escaping, contains both an array and a
9778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * backup function. We're not overriding the array decorator because we
9788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * want to keep this as fast as possible, so no calls to super.escape first.
9798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
9808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static class JavascriptCharEscaper extends FastCharEscaper {
9818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    public JavascriptCharEscaper(char[][] replacements) {
9838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      super(replacements, ' ', '~');
9848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
9858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    @Override protected char[] escape(char c) {
9878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // First check if our array has a valid escaping.
9888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c < replacementLength) {
9898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        char[] r = replacements[c];
9908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        if (r != null) {
9918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          return r;
9928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        }
9938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
9948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
9958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // This range is unescaped.
9968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (safeMin <= c && c <= safeMax) {
9978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return null;
9988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
9998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // we can do a 2 digit hex escape for chars less that 0x100
10018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c < 0x100) {
10028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        char[] r = new char[4];
10038b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[3] = HEX_DIGITS[c & 0xf];
10048b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        c >>>= 4;
10058b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[2] = HEX_DIGITS[c & 0xf];
10068b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[1] = 'x';
10078b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        r[0] = '\\';
10088b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return r;
10098b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
10108b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10118b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // 4 digit hex escape everything else
10128b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      char[] r = new char[6];
10138b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[5] = HEX_DIGITS[c & 0xf];
10148b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      c >>>= 4;
10158b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[4] = HEX_DIGITS[c & 0xf];
10168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      c >>>= 4;
10178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[3] = HEX_DIGITS[c & 0xf];
10188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      c >>>= 4;
10198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[2] = HEX_DIGITS[c & 0xf];
10208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[1] = 'u';
10218b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      r[0] = '\\';
10228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      return r;
10238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
10248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
10258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
10278b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * Escaper for HTML character escaping, contains both an array and a
10288b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * backup function.  We're not overriding the array decorator because we
10298b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * want to keep this as fast as possible, so no calls to super.escape first.
10308b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
10318b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static class HtmlCharEscaper extends FastCharEscaper {
10328b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10338b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    public HtmlCharEscaper(char[][] replacements) {
10348b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      super(replacements, Character.MIN_VALUE, '~');
10358b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
10368b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10378b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    @Override protected char[] escape(char c) {
10388b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // First check if our array has a valid escaping.
10398b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c < replacementLength) {
10408b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        char[] r = replacements[c];
10418b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        if (r != null) {
10428b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira          return r;
10438b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        }
10448b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
10458b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // ~ is ASCII 126, the highest value char that does not need
10478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // to be escaped
10488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c <= safeMax) {
10498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        return null;
10508b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
10518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      int index;
10538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (c < 1000) {
10548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        index = 4;
10558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      } else if (c < 10000) {
10568b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        index = 5;
10578b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      } else {
10588b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        index = 6;
10598b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
10608b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      char[] result = new char[index + 2];
10618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      result[0] = '&';
10628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      result[1] = '#';
10638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      result[index + 1] = ';';
10648b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10658b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // TODO(sven): Convert this to a sequence of shifts/additions
10668b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      // to avoid the division and modulo operators.
10678b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      int intValue = c;
10688b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      for (; index > 1; index--) {
10698b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        result[index] = HEX_DIGITS[intValue % 10];
10708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        intValue /= 10;
10718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
10728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      return result;
10738b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
10748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
10758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  /**
10778b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * A composite {@code CharEscaper} object that tries to escape characters
10788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * using a primary {@code CharEscaper} first and falls back to a secondary
10798b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   * one if there is no escaping.
10808b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira   */
10818b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static class FallThroughCharEscaper extends CharEscaper {
10828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10838b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    private final CharEscaper primary;
10848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    private final CharEscaper secondary;
10858b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10868b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    public FallThroughCharEscaper(CharEscaper primary, CharEscaper secondary) {
10878b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      this.primary = primary;
10888b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      this.secondary = secondary;
10898b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
10908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
10918b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    @Override
10928b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    protected char[] escape(char c) {
10938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      char result[] = primary.escape(c);
10948b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      if (result == null) {
10958b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        result = secondary.escape(c);
10968b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      }
10978b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira      return result;
10988b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira    }
10998b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  }
11008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
11018b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira  private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
11028b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira}