Update of /cvsroot/displaytag/table-ben/src/com/tablelib/tags
In directory sc8-pr-cvs1:/tmp/cvs-serv11035

Added Files:
        ActionTag.java ColumnDecorator.java ColumnTag.java 
        Decorator.java DecoratorImpl.java PagingInformation.java 
        PaginigInformationImpl.java SearchTag.java SetPropertyTag.java 
        SmartListHelper.java TableDecorator.java TableExporter.java 
        TableImpl.java TableTag.java TableTagExtraInfo.java 
Log Message:
initial checkin

--- NEW FILE: ActionTag.java ---
package com.tablelib.tags;

import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.JspException;

/**
 * Created by IntelliJ IDEA.
 * User: simps
 * Date: Feb 1, 2003
 * Time: 12:57:26 PM
 *
 * The actionTag is "verb" in the statmement created by each columnTag.
 *
 */
public class ActionTag extends BodyTagSupport implements Cloneable {
    private String href;
    private String actionCommand;

    public String getHref() {
        return href;
    }

    public void setHref(String href) {
        this.href = href;
    }

    public String getActionCommand() {
        return actionCommand;
    }

    public void setActionCommand(String actionCommand) {
        this.actionCommand = actionCommand;
    }

    /**
     * The TableTag will still be the work horse in this library.  The action tag
     * will still propigate through the Columntag to the TableTag.
     *
     * @throws javax.servlet.jsp.JspException if this tag is being used outside of a
     *    <tablelib:column> tag.
     **/
    public int doEndTag() throws JspException {
        final Object column = this.getParent();

        if (column == null || column instanceof ColumnTag == false) {
            throw new JspException(
                    "Can not use an action tag outside of a Columntag. "+
                    "Invalid parent = "+column.getClass().getName());
        }

        try {
            ColumnTag columnTag = (ColumnTag) column;
            columnTag.setAction((ActionTag)this.clone());
        } catch (CloneNotSupportedException e) {
            throw new JspException(e);
        }
        return super.doEndTag();
    }
}

--- NEW FILE: ColumnDecorator.java ---
/**
 * $Id: ColumnDecorator.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Status: Ok
 **/

package com.tablelib.tags;



public abstract class ColumnDecorator extends DecoratorImpl {
    public ColumnDecorator() {
        super();
    }

    public abstract String decorate(Object columnValue);
}


--- NEW FILE: ColumnTag.java ---
/**
 * This tag has undergone only minor changes.  It was renamed to ensure that there 
would be
 * no conflict with the original org.apache.taglib.display.ColumnTag
 *
 * Minor Changes:
 *  Cleaned up the get Cell Attributes call.
 *      <li>It could use more refactoring to consider reusing a buffer</li>
 *      <li>Caching the results to avoid reprocessing during a request</li>
 */

/**
 * $Id: ColumnTag.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Todo:
 *   - provide filters in some way?  Instead of just getting some bit of data
 *     also provide a way to feed that data through some other object that
 *     will reformat it in some way (like converting dates to another format)
 *   - update documentation, HTML column attributes are not set.
 *   - specify groupings.
 *   - error checking, value or property must be set.
 **/

package com.tablelib.tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Attributes:<p>
 *
 *   property       - the property method that is called to retrieve the
 *                    information to be displayed in this column.  This method
 *                    is called on the current object in the iteration for
 *                    the given row.  The property format is in typical struts
 *                    format for properties (required)
 *
 *   title          - the title displayed for this column.  if this is omitted
 *                    then the property name is used for the title of the column
 *                    (optional)
 *
 *   width          - the width of the column (gets passed along to the html
 *                    td tag). (optional)
 *
 *   group          - the grouping level (starting at 1 and incrementing) of
 *                    this column (indicates if successive contain the same
 *                    values, then they should not be displayed).  The level
 *                    indicates that if a lower level no longer matches, then
 *                    the matching for this higher level should start over as
 *                    well. If this attribute is not included, then no grouping
 *                    is performed. (optional)
 *
 *   decorator      - a class that should be used to "decorate" the underlying
 *                    object being displayed. If a decorator is specified for
 *                    the entire table, then this decorator will decorate that
 *                    decorator. (optional)
 *
 *   autolink       - if set to true, then any email addresses and URLs found
 *                    in the content of the column are automatically converted
 *                    into a hypertext link.
 *
 *
 *
 *   maxLength      - If this attribute is provided, then the column's displayed
 *                    is limited to this number of characters.  An elipse (...)
 *                    is appended to the end if this column is linked, and the
 *                    user can mouseover the elipse to get the full text.
 *                    (optional)
 *
 *   maxWords       - If this attribute is provided, then the column's displayed
 *                    is limited to this number of words.  An elipse (...) is
 *                    appended to the end if this column is linked, and the user
 *                    can mouseover the elipse to get the full text. (optional)
 */

public class ColumnTag extends BodyTagSupport implements Cloneable {

    private String property;
    private String title;
    private String sort;
    boolean doLink;
    private String group; /* If this property is set then the values have to be 
grouped */

    private String paramId;
    private String paramName;
    private String paramProperty;
    private int paramScope;
    private int maxLength;
    private int maxWords;

    private String width;
    private String align;
    private String background;
    private String bgcolor;
    private String height;
    private String nowrap;
    private String valign;
    private String clazz;
    private String headerClazz;
    private String doubleQuote;
    private String decorator;

    public void setProperty(final String v) {
        this.property = v;
    }

    public void setTitle(final String v) {
        this.title = v;
    }

    public void setSort(final String v) {
        this.sort = v;
    }

    public void setAutolink(final String v) {
        doLink = v.equals("true");
    }

    public void setGroup(final String v) {
        this.group = v;
    }

    public void setParamId(final String v) {
        this.paramId = v;
    }

    public void setParamName(final String v) {
        this.paramName = v;
    }

    public void setParamProperty(final String v) {
        this.paramProperty = v;
    }

    public void setParamScope(String parm) {
        if (parm == null)
            paramScope = PageContext.REQUEST_SCOPE;
        else if ((parm = parm.toLowerCase()).equals("request"))
            paramScope = PageContext.REQUEST_SCOPE;
        else if (parm.equals("session"))
            paramScope = PageContext.SESSION_SCOPE;
        else if (parm.equals("application"))
            paramScope = PageContext.APPLICATION_SCOPE;
        else
            paramScope = PageContext.PAGE_SCOPE;
    }

    public void setMaxLength(final int v) {
        this.maxLength = v;
    }

    public void setMaxWords(final int v) {
        this.maxWords = v;
    }

    public void setWidth(final String v) {
        this.width = v;
    }

    public void setAlign(final String v) {
        this.align = v;
    }

    public void setBackground(final String v) {
        this.background = v;
    }

    public void setBgcolor(final String v) {
        this.bgcolor = v;
    }

    public void setHeight(final String v) {
        this.height = v;
    }

    public void setNowrap(final String v) {
        this.nowrap = v;
    }

    public void setValign(final String v) {
        this.valign = v;
    }

    public void setStyleClass(final String v) {
        this.clazz = v;
    }

    public void setHeaderStyleClass(final String v) {
        this.headerClazz = v;
    }

    public void setDoubleQuote(final String v) {
        this.doubleQuote = v;
    }

    public void setDecorator(final String v) {
        this.decorator = v;
    }

    public String getProperty() {
        return this.property;
    }

    public String getTitle() {
        return this.title;
    }

    public String getSort() {
        return this.sort;
    }

    public String getGroup() {
        return this.group;
    }


    public String getParamId() {
        return this.paramId;
    }

    public String getParamName() {
        return this.paramName;
    }

    public String getParamProperty() {
        return this.paramProperty;
    }

    public int getParamScope() {
        return this.paramScope;
    }

    public int getMaxLength() {
        return this.maxLength;
    }

    public int getMaxWords() {
        return this.maxWords;
    }

    public String getWidth() {
        return this.width;
    }

    public String getAlign() {
        return this.align;
    }

    public String getBackground() {
        return this.background;
    }

    public String getBgcolor() {
        return this.bgcolor;
    }

    public String getHeight() {
        return this.height;
    }

    public String getNowrap() {
        return this.nowrap;
    }

    public String getValign() {
        return this.valign;
    }

    public String getStyleClass() {
        return this.clazz;
    }

    public String getHeaderStyleClass() {
        return this.headerClazz;
    }

    public String getDoubleQuote() {
        return this.doubleQuote;
    }

    public String getDecorator() {
        return this.decorator;
    }

    private List actions = Collections.EMPTY_LIST;
    public List getActions() {
        return actions;
    }
    public void setAction(ActionTag action) {
        if(this.actions.isEmpty()) this.actions = new ArrayList(5);
        this.actions.add(action);
    }

    // --------------------------------------------------------- Tag API methods

    /**
     * Passes attribute information up to the parent TableTag.<p>
     *
     * When we hit the end of the tag, we simply let our parent (which better
     * be a TableTag) know what the user wants to do with this column.
     * We do that by simple registering this tag with the parent.  This tag's
     * only job is to hold the configuration information to describe this
     * particular column.  The TableTag does all the work.
     *
     * @throws javax.servlet.jsp.JspException if this tag is being used outside of a
     *    <display:list...> tag.
     **/

    public int doEndTag() throws JspException {
        final Object parent = this.getParent();

        if (parent == null || parent instanceof TableTag == false) {
            throw new JspException("Can not use column tag outside of a " +
                    "TableTag. Invalid parent = " +
                    parent.getClass().getName());
        }

        // Need to clone the ColumnTag before passing it to the TableTag as
        // the ColumnTags can be reused by some containers, and since we are
        // using the ColumnTags as basically containers of data, we need to
        // save the original values, and not the values that are being changed
        // as the tag is being reused...


        /** tucked the add column into the try block.  **/
        try {
            ((TableTag) parent).addColumn(this.clone());
        } catch (CloneNotSupportedException e) {
            throw new JspException(e);
        } // shouldn't happen...
        return super.doEndTag();
    }


    /**
     * Takes all the column pass-through arguments and bundles them up as a
     * string that gets tacked on to the end of the td tag declaration.<p>
     **/

    private static final String DEF_CLAZZ_STR = " class=\"tableCell\"";
    private static final String DEF_VALIGNMENT = " valign=\"top\"";
    private static final String DEF_ALIGNMENT = " align=\"left\"";

    public String getCellAttributes() {
        final StringBuffer results = new StringBuffer(256);

        if (this.clazz == null)
            results.append(DEF_CLAZZ_STR);
        else {
            results.append(" class=\"");
            results.append(this.clazz);
            results.append("\"");
        }

        if (this.width != null) {
            results.append(" width=\"");
            results.append(this.width);
            results.append("\"");
        }

        if (this.align == null)
            results.append(DEF_ALIGNMENT);
        else {
            results.append(" align=\"");
            results.append(this.align);
            results.append("\"");
        }

        if (this.background != null) {
            results.append(" background=\"");
            results.append(this.background);
            results.append("\"");
        }

        if (this.bgcolor != null) {
            results.append(" bgcolor=\"");
            results.append(this.bgcolor);
            results.append("\"");
        }

        if (this.height != null) {
            results.append(" height=\"");
            results.append(this.height);
            results.append("\"");
        }

        if (this.nowrap != null) {
            results.append(" nowrap");
        }

        if (this.valign == null)
            results.append(DEF_VALIGNMENT);
        else {
            results.append(" valign=\"");
            results.append(this.valign);
            results.append("\"");
        }

        return results.toString();
    }
}
--- NEW FILE: Decorator.java ---
/**
 * This file has changed from it's original format.  The current purpose for a 
decorator is it's original stated purpose in the
 * infamous book Design Patterns / Elements of reuses (Gang of 4)
 *
 * Intent of a decorator:  "Attach additional responsibilities to an object 
dynamically..."
 *
 * Use in this package:  "Provide a signature for implementation classes to 'attach 
additional information to
 * an object dynamically'"
 *
 * @author Ben Simpson
 * Date: Jan 30, 2003
 *
 */

package com.tablelib.tags;

import java.util.Comparator;

public interface Decorator {

    /**
     * This method will take an object and transform it according to it's 
implementation.
     * @param obj
     * @return
     */
    public Object decorate(Object obj);
}

--- NEW FILE: DecoratorImpl.java ---
/**
 * $Id: DecoratorImpl.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Status: Ok
 *
 * Todo
 *   - push the appropriate stuff down into TableDecorator
 **/

package com.tablelib.tags;

import javax.servlet.jsp.PageContext;
import java.util.List;

/**
 * This class provides some basic functionality for all objects which serve
 * as decorators for the objects in the List being displayed.
 **/

public abstract class DecoratorImpl implements Decorator {
    private PageContext ctx = null;
    private List list = null;

    private Object obj = null;
    private int viewIndex = -1;
    private int listIndex = -1;


    public DecoratorImpl() {

    }

    public void init(final PageContext ctx, final List list) {
        this.ctx = ctx;
        this.list = list;
    }

    public String initRow(final Object obj, final int viewIndex, final int listIndex) {
        this.obj = obj;
        this.viewIndex = viewIndex;
        this.listIndex = listIndex;
        return "";
    }

    public String finishRow() {
        return "";
    }

    public void finish() {

    }

    public PageContext getPageContext() {
        return this.ctx;
    }

    public List getList() {
        return this.list;
    }

    public Object getObject() {
        return this.obj;
    }

    public int getViewIndex() {
        return this.viewIndex;
    }

    public int getListIndex() {
        return this.listIndex;
    }
}

--- NEW FILE: PagingInformation.java ---
/**
 * Software by JavaBen
 * User: Ben Simpson
 * Date: Feb 2, 2003
 * Time: 7:07:44 PM
 */
package com.tablelib.tags;

public interface PagingInformation {


     /**
     * This is a work in progress.  The pattern that has worked for me so far is to
     * incorporate much of what was done to the display tag library by Ed Hill.  Get 
that to
     * work in this environment, then start taking out the redundancies. :)
     * @return
     */
    public String getSearchResultsSummary();

    /**
     * @param url
     * @return
     */
    public String getPageNavigationBar(String url);
}

--- NEW FILE: PaginigInformationImpl.java ---
package com.tablelib.tags;

import com.tablelib.core.net.NetUtils;

import java.text.MessageFormat;
import java.util.List;
import java.util.Properties;

/**
 * Created by IntelliJ IDEA.
 * User: simps
 * Date: Feb 1, 2003
 * Time: 2:53:43 PM
 */
public class PaginigInformationImpl implements PagingInformation {

    private int currentPage;
    private int pageSize;
    private int pageCount;
    private int listSize;
    private Properties prop;
    private int[] ints;


    public PaginigInformationImpl(final Properties props,
                             final List pList,
                             final int pCurrentPage,
                             final int pPageSize
                             ) {
        this.prop = props;
        this.pageSize = pPageSize;
        this.currentPage = pCurrentPage;
        this.listSize = pList.size();

        computePageCount();
    }

    /**
     * Returns the computed number of pages it would take to show all the
     * elements in the list given the pageSize we are working with.
     *
     * This function was taken in whole from the Display Tag Library Smart Page Helper
     */

    void computePageCount() {

        if (pageSize == 0) {
            pageCount = 0;
            return;
        }
        final int div = listSize / this.pageSize;
        pageCount = listSize % this.pageSize == 0 ? div : div + 1;
        this.ints = new int[pageCount + 1];
        for (int i = 1; i <= pageCount; i++) ints[i] = i;

    }

