package net.moznion.micro.escape;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * String escaper for HTML.
 */
public class HTMLEscaper {
    private static final char[][] REPLACEMENTS;
    private static final char REPLACEMENT_MAX;

    static {
        final Map<Character, String> replaceMap = new HashMap<>(8);
        replaceMap.put('&', "&amp;");
        replaceMap.put('>', "&gt;");
        replaceMap.put('<', "&lt;");
        replaceMap.put('"', "&quot;");
        replaceMap.put('\'', "&#39;");

        // For IE. IE interprets back-quote as valid quoting characters
        // ref: https://rt.cpan.org/Public/Bug/Display.html?id=84971
        replaceMap.put('`', "&#96;");

        // For javascript templates (e.g. AngularJS and such javascript frameworks)
        // ref: https://github.com/angular/angular.js/issues/5601
        replaceMap.put('{', "&#123;");
        replaceMap.put('}', "&#125;");

        // convert to array from map
        REPLACEMENT_MAX = Collections.max(replaceMap.keySet());
        REPLACEMENTS = new char[REPLACEMENT_MAX + 1][];
        for (char c : replaceMap.keySet()) {
            REPLACEMENTS[c] = replaceMap.get(c).toCharArray();
        }
    }

    /**
     * Escape string for HTML.
     *
     * @param rawString raw string. If you give null, this method returns empty string.
     * @return escaped string.
     */
    public static String escape(final String rawString) {
        final char[][] replacements = REPLACEMENTS;
        final char replacementMax = REPLACEMENT_MAX;

        if (rawString == null) {
            return null;
        }

        final char[] rawChars = rawString.toCharArray();
        final int length = rawChars.length;

        char c;
        for (int i = 0; i < length; i++) {
            c = rawChars[i];
            if (c <= replacementMax && replacements[c] != null) {
                return _escape(rawChars, length, i);
            }
        }

        // No replacement target characters. Return raw argument.
        return rawString;
    }

    private static String _escape(final char[] chars, final int strlen, int cursor) {
        final char[][] replacements = REPLACEMENTS;
        final char replacementMax = REPLACEMENT_MAX;

        // 6 (== "&quot;".length()) is a magic coefficient for maximum length.
        final char[] buf = new char[strlen * 6];

        int beginCursor = 0;
        int bufIndex = 0;

        char c;
        char[] replacedCharacters;
        for (; cursor < strlen; cursor++) {
            c = chars[cursor];
            if (c <= replacementMax && (replacedCharacters = replacements[c]) != null) {
                final int lenOfReplaced = replacedCharacters.length;
                final int lenOfCopied = cursor - beginCursor;

                System.arraycopy(chars, beginCursor, buf, bufIndex, lenOfCopied);

                bufIndex += lenOfCopied;
                beginCursor = cursor + 1;

                System.arraycopy(replacedCharacters, 0, buf, bufIndex, lenOfReplaced);
                bufIndex += lenOfReplaced;
            }
        }

        if (beginCursor < cursor) {
            System.arraycopy(chars, beginCursor, buf, bufIndex, cursor - beginCursor);
            bufIndex += (cursor - beginCursor);
        }

        return new String(buf, 0, bufIndex);
    }
}

@@ -1639,65 +1639,8 @@
  %7D%0A%0A
-        final char%5B%5D rawChars = rawString.toCharArray();%0A
     
@@ -1669,20 +1669,23 @@
  raw
-Chars
+String
 .length
+()
 ;%0A%0A 
@@ -1765,16 +1765,24 @@
  raw
-Chars%5Bi%5D
+String.charAt(i)
 ;%0A  
@@ -1883,13 +1883,14 @@
 (raw
-Chars
+String
 , le
@@ -2067,20 +2067,24 @@
 nal 
-char%5B%5D chars
+String rawString
 , fi
@@ -2091,22 +2091,22 @@
 nal int 
-str
 len
+gth
 , int cu
@@ -2338,18 +2338,20 @@
 har%5B
-strlen * 6
+length + 100
 %5D;%0A%0A
@@ -2485,14 +2485,14 @@
 r %3C 
-str
 len
+gth
 ; cu
@@ -2521,21 +2521,32 @@
 c = 
+rawString.
 char
-s%5B
+At(
 cursor
-%5D
+)
 ;%0A  
@@ -2784,32 +2784,27 @@
     
-System.arraycopy(c
+rawString.getC
 hars
-, 
+(
 begi
@@ -2803,32 +2803,40 @@
 ars(beginCursor,
+ cursor,
  buf, bufIndex, 
@@ -2837,21 +2837,8 @@
 ndex
-, lenOfCopied
 );%0A%0A
@@ -3128,32 +3128,27 @@
     
-System.arraycopy(c
+rawString.getC
 hars
-, 
+(
 begi
@@ -3151,24 +3151,32 @@
 beginCursor,
+ cursor,
  buf, bufInd
@@ -3181,30 +3181,8 @@
 ndex
-, cursor - beginCursor
 );%0A 

Use string