package org.litesoft.template; import java.util.*; // Copyright Status: // // All Software available from LiteSoft.org (including this file) is // hereby released into the public domain. // // It is free! As in, you may use it freely in both commercial and // non-commercial applications, bundle it with your software // distribution, include it on a CD-ROM, list the source code in a book, // mirror the documentation at your own web site, or use it in any other // way you see fit. // // NO Warranty! // // All software is provided "as is". // // There is ABSOLUTELY NO WARRANTY OF ANY KIND: not for the design, fitness // (for a particular purpose), level of errors (or lack thereof), or // applicability of this software. The entire risk as to the quality // and performance of this software is with you. Should this software // prove defective, you assume the cost of all necessary servicing, repair // or correction. // // In no event unless required by applicable law or agreed to in writing // will any party who created or may modify and/or redistribute this // software, be liable to you for damages, including any general, // special, incidental or consequential damages arising out of the use or // inability to use this software (including but not limited to loss of // data or data being rendered inaccurate or losses sustained by you or // third parties or a failure of this software to operate with any // other programs), even if such holder or other party has been advised // of the possibility of such damages. // // NOTE: Should you discover a bug, have a recogmendation for a change, wish // to submit modifications, or wish to add new classes/functionality, // please email them to: // // changes44@litesoft.org // /** * TemplateFiller is a class to support the "population" of a * template. A template is a String Array (where each entry represents * at least one, and possibly multiple lines) with "substitution" * fields. A "substitution" field is demarkated by ".%" and "%.". * A "substitution" field, if it exists alone, at the beginning of * a line, "may" be substituted by multiple lines. * * Exceptions: All problems caught when the parameter(s) are checked (as * indicated/implied in the @param tags) will generate an IllegalArgumentException, * and means the API user has a problem. If a NullPointerException (or some * others, like: ClassCastException or ArrayIndexOutOfBoundsException) is thrown, * it means the API developer has a problem. Any Exception that is explicitly * thrown in the API, but unrelated to a parameter, will be listed in the throws * clause (and hopefully in the tag @throws). These may (but probably won't) be * checked Exceptions. * * @author George Smith * @version 1.2 02/07/02 Exception Policy, Use of IllegalArgument class. * @version 1.1 11/12/01 JavaDoc'd * @version 1.0 11/07/01 */ public class TemplateFiller { private static final int LINETYPE_NULL = 0; private static final int LINETYPE_NOSUB = 1; private static final int LINETYPE_ONLYSUB = 2; private static final int LINETYPE_MULTISUB = 3; private BlockProcessor[] zBlockProcessors; /** * Construct a TemplateFiller with a specific template (String array).
* * Under normal operation, the a TemplateFiller will be used over and * over, but with different Resolvers (by calling makePopulated).
* * @param pTemplate String array that IS a template (!null). */ public TemplateFiller( String[] pTemplate ) { if ( pTemplate == null ) throw new NullPointerException( "Template required" ); ArrayList blockProcessors = new ArrayList(); if ( pTemplate.length != 0 ) { ArrayList noSubs = new ArrayList(); SubstitutionDeterminer sd = new SubstitutionDeterminer( getStartsWith() , getEndsWith() ) ; for ( int i = 0 ; i < pTemplate.length ; i++ ) switch ( sd.determineLineType( pTemplate[i] ) ) { default: case LINETYPE_NULL : break; case LINETYPE_NOSUB : noSubs.add( sd.getLine() ); break; case LINETYPE_ONLYSUB : addNoSubs( blockProcessors , noSubs ); blockProcessors.add( new BlockProcLineIsSub( sd ) ); break; case LINETYPE_MULTISUB: addNoSubs( blockProcessors , noSubs ); blockProcessors.add( new BlockProcMultiSubsOnLine( sd ) ); break; } addNoSubs( blockProcessors , noSubs ); } zBlockProcessors = (BlockProcessor[]) blockProcessors.toArray( new BlockProcessor[blockProcessors.size()] ); } private void addNoSubs( ArrayList pBlockProcessors , ArrayList pNoSubs ) { if ( pNoSubs.size() != 0 ) { pBlockProcessors.add( new BlockProcNoSub( pNoSubs ) ); pNoSubs.clear(); } } /** * Generate a populated instance of this's Template.
* * The TemplateResolver controls what gets substituted into the * Template to generate the resulting returned/generated result * (String array)..
* * @param pTemplateResolver An object that provides substitution support * for a Template (!null).
* * @return a String array that is a populated template. */ public String[] makePopulated( TemplateResolver pTemplateResolver ) { if ( pTemplateResolver == null ) throw new NullPointerException("TemplateResolver required"); ArrayList list = new ArrayList(); for ( int i = 0 ; i < zBlockProcessors.length ; i++ ) zBlockProcessors[i].resolveBlock( list , pTemplateResolver ); return (String[]) list.toArray( new String[list.size()] ); } /** * Get the definition of the demarkation that a field starts with.
* * The Current value is ".%".
* * This is protected so that someone could extend this * class and override this method to provide for an alternative * StartsWith String. */ protected String getStartsWith() { return ".%"; } /** * Get the definition of the demarkation that a field ends with.
* * The Current value is "%.".
* * This is protected so that someone could extend this * class and override this method to provide for an alternative * EndsWith String. */ protected String getEndsWith() { return "%."; } private static class SubstitutionDeterminer { private String zStartsWith; private String zEndsWith; private int zStartsWithLength; private int zEndsWithLength; private String zLine; private String zSubstitutionKey; private String zLeftTextChunk; private int zSkip; public SubstitutionDeterminer( String pStartsWith , String pEndsWith ) { zStartsWithLength = (zStartsWith = pStartsWith).length(); zEndsWithLength = (zEndsWith = pEndsWith).length(); } public String getStartsWith() { return zStartsWith; } public String getEndsWith() { return zEndsWith; } public String getLine() { return zLine; } public String getSubstitutionKey() { return zSubstitutionKey; } public String getLeftTextChunk() { return zLeftTextChunk; } public int determineLineType( String pLine ) { if ( null == (zLine = pLine) ) return LINETYPE_NULL; zSkip = 0 ; if ( !isAnotherKey() ) return LINETYPE_NOSUB; if ( (getLeftTextChunk().length() == 0) && (zSkip == zLine.length()) ) return LINETYPE_ONLYSUB; return LINETYPE_MULTISUB; } public boolean isAnotherKey() { zSubstitutionKey = ""; zLeftTextChunk = zLine.substring( zSkip ); int zAt = zLeftTextChunk.indexOf( zStartsWith ); if ( zAt == -1 ) return false; int zAfterAt = zAt + zStartsWithLength; int zTo = zLeftTextChunk.indexOf( zEndsWith , zAfterAt ); if ( zTo == -1 ) return false; int zAfterTo = zTo + zEndsWithLength; zSubstitutionKey = zLeftTextChunk.substring( zAfterAt , zTo ); zLeftTextChunk = zLeftTextChunk.substring( 0 , zAt ); zSkip += zAfterTo ; return true; } } private static abstract class BlockProcessor { public abstract void resolveBlock( ArrayList pLines , TemplateResolver pTemplateResolver ); } private static class BlockProcNoSub extends BlockProcessor { private String[] zBlock; public BlockProcNoSub( ArrayList pBlock ) { zBlock = (String[]) pBlock.toArray( new String[pBlock.size()] ); } public void resolveBlock( ArrayList pLines , TemplateResolver pTemplateResolver ) { for ( int i = 0 ; i < zBlock.length ; i++ ) pLines.add( zBlock[i] ) ; } } private static abstract class BlockProcSub extends BlockProcessor { private String zLine; protected BlockProcSub( String pLine ) { zLine = pLine; } protected void notFound( ArrayList pLines , String pSubstitutionKey ) { pLines.add( "*** NOT FOUND (" + pSubstitutionKey + "): " + zLine ); } protected void InvalidUsage( ArrayList pLines , String pSubstitutionKey ) { pLines.add( "*** INVALID USAGE (" + pSubstitutionKey + "): " + zLine ); } } private static class BlockProcLineIsSub extends BlockProcSub { private String zSubstitutionKey; public BlockProcLineIsSub( SubstitutionDeterminer pSubstitutionDeterminer ) { super( pSubstitutionDeterminer.getLine() ) ; zSubstitutionKey = pSubstitutionDeterminer.getSubstitutionKey() ; } public void resolveBlock( ArrayList pLines , TemplateResolver pTemplateResolver ) { TemplateSubstitutionFieldControl fc = pTemplateResolver.getSubstitutionFieldControl( zSubstitutionKey ); if ( fc == null ) notFound( pLines , zSubstitutionKey ); else { String[] sub = fc.getMultiLineSubstitution(); if ( sub == null ) InvalidUsage( pLines , zSubstitutionKey ); else for ( int i = 0 ; i < sub.length ; i++ ) { String line = sub[i] ; if ( line != null ) pLines.add( line ); } } } } private static class BlockProcMultiSubsOnLine extends BlockProcSub { private String zStartsWith; private String zEndsWith; private String[] zLineParts; // Alternating text,key,text,key... public BlockProcMultiSubsOnLine( SubstitutionDeterminer pSubstitutionDeterminer ) { super( pSubstitutionDeterminer.getLine() ) ; zStartsWith = pSubstitutionDeterminer.getStartsWith(); zEndsWith = pSubstitutionDeterminer.getEndsWith(); ArrayList parts = new ArrayList(); do { parts.add( pSubstitutionDeterminer.getLeftTextChunk() ); parts.add( pSubstitutionDeterminer.getSubstitutionKey() ) ; } while ( pSubstitutionDeterminer.isAnotherKey() ); parts.add( pSubstitutionDeterminer.getLeftTextChunk() ); zLineParts = (String[]) parts.toArray( new String[parts.size()] ); } public void resolveBlock( ArrayList pLines , TemplateResolver pTemplateResolver ) { StringBuffer sb = new StringBuffer( zLineParts[0] ); for ( int i = 1 ; i < zLineParts.length ; i++ ) { String substitutionKey = zLineParts[i++]; TemplateSubstitutionFieldControl fc = pTemplateResolver.getSubstitutionFieldControl( substitutionKey ) ; if ( fc == null ) { notFound( pLines , substitutionKey ); sb.append( zStartsWith ); sb.append( substitutionKey ); sb.append( zEndsWith ); } else { String sub = fc.getStringSubstitution(); if ( sub == null ) InvalidUsage( pLines , substitutionKey ); else sb.append( sub ); } sb.append( zLineParts[i] ); } pLines.add( sb.toString() ); } } }