All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.firebirdsql.gds.DefaultMessageTemplate Maven / Gradle / Ivy

There is a newer version: 6.0.0-beta-1
Show newest version
/*
 * Firebird Open Source JDBC Driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a source control history command.
 *
 * All rights reserved.
 */
package org.firebirdsql.gds;

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;
import static org.firebirdsql.gds.ISCConstants.isc_formatted_exception;

/**
 * Message template holds the template text, error code, SQLstate, and parameter count, and can render the message.
 * 

* The template text uses a simplified form of {@code MessageFormat}. Basically, {@code {n}} always represents * a parameter with index {@code n}, where {@code n >= 0}. There are no escapes defined. *

* * @since 6 */ @NullMarked final class DefaultMessageTemplate extends MessageTemplate { private static final int PARAM_SIZE_FACTOR = 20; private static final int COUNT_UNKNOWN = -1; private static final Pattern MESSAGE_PARAM_PATTERN = Pattern.compile("\\{(\\d+)}"); private final int errorCode; private final String templateText; private final @Nullable String sqlState; private int parameterCount; /** * Creates a message template with {@code errorCode} and {@code templateText}, and SQLstate {@code null}. * * @param errorCode * error code * @param templateText * template text */ DefaultMessageTemplate(int errorCode, String templateText) { this(errorCode, templateText, null, COUNT_UNKNOWN); } /** * Creates a message template with {@code errorCode}, {@code templateText}, and {@code sqlState}. * * @param errorCode * error code * @param templateText * template text * @param sqlState * SQLstate (can be {@code null}) * @throws IllegalArgumentException * if {@code sqlState} is non-{@code null}, and not 5 characters long */ DefaultMessageTemplate(int errorCode, String templateText, @Nullable String sqlState) { this(errorCode, templateText, sqlState, COUNT_UNKNOWN); } /** * Creates a message template with {@code errorCode}, {@code templateText}, {@code sqlState} and a parameter count. * * @param errorCode * error code * @param templateText * template text * @param sqlState * SQLstate (can be {@code null}) * @param parameterCount * number of parameters in template, use {@code -1} ({@link #COUNT_UNKNOWN}) to determine the count on first * use; the parameter count should be determined by the highest parameter number + 1 * @throws IllegalArgumentException * if {@code sqlState} is non-{@code null}, and not 5 characters long */ private DefaultMessageTemplate(int errorCode, String templateText, @Nullable String sqlState, int parameterCount) { assert parameterCount >= -1 : "parameterCount must be greater than or equal to -1"; this.errorCode = errorCode; this.templateText = requireNonNull(templateText, "templateText"); // This default message template allows null, while validateSqlState rejects null this.sqlState = sqlState != null ? MessageTemplate.validateSqlState(sqlState) : null; this.parameterCount = parameterCount; } @Override public int errorCode() { return errorCode; } @Override public @Nullable String sqlState() { return sqlState; } /** * @return template text of this message * @see #appendMessage(StringBuilder, List) */ String templateText() { return templateText; } /** * Number of parameters in the message. *

* Phrased differently, this is the highest parameter number + 1. That is, even if a template only has one * parameter, if that number is — for example — 5, then the parameter count is 6 because * parameters 0 - 4 are counted even if they do not occur in the template itself. And having two parameters 0, * means the parameter count is 1, not 2. *

* * @return parameter count */ int parameterCount() { // NOTE: We accept that concurrent invocation on multiple threads may duplicate the work done if (parameterCount != COUNT_UNKNOWN) return parameterCount; return parameterCount = getParamCountInternal(); } private int getParamCountInternal() { int maxIndex = -1; Matcher matcher = createParameterMatcher(); while (matcher.find()) { int parameterIndex = Integer.parseInt(matcher.group(1)); maxIndex = Math.max(maxIndex, parameterIndex); } return maxIndex + 1; } @Override public MessageTemplate withDefaultSqlState(String defaultSqlState) { if (this.sqlState != null) return this; return withSqlState(defaultSqlState); } @Override public MessageTemplate withSqlState(String sqlState) { if (requireNonNull(sqlState, "sqlState").equals(this.sqlState)) return this; return new OverriddenSqlStateMessageTemplate(this, sqlState); } private Matcher createParameterMatcher() { return MESSAGE_PARAM_PATTERN.matcher(templateText); } /** * Appends the message, formatted using {@code parameters}, to {@code messageBuffer}. *

* Parameters that are missing or {@code null} are rendered as {@code (null)}. Excess parameters are concatenated * to the end of the message, unless the {@code errorCode} is {@link ISCConstants#isc_formatted_exception}. *

* * @param messageBuffer * string builder to append to * @param parameters * parameters to use for formatting (never {@code null}, may be empty) */ @Override public void appendMessage(StringBuilder messageBuffer, List parameters) { int templateParameterCount = parameterCount(); int actualParameterCount = parameters.size(); messageBuffer.ensureCapacity(messageBuffer.length() + estimateBufferCapacity(Math.max(templateParameterCount, actualParameterCount))); Matcher matcher = createParameterMatcher(); while (matcher.find()) { int paramIndex = Integer.parseInt(matcher.group(1)); Object parameterValue = paramIndex < actualParameterCount ? parameters.get(paramIndex) : null; matcher.appendReplacement(messageBuffer, ""); // Append separately to avoid having to quote the replacement string messageBuffer.append(parameterValue != null ? parameterValue : "(null)"); } matcher.appendTail(messageBuffer); if (errorCode != isc_formatted_exception && actualParameterCount > templateParameterCount) { // Include extra parameters at the end of the message for (Object extraParameter : parameters.subList(templateParameterCount, actualParameterCount)) { messageBuffer.append("; ").append(extraParameter != null ? extraParameter : "(null)"); } } } private int estimateBufferCapacity(int parameterSize) { return templateText.length() + parameterSize * PARAM_SIZE_FACTOR; } /** * Creates a message template with a "not found" message for {@code errorCode} and SQLstate {@code null}. * * @param errorCode * error code * @return message template */ static MessageTemplate notFound(int errorCode) { return new DefaultMessageTemplate(errorCode, "No message for code " + errorCode + " found.", null, 0); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy