Re: refactoring
On Tue, 07 Aug 2007 12:48:26 GMT, Roedy Green <see_website@mindprod.com.invalid> wrote, quoted or indirectly quoted someone who said :
Here is the code I am trying to tidy up. Of course my mind goes blank
on all the other code I either tidied or attempted to tidy with a
similar "multiple outputs" problem.
Here is how I refactored it, using an enum whimsically called SunSpot.
The code is much longer, and somewhat slower since it does a linear search
rather than a nested search, however it is definitely easier to maintain,
much easier to add an alias, and easier to proofread.
The nice thing is everything about a given SunSpot is all in one place.
Previously the aliases were handled separately from the main logic.
the Enum constant acts like a carrier for the three booleans.
package com.mindprod.htmlmacros;
import com.mindprod.common11.StringTools;
import static com.mindprod.htmlmacros.SunJDKandJREVersions.*;
import java.io.File;
/**
* Generate a reference to Sun documentation both on-line and on local hard
* disk.
*
* @author Roedy Green
* @noinspection WeakerAccess,UnusedDeclaration,WeakerAccess
*/
public final class Sun implements Macro {
// -------------------------- PUBLIC STATIC METHODS --------------------------
/**
* guts to Generate reference to a Sun HTML document.
*
* @param fileBeingProcessed File being processed that contains the macro to
* be expanded.
* @param desc human descriptinon of what it is.
* @param ref reference e.g api/java/awt/package-summary.html
* /s or \s. Generates http://java.sun.com/j2se/1.6.0/docs/api/java/awt/package-summary.html
*
* @return expand href both to on-line and local.
*
* @noinspection WeakerAccess
*/
public static String expand( File fileBeingProcessed,
String desc,
String ref )
{
/* chop lead / if any */
char firstChar = ref.charAt( 0 );
if ( firstChar == '/' || firstChar == File.separatorChar )
{
ref = ref.substring( 1 );
}
/** reference with / instead of File.separatorChar .
* partial or complete url.
*/
String refWithSlashes = ref.replace( File.separatorChar, '/' );
// find matching enum for this spot on Sun's site.
SunSpot sunSpot = SunSpot.valueOfAlias( refWithSlashes );
if ( sunSpot == null )
{
throw new IllegalArgumentException(
" unregonised spot on Sun's site specified in Sun macro "
+ refWithSlashes );
}
return buildSunLinks( sunSpot.isJavase6(),
sunSpot.isLocalRecent(),
sunSpot.isLocalOld(),
sunSpot.canonicalMatch( refWithSlashes ),
sunSpot.elaborate( desc ),
fileBeingProcessed );
}
// -------------------------- PUBLIC INSTANCE METHODS --------------------------
/**
* typical use: <!-- macro Sun "JButton" api/javax/swing/JButton.html
* --> <br> see sample at mindprod/jgloss/applet.html Generate a
* reference to a Sun HTML document
*
* @param parms first is description, second the reference
* api/java/awt/package-summary.html Can use
* either /s or \s in the url. Generates
* http://java.sun.com/javase/package-summary.html
* @param fileBeingProcessed File being processed that contains the macro to
* be expanded.
* @param quiet true if want progress messages suppressed
*
* @return expanded macro HTML
*/
public final String expand( String[] parms,
File fileBeingProcessed,
boolean quiet )
{
if ( !quiet )
{
System.out.print( "S" );
}
if ( parms.length != 2 )
{
throw new IllegalArgumentException(
"Sun needs description reference." );
}
String desc = parms[ 0 ];
String ref = parms[ 1 ];
return expand( fileBeingProcessed, desc, ref );
}
// -------------------------- STATIC METHODS --------------------------
/**
* Build HTML to describe the J: drive links.
*
* @param fileBeingProcessed File where the macro is being expanded.
*
* @return generated HTML
*/
private static String buildJDriveDescription( File fileBeingProcessed )
{
StringBuilder sb = new StringBuilder( 200 );
sb.append( "on your local <a class=\"plain\" href=\"" );
sb.append( Tools.linkWithSlashes( fileBeingProcessed,
"jgloss/jdrive.html" ) );
sb.append( "\">J: drive</a>." );
return sb.toString();
}
/**
* Build HTML for link to Sun website.
*
* @param javase6 true if this is a link in the javase/6/directory
* @param refWithSlashes reference with / instead of File.separatorChar.
* partial or complete url.
* @param elaborateDesc human-readable description for the link.
*
* @return generated HTML
*/
private static String buildLinkToSun( boolean javase6,
String refWithSlashes,
String elaborateDesc )
{
// not localRecent, just ref to Sun site.
StringBuilder sb = new StringBuilder( 200 );
sb.append( "<a class=\"offsite\" href=\"http://java.sun.com/" );
if ( javase6 )
{
sb.append( "javase/6/" );
}
sb.append( refWithSlashes );
sb.append( "\">" );
sb.append( elaborateDesc );
sb.append( "</a>" );
return sb.toString();
}
/**
* Build HTML for link to JavaDoc in the current JDK.
*
* @param refWithSlashes reference with / instead of File.separatorChar .
* partial or complete url.
*
* @return generated HTML
*/
private static String buildLocalLinkToCurrentJDK( String refWithSlashes )
{
StringBuilder sb = new StringBuilder( 200 );
sb.append( "<a href=\"" );
sb.append( JDK_URL );
sb.append( "/" );
sb.append( refWithSlashes );
sb.append( "\">" );
sb.append( "in the JDK " );
sb.append( JDK_FULL_VERSION );
sb.append( "</a> " );
return sb.toString();
}
/**
* Build HTML for link to JavaDoc in the two older JDKs.
*
* @param refWithSlashes reference with / instead of File.separatorChar.
* partial or complete url.
*
* @return generated HTML
*/
private static String buildLocalLinkToOldJDKs( String refWithSlashes )
{
StringBuilder sb = new StringBuilder( 200 );
sb.append( "or in the older <a href=\"" );
sb.append( OLD_JDK_URL );
sb.append( "/" );
sb.append( refWithSlashes );
sb.append( "\">JDK " );
sb.append( OLD_JDK_FULL_VERSION );
sb.append( "</a> " );
sb.append( "or the even older <a href=\"" );
sb.append( ANCIENT_JDK_URL );
sb.append( "/" );
sb.append( refWithSlashes );
sb.append( "\">JDK " );
sb.append( ANCIENT_JDK_FULL_VERSION );
sb.append( "</a> " );
return sb.toString();
}
/**
* Build HTML to for links to Sun remote and local.
*
* @param javase6 true = on Sun's site this is a reference to the
* javase/6/ directory.
* @param localRecent true = docs also locally available is the JDK
* docs.
* @param localOld true = docs available in old locally available
* JDK docs.
* @param refWithSlashes reference with / instead of File.separatorChar.
* partial or complete url.
* @param elaborateDesc expanded human-readable description for the
* link.
* @param fileBeingProcessed File where the macro is being expanded.
*
* @return generated HTML
*/
private static String buildSunLinks( boolean javase6,
boolean localRecent,
boolean localOld,
String refWithSlashes,
String elaborateDesc,
File fileBeingProcessed )
{
refWithSlashes = Tools.armourSpaces( refWithSlashes );
// now we have the data we need, we can build the HTML
StringBuilder sb = new StringBuilder( 400 );
sb.append( "\n" );
sb.append( "<div class=\"sun\">" );
if ( localRecent )
{
sb.append( elaborateDesc );
sb.append( " : available:" );
sb.append( "<ul>\n" );
sb.append( "<li>\n" );
sb.append( buildLinkToSun( javase6,
refWithSlashes,
"on the web at java.Sun.com" ) );
sb.append( "</li>\n" );
sb.append( "<li>\n" );
sb.append( buildLocalLinkToCurrentJDK( refWithSlashes ) );
if ( localOld )
{
// jre, platform and technotes might not exist in older versions.
// Reorganised too much to automate conversion to old URL
sb.append( buildLocalLinkToOldJDKs( refWithSlashes ) );
}
sb.append( buildJDriveDescription( fileBeingProcessed ) );
sb.append( "</li>\n" );
sb.append( "</ul>\n" );
}
else
{
// just a single link to sun without any additional wording.
sb.append( buildLinkToSun( javase6,
refWithSlashes,
elaborateDesc ) );
}
sb.append( "</div>\n" );
return sb.toString();
}
// -------------------------- INNER CLASSES --------------------------
/**
* enum used by Sun to encode spots on Sun's site we link to, and how they
* are handled. Nested in Sun class.
*/
@SuppressWarnings ( {"EnumeratedClassNamingConvention"} )
enum SunSpot {
TRAINING( false, false, false, "developer/onlineTraining/" )
{
String elaborate( String desc )
{
return "Sun's Developer On-line Training on " + bold(
desc );
}
},
TECHNICAL( false, false, false, "developer/technicalArticles/" )
{
String elaborate( String desc )
{
return "Sun's Developer Technical Articles on " + bold(
desc );
}
},
/**
* put at end, since it is a catch all in case no more specific technote
* category matches.
*/
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
DEVELOPER_DEFAULT( false, false, false, "developer/" )
{
String elaborate( String desc )
{
return "Sun's Developer Documentation on "
+ bold( desc );
}
},
CODECONV( false,
false,
false,
"docs/books/codeconv/",
"books/codeconv/",
"docs/codeconv/",
"codeconv/" )
{
String elaborate( String desc )
{
return "Sun's Coding Conventions: " + bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
JLS( false, false, false, "docs/books/jls/third_edition/html/", "jls/" )
{
String elaborate( String desc )
{
return "Sun's JLS (<b>J</b>ava <b>L</b>anguage <b>S</b>pecification): "
+ bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
JNI( false, false, false, "docs/books/jni/", "books/jni/", "jni/" )
{
String elaborate( String desc )
{
return "Sun's JNI (<b>J</b>ave <b>N</b>anive <b>I</b>nterface) Specification: "
+ bold( desc );
}
},
TUTORIAL( false,
false,
false,
"docs/books/tutorial/",
"books/tutorial/",
"tutorial/" )
{
String elaborate( String desc )
{
return "Sun's tutorial on " + bold( desc );
}
},
VMSPEC( false,
false,
false,
"docs/books/vmspec/",
"books/vmspec/",
"vmspec/" )
{
String elaborate( String desc )
{
return "Sun's VM (<b>V</b>irtual <b>M</b>achine) Specification: "
+ bold( desc );
}
},
/**
* put at end, since it is a catch all in case no more specific book
* category matches.
*/
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
BOOKS_DEFAULT( false, false, false, "docs/books/", "books/" )
{
String elaborate( String desc )
{
return "Sun's Book on " + bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
API( true, true, true, "docs/api/", "api/" )
{
String elaborate( String desc )
{
// strip any leading the and trailing class
desc = StringTools.removeHead( desc, "the " );
desc = StringTools.removeTail( desc, " class" );
// apply styles to class/method name
final int dotPlace = desc.indexOf( '.' );
final String decoratedDesc;
if ( dotPlace < 0 )
{
decoratedDesc =
"the <span class=\"jclass\">"
+ desc
+ "</span> class";
}
else
{
final String firstPart =
desc.substring( 0, dotPlace );
final boolean firstIsClass =
firstPart.length() <= 0
|| Character.isUpperCase( firstPart.charAt(
0 ) );
final String secondPart =
desc.substring( dotPlace + 1 );
final boolean secondIsClass =
secondPart.length() <= 0
|| Character.isUpperCase( secondPart.charAt(
0 ) );
// don't insert words class and methods, since obvious from dot.
decoratedDesc =
"<span class=\""
+ ( firstIsClass ? "jclass" : "jmethod" )
+ "\">"
+ firstPart
+ "</span>.<span class=\""
+ ( secondIsClass ? "jclass" : "jmethod" )
+ "\">"
+ secondPart
+ "</span>";
}
return "Sun's Javadoc on " + decoratedDesc;
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
JDK( true, true, false, "docs/jdk/", "jdk/" )
{
String elaborate( String desc )
{
return "Sun's JDK Guide to " + bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
JRE( true, true, false, "docs/jre/", "jre/" )
{
String elaborate( String desc )
{
return "Sun's JRE Guide to " + bold( desc );
}
},
LEGAL( true, false, true, "docs/legal/", "legal/" )
{
String elaborate( String desc )
{
return "Sun's Legal Guide to " + bold( desc );
}
},
PLATFORM( true, false, true, "docs/platform/", "platform/" )
{
String elaborate( String desc )
{
return "Sun's JDK Platform Guide to " + bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
TECHNOTE_GUIDES( true,
false,
true,
"docs/technotes/guides/",
"technotes/guides/" )
{
String elaborate( String desc )
{
return "Sun's JDK Technote Guide on " + bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
TECHNOTE_TOOLS( true,
false,
true,
"docs/technotes/tools/",
"technotes/tools/",
"tools/" )
{
String elaborate( String desc )
{
if ( desc.endsWith( ".exe" ) )
{
return "Sun's JDK Tool Guide to <span class=\"exe\">"
+ desc
+ "</span>";
}
else
{
return "Sun's JDK Tool Guide to " + bold( desc );
}
}
},
/**
* put at end, since it is a catch all in case no more specific technote
* category matches.
*/
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
TECHNOTE_DEFAULT( true, false, true, "docs/technotes/", "technotes/" )
{
String elaborate( String desc )
{
return "Sun's JDK Technotes on " + bold( desc );
}
},
/**
* put at end, since it is a catch all in case no more category
* matches.
*/
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
DOCS_DEFAULT( false, false, false, "docs/" )
{
String elaborate( String desc )
{
return "Sun's documentation on " + bold( desc );
}
},
GUIDE( false, false, false, "guide/" )
{
String elaborate( String desc )
{
return "Sun's Guide to " + bold( desc );
}
},
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
J2EE( false, false, false, "j2ee/" )
{
String elaborate( String desc )
{
return "Sun's J2EE docs on " + bold( desc );
}
},
JAVADOC( false, false, false, "j2se/javadoc/" )
{
String elaborate( String desc )
{
return "Sun's JavaDoc documentation on " + bold( desc );
}
},
PERFORMANCE( false, false, false, "performance/jvmstat" )
{
String elaborate( String desc )
{
return "Sun's Performance documentation on " + bold(
desc );
}
},
PRODUCTS( false, false, false, "products/" )
{
String elaborate( String desc )
{
return "Sun's Product Info on " + bold( desc );
}
},
WEBNOTES( true, false, false, "webnotes/" )
{
String elaborate( String desc )
{
return "Sun's Release notes on " + bold( desc );
}
},
WEBSERVICES( false, false, false, "webservices/" )
{
String elaborate( String desc )
{
return "Sun's Glassfish community webservice: " + bold(
desc );
}
},
/**
* put at end, since it is a catch all in case no more category
* matches.
*/
@SuppressWarnings ( {"EnumeratedConstantNamingConvention"} )
GENERAL_DEFAULT( false, false, false, "" )
{
String elaborate( String desc )
{
return "Sun's documentation on " + bold( desc );
}
};
/**
* constructor
*
* @param javase6 true on Sun's site is this a reference to the
* http://java.sun.com/javase/6/ directory. False,
* implies http://java.sun.com/
* @param localOld Are the docs available in old locally available
* JDK docs?
* @param localRecent Are the docs also locally available in the
* recent JDK docs?
* @param startsWith string URL in macro starts with that indentifies
* it.
* @param altStartsWith variable length list of aliases for startsWith.
*/
SunSpot( boolean javase6,
boolean localOld,
boolean localRecent,
String startsWith,
String... altStartsWith )
{
this.javase6 = javase6;
this.localOld = localOld;
this.localRecent = localRecent;
assert startsWith.endsWith( "/" )
|| startsWith.length()
== 0 : "missing trailing / on canonicalStartsWith";
this.canonicalStartsWith = startsWith;
assert altStartsWith.length < 1
|| altStartsWith[ 0 ].endsWith( "/" ) : "missing / on altStartsWith[0]";
assert altStartsWith.length < 2
|| altStartsWith[ 1 ].endsWith( "/" ) : "missing / on altStartsWith[1]";
this.altStartsWith = altStartsWith;
}
/**
* @param url url from the macro command line.
*
* @return corected url if match, null if no match.
*/
private String canonicalMatch( String url )
{
if ( url.startsWith( canonicalStartsWith ) )
{
return url;
}
for ( String alt : altStartsWith )
{
if ( url.startsWith( alt ) )
{
// replace alternate head with canonical one.
return canonicalStartsWith + url.substring( alt.length() );
}
}
return null;
}
/**
* Find matching enum constant that matches either the
* canonicalStartsWith or an alias.
*
* @param url url from the macro command line.
*
* @return matching enum constant, GENERAL_DEFAULT if no specific
* match.
*/
static SunSpot valueOfAlias( String url )
{
for ( SunSpot sunSpot : SunSpot.values() )
{
String canonicalUrl = sunSpot.canonicalMatch( url );
if ( canonicalUrl != null )
{
return sunSpot;
}
}
return GENERAL_DEFAULT;
}
/**
* get decorated long version of the description to display
*
* @param desc description from the macro line
*
* @return expanded long verion of the description.
*/
abstract String elaborate( String desc );
/**
* what the URL specified in the macro starts with. This is the official
* prefix for URLs pointing to this spot on Sun's site.
*/
private final String canonicalStartsWith;
/**
* aliases canonicalStartsWith strings.
*/
private final String[] altStartsWith;
/**
* get bold version of description.
*
* @param desc description of this link.
*
* @return bold version of description.
*/
private static String bold( String desc )
{
return "<b>" + desc + "</b>";
}
/**
* Are the docs also locally available in the recent JDK docs?
*/
private final boolean localRecent;
/**
* Are the docs available in old locally available JDK docs?
*/
private final boolean localOld;
/**
* true on Sun's site is this a reference to the http://java.sun.com/javase/6/
* directory. False, implies http://java.sun.com/
*/
private final boolean javase6;
/**
* Are the docs also locally available in the recent JDK docs?
*
* @return true if available in local recent JDK.
*/
boolean isLocalRecent()
{
return localRecent;
}
/**
* Are the docs also locally available in the old JDK docs?
*
* @return true if available in local old JDK.
*/
boolean isLocalOld()
{
return localOld;
}
/**
* Is this is javase/6 dir?
*
* @return true on Sun's site is this a reference to the
* http://java.sun.com/javase/6/ directory. False, implies
* http://java.sun.com/
*/
boolean isJavase6()
{
return javase6;
}
}// end SunSpon enum
}// end Sun
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com