// Copyright 2004 Bradford Holcombe. All rights reserved.
package com.bradfordholcombe.JSPWiki.filters;
import com.ecyrd.jspwiki.WikiContext;
import com.ecyrd.jspwiki.filters.BasicPageFilter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;
/**
Breaks up a list of <ul> lists into column format. Thread-safe.
@author Bradford Holcombe
*/
public class ColumnFilter extends BasicPageFilter
{
/**
* Wikiname of the page to apply this filter to.
*/
private String pageName;
/**
* if true, sort list entries.
*/
private boolean sort = false;
/**
* Number of columns to create.
*/
private int columnCount = DEFAULT_COLUMN_COUNT_PROPERTY;
/**
* Name of the wiki page name property
*/
private static final String PAGE_TITLE_PROPERTY = "pageTitle";
/**
* Name of the number of columns property.
*/
private static final String COLUMN_COUNT_PROPERTY = "columnCount";
/**
* Name of the sort property.
*/
private static final String SORT_PROPERTY = "sort";
/**
* Default number of columns.
*/
private static final int DEFAULT_COLUMN_COUNT_PROPERTY = 5;
/**
* @see com.ecyrd.jspwiki.filters.PageFilter#initialize(java.util.Properties)
*/
public final void initialize( final Properties props )
{
pageName = (String)props.get( PAGE_TITLE_PROPERTY );
if( props.containsKey( COLUMN_COUNT_PROPERTY ) )
{
//System.out.println( props.get( COLUMN_COUNT_PROPERTY ) );
columnCount = Integer.valueOf( (String)props.get( COLUMN_COUNT_PROPERTY ) ).intValue();
}
if( props.containsKey( SORT_PROPERTY ) )
{
//System.out.println( props.get( SORT_PROPERTY ) );
sort = Boolean.valueOf( (String)props.get( SORT_PROPERTY ) ).booleanValue();
}
//System.out.println( "ColumnFilter:" + this + " " + pageName + " " + columnCount + " " + sort );
}
/**
* @see com.ecyrd.jspwiki.filters.PageFilter#preTranslate(com.ecyrd.jspwiki.WikiContext, java.lang.String)
*/
public final String preTranslate( final WikiContext wikiContext, final String wikiContent )
{
if( !sort )
{
return wikiContent;
}
if( wikiContext != null && !wikiContext.getPage().getName().equals( pageName ) )
{
return wikiContent;
}
int listStart = wikiContent.indexOf( "*" );
int listEnd = wikiContent.length();
if( listStart == -1 )
{
return wikiContent;
}
String originalList = wikiContent.substring( listStart, listEnd );
String newList = sort( originalList );
StringBuffer newPage = new StringBuffer();
newPage.append( wikiContent );
newPage.replace( listStart, listEnd, newList );
return newPage.toString();
}
/**
* Return the wiki list alphabetised and sectioned.
*
* @param list
* @return
*/
private final String sort( final String list )
{
Comparator comparator = new Comparator()
{
public final int compare( final Object o1, final Object o2 )
{
if( !( o1 instanceof String ) || !( o2 instanceof String ) )
{
throw new ClassCastException( "Can't compare " + o1.getClass().getName() );
}
return normalizeListItem( (String)o1 ).compareTo( normalizeListItem( (String)o2 ) );
}
};
SortedSet itemSet = new TreeSet( comparator );
int cursor = 0;
int listLength = list.length();
while( cursor <= listLength )
{
int lineEnd = list.indexOf( "\n", cursor + 1 );
if( lineEnd == -1 )
{
lineEnd = list.length();
}
String line = list.substring( cursor, lineEnd );
itemSet.add( line + "\n" );
cursor += line.length() + 1;
}
StringBuffer newList = new StringBuffer();
char section = '?';
for( Iterator sorter = itemSet.iterator(); sorter.hasNext(); )
{
String line = (String)sorter.next();
String normalLine = normalizeListItem( line );
char sectionChar = normalLine.charAt( 0 );
if( sectionChar >= '0' && sectionChar <= '9' )
{
if( section == '?' )
{
newList.append( "!0-9\n" );
section = '0';
}
}
else if( sectionChar > section )
{
newList.append( "!" ).append( Character.toUpperCase( sectionChar ) ).append( "\n" );
section = sectionChar;
}
newList.append( line );
}
return newList.toString();
}
/**
* Returns the alphabetical sort normalized version of a string.
*
* @param s String to normalize
* @return Normalized string
*/
private final String normalizeListItem( final String s )
{
String s1 = s.toLowerCase();
if( s1.startsWith( "*" ) )
{
s1 = s1.substring( 1 );
}
if( s1.startsWith( "[" ) )
{
s1 = s1.substring( 1 );
}
if( s1.startsWith( "the " ) )
{
s1 = s1.substring( 4 );
}
if( s1.startsWith( "a " ) )
{
s1 = s1.substring( 2 );
}
if( s1.startsWith( "an " ) )
{
s1 = s1.substring( 3 );
}
if( s1.startsWith( "\"" ) )
{
s1 = s1.substring( 1 );
}
return s1;
}
/**
* This method is called after a page has been fed through the TranslatorReader,
* so anything you are seeing here is translated content.
*
* @param wikiContext The current wikicontext.
* @param htmlContent WikiMarkup.
* @return Translated content.
* @see com.ecyrd.jspwiki.filters.PageFilter#postTranslate
*/
public final String postTranslate( final WikiContext wikiContext, final String htmlContent )
{
if( wikiContext != null && !wikiContext.getPage().getName().equals( pageName ) )
{
return htmlContent;
}
if( columnCount == 1 )
{
return htmlContent;
}
int columnPercent = 100 / columnCount;
int listStart = findListStart( htmlContent );
int listEnd = findListEnd( htmlContent );
if( listStart == -1 || listEnd == -1 )
{
return htmlContent;
}
String originalList = htmlContent.substring( listStart, listEnd );
// add CSS class
originalList = addClassAttributes( originalList );
String lineAccurateList = normalize( originalList );
String newList = breakup( lineAccurateList, columnPercent );
StringBuffer newPage = new StringBuffer();
newPage.append( htmlContent );
newPage.replace( listStart, listEnd, newList );
return newPage.toString();
}
/**
* Add a class attribute to all important tags.
*
* @param content Orignal HTML
* @return tagged HTML
*/
private final String addClassAttributes( final String content )
{
StringBuffer result = new StringBuffer( content );
int cursor = result.indexOf( "