    /**
     * This is a work in progress.  The pattern that has worked for me so far is to
     * incorporate much of what was done to the display tag library by Ed Hill.  Get 
that to
     * work in this environment, then start taking out the redundancies. :)
     * // @todo
     * @return
     */
    public String getSearchResultsSummary() {
        final StringBuffer buff = new StringBuffer(256);
        if (listSize < 2) {
            if (listSize == 0)
                buff.append(prop.getProperty("paging.banner.no_items_found"));
            else
                buff.append(prop.getProperty("paging.banner.one_item_found"));
            return 
buff.append(prop.getProperty("paging.banner.items_name")).toString();
        } else if ((currentPage - 1) * this.pageSize == 
this.getLastIndexForCurrentPage()) {
            final Object[] objs = {new Integer(listSize),
                             prop.getProperty("paging.banner.items_name"),
                             prop.getProperty("paging.banner.items_name")};

            return MessageFormat.format(
                    prop.getProperty("paging.banner.all_items_found"), objs);
        } else {
            final Object[] objs = {new Integer(listSize),
                             prop.getProperty("paging.banner.items_name"),
                             new Integer((currentPage - 1) * this.pageSize),
                             new Integer(this.getLastIndexForCurrentPage() + 1)};

            return MessageFormat.format(
                    prop.getProperty("paging.banner.some_items_found"), objs);
        }
    }

    protected int getLastIndexForCurrentPage() {
        final int firstIndex = (currentPage - 1) * this.pageSize;
        final int pageIndex = this.pageSize - 1;
        final int lastIndex = this.listSize - 1;

        return Math.min(firstIndex + pageIndex, lastIndex);
    }


    /**
     * @param url
     * @return
     */
    public String getPageNavigationBar(final String url) {
        if (pageCount < 2) return "<b>1</b>";
        final int maxPages = this.pageCount > 8 ? 8 : this.pageCount;
        final StringBuffer retBuff = new StringBuffer(url.length() * maxPages * 
2).append("<b>[");


        retBuff.append((this.currentPage > 1 ? makeFirst(url) : "First")).append(" ");
        retBuff.append((this.currentPage > 1 ? makePrevious(url) : "Prev")).append("] 
");

        appendIntsToBuff(retBuff, maxPages, url);

        retBuff.append(" [").append((this.currentPage < pageCount ? makeNext(url) : 
"Next")).append(" ");
        retBuff.append((this.currentPage < pageCount ? makeLast(url) : 
"Last")).append("] </b>");
        return retBuff.toString();
    }

    /**
     * Parts of this algorithm were taken from the SmartListHelper class in Ed Hill's
     * Display tag library.
     *
     * @param buff
     * @param maxPagesToShow
     * @param url
     */
    private void appendIntsToBuff(final StringBuffer buff, final int maxPagesToShow, 
final String url) {
        int startInt = currentPage < maxPagesToShow ? 1 : currentPage;
        final int endInt;
        if (currentPage < maxPagesToShow) {
            endInt = pageCount < maxPagesToShow ? pageCount : maxPagesToShow;
        } else {
            final int pc = pageCount + 1;
            while (startInt + maxPagesToShow > pc) startInt--;
            endInt = startInt + maxPagesToShow - 1;
        }

        for (int i = startInt; i <= endInt; i++) {
            if (i != startInt) buff.append(", ");
            buff.append(makeNumberedLink(url, i));
        }
    }

    private static final String makeFirst(final String url) {
        return NetUtils.wrapLink(url + 1, null, "First");
    }

    private String makePrevious(final String url) {
        return NetUtils.wrapLink(url + (currentPage - 1), null, "Prev");
    }

    /**
     * This method focuses on making only the Next link in the
     * navigation header bar
     * @param url
     * @return
     */
    private String makeNext(final String url) {
        return NetUtils.wrapLink(url + (currentPage + 1), null, "Next");
    }

    /**
     * This method focuses on making only the "Last Link" from the navigation
     * header
     * @param url
     * @return
     */
    private String makeLast(final String url) {
        return NetUtils.wrapLink(url + this.pageCount, null, "Last");
    }

    /**
     * This is the method where the general numbered links in the
     * navigation header bar will be made.
     * @param url
     * @param n
     * @return
     */
    private String makeNumberedLink(final String url, final int n) {
        if (n == currentPage) return String.valueOf(n);
        return NetUtils.wrapLink(url + n, null, n);
    }
}

--- NEW FILE: SearchTag.java ---
/**
 * Software by JavaBen
 * User: Ben Simpson
 * Date: Feb 18, 2003
 * Time: 8:39:39 AM
 */
package com.tablelib.tags;

import com.tablelib.core.taglibs.BaseTag;

/**
 * This class follows the same algorithm that the Table Tag follows except for the 
following:
 *  <ul><li>It has an embedded search field (optional).
 */
public class SearchTag extends BaseTag {

    /**
     *          <tablelib:table  name:=\"tablename\" key=\"list\">
     *              <tablelib:search properties(titles or properties) />
     *              <tablelib:column property=\"propName\" title=\"tname\" sortable />
     *              <tablelib:column property=\"propNameTwo\" title=\"tnameTwo\" />
     *              <tablelib:column proprty=\"id\">
     *              <tablelib:column proprty=\"imageUrl\">
     *                  <tablelib:image width=\"200\" height=\"200\">
     *                      <tablelib:action href=\"view.do?id=\" />
     *                  </tablelib:image>
     *              </tablelib:column>
     *                  <tablelib:action href=\"delete.do?id=\" command=\"delete\"/>
     *                  <tablelib:action href=\"edit.do?id=\" command=\"edit\"/>
     *              </tablelib:column>
     *          </tablelib>
     *
     */

}

--- NEW FILE: SetPropertyTag.java ---
/**
 * $Id: SetPropertyTag.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Status: Under Development
 *
 * Todo
 *   - implementation
 *   - documentation (javadoc, examples, etc...)
 *   - junit test cases
 **/

package com.tablelib.tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;

/**
 * One line description of what this class does.
 *
 * More detailed class description, including examples of usage if applicable.
 **/

public class SetPropertyTag extends BodyTagSupport implements Cloneable {
    private String name;
    private String value;

    public void setName(String v) {
        this.name = v;
    }

    public void setValue(String v) {
        this.value = v;
    }

    public String getName() {
        return this.name;
    }

    public String getValue() {
        return this.value;
    }

    // --------------------------------------------------------- Tag API methods

    /**
     * Passes attribute information up to the parent TableTag.<p>
     *
     * When we hit the end of the tag, we simply let our parent (which better
     * be a TableTag) know what the user wants to change a property value, and
     * we pass the name/value pair that the user gave us, up to the parent
     *
     * @throws javax.servlet.jsp.JspException if this tag is being used outside of a
     *    <display:list...> tag.
     **/

    public int doEndTag() throws JspException {
        Object parent = this.getParent();

        if (parent == null) {
            throw new JspException("Can not use column tag outside of a " +
                    "TableTag. Invalid parent = null");
        }

        if (!(parent instanceof TableTag)) {
            throw new JspException("Can not use column tag outside of a " +
                    "TableTag. Invalid parent = " +
                    parent.getClass().getName());
        }

        ((TableTag) parent).setProperty(this.name, this.value);

        return super.doEndTag();
    }

}

--- NEW FILE: SmartListHelper.java ---
/**
 * $Id: SmartListHelper.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Status: Under Development
 **/

package com.tablelib.tags;

import com.tablelib.core.lang.NumberUtils;
import com.tablelib.core.net.NetUtils;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * This is a little utility class that the SmartListTag uses to chop up a
 * List of objects into small bite size pieces that are more suitable for
 * display.
 *
 * This class is a stripped down version of the WebListHelper that we got
 * from Tim Dawson ([EMAIL PROTECTED])
 */

class SmartListHelper extends Object {
    private List masterList;
    private int pageSize;
    private int pageCount;
    private int currentPage;

    private Properties prop = null;


    /**
     * Creates a SmarListHelper instance that will help you chop up a list
     * into bite size pieces that are suitable for display.
     */

