/* * [InterfaceType.java] * * Summary: enum for the three types of comparison interface we can generate. * * 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-05-21 initial version */ package com.mindprod.comparatorcutter; import com.mindprod.common18.BigDate; import com.mindprod.common18.JavaVersion; import com.mindprod.fastcat.FastCat; /** * enum for the three types of comparison interface we can generate. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2009-05-21 initial version * @since 2009-05-21 */ public enum InterfaceType { COMPARABLE { /** * compose Java source code for a Comparable. * @param javaVersion which version of Java to generate code for. * @param checkForNull true if we should generate code to check for null sort key values. * @param oClass class name of Objects we are comparing. * @param comment comment about what the generated code does. * @param sortKeys array of SortKeys for fields to be sorted * @return Java source code snippet */ String compose( final JavaVersion javaVersion, final boolean checkForNull, final String gClass, final String oClass, final String comment, final SortKey... sortKeys ) { final String today = BigDate.localToday().toString(); final FastCat sb = new FastCat( 80 ); sb.append( "// <> Generated Comparable Java code to be inserted into " ); sb.append( oClass ); sb.append( ".java.\n" ); sb.append( "// <> package ...\n" ); sb.append( "// <> import ...\n" ); sb.append( "// <> no need to import java.lang.Comparable;\n" ); sb.append( "import com.mindprod.common18.Misc;\n" ); if ( javaVersion.supportsAnnotations() ) { sb.append( "// <> @NotNull is for the IntelliJ IDE. You can remove it if your IDE does not " + "support it.\n" ); } sb.append( "\n" ); sb.append( "class " ); sb.append( oClass ); sb.append( " implements Comparable" ); if ( javaVersion.supportsGenerics() ) { sb.append( "<" ); sb.append( oClass ); sb.append( ">\n" ); } sb.append( "\n" ); sb.append( "// <> {\n" ); sb.append( "// <> Insert the generated code, discarding the <> locating comments.\n" ); sb.append( "\n" ); // compareTo method sb.append( "/**\n" ); sb.append( " * " ); sb.append( comment ); sb.append( ".\n" ); sb.append( " * Defines default the sort order for " ); sb.append( oClass ); sb.append( " Objects.\n" ); sb.append( " * Compare this " ); sb.append( oClass ); sb.append( " with another " ); sb.append( oClass ); sb.append( "\n" ); if ( sortKeys.length != 0 ) { sb.append( " * " ); sb.append( describeSortKeyOrderInWords( sortKeys ) ); sb.append( "\n" ); } sb.append( " * Informally, returns (this-other) or +ve if this sorts after other.\n" ); sb.append( " *\n" ); sb.append( buildSummary( javaVersion, comment, today ) ); sb.append( " * @param other other " ); sb.append( oClass ); sb.append( " to compare with this one\n" ); sb.append( " *\n" ); sb.append( " * @return +ve if this>other, 0 if this==other, -ve if this<other\n" ); sb.append( " */\n" ); sb.append( "public final int compareTo( " ); if ( javaVersion.supportsGenerics() ) { if ( javaVersion.supportsAnnotations() ) { sb.append( "@NotNull " ); } sb.append( oClass ); } else { sb.append( "Object" ); } sb.append( " other )\n" ); sb.append( "{\n" ); if ( sortKeys.length == 0 ) { sb.append( "// no sort keys defined yet.\n" ); sb.append( "return 0; // dummy code for now\n" ); } else { sb.append( generateKeyComparisons( sortKeys, true /* comparator */, javaVersion, checkForNull, oClass ) ); } sb.append( "}\n" ); return sb.toString(); } int scrollTo() { return 500; } public String toString() { return "Comparable (class default)"; } }, COMPARATOR_TOP_LEVEL { /** * compose Java source code for a Comparator. * @param javaVersion which version of Java to generate code for. * @param checkForNull true if we should generate code to check for null sort key values. * @param gClass class name of the Comparator we are generating. * @param oClass class name of Objects we are comparing. * @param comment comment about what the generated class does. * @param sortKeys array of SortKeys for fields to be sorted * @return Java source code snippet */ String compose( final JavaVersion javaVersion, final boolean checkForNull, final String gClass, final String oClass, final String comment, final SortKey... sortKeys ) { final String today = BigDate.localToday().toString(); final FastCat sb = new FastCat( sortKeys.length + 122 ); sb.append( "// <> Generated Comparator Java code to save as " ); sb.append( gClass ); sb.append( ".java.\n" ); sb.append( "// <> Insert the generated code, discarding the <> locating comments.\n" ); if ( javaVersion.supportsAnnotations() ) { sb.append( "// <> @NotNull is for the IntelliJ IDE. You can remove it if your IDE does not " + "support it.\n" ); } sb.append( "/*\n" ); sb.append( " * [" ); sb.append( gClass ); sb.append( ".java]\n" ); sb.append( " *\n" ); sb.append( buildSummary( javaVersion, comment, today ) ); sb.append( " */\n" ); sb.append( "// <> package ...\n" ); sb.append( "import java.util.Comparator;\n" ); sb.append( "import com.mindprod.common18.Misc;\n" ); sb.append( "\n" ); sb.append( "/**\n" ); sb.append( " * " ); sb.append( comment ); sb.append( ".\n" ); sb.append( " *

\n" ); sb.append( " * Defines an alternate sort order for " ); sb.append( oClass ); sb.append( ".\n" ); sb.append( " *\n" ); sb.append( " * @author Roedy Green\n" ); sb.append( " * @version ", ComparatorCutter.VERSION_STRING, " " ); sb.append( today ); sb.append( " - initial release\n" ); sb.append( " * @since " ); sb.append( today ); sb.append( "\n" ); sb.append( " */\n" ); sb.append( "class " ); sb.append( gClass ); sb.append( " implements Comparator" ); if ( javaVersion.supportsGenerics() ) { sb.append( "<" ); sb.append( oClass ); sb.append( ">" ); } sb.append( "\n" ); sb.append( "{\n" ); sb.append( "" ); // compare method sb.append( "/**\n" ); sb.append( " * " ); sb.append( comment ); sb.append( ".\n" ); sb.append( " * Defines an alternate sort order for " ); sb.append( oClass ); sb.append( " " ); sb.append( " * Compare two " ); sb.append( oClass ); sb.append( " Objects.\n" ); if ( sortKeys.length != 0 ) { sb.append( " * " ); sb.append( describeSortKeyOrderInWords( sortKeys ) ); sb.append( "\n" ); } sb.append( " * Informally, returns (a-b), or +ve if a sorts after b.\n" ); sb.append( " *\n" ); sb.append( buildSummary( javaVersion, comment, today ) ); sb.append( " * @param a first " ); sb.append( oClass ); sb.append( " to compare\n" ); sb.append( " * @param b second " ); sb.append( oClass ); sb.append( " to compare\n" ); sb.append( " *\n" ); sb.append( " * @return +ve if a>b, 0 if a==b, -ve if a<b\n" ); sb.append( " */\n" ); sb.append( "public final int compare( " ); if ( javaVersion.supportsGenerics() ) { if ( javaVersion.supportsAnnotations() ) { sb.append( "@NotNull " ); } sb.append( oClass ); sb.append( " a, " ); if ( javaVersion.supportsAnnotations() ) { sb.append( "@NotNull " ); } sb.append( oClass ); sb.append( " b" ); } else { sb.append( "Object a, Object b" ); } sb.append( " )\n" ); sb.append( "{\n" ); if ( sortKeys.length == 0 ) { sb.append( "// no sort keys defined yet.\n" ); sb.append( "return 0; // dummy code for now\n" ); } else { // generate code for each sort field. sb.append( generateKeyComparisons( sortKeys, false /* comparable */, javaVersion, checkForNull, oClass ) ); } sb.append( "}\n" ); sb.append( "}\n" ); return sb.toString(); } int scrollTo() { return 780; } public String toString() { return "Comparator (top level)"; } }, COMPARATOR_NESTED { /** * compose Java source code for a Comparator. * @param javaVersion which version of Java to generate code for. * @param checkForNull true if we should generate code to check for null sort key values. * @param gClass class name of the Comparator we are generating. * @param oClass class name of Objects we are comparing. * @param comment comment about what the generated class does. * @param sortKeys array of SortKeys for fields to be sorted * @return Java source code snippet */ String compose( final JavaVersion javaVersion, final boolean checkForNull, final String gClass, final String oClass, final String comment, final SortKey... sortKeys ) { final FastCat sb = new FastCat( 90 ); sb.append( "// <> Generated Comparator Java code to insert in " ); sb.append( oClass ); sb.append( ".java as a nested static class, discarding the comments above the package line.\n" ); sb.append( "// <> Insert the generated code, discarding the <> locating comments.\n" ); if ( javaVersion.supportsAnnotations() ) { sb.append( "// <> @NotNull is for the IntelliJ IDE. You can remove it if your IDE does not " + "support it.\n" ); } sb.append( "// <> ...\n" ); sb.append( "\n" ); // comparator class sb.append( "/**\n" ); sb.append( " * " ); sb.append( comment ); sb.append( ".\n" ); sb.append( " *

\n" ); sb.append( " * Defines an alternate sort order for " ); sb.append( oClass ); sb.append( ".\n" ); sb.append( " */\n" ); sb.append( "static class " ); sb.append( gClass ); sb.append( " implements Comparator" ); if ( javaVersion.supportsGenerics() ) { sb.append( "<" ); sb.append( oClass ); sb.append( ">" ); } sb.append( "\n" ); sb.append( "{\n" ); sb.append( "\n" ); // compare method sb.append( "/**\n" ); sb.append( " * " ); sb.append( comment ); sb.append( ".\n" ); sb.append( " * Defines an alternate sort order for " ); sb.append( oClass ); sb.append( " for Java " ); sb.append( javaVersion.toString() ); sb.append( "+.\n" ); sb.append( " * Compare two " ); sb.append( oClass ); sb.append( " Objects.\n" ); if ( sortKeys.length != 0 ) { sb.append( " * " ); sb.append( describeSortKeyOrderInWords( sortKeys ) ); sb.append( "\n" ); } sb.append( " * Informally, returns (a-b), or +ve if a comes after b.\n" ); sb.append( " * The Java source code for this Comparator was generated by the \n" ); sb.append( " * Canadian Mind Products ComparatorCutter Applet at http://mindprod" + ".com/applet/comparatorcutter.html\n" ); sb.append( " * For non-military use only. http://mindprod.com/contact/nonmil.html\n" ); sb.append( " *\n" ); sb.append( " * @param a first " ); sb.append( oClass ); sb.append( " to compare\n" ); sb.append( " * @param b second " ); sb.append( oClass ); sb.append( " to compare\n" ); sb.append( " *\n" ); sb.append( " * @return +ve if a>b, 0 if a==b, -ve if a<b\n" ); sb.append( " */\n" ); sb.append( "public final int compare( " ); if ( javaVersion.supportsGenerics() ) { if ( javaVersion.supportsAnnotations() ) { sb.append( "@NotNull " ); } sb.append( oClass ); sb.append( " a, " ); if ( javaVersion.supportsAnnotations() ) { sb.append( "@NotNull " ); } sb.append( oClass ); sb.append( " b" ); } else { sb.append( "Object a, Object b" ); } sb.append( " )\n" ); sb.append( "{\n" ); if ( sortKeys.length == 0 ) { sb.append( "// no sort keys defined yet.\n" ); sb.append( "return 0; // dummy code for now\n" ); } else { // generate code for each sort field. sb.append( generateKeyComparisons( sortKeys, false /* comparable */, javaVersion, checkForNull, oClass ) ); } sb.append( "}\n" ); sb.append( "}\n" ); return sb.toString(); } int scrollTo() { return 780; } public String toString() { return "Comparator (nested)"; } }; /** * generate code for the identification summary comment * * @param javaVersion target version of Java * @param comment comment describing comparator * @param today YYYY-MM-DD today * * @return generated Java code */ private static String buildSummary( final JavaVersion javaVersion, final String comment, final String today ) { final FastCat sb = new FastCat( 17 ); sb.append( " * Summary: " ); sb.append( comment ); sb.append( ".\n" ); sb.append( " *\n" ); sb.append( " * Requires: JDK " ); sb.append( javaVersion.toString() ); sb.append( "+.\n" ); sb.append( " *\n" ); sb.append( " * Java code generated with: Canadian Mind Products ComparatorCutter.\n" ); sb.append( " *\n" ); sb.append( " * Version History:\n" ); sb.append( " * ", ComparatorCutter.VERSION_STRING, " " ); sb.append( today ); sb.append( " - initial release\n" ); sb.append( " *\n" ); return sb.toString(); } /** * Describe sort key order in words. * * @param sortKeys non-empty array of SortKeys * * @return sentence describing sort key order in English narrative terminated by a period. */ private static String describeSortKeyOrderInWords( final SortKey... sortKeys ) { final FastCat sb = new FastCat( 6 * sortKeys.length + 2 ); assert sortKeys.length != 0 : "empty sort keys list"; sb.append( "Compares " ); // Generate comment to describe the sort. for ( int i = 0; i < sortKeys.length; i++ ) { final SortKey sortKey = sortKeys[ i ]; final boolean isFieldDescending = sortKey.isFieldDescending(); String fieldName = sortKey.getFieldName(); if ( fieldName.length() == 0 ) { fieldName = "whole object"; } final String sortType = sortKey.getFieldType().getSortType(); if ( isFieldDescending ) { sb.append( "descending " ); } sb.append( fieldName ); sb.append( " " ); sb.append( sortType ); // treat last line differently if ( i < sortKeys.length - 1 ) { sb.append( " then " ); } } sb.append( "." ); return sb.toString(); } /** * Generate the code to compares sort fields. Used in both Comparators and Comparables. * * @param sortKeys array of descriptors of the keys * @param isComparator true if we should generate code for a Comparator, false for a Comparable. * @param javaVersion * @param checkForNull true if we should generate code to check for null sort key values. * @param oClass class of the fields being compared * * @return String containing the Java source code */ private static String generateKeyComparisons( final SortKey[] sortKeys, final boolean isComparator, final JavaVersion javaVersion, final boolean checkForNull, final String oClass ) { final FastCat sb = new FastCat( sortKeys.length * 2 ); int diffIndex = 0; // generate code for each sort field. for ( int i = 0; i < sortKeys.length; i++ ) { final SortKey sortKey = sortKeys[ i ]; final FieldType fieldType = sortKey.getFieldType(); final boolean isFieldDescending = sortKey.isFieldDescending(); final String fieldName = sortKey.getFieldName(); if ( i < sortKeys.length - 1 ) { sb.append( fieldType.generateIntermediateCompareToLine( javaVersion, isComparator, checkForNull, oClass, isFieldDescending, fieldName, diffIndex++ ) ); if ( fieldType == FieldType.CASE_INSENSITIVE_STRING ) { // add a case-sensitive tie-breaker sb.append( FieldType.CASE_SENSITIVE_STRING.generateIntermediateCompareToLine( javaVersion, isComparator, checkForNull, oClass, isFieldDescending, fieldName, diffIndex++ ) ); } } else { // last pair to compare. Don't need diff variable. if ( fieldType == FieldType.CASE_INSENSITIVE_STRING ) { sb.append( fieldType.generateIntermediateCompareToLine( javaVersion, isComparator, checkForNull, oClass, isFieldDescending, fieldName, diffIndex++ ) ); // add a case-sensitive tie-breaker sb.append( FieldType.CASE_SENSITIVE_STRING.generateLastCompareToLine( javaVersion, isComparator, checkForNull, /* even if checked, could be null null */ oClass, isFieldDescending, fieldName ) ); } else { sb.append( fieldType.generateLastCompareToLine( javaVersion, isComparator, checkForNull, oClass, isFieldDescending, fieldName ) ); } } } return sb.toString(); } /** * compose Java code for this interface type * * @param javaVersion Which Java version to generate code for * @param checkForNull true if we should generate code to check for null sort key values. * @param gClass class name of the Comparator we are generating. * @param oClass class name of Objects we are comparing. * @param comment comment about what the generated class does. * @param sortKeys array of SortKeys for fields to be sorted * * @return Java source code snippet */ abstract String compose( JavaVersion javaVersion, final boolean checkForNull, String gClass, String oClass, String comment, SortKey... sortKeys ); /** * after code in generated, where to jump to in the scroll * * @return number of pixels from the top */ abstract int scrollTo(); }