--- /dev/null
+package org.simantics.scl.compiler.markdown.internal;
+
+import gnu.trove.map.hash.TCharObjectHashMap;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+
+public class HtmlEscape {
+
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+
+ private static final TCharObjectHashMap<String> ESCAPED_CHARS = new TCharObjectHashMap<String>();
+ static {
+ ESCAPED_CHARS.put('<', "<");
+ ESCAPED_CHARS.put('>', ">");
+ ESCAPED_CHARS.put('"', """);
+ ESCAPED_CHARS.put('&', "&");
+ }
+
+ public static CharSequence escape(CharSequence text) {
+ int length = text.length();
+ for(int i=0;i<length;++i) {
+ char c = text.charAt(i);
+ String esc = ESCAPED_CHARS.get(c);
+ if(esc != null) {
+ StringBuilder b = new StringBuilder(length+16);
+ b.append(text, 0, i);
+ b.append(esc);
+ for(++i;i<length;++i) {
+ c = text.charAt(i);
+ esc = ESCAPED_CHARS.get(c);
+ if(esc != null)
+ b.append(esc);
+ else
+ b.append(c);
+ }
+ return b.toString();
+ }
+ }
+ return text;
+ }
+
+ public static StringBuilder escapeURL(CharSequence str) {
+ StringBuilder result = new StringBuilder();
+ for(int i=0;i<str.length();++i) {
+ char c = str.charAt(i);
+ if(c < 0 || c >= 128) {
+ ByteBuffer bs = UTF8.encode(CharBuffer.wrap(new char[] {c}));
+ for(int j=0;j<bs.limit();++j)
+ result.append(percentEncode(bs.get()));
+ }
+ else if(URL_CAN_CONTAIN[(int)c])
+ result.append(c);
+ else if(c == '&')
+ result.append("&");
+ else
+ result.append(percentEncode(c));
+ }
+ return result;
+ }
+
+ private static String percentEncode(int c) {
+ if(c < 0)
+ c += 256;
+ String hex = Integer.toHexString(c).toUpperCase();
+ if(hex.length() == 1)
+ return "%0" + hex;
+ else
+ return "%" + hex;
+ }
+
+ private static final boolean[] URL_CAN_CONTAIN = new boolean[] {
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, true, false, true, true, true, false, false, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true,
+ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false,
+ };
+}