aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/jformatstring/java/edu/umd/cs/findbugs/formatStringChecker/FormatSpecifier.java
diff options
context:
space:
mode:
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.java425
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