001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2023, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014
015package ch.qos.logback.core.encoder;
016
017public class JsonEscapeUtil {
018
019    protected final static char[] HEXADECIMALS_TABLE = "0123456789ABCDEF".toCharArray();
020
021    static final int ESCAPE_CODES_COUNT = 32;
022
023    static final String[] ESCAPE_CODES = new String[ESCAPE_CODES_COUNT];
024
025    // From RFC-8259 page 5
026
027    //  "    quotation mark  U+0022 -- escaped as \"
028    //  \    reverse solidus U+005C -- escaped as \\
029    //  /    solidus         U+002F -- escaped as \/
030
031    //  backspace       U+0008  -- escaped as \b
032    //  tab             U+0009  -- escaped as \t
033    //  line feed       U+000A  -- escaped as \n
034    //  form feed       U+000C -- escaped as \f
035    //  carriage return U+000D -- escaped as \r
036
037    static {
038        for (char c = 0; c < ESCAPE_CODES_COUNT; c++) {
039
040            switch (c) {
041            case 0x08:
042                ESCAPE_CODES[c] = "\\b";
043                break;
044            case 0x09:
045                ESCAPE_CODES[c] = "\\t";
046                break;
047            case 0x0A:
048                ESCAPE_CODES[c] = "\\n";
049                break;
050            case 0x0C:
051                ESCAPE_CODES[c] = "\\f";
052                break;
053            case 0x0D:
054                ESCAPE_CODES[c] = "\\r";
055                break;
056            default:
057                ESCAPE_CODES[c] = _computeEscapeCodeBelowASCII32(c);
058            }
059        }
060    }
061
062    // this method should not be called by methods except the static initializer
063    private static String _computeEscapeCodeBelowASCII32(char c) {
064        if (c > 32) {
065            throw new IllegalArgumentException("input must be less than 32");
066        }
067
068        StringBuilder sb = new StringBuilder(6);
069        sb.append("\\u00");
070
071        int highPart = c >> 4;
072        sb.append(HEXADECIMALS_TABLE[highPart]);
073
074        int lowPart = c & 0x0F;
075        sb.append(HEXADECIMALS_TABLE[lowPart]);
076
077        return sb.toString();
078    }
079
080    //  "    quotation mark  U+0022 -- escaped as \"
081    //  \    reverse solidus U+005C -- escaped as \\
082    //  /    solidus         U+002F -- escaped as \/
083
084    static String getObligatoryEscapeCode(char c) {
085        if (c < 32)
086            return ESCAPE_CODES[c];
087        if (c == 0x22)
088            return "\\\"";
089        if (c == 0x2F)
090            return "\\/";
091        if (c == 0x5C)
092            return "\\\\";
093
094        return null;
095    }
096
097    static public String jsonEscapeString(String input) {
098        int length = input.length();
099        int lenthWithLeeway = (int) (length * 1.1);
100
101        StringBuilder sb = new StringBuilder(lenthWithLeeway);
102        for (int i = 0; i < length; i++) {
103            final char c = input.charAt(i);
104            String escaped = getObligatoryEscapeCode(c);
105            if (escaped == null)
106                sb.append(c);
107            else {
108                sb.append(escaped);
109            }
110        }
111
112        return sb.toString();
113    }
114
115}