    protected SmartListHelper(List list, int pageSize, Properties prop) {

        if (list == null || pageSize < 1) {
            throw new IllegalArgumentException("Bad arguments passed into " +
                    "SmartListHelper() constructor");
        }

        this.prop = prop;
        this.pageSize = pageSize;
        this.masterList = list;
        this.pageCount = this.computedPageCount();
        this.currentPage = 1;
    }


    /**
     * Returns the computed number of pages it would take to show all the
     * elements in the list given the pageSize we are working with.
     */

    protected int computedPageCount() {

        int result = 0;

        if ((this.masterList != null) && (this.pageSize > 0)) {
            int size = this.masterList.size();
            int div = size / this.pageSize;
            int mod = size % this.pageSize;
            result = (mod == 0) ? div : div + 1;
        }

        return result;

    }


    /**
     * Returns the index into the master list of the first object that
     * should appear on the current page that the user is viewing.
     */

    protected int getFirstIndexForCurrentPage() {
        return this.getFirstIndexForPage(this.currentPage);
    }

    /**
     * Returns the index into the master list of the last object that should
     * appear on the current page that the user is viewing.
     */

    protected int getLastIndexForCurrentPage() {
        return this.getLastIndexForPage(this.currentPage);
    }


    /**
     * Returns the index into the master list of the first object that
     * should appear on the given page.
     */

    protected int getFirstIndexForPage(int page) {
        return ((page - 1) * this.pageSize);
    }


    /**
     * Returns the index into the master list of the last object that should
     * appear on the given page.
     */

    protected int getLastIndexForPage(int page) {
        int firstIndex = this.getFirstIndexForPage(page);
        int pageIndex = this.pageSize - 1;
        int lastIndex = this.masterList.size() - 1;

        return Math.min(firstIndex + pageIndex, lastIndex);
    }


    /**
     * Returns a subsection of the list that contains just the elements that
     * are supposed to be shown on the current page the user is viewing.
     */

    protected List getListForCurrentPage() {
        return this.getListForPage(this.currentPage);
    }


    /**
     * Returns a subsection of the list that contains just the elements that
     * are supposed to be shown on the given page.
     */

    protected List getListForPage(int page) {
        List list = new ArrayList(this.pageSize + 1);

        int firstIndex = this.getFirstIndexForPage(page);
        int lastIndex = this.getLastIndexForPage(page);

        for (int i = firstIndex; i <= lastIndex; i++) {
            list.add(this.masterList.get(i));
        }

        return list;
    }


    /**
     * Set's the page number that the user is viewing.
     *
     * @throws java.lang.IllegalArgumentException if the page provided is invalid.
     */

    protected void setCurrentPage(int page) {
        if (page < 1 || page > this.pageCount) {
            Object[] objs = {new Integer(page), new Integer(pageCount)};
            throw new IllegalArgumentException(
                    MessageFormat.format(prop.getProperty("error.msg.invalid_page"), 
objs));
        }

        this.currentPage = page;
    }


    /**
     * Return the little summary message that lets the user know how many
     * objects are in the list they are viewing, and where in the list they
     * are currently positioned.  The message looks like:
     *
     *   nnn <item(s)> found, displaying nnn to nnn.
     *
     * <item(s)> is replaced by either itemName or itemNames depending on if
     * it should be signular or plurel.
     */

    protected String getSearchResultsSummary() {

        if (this.masterList.size() == 0) {
            Object[] objs = {prop.getProperty("paging.banner.items_name")};
            return MessageFormat.format(
                    prop.getProperty("paging.banner.no_items_found"), objs);
        } else if (this.masterList.size() == 1) {
            Object[] objs = {prop.getProperty("paging.banner.item_name")};
            return MessageFormat.format(
                    prop.getProperty("paging.banner.one_item_found"), objs);
        } else if (this.getFirstIndexForCurrentPage() == 
this.getLastIndexForCurrentPage()) {
            Object[] objs = {new Integer(this.masterList.size()),
                             prop.getProperty("paging.banner.items_name"),
                             prop.getProperty("paging.banner.items_name")};

            return MessageFormat.format(
                    prop.getProperty("paging.banner.all_items_found"), objs);
        } else {
            Object[] objs = {new Integer(this.masterList.size()),
                             prop.getProperty("paging.banner.items_name"),
                             new Integer(this.getFirstIndexForCurrentPage() + 1),
                             new Integer(this.getLastIndexForCurrentPage() + 1)};

            return MessageFormat.format(
                    prop.getProperty("paging.banner.some_items_found"), objs);
        }
    }


    /**
     * Returns a string containing the nagivation bar that allows the user
     * to move between pages within the list.
     *
     * The urlFormatString should be a URL that looks like the following:
     *
     * http://.../somepage.page?page={0}
     */

    protected String getPageNavigationBar(String urlFormatString) {
        MessageFormat form = new MessageFormat(urlFormatString);

        int maxPages = 
NumberUtils.parseInt(prop.getProperty("paging.banner.group_size"), 8);

        int currentPage = this.currentPage;
        int pageCount = this.pageCount;
        int startPage = 1;
        int endPage = maxPages;

        if (pageCount == 1 || pageCount == 0) {
            return "<b>1</b>";
        }

        if (currentPage < maxPages) {
            startPage = 1;
            endPage = maxPages;
            if (pageCount < endPage) {
                endPage = pageCount;
            }
        } else {
            startPage = currentPage;
            while (startPage + maxPages > (pageCount + 1)) {
                startPage--;
            }

            endPage = startPage + (maxPages - 1);
        }

        boolean includeFirstLast = 
prop.getProperty("paging.banner.include_first_last").equals("true");

        String msg = "";
        if (currentPage == 1) {
            if (includeFirstLast) {
                msg += "[" + prop.getProperty("paging.banner.first_label") +
                        "/" + prop.getProperty("paging.banner.prev_label") + "] ";
            } else {
                msg += "[" + prop.getProperty("paging.banner.prev_label") + "] ";
            }
        } else {
            Object[] objs = {new Integer(currentPage - 1)};
            Object[] v1 = {new Integer(1)};
            if (includeFirstLast) {
                msg += "[<a href=\"" + form.format(v1) + "\">" +
                        prop.getProperty("paging.banner.first_label") + "</a>/<a 
href=\"" +
                        form.format(objs) + "\">" +
                        prop.getProperty("paging.banner.prev_label") + "</a>] ";
            } else {
                msg += "[" + NetUtils.wrapLink(form.format(objs), "", 
prop.getProperty("paging.banner.prev_label")) + "]";
            }
        }

        for (int i = startPage; i <= endPage; i++) {
            msg += i == currentPage ? "<b>" + i + "</b>" :
                    NetUtils.wrapLink(form.format(new Object[]{i + ""}), "", i + "");
            msg += i != endPage ? ", " : " ";
        }

        if (currentPage == pageCount) {
            if (includeFirstLast) {
                msg += "[" + prop.getProperty("paging.banner.next_label") +
                        "/" + prop.getProperty("paging.banner.last_label") + "] ";
            } else {
                msg += "[" + prop.getProperty("paging.banner.next_label") + "] ";
            }
        } else {
            Object[] objs = {new Integer(currentPage + 1)};
            Object[] v1 = {new Integer(pageCount)};
            if (includeFirstLast) {
                msg += "[<a href=\"" + form.format(objs) + "\">" +
                        prop.getProperty("paging.banner.next_label") + "</a>/<a 
href=\"" +
                        form.format(v1) + "\">" +
                        prop.getProperty("paging.banner.last_label") + "</a>] ";
            } else {
                msg += "[<a href=\"" + form.format(objs) + "\">" +
                        prop.getProperty("paging.banner.next_label") + "</a>] ";
            }
        }

        return msg;
    }
}

--- NEW FILE: TableDecorator.java ---
/**
 * $Id: TableDecorator.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Status: Ok
 *
 * Todo:
 *   - setup the correct deprecation rules, so that people know to extend
 *     this class, rather then the Decorator class.
 **/

package com.tablelib.tags;



public class TableDecorator extends DecoratorImpl {
    public TableDecorator() {
        super();
    }
}

--- NEW FILE: TableExporter.java ---
/**
 * Created by IntelliJ IDEA.
 * User: Benjamin Simpson
 * Date: Jan 11, 2003
 * Time: 8:44:51 PM
 */

package com.tablelib.tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTag;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

import com.tablelib.core.lang.StringUtils;

/**
 * The class TableExporter is the encapsulation the routine
 * of formatting and sending the table data back via the response.
 * The data is typed by the named values in the Download types array.
 *
 * The types seem pretty solid for now, so they dont need to be grabbed from
 * a configuration file.
 *
 * The routines themselves are based upon the data processing methods once found
 * in the display tag library by Ed Hill 
(http://edhill.its.uiowa.edu/display-docs-0.8/).
 *
 * The emphasis on this refactoring was not speed as much as maintainability.  It is 
the intent of
 * this programmer to come back and do the following:
 *
 */
public final class TableExporter {

    public static final int EXPORT_TYPE_NONE = -1;
    public static final int EXPORT_TYPE_CSV = 0;
    public static final int EXPORT_TYPE_EXCEL = 1;
    public static final int EXPORT_TYPE_XML = 2;
    private static final String[] DOWNLOAD_TYPES = new String[]{ "text/csv", 
"application/vnd.ms-excel", "text/xml" };
    private static final char[] EOL_TYPES = new char[]{  '\n', '\n', '\n' };
    private static final char[] DELIM_TYPES = new char[]{ ',', 't', '\n' };

    /**
     *
     * @param type
     * @param tag
     * @param data
     * @return Tag.SKIP_BODY
     * @throws Exception
     */
    public static int export(final int type, final TableTag tag, final List data) 
throws Exception {

        final JspWriter out = tag.getOut();
        out.clear();
        tag.getResponse().setContentType(DOWNLOAD_TYPES[type]);
        out.write(type == EXPORT_TYPE_CSV ? getRawData(data, tag) :
                type == EXPORT_TYPE_EXCEL ? getExcelData(data, tag) :
                getXMLData(data, tag)
        );
        out.flush();
        return BodyTag.SKIP_PAGE;
    }

    /**
     * This returns a table of data in XML format
     *
     * FIXME - this obviously needs cleaned up...
     */

    private static String getXMLData(final List rows, final TableTag tableTag) throws 
JspException {
        final StringBuffer buf = new StringBuffer(8000);
        final List columns = tableTag.columns;
        int rowcnt = 0;
        buf.append("<table>\n");
        for (Iterator it = rows.iterator(); it.hasNext();) {
            Object obj =  it.next();
            tableTag.setPageContextAttribute("smartRow", obj);
            buf.append("<row>\n");
            Object value;
            for (int i = 0, len = columns.size(); i < len; i++) {
                ColumnTag tag = (ColumnTag) columns.get(i);
                value = tag.getProperty().equals("table_index") ?
                            String.valueOf(rowcnt) :
                            tableTag.lookup("smartRow", tag.getProperty(), 
tableTag.scope);

                if (value == null) value = "";
                // TODO - need to escape this or something...
                buf.append("<column>" + value + "</column>\n");
            }
            rowcnt++;
            buf.append("</row>\n");
        }
        return buf.append("</table>\n").toString();
    }

    /**
     * This returns a table of data in Excel format
     */
    private static final char xls_delim = '\t';
    private static final char xls_eol = '\n';
    private static String getExcelData(final List rows, final TableTag tableTag) 
throws JspException {
        final ArrayList lines = new ArrayList(rows.size()+1);
        final char eol = xls_eol;
        final char delim = xls_delim;
        final int colsSize = tableTag.columns.size();
        lines.add(getHeaderBuffer(EXPORT_TYPE_EXCEL,colsSize,tableTag));


        for(int rowcnt = 0, len = rows.size(); rowcnt < len; rowcnt++) {
            Object row = rows.get(rowcnt);
            StringBuffer buf = new StringBuffer(256);
           // if (decorated && tableTag.dec != null) {
           //     buf.append(tableTag.dec.initRow(row, rowcnt, rowcnt + 
tableTag.getOffsetValue()));
          //  }
            tableTag.setPageContextAttribute("smartRow", row);
            for (int i = 0; i < colsSize; i++) {
                ColumnTag tag = (ColumnTag) tableTag.columns.get(i);
                Object value = null;
                String prop = tag.getProperty();
                if(prop.equals("table_index")) value = String.valueOf(rowcnt);
                else value = tableTag.lookup("smartRow",prop, tableTag.scope);

                if (value == null) value = "";
                if (tag.getDoubleQuote() != null) value = "\"" + value + "\"";
                buf.append(value);
                if (colsSize > (i + 1)) buf.append(delim);
            }
            lines.add(buf.toString().trim());
        }
        return StringUtils.delimit(lines.toArray(),eol);
    }

    private static String getRawData(final List rows, final TableTag tableTag) throws 
JspException {
        final ArrayList lines = new ArrayList(rows.size() +1);
        final int colsSize = tableTag.columns.size();
        final StringBuffer buf = new StringBuffer(8000);
        lines.add(getHeaderBuffer(EXPORT_TYPE_CSV,colsSize,tableTag));
        for(int rowcnt = 0, len = rows.size(); rowcnt < len; rowcnt++) {
            Object row = rows.get(rowcnt);

            //if (decorated && tableTag.dec != null) {
           //     String rt = tableTag.dec.initRow(row, rowcnt, rowcnt + 
tableTag.getOffsetValue());
           //     if (rt != null) buf.append(rt);
           // }
            tableTag.setPageContextAttribute("smartRow", row);
            for (int i = 0; i < colsSize; i++) {
                ColumnTag tag = (ColumnTag) tableTag.columns.get(i);
                Object value = null;

                String prop = tag.getProperty();
                if(prop.equals("table_index")) value = String.valueOf(rowcnt);
                else value = tableTag.lookup("smartRow",prop, tableTag.scope);

                if (value == null) value = "";
                if (tag.getDoubleQuote() != null) value = "\"" + value + "\"";
                buf.append(value);
                if (colsSize > (i + 1)) buf.append(DELIM_TYPES[EXPORT_TYPE_CSV]);
            }
            lines.add(buf.append(EOL_TYPES[EXPORT_TYPE_CSV]).toString());
            rowcnt++;
        }
        return buf.toString();
    }

    /**
     * Processor utilities
     */
    private static final String getHeaderBuffer(final int typeOfExport, final int 
colsSize, final TableTag tableTag) {
        final String [] headers = new String [colsSize];
        final ColumnTag [] cols = new ColumnTag[colsSize];
        tableTag.columns.toArray(cols);
        String tmp;
        for(int i = 0; i < colsSize; i++) {
            headers[i]  = (tmp = cols[i].getTitle()) == null ? cols[i].getProperty() : 
tmp;
        }
        return StringUtils.delimit(headers,DELIM_TYPES[typeOfExport]);
    }
}
--- NEW FILE: TableImpl.java ---
/**
 * Created by IntelliJ IDEA.
 * User: Benjamin Simpson
 * Date: Jan 11, 2003
 * Time: 8:44:51 PM
 */

package com.tablelib.tags;

import com.tablelib.core.table.Table;

public class TableImpl implements Table {

    private Object key = null;
    private int width = 100;
    private boolean isWidthPercentage = true;
    private int height = -1;
    private String backGround = null;
    private int border = 0;
    private int cellPadding = -1;
    private int cellSpacing = -1;
    private int hSpace = -1;
    private int vSpace = -1;
    private String bgColor = "#FFFFFF";


