001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.site;
021
022import javax.annotation.Nullable;
023
024/**
025 * Represents a single entry in the client-side search index.
026 *
027 * @param title display name
028 * @param url relative URL
029 * @param category category label
030 * @param type document type
031 * @param description short description
032 * @param keywords comma-separated keywords
033 * @param since the version this module/property was introduced (can be empty)
034 * @param weight ranking weight for search results
035 */
036public record SearchIndexEntry(String title, String url, String category,
037                            String type, String description, String keywords,
038                            String since, int weight) {
039
040    /** String literal for comma. */
041    private static final String COMMA = ",";
042
043    /** Maximum ASCII value for the lookup table. */
044    private static final int ASCII_MAX = 128;
045
046    /** Lookup table for JSON escape sequences for characters < 128 (ASCII range). */
047    private static final String[] JSON_ESCAPES;
048
049    static {
050        JSON_ESCAPES = new String[ASCII_MAX];
051        JSON_ESCAPES['\\'] = "\\\\";
052        JSON_ESCAPES['\"'] = "\\\"";
053        JSON_ESCAPES['\n'] = "\\n";
054        JSON_ESCAPES['\r'] = "\\r";
055        JSON_ESCAPES['\t'] = "\\t";
056    }
057
058    /**
059     * Serialises this entry to a compact JSON object string.
060     *
061     * @return JSON object string
062     */
063    public String toJson() {
064        return "{"
065            + "\"title\":" + jsonString(title) + COMMA
066            + "\"url\":" + jsonString(url) + COMMA
067            + "\"category\":" + jsonString(category) + COMMA
068            + "\"type\":" + jsonString(type) + COMMA
069            + "\"description\":" + jsonString(description) + COMMA
070            + "\"since\":" + jsonString(since) + COMMA
071            + "\"weight\":" + weight + COMMA
072            + "\"keywords\":" + jsonString(keywords)
073            + "}";
074    }
075
076    /**
077     * Wraps a string value in JSON double quotes and escapes special characters.
078     *
079     * @param value the raw string to encode
080     * @return JSON-safe quoted string, or {@code ""} if value is null
081     */
082    private static String jsonString(@Nullable String value) {
083        String result = "\"\"";
084        if (value != null) {
085            final int length = value.length();
086            final StringBuilder sb = new StringBuilder(length + 2);
087            sb.append('\"');
088            for (int index = 0; index < length; index++) {
089                final char ch = value.charAt(index);
090                final String escape;
091                if (ch < ASCII_MAX) {
092                    escape = JSON_ESCAPES[ch];
093                }
094                else {
095                    escape = null;
096                }
097                if (escape == null) {
098                    sb.append(ch);
099                }
100                else {
101                    sb.append(escape);
102                }
103            }
104            sb.append('\"');
105            result = sb.toString();
106        }
107        return result;
108    }
109
110}