blob: b4b3d028d00ec5c1720bf4f4703bbf6cd12369de [file] [log] [blame]
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed 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 com.google.devtools.build.lib.actions;
/**
* Implementation of a formatter that supports only a single '%s'
*
* <p>This implementation is used in command line item expansions that use formatting. We use a
* custom implementation to improve performance and avoid GC.
*/
public class SingleStringArgFormatter {
/**
* Returns true if the format string contains a single '%s'.
*
* <p>%% escape sequences are supported.
*/
public static boolean isValid(String formatStr) {
return onlyOccurrence(formatStr, formatStr.length()) != -1;
}
/**
* Returns the equivalent result of <code>String.format(formatStr, subject)</code>, under the
* assumption that the format string contains a single %s.
*
* <p>Use {@link #isValid} to validate the format string.
*
* @throws IllegalArgumentException if the format string is invalid.
*/
public static String format(String formatStr, String subject) {
int n = formatStr.length();
int idx = onlyOccurrence(formatStr, n);
if (idx < 0) {
throw new IllegalArgumentException(
"Expected format string with single '%s', found: " + formatStr);
}
return new StringBuilder(n + subject.length() - 2)
.append(formatStr, 0, idx)
.append(subject)
.append(formatStr, idx + 2, n)
.toString();
}
/*
* Returns the index of the only occurrence of %s. Skips any %%. Returns -1 if {@code formatStr}
* is malformed.
*/
private static int onlyOccurrence(String formatStr, int n) {
int idx = nextOccurrence(formatStr, n, 0);
if (idx < 0) {
return -1;
}
// Only one occurrence please
if (nextOccurrence(formatStr, n, idx + 2) != -1) {
return -1;
}
return idx;
}
/**
* Returns next occurrence of %s. Skips any %%.
*
* @return
* <li>[0-n]: Index of next %s
* <li>-1: No %s found until end of string
* <li>-2: Illegal sequence found, eg. %f
*/
private static int nextOccurrence(String formatStr, int n, int idx) {
while (idx < n) {
idx = formatStr.indexOf('%', idx);
if (idx == -1) {
break;
}
if ((idx + 1) < n) {
char c = formatStr.charAt(idx + 1);
if (c == 's') {
return idx;
}
if (c != '%') {
// Illegal sequence found
return -2;
}
idx += 2;
} else {
// Terminating '%' found, illegal
return -2;
}
}
return -1;
}
}