diff options
Diffstat (limited to 'third_party/jformatstring/java/edu/umd/cs/findbugs/formatStringChecker/FormatSpecifier.java')
-rw-r--r-- | third_party/jformatstring/java/edu/umd/cs/findbugs/formatStringChecker/FormatSpecifier.java | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/third_party/jformatstring/java/edu/umd/cs/findbugs/formatStringChecker/FormatSpecifier.java b/third_party/jformatstring/java/edu/umd/cs/findbugs/formatStringChecker/FormatSpecifier.java new file mode 100644 index 0000000000..24ac3d067d --- /dev/null +++ b/third_party/jformatstring/java/edu/umd/cs/findbugs/formatStringChecker/FormatSpecifier.java @@ -0,0 +1,425 @@ +/* + * 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.io.Serializable; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.Date; +import java.util.FormatFlagsConversionMismatchException; +import java.util.GregorianCalendar; +import java.util.IllegalFormatFlagsException; +import java.util.IllegalFormatPrecisionException; +import java.util.IllegalFormatWidthException; +import java.util.MissingFormatWidthException; +import java.util.UnknownFormatConversionException; + +public class FormatSpecifier { + + private int index = -1; + private Flags f = Flags.NONE; + private int width; + private int precision; + private boolean dt = false; + private char c; + private final String source; + + public String toString() { + return source; + } + + private int index(String s) throws FormatterNumberFormatException { + if (s != null) { + try { + index = Integer.parseInt(s.substring(0, s.length() - 1)); + } catch (NumberFormatException x) { + throw new FormatterNumberFormatException(s, "index"); + } + } else { + index = 0; + } + return index; + } + + public int index() { + return index; + } + + private Flags flags(String s) { + f = Flags.parse(s); + if (f.contains(Flags.PREVIOUS)) + index = -1; + return f; + } + + Flags flags() { + return f; + } + + private int width(String s) throws FormatterNumberFormatException { + width = -1; + if (s != null) { + try { + width = Integer.parseInt(s); + if (width < 0) + throw new IllegalFormatWidthException(width); + } catch (NumberFormatException x) { + throw new FormatterNumberFormatException(s, "width"); + } + } + return width; + } + + private int precision(String s) throws FormatterNumberFormatException { + precision = -1; + if (s != null) { + try { + // remove the '.' + precision = Integer.parseInt(s.substring(1)); + if (precision < 0) + throw new IllegalFormatPrecisionException(precision); + } catch (NumberFormatException x) { + throw new FormatterNumberFormatException(s, "precision"); + } + } + return precision; + } + + int precision() { + return precision; + } + + private char conversion(String s) { + c = s.charAt(0); + if (!dt) { + if (!Conversion.isValid(c)) + throw new UnknownFormatConversionException(String.valueOf(c)); + if (Character.isUpperCase(c)) + f.add(Flags.UPPERCASE); + c = Character.toLowerCase(c); + if (Conversion.isText(c)) + index = -2; + } + return c; + } + + FormatSpecifier(String source, String[] sa) + throws FormatFlagsConversionMismatchException, + FormatterNumberFormatException { + int idx = 0; + this.source = source; + index(sa[idx++]); + flags(sa[idx++]); + width(sa[idx++]); + precision(sa[idx++]); + + if (sa[idx] != null) { + dt = true; + if (sa[idx].equals("T")) + f.add(Flags.UPPERCASE); + } + conversion(sa[++idx]); + + if (dt) + checkDateTime(); + else if (Conversion.isGeneral(c)) + checkGeneral(); + else if (Conversion.isCharacter(c)) + checkCharacter(); + else if (Conversion.isInteger(c)) + checkInteger(); + else if (Conversion.isFloat(c)) + checkFloat(); + else if (Conversion.isText(c)) + checkText(); + else + throw new UnknownFormatConversionException(String.valueOf(c)); + } + + private void checkGeneral() throws FormatFlagsConversionMismatchException { + if ((c == Conversion.BOOLEAN || c == Conversion.HASHCODE) + && f.contains(Flags.ALTERNATE)) + failMismatch(Flags.ALTERNATE, c); + // '-' requires a width + if (width == -1 && f.contains(Flags.LEFT_JUSTIFY)) + throw new MissingFormatWidthException(toString()); + checkBadFlags(Flags.PLUS, Flags.LEADING_SPACE, Flags.ZERO_PAD, + Flags.GROUP, Flags.PARENTHESES); + } + + private void checkDateTime() throws FormatFlagsConversionMismatchException { + if (precision != -1) + throw new IllegalFormatPrecisionException(precision); + if (!DateTime.isValid(c)) + throw new UnknownFormatConversionException("t" + c); + checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE, + Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES); + // '-' requires a width + if (width == -1 && f.contains(Flags.LEFT_JUSTIFY)) + throw new MissingFormatWidthException(toString()); + } + + private void checkCharacter() throws FormatFlagsConversionMismatchException { + if (precision != -1) + throw new IllegalFormatPrecisionException(precision); + checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE, + Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES); + // '-' requires a width + if (width == -1 && f.contains(Flags.LEFT_JUSTIFY)) + throw new MissingFormatWidthException(toString()); + } + + private void checkInteger() throws FormatFlagsConversionMismatchException { + checkNumeric(); + if (precision != -1) + throw new IllegalFormatPrecisionException(precision); + + if (c == Conversion.DECIMAL_INTEGER) + checkBadFlags(Flags.ALTERNATE); + else + checkBadFlags(Flags.GROUP); + } + + private void checkBadFlags(Flags... badFlags) + throws FormatFlagsConversionMismatchException { + for (int i = 0; i < badFlags.length; i++) + if (f.contains(badFlags[i])) + failMismatch(badFlags[i], c); + } + + private void checkFloat() throws FormatFlagsConversionMismatchException { + checkNumeric(); + if (c == Conversion.DECIMAL_FLOAT) { + } else if (c == Conversion.HEXADECIMAL_FLOAT) { + checkBadFlags(Flags.PARENTHESES, Flags.GROUP); + } else if (c == Conversion.SCIENTIFIC) { + checkBadFlags(Flags.GROUP); + } else if (c == Conversion.GENERAL) { + checkBadFlags(Flags.ALTERNATE); + } + } + + private void checkNumeric() { + if (width != -1 && width < 0) + throw new IllegalFormatWidthException(width); + + if (precision != -1 && precision < 0) + throw new IllegalFormatPrecisionException(precision); + + // '-' and '0' require a width + if (width == -1 + && (f.contains(Flags.LEFT_JUSTIFY) || f + .contains(Flags.ZERO_PAD))) + throw new MissingFormatWidthException(toString()); + + // bad combination + if ((f.contains(Flags.PLUS) && f.contains(Flags.LEADING_SPACE)) + || (f.contains(Flags.LEFT_JUSTIFY) && f + .contains(Flags.ZERO_PAD))) + throw new IllegalFormatFlagsException(f.toString()); + } + + private void checkText() { + if (precision != -1) + throw new IllegalFormatPrecisionException(precision); + switch (c) { + case Conversion.PERCENT_SIGN: + if (f.valueOf() != Flags.LEFT_JUSTIFY.valueOf() + && f.valueOf() != Flags.NONE.valueOf()) + throw new IllegalFormatFlagsException(f.toString()); + // '-' requires a width + if (width == -1 && f.contains(Flags.LEFT_JUSTIFY)) + throw new MissingFormatWidthException(toString()); + break; + case Conversion.LINE_SEPARATOR: + if (width != -1) + throw new IllegalFormatWidthException(width); + if (f.valueOf() != Flags.NONE.valueOf()) + throw new IllegalFormatFlagsException(f.toString()); + break; + default: + throw new UnknownFormatConversionException(String.valueOf(c)); + } + } + + public void print(String arg, int argIndex) + throws IllegalFormatConversionException, + FormatFlagsConversionMismatchException { + + try { + if (arg.charAt(0) == '[') + + failConversion(arg); + + if (dt) { + printDateTime(arg); + return; + } + switch (c) { + case Conversion.DECIMAL_INTEGER: + case Conversion.OCTAL_INTEGER: + case Conversion.HEXADECIMAL_INTEGER: + printInteger(arg); + break; + case Conversion.SCIENTIFIC: + case Conversion.GENERAL: + case Conversion.DECIMAL_FLOAT: + case Conversion.HEXADECIMAL_FLOAT: + printFloat(arg); + break; + case Conversion.CHARACTER: + case Conversion.CHARACTER_UPPER: + printCharacter(arg); + break; + case Conversion.BOOLEAN: + printBoolean(arg); + break; + case Conversion.STRING: + case Conversion.HASHCODE: + case Conversion.LINE_SEPARATOR: + case Conversion.PERCENT_SIGN: + break; + default: + throw new UnknownFormatConversionException(String.valueOf(c)); + } + } catch (IllegalFormatConversionException e) { + e.setArgIndex(argIndex); + throw e; + } + } + + private boolean matchSig(String signature, Class<?>... classes) { + for (Class<?> c : classes) + if (matchSig(signature, c)) + return true; + return false; + } + + private boolean matchSig(String signature, Class<?> c) { + return signature.equals("L" + c.getName().replace('.', '/') + ";"); + } + + private boolean mightBeUnknown(String arg) { + if (matchSig(arg, Object.class) || matchSig(arg, Number.class) + || matchSig(arg, Serializable.class) + || matchSig(arg, Comparable.class)) + return true; + return false; + } + + private void printInteger(String arg) + throws IllegalFormatConversionException, + FormatFlagsConversionMismatchException { + if (mightBeUnknown(arg)) + return; + if (matchSig(arg, Byte.class, Short.class, Integer.class, Long.class)) + printLong(); + else if (matchSig(arg, BigInteger.class)) { + } else + failConversion(arg); + } + + private void printFloat(String arg) throws IllegalFormatConversionException { + if (mightBeUnknown(arg)) + return; + if (matchSig(arg, Float.class, Double.class)) { + } else if (matchSig(arg, BigDecimal.class)) { + printBigDecimal(arg); + } else + failConversion(arg); + } + + private void printDateTime(String arg) + throws IllegalFormatConversionException { + if (mightBeUnknown(arg)) + return; + if (matchSig(arg, Long.class, Date.class, java.sql.Date.class, + java.sql.Time.class, java.sql.Timestamp.class, Calendar.class, + GregorianCalendar.class)) { + } else { + failConversion(arg); + } + + } + + private void printCharacter(String arg) + throws IllegalFormatConversionException { + if (mightBeUnknown(arg)) + return; + if (matchSig(arg, Character.class, Byte.class, Short.class, + Integer.class)) { + } else { + failConversion(arg); + } + + } + + private void printBoolean(String arg) + throws IllegalFormatConversionException { + if (mightBeUnknown(arg)) + return; + if (matchSig(arg, Boolean.class)) + return; + failConversion(arg); + } + + private void printLong() throws FormatFlagsConversionMismatchException { + + if (c == Conversion.OCTAL_INTEGER) { + checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE, Flags.PLUS); + } else if (c == Conversion.HEXADECIMAL_INTEGER) { + checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE, Flags.PLUS); + } + + } + + private void printBigDecimal(String arg) + throws IllegalFormatConversionException { + if (c == Conversion.HEXADECIMAL_FLOAT) + failConversion(arg); + } + + private void failMismatch(Flags f, char c) + throws FormatFlagsConversionMismatchException { + String fs = f.toString(); + throw new FormatFlagsConversionMismatchException(fs, c); + } + + private void failConversion(String arg) + throws IllegalFormatConversionException { + if (dt) + throw new IllegalFormatConversionException(this.toString(), + f.contains(Flags.UPPERCASE) ? 'T' : 't', arg); + throw new IllegalFormatConversionException(this.toString(), c, arg); + } + +}
\ No newline at end of file |