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

org.checkerframework.checker.formatter.qual.ConversionCategory Maven / Gradle / Ivy

There is a newer version: 4.1.2
Show newest version
package org.checkerframework.checker.formatter.qual;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.checkerframework.dataflow.qual.Pure;

/**
 * Elements of this enumeration are used in a {@link Format Format} annotation to indicate the valid
 * types that may be passed as a format parameter. For example:
 *
 * 
* *
{@literal @}Format({ConversionCategory.GENERAL, ConversionCategory.INT})
 * String f = "String '%s' has length %d";
 * String.format(f, "Example", 7);
* *
* * The annotation indicates that the format string requires any Object as the first parameter * ({@link ConversionCategory#GENERAL}) and an integer as the second parameter ({@link * ConversionCategory#INT}). * * @see Format * @checker_framework.manual #formatter-checker Format String Checker */ public enum ConversionCategory { /** Use if the parameter can be of any type. Applicable for conversions b, B, h, H, s, S. */ GENERAL(null /* everything */, "bBhHsS"), /** * Use if the parameter is of a basic types which represent Unicode characters: char, Character, * byte, Byte, short, and Short. This conversion may also be applied to the types int and * Integer when Character.isValidCodePoint(int) returns true. Applicable for conversions c, C. */ CHAR(new Class[] {Character.class, Byte.class, Short.class, Integer.class}, "cC"), /** * Use if the parameter is an integral type: byte, Byte, short, Short, int and Integer, long, * Long, and BigInteger. Applicable for conversions d, o, x, X. */ INT( new Class[] {Byte.class, Short.class, Integer.class, Long.class, BigInteger.class}, "doxX"), /** * Use if the parameter is a floating-point type: float, Float, double, Double, and BigDecimal. * Applicable for conversions e, E, f, g, G, a, A. */ FLOAT(new Class[] {Float.class, Double.class, BigDecimal.class}, "eEfgGaA"), /** * Use if the parameter is a type which is capable of encoding a date or time: long, Long, * Calendar, and Date. Applicable for conversions t, T. */ TIME(new Class[] {Long.class, Calendar.class, Date.class}, "tT"), /** * In a format string, multiple conversions may be applied to the same parameter. This is * seldomly needed, but the following is an example of such use: * *
     *   format("Test %1$c %1$d", (int)42);
     * 
* * In this example, the first parameter is interpreted as both a character and an int, therefore * the parameter must be compatible with both conversion, and can therefore neither be char nor * long. This intersection of conversions is called CHAR_AND_INT. * *

One other conversion intersection is interesting, namely the intersection of INT and TIME, * resulting in INT_AND_TIME. * *

All other intersection either lead to an already existing type, or NULL, in which case it * is illegal to pass object's of any type as parameter. */ CHAR_AND_INT(new Class[] {Byte.class, Short.class, Integer.class}, null), INT_AND_TIME(new Class[] {Long.class}, null), /** * Use if no object of any type can be passed as parameter. In this case, the only legal value * is null. This is seldomly needed, and indicates an error in most cases. For example: * *

     *   format("Test %1$f %1$d", null);
     * 
* * Only null can be legally passed, passing a value such as 4 or 4.2 would lead to an exception. */ NULL(new Class[0], null), /** * Use if a parameter is not used by the formatter. This is seldomly needed, and indicates an * error in most cases. For example: * *
     *   format("Test %1$s %3$s", "a","unused","b");
     * 
* * Only the first "a" and third "b" parameters are used, the second "unused" parameter is * ignored. */ UNUSED(null /* everything */, null); ConversionCategory(Class[] types, String chars) { this.types = types; this.chars = chars; } @SuppressWarnings("ImmutableEnumChecker") // TODO: clean this up! public final Class[] types; public final String chars; /** * Use this function to get the category associated with a conversion character. For example: * *
* *
     * ConversionCategory.fromConversionChar('d') == ConversionCategory.INT;
     * 
* *
*/ public static ConversionCategory fromConversionChar(char c) { for (ConversionCategory v : new ConversionCategory[] {GENERAL, CHAR, INT, FLOAT, TIME}) { if (v.chars.contains(String.valueOf(c))) { return v; } } throw new IllegalArgumentException("Bad conversion character " + c); } private static Set arrayToSet(E[] a) { return new HashSet<>(Arrays.asList(a)); } public static boolean isSubsetOf(ConversionCategory a, ConversionCategory b) { return intersect(a, b) == a; } /** * Returns the intersection of two categories. This is seldomly needed. * *
* *
     * ConversionCategory.intersect(INT, TIME) == INT_AND_TIME;
     * 
* *
* * @param a a category * @param b a category * @return the intersection of the two categories (their greatest lower bound) */ public static ConversionCategory intersect(ConversionCategory a, ConversionCategory b) { if (a == UNUSED) { return b; } if (b == UNUSED) { return a; } if (a == GENERAL) { return b; } if (b == GENERAL) { return a; } Set> as = arrayToSet(a.types); Set> bs = arrayToSet(b.types); as.retainAll(bs); // intersection for (ConversionCategory v : new ConversionCategory[] { CHAR, INT, FLOAT, TIME, CHAR_AND_INT, INT_AND_TIME, NULL }) { Set> vs = arrayToSet(v.types); if (vs.equals(as)) { return v; } } throw new RuntimeException(); } /** * Returns the union of two categories. This is seldomly needed. * *
* *
     * ConversionCategory.union(INT, TIME) == GENERAL;
     * 
* *
* * @param a a category * @param b a category * @return the union of the two categories (their least upper bound) */ public static ConversionCategory union(ConversionCategory a, ConversionCategory b) { if (a == UNUSED || b == UNUSED) { return UNUSED; } if (a == GENERAL || b == GENERAL) { return GENERAL; } if ((a == CHAR_AND_INT && b == INT_AND_TIME) || (a == INT_AND_TIME && b == CHAR_AND_INT)) { // This is special-cased because the union of a.types and b.types // does not include BigInteger.class, whereas the types for INT does. // Returning INT here to prevent returning GENERAL below. return INT; } Set> as = arrayToSet(a.types); Set> bs = arrayToSet(b.types); as.addAll(bs); // union for (ConversionCategory v : new ConversionCategory[] { NULL, CHAR_AND_INT, INT_AND_TIME, CHAR, INT, FLOAT, TIME }) { Set> vs = arrayToSet(v.types); if (vs.equals(as)) { return v; } } return GENERAL; } private String className(Class cls) { if (cls == Boolean.class) { return "boolean"; } if (cls == Character.class) { return "char"; } if (cls == Byte.class) { return "byte"; } if (cls == Short.class) { return "short"; } if (cls == Integer.class) { return "int"; } if (cls == Long.class) { return "long"; } if (cls == Float.class) { return "float"; } if (cls == Double.class) { return "double"; } return cls.getSimpleName(); } /** Returns a pretty printed {@link ConversionCategory}. */ @Pure @Override public String toString() { StringBuilder sb = new StringBuilder(this.name()); sb.append(" conversion category (one of: "); boolean first = true; for (Class cls : this.types) { if (!first) { sb.append(", "); } sb.append(className(cls)); first = false; } sb.append(")"); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy