/* * [FieldType.java] * * Summary: Enumeration of various data types for which we handle interconversions. * * Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2009-04-22 initial release */ package com.mindprod.comparatorcutter; import com.mindprod.common18.JavaVersion; import com.mindprod.common18.ST; import com.mindprod.fastcat.FastCat; /** * Enumeration of various data types for which we handle interconversions. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2009-04-22 initial release * @since 2009 */ @SuppressWarnings( { "UnusedDeclaration" } ) public enum FieldType { // templates defined for ascending // %first = a. or this. %getter is method() %field is field // If leave off old template, will use new template. SPARE( "-- spare --", "", "", null, false /* not nullable */ ), CASE_SENSITIVE_STRING( "String (case-sensitive)", "case sensitively", "%first.%field.compareTo( %second%.field )", null, true ), CASE_INSENSITIVE_STRING( "String (case-insensitive)", "case insensitively", "%first.%field.compareToIgnoreCase( %second.%field )", null, true ), STRING_LENGTH( "String (length)", "length", "%first.%field.length() - %second.%field.length()", null, true ), COMPARABLE( "Comparable Object", "Comparable", "%first.%field.compareTo( %second.%field )", null, true ), BOOLEAN( "boolean", "logically", "( %first.%field ? 1 : 0 ) - ( %second.%field ? 1 : 0 )", null, false ), SIGNEDBYTE( "byte", "numerically", "%first.%field - %second.%field", null, false ), UNSIGNEDBYTE( "/*unsigned*/ byte", "numerically", "( %first.%field & 0xff ) - ( %second.%field & 0xff )", null, false ), SHORT( "short", "numerically", "%first.%field - %second.%field", null, false ), CHAR( "char", "alphabetically", "%first.%field - %second.%field", null, false ), INT( "int", "numerically", "Integer.compare( %first.%field, %second.%field )", "Misc.compare( %first.%field, %second.%field )", false ), LONG( "long", "numerically", "Long.compare ( %first.%field - %second.%field )", "Misc.compare( %first.%field, %second.%field )", false ), FLOAT( "float", "numerically", "Float.compare( %first.%field - %second.%field )", "Misc.compare( %first.%field, %second.%field )", false ), DOUBLE( "double", "numerically", "Double.compare( %first.%field - %second.%field )", "Misc.compare( %first.%field, %second.%field )", false ), ENUM( "enum", "ordinal", "Integer.compare(%first.%field.ordinal(), %second.%field.ordinal())", "Misc.compare(%first.%field.ordinal(), %second.%field.ordinal())", true /* nullable */ ), PMETHOD( "method returning primitive", "primitive", "Integer.compare(%first.%getter(), %second.%getter())", "Misc.compare( %first.%getter(), %second.%getter() )", false /* nullable */ ), OMETHOD( "method returning Object", "Object", "%first.%getter().compareTo(%second.%getter())", null, true /* nullable */ ); /* * template for how to compare this type of field. contains replacement strings %fieldName %first %second * Gives result for ascending. */ private final String comparatorTemplate; /** * Java type name */ private final String fieldTypeName; /** * template for how to compare this type of field. contains replacement strings %fieldName %first %second * Gives result for ascending. For use prior to Integer.compare */ private final String oldTemplate; /** * how you would describe the sorting order. e.g. case sensitively, case insensitively, numerically... */ private final String sortType; /** * is it possible for this field type to be null. */ private final boolean isNullable; /** * constructor * * @param fieldTypeName Java type name * @param sortType how you would describe the sorting order. e.g. case sensitively, case insensitively, * numerically * @param comparatorTemplate template code to generate a comparison * @param oldTemplate template for old versions of Java prior to 1.7 that do not support Integer.compare * @param isNullable is it possible for this field type to be null. */ FieldType( final String fieldTypeName, final String sortType, final String comparatorTemplate, final String oldTemplate, final boolean isNullable ) { this.fieldTypeName = fieldTypeName; this.sortType = sortType; this.comparatorTemplate = comparatorTemplate; this.oldTemplate = oldTemplate; this.isNullable = isNullable; } /** * Compare one field for the Comparable/Comparator * * @param javaVersion which version to generate Java for * @param diffExp e.g. diff or Misc.signum( diff ) or Long.signum( diff ) * * @return code to compare fields */ private static String generateCompareToLine( final JavaVersion javaVersion, final String diffExp ) { final StringBuilder sb = new StringBuilder( 3 ); // "return a.x - b.x;" or "return this.x - other.x" sb.append( "return " ); sb.append( diffExp ); sb.append( ";\n" ); return sb.toString(); } /** * indent generated text * * @param sb StringBuilder where we are accumulating generated text. * @param level How many levels of indentation, in groups of 4 cols. */ private static void indent( FastCat sb, int level ) { sb.append( ST.spaces( level * 4 ) ); } /** * replace variables in comparison template * * @param javaVersion which version to generate Java for * @param isComparable true if want a code for a Comparable, false for a Comparator * @param checkForNull true if should check for null * @param oClass the class of the objects being compared. * @param isFieldDescending true if want a descending comparison * @param fieldname name of sort key field * * @return Code customised to compare two fields of this fieldType */ private String expandCompareToTemplate( final JavaVersion javaVersion, final boolean isComparable, final boolean checkForNull, final String oClass, final boolean isFieldDescending, final String fieldname ) { // this is FieldType final String first; final String second; if ( javaVersion.supportsGenerics() ) { if ( isComparable ) { if ( isFieldDescending ) { first = "other"; second = "this"; } else { first = "this"; second = "other"; } } else { // comparator if ( isFieldDescending ) { first = "b"; second = "a"; } else { first = "a"; second = "b"; } } } else { // no generics if ( isComparable ) { if ( isFieldDescending ) { first = "( ( " + oClass + " ) other )"; second = "this"; } else { first = "this"; second = "( ( " + oClass + " ) other )"; } } else { // comparator if ( isFieldDescending ) { first = "( ( " + oClass + " ) b )"; second = "( ( " + oClass + " ) a )"; } else { first = "( ( " + oClass + " ) a )"; second = "( ( " + oClass + " ) b )"; } } } final String template = ( oldTemplate == null || javaVersion.supportsIntegerCompare() ) ? comparatorTemplate : oldTemplate; if ( this.isNullable && checkForNull ) { final FastCat sb = new FastCat( 7 ); sb.append( "%first%getter != null ? %second%getter != null ?\n" ); indent( sb, 3 ); sb.append( "( " ); sb.append( template ); sb.append( " )\n" ); indent( sb, 3 ); sb.append( " : 1 : %second%getter != null ? -1 : 0" ); return sb.toString().replace( "%first", first ).replace( "%second", second ).replace( "%getter", fieldname ).replace( "%field", fieldname ); } else { return template.replace( "%first", first ).replace( "%second", second ).replace( "%getter", fieldname ).replace( "%field", fieldname ); } } /** * generate Java code to compare two fields, * when it is not the last field being compared. * Used for Comparators and Comparables. * * @param javaVersion which version to generate Java for * @param isComparator true if generated a Comparable, false if a Comparator * @param checkForNull true if we should generate code to check for null sort key values. * @param oClass the class of the objects being compared. * @param isFieldDescending true if want a descending comparison * @param fieldName name of field being compared. * @param unique number to generate unique variables 1..n * * @return Java source code */ @SuppressWarnings( { "SameParameterValue" } ) public String generateIntermediateCompareToLine( final JavaVersion javaVersion, final boolean isComparator, final boolean checkForNull, final String oClass, boolean isFieldDescending, String fieldName, final int unique ) { // this is FieldType final FastCat sb = new FastCat( 18 ); indent( sb, 2 ); sb.append( "int " ); final String diffVar = "diff" + ( unique > 0 ? Integer.toString( unique ) : "" ); sb.append( diffVar ); sb.append( " = " ); sb.append( expandCompareToTemplate( javaVersion, isComparator, checkForNull, oClass, isFieldDescending, fieldName ) ); sb.append( ";\n" ); indent( sb, 2 ); sb.append( "if ( " ); sb.append( diffVar ); sb.append( " != 0 )\n" ); indent( sb, 3 ); sb.append( "{\n" ); indent( sb, 3 ); sb.append( generateCompareToLine( javaVersion, diffVar ) ); indent( sb, 3 ); sb.append( "}\n" ); return sb.toString(); } /** * generate Java code to compare two fields, when it is the last field being compared. * Used for Comparators and Comparables. * * @param javaVersion which version to generate Java for * @param isComparable true if want a code for a Comparable, false for a Comparator * @param checkForNull true if we should generate code to check for null sort key values. * @param oClass the class of the objects being compared. * @param isFieldDescending true if want a descending comparison * @param fieldName name of sort key field * * @return Code customised to compare two fields of this fieldType */ public String generateLastCompareToLine( final JavaVersion javaVersion, final boolean isComparable, final boolean checkForNull, final String oClass, final boolean isFieldDescending, final String fieldName ) { final FastCat sb = new FastCat( 2 ); final String diffExp = expandCompareToTemplate( javaVersion, isComparable, checkForNull, oClass, isFieldDescending, fieldName ); indent( sb, 2 ); sb.append( generateCompareToLine( javaVersion, diffExp ) ); return sb.toString(); } /** * how you would describe the sorting order. * * @return e.g. case sensitively, case insensitively, numerically */ public String getSortType() { return sortType; } /** * string for JComboBox display * * @return field type name */ public String toString() { return fieldTypeName; } }