blob: e906d5e9805a0d5d25847d4602218dad63514e50 [file] [log] [blame]
/*
* Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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 GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* This file has been extensively modified from the original Sun implementation
* to provide for compile time checking of Format Strings.
*
* These modifications were performed by Bill Pugh, this code is free software.
*
*/
package edu.umd.cs.findbugs.formatStringChecker;
import java.util.ArrayList;
import java.util.FormatFlagsConversionMismatchException;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.UnknownFormatConversionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class Formatter {
public static void check(String format, String... args)
throws ExtraFormatArgumentsException,
IllegalFormatConversionException, IllegalFormatException,
FormatFlagsConversionMismatchException,
MissingFormatArgumentException, FormatterNumberFormatException {
// index of last argument referenced
int last = -1;
// last ordinary index
int lasto = -1;
// last index used
int maxIndex = -1;
for (FormatSpecifier fs : parse(format)) {
int index = fs.index();
switch (index) {
case -2:
// ignore it
break;
case -1: // relative index
if (last < 0 || last > args.length - 1)
throw new MissingFormatArgumentException(last,
fs.toString());
fs.print(args[last], last);
break;
case 0: // ordinary index
lasto++;
last = lasto;
if (lasto > args.length - 1)
throw new MissingFormatArgumentException(lasto,
fs.toString());
maxIndex = Math.max(maxIndex, lasto);
fs.print(args[lasto], lasto);
break;
default: // explicit index
last = index - 1;
if (last > args.length - 1)
throw new MissingFormatArgumentException(last,
fs.toString());
maxIndex = Math.max(maxIndex, last);
fs.print(args[last], last);
break;
}
}
if (maxIndex < args.length - 1) {
throw new ExtraFormatArgumentsException(args.length, maxIndex + 1);
}
}
// %[argument_index$][flags][width][.precision][t]conversion
private static final String formatSpecifier = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
private static Pattern fsPattern = Pattern.compile(formatSpecifier);
// Look for format specifiers in the format string.
private static List<FormatSpecifier> parse(String s)
throws FormatFlagsConversionMismatchException,
FormatterNumberFormatException {
ArrayList<FormatSpecifier> al = new ArrayList<FormatSpecifier>();
Matcher m = fsPattern.matcher(s);
int i = 0;
while (i < s.length()) {
if (m.find(i)) {
// Anything between the start of the string and the beginning
// of the format specifier is either fixed text or contains
// an invalid format string.
if (m.start() != i) {
// Make sure we didn't miss any invalid format specifiers
checkText(s.substring(i, m.start()));
}
// Expect 6 groups in regular expression
String[] sa = new String[6];
for (int j = 0; j < m.groupCount(); j++) {
sa[j] = m.group(j + 1);
}
al.add(new FormatSpecifier(m.group(0), sa));
i = m.end();
} else {
// No more valid format specifiers. Check for possible invalid
// format specifiers.
checkText(s.substring(i));
// The rest of the string is fixed text
break;
}
}
return al;
}
private static void checkText(String s) {
int idx;
// If there are any '%' in the given string, we got a bad format
// specifier.
if ((idx = s.indexOf('%')) != -1) {
char c = (idx > s.length() - 2 ? '%' : s.charAt(idx + 1));
throw new UnknownFormatConversionException(String.valueOf(c));
}
}
}