    public void declareTable(final StringBuffer buff) {
        buff.append("\n<table class=\"table\"");
        buff.append(" width=\"").append(this.width);
        if (isWidthPercentage) buff.append('%');
        buff.append('\"');
        buff.append(" border=\"").append(this.border).append('\"');
        if (this.cellSpacing > 0)
            buff.append(" cellspacing=\"").append(this.cellSpacing).append('\"');

        if (this.cellPadding > 0)
            buff.append(" cellpadding=\"").append(this.cellPadding).append('\"');

        if (this.backGround != null)
            buff.append(" background=\"").append(this.backGround).append("\"");

        if (this.bgColor != null)
            buff.append(" bgcolor=\"").append(this.bgColor).append("\"");

        if (this.height > 0)
            buff.append(" height=\"").append(this.height).append("\"");

        if (this.hSpace > 0)
            buff.append(" hspace=\"").append(this.hSpace).append("\"");

        if (this.vSpace > 0)
            buff.append(" vspace=\"").append(this.vSpace).append("\"");

        buff.append(" >\n");
    }

    public boolean isWidthPercentage() {
        return isWidthPercentage;
    }

    public void setWidthIsPercentage(final boolean isPercentage) {
        isWidthPercentage = isPercentage;
    }

    public Object getKey() {
        return key;
    }

