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

com.github.tommyettinger.digital.Stringf Maven / Gradle / Ivy

There is a newer version: 0.5.1
Show newest version
/*
 * Copyright (c) 2024 See AUTHORS file.
 *
 * 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.github.tommyettinger.digital;

/**
 * A limited polyfill for {@code String.format()} where the platform doesn't provide it, such as on GWT or TeaVM.
 * This supports {@link #format(String, Object...)} with only some of the formatting options in the actual, much-larger
 * Formatter class: {@code %% , %s , %d , %f , %x , %X}, and some precision options, such as {@code %.3f} . Scientific
 * notation is not yet supported, though it could be added in. Some features will probably never be added here, like
 * time and date formatting.
 * 
* Some useful features here are that {@link #appendf(StringBuilder, String, Object...)} allows reusing a StringBuilder, * {@link #printf(String, Object...)} acts like a method on OutputStream (which is typically not provided if * String.format() isn't), and {@link #printfn(String, Object...)} acts like printf() with a newline like println(). */ public final class Stringf { private Stringf(){} /** * Uses the given {@code args} to fill out the format String {@code fmt}, puts that in a new {@link StringBuilder}, * and returns that. * @param fmt the format String; only supports a subset of Formatter options * @param args the parameters to {@code fmt}, as an array or varargs * @return a new {@link StringBuilder} containing the formatted text */ public static StringBuilder appendf(String fmt, Object... args) { return appendf(new StringBuilder(fmt.length()), fmt, args); } /** * Appends the given format String {@code fmt} using the given {@code args}, if any, to {@code sb}, and returns * {@code sb} after changing it. * @param sb will be modified in-place * @param fmt the format String; only supports a subset of Formatter options * @param args the parameters to {@code fmt}, as an array or varargs * @return {@code sb}, after modifications */ public static StringBuilder appendf(StringBuilder sb, String fmt, Object... args) { int len = fmt.length(), arg = 0; for (int i = 0; i < len; i++) { char curr = fmt.charAt(i); if(curr == '%') { curr = fmt.charAt(++i); if (curr == '%') { // literal percent sign sb.append('%'); } else if (curr == 's') { // string sb.append(args[arg++].toString()); } else if (curr == 'd') { // digit, decimal sb.append(((Number) args[arg++]).longValue()); } else if (curr == 'f') { // float double d = ((Number) args[arg++]).doubleValue(); Base.BASE10.appendDecimal(sb, d, d < 0.0 ? 6 : 5); } else if (curr == 'x') { // digit, hex (uncapitalized) sb.append(Base.BASE16.signed(((Number) args[arg++]).longValue()).toLowerCase()); } else if (curr == 'X') { // digit, hex (capitalized) Base.BASE16.appendSigned(sb, ((Number) args[arg++]).longValue()); } else if (curr == '0') { // zero-padding curr = fmt.charAt(++i); if(curr == 'x'){ sb.append(Base.BASE16.unsigned(((Number) args[arg++]).longValue()).toLowerCase()); } else if(curr == 'X'){ Base.BASE16.appendUnsigned(sb, ((Number) args[arg++]).longValue()); } else { int length = Base.BASE10.readInt(fmt, i, len); while ((curr = fmt.charAt(++i)) >= '0' && curr <= '9'){ } if(curr == 'x') sb.append(TextTools.safeSubstring(Base.BASE16.unsigned(((Number) args[arg++]).longValue()), 16 - length, 16).toLowerCase()); else if(curr == 'X') sb.append(TextTools.safeSubstring(Base.BASE16.unsigned(((Number) args[arg++]).longValue()), 16 - length, 16)); else if(curr == 'd') { String num = Base.BASE10.unsigned(((Number) args[arg++]).longValue()); sb.append(TextTools.safeSubstring(num, num.length() - length, num.length())); } } } else if (curr == '.') { curr = fmt.charAt(++i); if(curr == 'f'){ Base.BASE10.appendDecimal(sb, ((Number) args[arg++]).doubleValue()); } else { int precision = Base.BASE10.readInt(fmt, i, len); while ((curr = fmt.charAt(++i)) >= '0' && curr <= '9'){ } if(curr == 'f') { double d = ((Number) args[arg++]).doubleValue(); Base.BASE10.appendDecimal(sb, d, -10000, precision); } } } else if (curr >= '1' && curr <= '9') { int length = Base.BASE10.readInt(fmt, i, len); while ((curr = fmt.charAt(++i)) >= '0' && curr <= '9'){ } if(curr == 'x') { String num = Base.BASE16.signed(((Number) args[arg++]).longValue()); sb.append(TextTools.safeSubstring(num, num.length() - length, num.length()).toLowerCase()); } else if(curr == 'X') { String num = Base.BASE16.signed(((Number) args[arg++]).longValue()); sb.append(TextTools.safeSubstring(num, num.length() - length, num.length())); } else if(curr == 'd') { String num = Base.BASE10.signed(((Number) args[arg++]).longValue()); sb.append(TextTools.safeSubstring(num, num.length() - length, num.length())); } } } else { sb.append(curr); } } return sb; } /** * Uses the given {@code args} to fill out the format String {@code fmt}, and returns that as a String * @param fmt the format String; only supports a subset of Formatter options * @param args the parameters to {@code fmt}, as an array or varargs * @return a new String containing the formatted text */ public static String format(String fmt, Object... args) { return appendf(fmt, args).toString(); } /** * Uses the given {@code args} to fill out the format String {@code fmt}, and prints that to {@code stdout}. * This is comparable to {@code java.io.PrintStream#print}. * @param fmt the format String; only supports a subset of Formatter options * @param args the parameters to {@code fmt}, as an array or varargs */ public static void printf(String fmt, Object... args) { System.out.print(appendf(fmt, args)); } /** * Uses the given {@code args} to fill out the format String {@code fmt}, and prints that to {@code stdout} with an * appended newline. This is comparable to {@code java.io.PrintStream#println}. * @param fmt the format String; only supports a subset of Formatter options * @param args the parameters to {@code fmt}, as an array or varargs */ public static void printfn(String fmt, Object... args) { System.out.println(appendf(fmt, args)); } // public static void main(String[] args) { // printfn("Coming up to the plate, it's number %d, %s!", 42, "Jackie Robinson"); // printfn("His batting average is an outstanding %.3f, truly remarkable.", 0.415); // } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy