JSONFilter.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.json;
/**
* Provides escaping of values so they can be included in a JSON document.
* Escaping is based on the definition of JSON found in
* <a href="https://www.rfc-editor.org/rfc/rfc8259.html">RFC 8259</a>.
*/
public class JSONFilter {
/**
* Escape the given char.
* @param c the char
* @return a char array with the escaped sequence
*/
public static char[] escape(char c) {
if (c < 0x20 || c == 0x22 || c == 0x5c || Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) {
char popular = getPopularChar(c);
if (popular > 0) {
return new char[] { '\\', popular };
} else {
StringBuilder escaped = new StringBuilder(6);
escaped.append("\\u");
escaped.append(String.format("%04X", Integer.valueOf(c)));
return escaped.toString().toCharArray();
}
} else {
char[] result = new char[1];
result[0] = c;
return result;
}
}
/**
* Escape the given string.
* @param input the string
* @return the escaped string
*/
public static String escape(String input) {
return escape(input, 0, input.length()).toString();
}
/**
* Escape the given char sequence.
* @param input the char sequence
* @return the escaped char sequence
*/
public static CharSequence escape(CharSequence input) {
return escape(input, 0, input.length());
}
/**
* Escape the given char sequence.
* @param input the char sequence
* @param off the offset on which escaping will start
* @param length the length which should be escaped
* @return the escaped char sequence corresponding to the specified range
*/
public static CharSequence escape(CharSequence input, int off, int length) {
/*
* While any character MAY be escaped, only U+0000 to U+001F (control
* characters), U+0022 (quotation mark) and U+005C (reverse solidus)
* MUST be escaped.
*/
StringBuilder escaped = null;
int lastUnescapedStart = off;
for (int i = off; i < length; i++) {
char c = input.charAt(i);
if (c < 0x20 || c == 0x22 || c == 0x5c || Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) {
if (escaped == null) {
escaped = new StringBuilder(length + 20);
}
if (lastUnescapedStart < i) {
escaped.append(input.subSequence(lastUnescapedStart, i));
}
lastUnescapedStart = i + 1;
char popular = getPopularChar(c);
if (popular > 0) {
escaped.append('\\').append(popular);
} else {
escaped.append("\\u");
escaped.append(String.format("%04X", Integer.valueOf(c)));
}
}
}
if (escaped == null) {
if (off == 0 && length == input.length()) {
return input;
} else {
return input.subSequence(off, length - off);
}
} else {
if (lastUnescapedStart < length) {
escaped.append(input.subSequence(lastUnescapedStart, length));
}
return escaped.toString();
}
}
private JSONFilter() {
// Utility class. Hide the default constructor.
}
private static char getPopularChar(char c) {
switch (c) {
case '"':
case '\\':
case '/':
return c;
case 0x8:
return 'b';
case 0xc:
return 'f';
case 0xa:
return 'n';
case 0xd:
return 'r';
case 0x9:
return 't';
default:
return 0;
}
}
}