/* * 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)); } } }