    public void setKey(final Object key) {
        this.key = key;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(final int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(final int height) {
        this.height = height;
    }

    public String getBackGround() {
        return backGround;
    }

    public void setBackGround(final String backGround) {
        this.backGround = backGround;
    }

    public int getBorder() {
        return border;
    }

    public void setBorder(final int border) {
        this.border = border;
    }

    public int getCellPadding() {
        return cellPadding;
    }

    public void setCellPadding(final int cellPadding) {
        this.cellPadding = cellPadding;
    }

    public int getCellSpacing() {
        return cellSpacing;
    }

    public void setCellSpacing(final int cellSpacing) {
        this.cellSpacing = cellSpacing;
    }

    public int getHSpace() {
        return hSpace;
    }

    public void setHSpace(final int hSpace) {
        this.hSpace = hSpace;
    }

    public int getVSpace() {
        return vSpace;
    }

    public void setVSpace(final int vSpace) {
        this.vSpace = vSpace;
    }

    public String getBgColor() {
        return bgColor;
    }

    public void setBgColor(final String bgColor) {
        this.bgColor = bgColor;
    }
}

--- NEW FILE: TableTag.java ---
/**
 * Created by IntelliJ IDEA.
 * User: Benjamin Simpson
 * Date: Jan 11, 2003
 * Time: 8:44:51 PM
 */

package com.tablelib.tags;

import com.tablelib.core.lang.ArrayUtils;
import com.tablelib.core.lang.NumberUtils;
import com.tablelib.core.lang.StringUtils;
import com.tablelib.core.net.NetUtils;
import com.tablelib.core.table.Table;
import com.tablelib.core.taglibs.BaseTag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import java.text.MessageFormat;
import java.util.*;

import org.apache.commons.beanutils.BeanUtils;


public class TableTag extends BaseTag {

    private static final boolean SORT_ORDER_DECENDING = true;
    private static final boolean SORT_ORDER_ASCENDING = false;
    private Table table = null;
    public List columns;
    private List list = null;
    private String key = null;
    private String property = null;
    public int scope = PageContext.REQUEST_SCOPE;
    private String pagesize = null;
    private String export = null;

    // Variables that get set by investigating the request parameters
    private int sortColumn = -1;
    private PaginigInformationImpl pagingInformation;
    private boolean sortOrder = SORT_ORDER_ASCENDING;
    private int pageNumber = 1;
    private int exportType = TableExporter.EXPORT_TYPE_NONE;


    public TableTag() {
        this.table = new TableImpl();
    }

    public void setHeight(final int parm) {
        table.setHeight(parm);
    }

    public void setWidth(String parm) {
        table.setWidthIsPercentage(parm.indexOf("%") > -1);
        parm = table.isWidthPercentage() ? parm.substring(0, parm.length() - 1) : parm;
        table.setWidth(NumberUtils.parseInt(parm, -1));
    }

    public void setBorder(final int parm) {
        table.setBorder(parm);
    }

    public void setCellspacing(final int parm) {
        table.setCellSpacing(parm);
    }

    public void setCellpadding(final int parm) {
        table.setCellPadding(parm);
    }

    public void setBackGround(final String parm) {
        table.setBackGround(parm);
    }

    public void setBgColor(final String parm) {
        table.setBgColor(parm);
    }

    public void setHSpace(final int parm) {
        table.setHSpace(parm);
    }

    public void setVSpace(final int parm) {
        table.setVSpace(parm);
    }

    public void setList(final Object obj) {
        this.list = (List) obj;
    }

    public void setKey(final String parm) {
        this.key = parm;
    }

    public void setProperty(final String parm) {
        this.property = parm;
    }

    public void setScope(String parm) {
        if (parm == null)
            scope = PageContext.REQUEST_SCOPE;
        else if ((parm = parm.toLowerCase()).equals("request"))
            scope = PageContext.REQUEST_SCOPE;
        else if (parm.equals("session"))
            scope = PageContext.SESSION_SCOPE;
        else if (parm.equals("application"))
            scope = PageContext.APPLICATION_SCOPE;
        else
            scope = PageContext.PAGE_SCOPE;
    }

    public void setPagesize(final String parm) {
        this.pagesize = parm;
    }

    public void setExport(final String parm) {
        this.export = parm;
    }

    public Object getList() {
        return this.list;
    }

    public String getProperty() {
        return this.property;
    }

    public String getPagesize() {
        return this.pagesize;
    }

    public void reset() {
        this.pageNumber = 1;
        this.sortColumn = 1;
    }

    private int parseInt(final String keyOrVal, final int def, final boolean negsOk) {
        if (keyOrVal == null) return def;   //if the key or value does not exist, do 
the default!
        int ret;
        try {
            ret = Integer.parseInt(keyOrVal);
        } catch (NumberFormatException nfe) {
            Integer obj = (Integer) pageContext.findAttribute(keyOrVal);
            ret = obj == null ? 0 : obj.intValue();
        }
        return !negsOk && ret < 0 ? def : ret;
    }


    /**
     * Returns the pagesize that the person provided as an int.  If the user does
     * not provide a pagesize, or we can't figure out what they are trying to tell
     * us, then we default to 0.
     *
     * @return the maximum number of objects that should be shown on any one page
     *    when displaying the list.  Setting this value also indicates that the
     *    person wants us to manage the paging of the list.
     **/

    private int getPagesizeValue() {
        return parseInt(this.pagesize, 0, false);
    }

    /**
     * This method is called by the columns themselves.
     * This method was inherited from the display taglibrary.
     * @param obj
     */
    public void addColumn(final Object obj) {
        columns.add(obj);
    }

    /**
     * When the tag starts, we just initialize some of our variables, and do a
     * little bit of error checking to make sure that the user is not trying
     * to give us parameters that we don't expect.
     **/
    public int doStartTag() throws JspException {
        columns = new ArrayList(10);

        if (getParameter("page", "").length() > 0) {
            this.pageNumber = NumberUtils.parseInt(getParameter("page"), 1);
        }

        if (getParameter("sort", "").length() > 0) {
            this.sortColumn = NumberUtils.parseInt(getParameter("sort", "0"), 0);
            this.sortOrder = getParameter("sort", "").equals("dec") ?
                    SORT_ORDER_DECENDING : SORT_ORDER_ASCENDING;
        }

        this.exportType = TableExporter.EXPORT_TYPE_NONE;
        if (this.export != null) {
            if (getParameter("exportType") != null) {
                this.exportType = NumberUtils.parseInt(getParameter("exportType"), 
TableExporter.EXPORT_TYPE_NONE);
            }
        }
        return super.doStartTag();
    }


    /**
     * Draw the table.  This is the meat of the tag set.
     * The method getHTMLData slice is the method that will actually
     * draw the page.  The rest will perform an export.
     */
    public int doEndTag() throws JspException {
        final List slice = getPageSlice();
        try {
            if (this.exportType == TableExporter.EXPORT_TYPE_NONE) {
                write(getHTMLData(slice));
                return EVAL_PAGE;
            }

            return TableExporter.export(this.exportType,
                    this,
                    getProperty("export.amount").equals("list") ? this.list : slice);
        } catch (Exception e) {
            throw new JspException(e.getMessage());
        }

    }


    /**
     * This returns a list of all of the data that will be displayed on the
     * page via the table tag.  This might include just a subset of the total
     * data in the list due to to paging being active, or the user asking us
     * to just show a subset, etc...<p>
     *
     * The list that is returned from here is not the original list, but it
     * does contain references to the same objects in the original list, so that
     * means that we can sort and reorder the list, but we can't mess with the
     * data objects in the list.
     */

    private List getPageSlice() {
        if (this.list == null) {
            this.list = getListData(key, property, scope);
            if (this.list == null) {
                this.list = Collections.EMPTY_LIST;
            }
        }
        sortDataIfNeeded();
        if (getParameter("sort") != null) {
            if (!getProperty("sort.behavior").equals("page")) {
                this.pageNumber = 1;
            }
        }


        int pageSize = this.getPagesizeValue();
        if(pageSize == 0) return list;
        pagingInformation = new 
PaginigInformationImpl(this.getProperties(),list,pageNumber,pageSize);
        int start = pageSize * (pageNumber-1);
        int listSize = list.size();
        int end = start + pageSize;
        if(end > listSize) end = listSize;
        return start == 0 && end == listSize ? list : list.subList(start,end);
    }


    /**
     * This method will sort the data in either ascending or decending order
     * based on the user clicking on the column headers.
     */

    private void sortDataIfNeeded() {
        String prop = ((ColumnTag) columns.get(this.sortColumn == -1 ? 0 : 
this.sortColumn)).getProperty();
        try {
            Object [] values = list.toArray();
            ArrayUtils.sort(values,prop,this.sortOrder);
            list = Arrays.asList(values);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private Hashtable previousRow = null;
    private Hashtable nextRow = null;

    private String getRowClass(final int count) {
        return count % 2 == 0 ? " class=\"tableRowOdd\"" : " class=\"tableRowEven\"";
    }

    private String getHTMLData(final List rows) throws Exception {
        final int colsSize = this.columns.size();
        final ArrayList lines = new ArrayList(rows.size() * colsSize);

        /** This algrithm looks perishable :) **/
        previousRow = new Hashtable(colsSize, 1f); // variables to hold the previous 
row columns values.
        nextRow = new Hashtable(colsSize, 1f); // variables to hold next row column 
values.
        lines.add(getTableHeader());


        // this should be done earlier in the process:  this will permit removal of 
the trivial doendtag do one thing...
        final Object[] colAttributes = ArrayUtils.accessValues(columns.toArray(), 
"getCellAttributes", ColumnTag.class);
        for (int rowcnt = 0, len = rows.size(); rowcnt < len; rowcnt++) {


            /** this is our row: an expected type of data might be a
             * Struts Form Bean an Object [] or a map.
             * if a formbean we are expecting to be able to have access to the values.
             */

            Object row = rows.get(rowcnt);
            lines.add(new 
StringBuffer("<tr").append(getRowClass(rowcnt)).append(">\n").toString());
            for (int i = 0; i < colsSize; i++) {
                StringBuffer colBuffer = new StringBuffer(256);
                ColumnTag tag = (ColumnTag) this.columns.get(i);
                colBuffer.append("<td ").append(colAttributes[i]).append(">");
                String valueStr = getColumnValue(tag, row, rowcnt);
                StringBuffer xsBuffer = new StringBuffer(64);
                StringBuffer valBuffer = new StringBuffer(64);
                final int valLength = valueStr == null ? (valueStr = 
String.valueOf(valueStr)).length() : valueStr.length();
                final int maxLength = tag.getMaxLength();
                final int maxWords = tag.getMaxWords();
                while ((maxLength > 0 && valLength > maxLength) || maxWords > 0) {
                    if (maxLength > 0 && valLength > maxLength) {
                        xsBuffer.append("..." + valueStr.substring(maxLength, 
valLength));
                        valBuffer.append(valueStr.substring(0, maxLength));
                        break;
                    }
                    if (maxWords > 0) {
                        final StringTokenizer st = new StringTokenizer(valueStr);
                        final int numTokens = st.countTokens();
                        if (numTokens <= maxWords) break;
                        for (int x = 0; x < maxWords; x++) {
                            if (!st.hasMoreTokens()) continue;
                            valBuffer.append(st.nextToken() + " ");
                        }
                        if (st.hasMoreTokens()) {
                            xsBuffer.append("...");
                            while (st.hasMoreTokens()) 
xsBuffer.append(st.nextToken()).append(" ");
                        }
                        break;
                    }
                }
                boolean chopped = xsBuffer.length() > 0;
                if (tag.doLink && !chopped) 
valBuffer.append(NetUtils.autolink(valueStr.toString()));



                if (tag.getGroup() != null) {
                    try {
                        colBuffer.append(this.groupColumns(valueStr, new 
Integer(tag.getGroup()).intValue()));
                    } catch (Exception e) {
                        System.err.println(e.getMessage());
                        e.printStackTrace(System.err);
                    }
                } else {
                    if (chopped) {

                        colBuffer.append(valBuffer);
                        colBuffer.append("<a style=\"cursor: help;\" title=\"" + 
xsBuffer + "\">...</a>");
                    } else {
                        colBuffer.append(valueStr);
                    }

                }

                colBuffer.append("</td>\n");
                lines.add(colBuffer.toString());
            }

            // Special case, if they didn't provide any columns, then just spit out
            // the object's string representation to the table.

            if (this.columns.isEmpty()) {
                lines.add("<td class=\"tableCell\">" + row + "</td>");
            }

            lines.add("</tr>");

        }
        //end row!!!

        if (rows.isEmpty()) {
            lines.add("<tr class=\"tableRowOdd\">");
            lines.add("<td class=\"tableCell\" align=\"center\" colspan=\"" +
                    (colsSize + 1) + "\">" +
                    getProperty("basic.msg.empty_list") + "</td></tr>");
        }

        lines.add(this.getTableFooter());
        lines.add("</table>\n");

        return StringUtils.delimit(lines.toArray(), '\n');
    }

    private String getColumnValue(final ColumnTag tag, Object row, final int rowcnt)  {
        String value;
        final String property = tag.getProperty();
        try {
            if(property.toLowerCase().equals("table_index")) return 
String.valueOf(rowcnt);
            value = BeanUtils.getSimpleProperty(row,tag.getProperty());
        } catch (Exception e) {
            e.printStackTrace();
            value = " not able to get value";
        }
        List actions = tag.getActions();
        String val = value.toString();
        if(actions.isEmpty()) return val;
        StringBuffer ret = new StringBuffer(actions.size()*20);
        String delim = "&nbsp;";
        for(int i = 0, len = actions.size(); i < len; i++) {
            if(i != 0) ret.append(delim);
            ActionTag action = (ActionTag) actions.get(i);
            boolean needsStartingMark = action.getHref().indexOf('?') == -1;
            String href = action.getHref() + (needsStartingMark ? "?" : "&") + 
property +"="+val;
            ret.append(NetUtils.wrapLink(href+val,null,action.getActionCommand()));
        }
        return ret.toString();
    }

    /**
     * Generates the table header, including the first row of the table which
     * displays the titles of the various columns
     **/

    private String getTableHeader() {
        final StringBuffer buf = new StringBuffer(1000);
        final int pagesizeValue = this.getPagesizeValue();

        final String url = getRequest().getRequestURL().toString();
        table.declareTable(buf);

        // If they don't want the header shown for some reason, then stop here.

        if (!this.getProperty("basic.show.header").equals("true")) {
            return buf.toString();
        }

        if (pagesizeValue != 0 ){
            buf.append("<tr><td width=\"100%\" colspan=\"" + this.columns.size());
            buf.append("\"><table width=\"100%\" border=\"0\" cellspacing=\"0\" ");
            buf.append("cellpadding=\"0\"><tr class=\"tableRowAction\">");
            buf.append("<td align=\"left\" valign=\"bottom\" class=\"");
            buf.append("tableCellAction\">");
            buf.append(pagingInformation.getSearchResultsSummary());
            buf.append("</td>\n");
            buf.append("<td valign=\"bottom\" align=\"right\" class=\"");
            buf.append("tableCellAction\">\n");
            buf.append(pagingInformation.getPageNavigationBar(url + "?page="));
            buf.append("</td>\n</tr></table></td></tr>\n");
        }


        buf.append("<tr class=\"tableRowHeader\">");

        for (int i = 0, len = this.columns.size(); i < len; i++) {
            ColumnTag tag = (ColumnTag) this.columns.get(i);

            buf.append("<th");
            if (tag.getWidth() != null)
                buf.append(" width=\"" + tag.getWidth() + "\"");

            if (tag.getAlign() != null)
                buf.append(" align=\"" + tag.getAlign() + "\"");

            if (tag.getHeaderStyleClass() != null) {
                buf.append(" class=\"" + tag.getHeaderStyleClass() + "\">");
            } else {
                buf.append(" class=\"tableCellHeader\">");
            }

            String header = tag.getTitle();
            if (header == null) {
                header = StringUtils.toUpperCaseAt(tag.getProperty(), 0);
            }

            if (tag.getSort() != null) {
                if (this.sortOrder == SORT_ORDER_ASCENDING) {
                    buf.append("<a href=\"" + url + "?order=dec&sort=" + i + "\" 
class=\"tableCellHeader\">");
                } else {
                    buf.append("<a href=\"" + url + "?order=asc&sort=" + i + "\" 
class=\"tableCellHeader\">");
                }
                buf.append(header);
                buf.append("</a>");
            } else {
                buf.append(header);
            }

            buf.append("</th>\n");
        }

        // Special case, if they don't provide any columns, then just set
        // the title to a message, telling them to provide some...

        if (columns.isEmpty()) {
            buf.append("<td><b>").append(getProperty("error.msg.no_column_tags"));
            buf.append("</b></td>");
        }

        buf.append("</tr>\n");

        return buf.toString();
    }


    /**
     * Generates table footer with links for export commands.
     **/

    private String getTableFooter() {
        final StringBuffer buf = new StringBuffer(1000);
        final int pagesizeValue = this.getPagesizeValue();

        String url = getRequest().getRequestURL().toString();

        // Put the page stuff there if it needs to be there...

        if (getProperty("paging.banner.placement").equals("both") || 
getProperty("paging.banner.placement").equals("bottom")) {
            if (pagesizeValue != 0 ) {
                buf.append("<tr><td width=\"100%\" colspan=\"" + columns.size());
                buf.append("\"><table width=\"100%\" border=\"0\" cellspacing=\"0\" ");
                buf.append("cellpadding=\"0\"><tr class=\"tableRowAction\">");
                buf.append("<td align=\"left\" valign=\"bottom\" class=\"");
                buf.append("tableCellAction\">");
                buf.append(pagingInformation.getSearchResultsSummary());
                buf.append("</td>\n");
                buf.append("<td valign=\"bottom\" align=\"right\" class=\"");
                buf.append("tableCellAction\">\n");
                buf.append(pagingInformation.getPageNavigationBar(url + "?page="));
                buf.append("</td>\n</tr></table></td></tr>\n");
            }
        }

        final String qString = getRequest().getQueryString();
        url += qString != null && !qString.equals("") ? "?" + qString + "&" : "?";

        if (this.export != null) {
            buf.append("<tr><td align=\"left\" width=\"100%\" colspan=\"" + 
this.columns.size() + "\">");
            buf.append("<table width=\"100%\" border=\"0\" cellspacing=\"0\" 
cellpadding=0>");
            buf.append("\t<tr class=\"tableRowAction\">");
            buf.append("\t\t<td align=\"left\" valign=\"bottom\" 
class=\"tableCellAction\"><br><Br>");

            // Figure out what formats they want to export, make up a little string

            StringBuffer formats = new StringBuffer();
            if (getProperty("export.csv").equals("true")) {
                formats.append("<a href=\"")
                        .append(url)
                        .append("exportType=")
                        .append(TableExporter.EXPORT_TYPE_CSV)
                        .append("\">")
                        .append(getProperty("export.csv.label"))
                        .append("</a>\n");
            }

            if (getProperty("export.excel").equals("true")) {
                if (formats.length() > 0) 
formats.append(getProperty("export.banner.sepchar"));
                formats.append(NetUtils.wrapLink(url + "exportType=" + 
TableExporter.EXPORT_TYPE_EXCEL + "", "", "Excel") + "\n");
            }

            if (getProperty("export.xml").equals("true")) {
                if (formats.length() > 0) 
formats.append(getProperty("export.banner.sepchar"));
                formats.append("<a href=\"" + url + "exportType=" + 
TableExporter.EXPORT_TYPE_XML + "\">");
                formats.append(getProperty("export.xml.label") + "</a>\n");
            }

            Object[] objs = {formats.toString()};
            buf.append(MessageFormat.format(getProperty("export.banner"), objs));
            buf.append("\t\t</td></tr></table>\n");
            buf.append("</td></tr>");
        }

        return buf.toString();
    }


    /**
     *   This takes a cloumn value and grouping index as the argument.
     *   It then groups the column and returns the appropritate string back to the
     *   caller.
     */

    private String groupColumns(final String value,
                                final int group) {
        final Integer groupInteger = new Integer(group);
        if (groupInteger.intValue() == 1 && !this.nextRow.isEmpty()) { // we are at 
the begining of the next row so copy the contents from .
            // nextRow to the previousRow.
            this.previousRow.clear();
            this.previousRow.putAll(nextRow);
            this.nextRow.clear();
        }


        if (!this.nextRow.containsKey(groupInteger)) {
            // Key not found in the nextRow so adding this key now... remember all the 
old values.
            this.nextRow.put(groupInteger, value);
        }

        /**
         *  Start comparing the value we received, along with the grouping index.
         *  if no matching value is found in the previous row then return the value.
         *  if a matching value is found then this value should not get printed out
         *  so reuturn ""
         **/

        if (this.previousRow.containsKey(groupInteger)) {
            for (int x = 1; x <= group; x++) {
                final Integer xInteger = new Integer(x);
                if (this.previousRow.get(xInteger).equals(this.nextRow.get(xInteger))) 
continue;
                 // no match found so return this value back to the caller.
                    return value;
                }
            }
        }
        return previousRow.isEmpty() ? value : "";
    }
}
--- NEW FILE: TableTagExtraInfo.java ---
/**
 * $Id: TableTagExtraInfo.java,v 1.1 2003/07/18 13:19:57 javabencom Exp $
 *
 * Status: Under Development
 *
 * Todo
 *   - impementation
 *   - documentation (javadoc, examples, etc...)
 *   - junit test cases
 **/

package com.tablelib.tags;

import javax.servlet.jsp.tagext.TagData;
import javax.servlet.jsp.tagext.TagExtraInfo;
import javax.servlet.jsp.tagext.VariableInfo;

/**
 * One line description of what this class does.
 *
 * More detailed class description, including examples of usage if applicable.
 **/

public class TableTagExtraInfo extends TagExtraInfo {
    public VariableInfo[] getVariableInfo(final TagData data) {
        return new VariableInfo[]{
            new VariableInfo("table_index",
                    "java.lang.Integer",
                    true,
                    VariableInfo.NESTED),

            new VariableInfo("table_item",
                    "java.lang.Object",
                    true,
                    VariableInfo.NESTED),

        };
    }
}




-------------------------------------------------------
This SF.net email is sponsored by: VM Ware
With VMware you can run multiple operating systems on a single machine.
WITHOUT REBOOTING! Mix Linux / Windows / Novell virtual machines at the
same time. Free trial click here: http://www.vmware.com/wl/offer/345/0
_______________________________________________
displaytag-devel mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/displaytag-devel

Reply via email to