WepsTableMeta.java [src/usda/weru/util/table] Revision: default  Date:
/*
 * WepsTableMeta.java
 *
 * Created on June 8, 2006, 12:16 PM
 *
 */
package usda.weru.util.table;

import java.io.File;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import org.jdom.Element;

/**
 * Contains configuration information for a WepsTable object.
 * @author Joseph Levin
 */
public class WepsTableMeta implements XmlObject, Serializable {

    private static int c_anonymousId = 100;
    protected List<Column> c_columns;
//    protected transient Label[][] c_labelMatrix;
    protected WepsTableMeta c_parent;    //Inheritable values.
//    protected Hashtable<String, CellStyle> c_cellStyleMap;
    protected Hashtable<String, Column> c_columnMap;
    protected int c_frozenColumns = WepsTableEnum.NO_VALUE;
    protected int c_frozenRows = WepsTableEnum.NO_VALUE;
    protected int c_columnLabelDisplay = 1;
    protected int c_rowLabelDisplay = WepsTableEnum.NO_VALUE;
//    protected String c_defaultCellStyleId;
//    protected String c_defaultLabelStyleId;
//    protected CellStyle c_defaultCellStyle;
//    protected CellStyle c_defaultLabelStyle;

    /**
     * Creates a new instance of WepsTableMeta
     */
    public WepsTableMeta() {
        init();
    }

    /**
     * Initialize data structures that will hold information about the table.
     */
    public void init() {
//        c_cellStyleMap = new Hashtable<String, CellStyle>();
        c_columnMap = new Hashtable<String, Column>();
        c_columns = new Vector<Column>();
    }

    /**
     * Load table configuration information from an org.jdom XML node.
     * @param node An org.jdom.Element containing configuration information for the table.
     */
    public void fromXml(Element node) {
        init();
        loadParent(node);
//        loadCellStyles(node);
        loadColumnStyles(node);
        loadColumns(node);
//        loadRowLabelDisplay(node);
//        loadColumnLabelDisplay(node);
//        loadFrozenRows(node);
//        loadFrozenColumns(node);
    }

//    /**
//     * Apply the current configuration settings to the specified WepsTable.
//     * @param table The usda.weru.util.table.WepsTable to apply the current configuration to.
//     */
//    public void applyToTable(WepsTable table) {
//        table.setRepaintEnabled(false);
//        //Frozen rows/columns
//        int frozenRows = getFrozenRows();
//        if (frozenRows >= 0) {
//            table.setFrozenRows(frozenRows);
//        }
//
//        int frozenColumns = getFrozenColumns();
//        if (frozenColumns >= 0) {
//            table.setFrozenColumns(frozenColumns);
//        }
//
//        //Label Display
//        table.setRowLabelDisplay(isRowLabelDisplayed());
//        table.setColumnLabelDisplay(isColumnLabelDisplayed());
//
//        //Default styles
//        CellStyle defaultCellStyle = getDefaultCellStyle();
//        if (defaultCellStyle != null) {
//            table.setDefaultCellStyle(defaultCellStyle);
//        }
//
//        CellStyle defaultLabelStyle = getDefaultLabelStyle();
//        if (defaultLabelStyle != null) {
//            table.setDefaultLabelStyle(defaultLabelStyle);
//        }
//
//
//        //Set column widths
//        for (int c = 0; c < getColumnCount(); c++) {
//            Column column = getColumn(c);
//            table.setPixelWidth(c, column.getWidth());
//
//            int min = column.getMinWidth();
//            if (min != WepsTableEnum.NO_VALUE) {
//                table.setMinWidth(c, min);
//            }
//
//            int max = column.getMaxWidth();
//            if (max != WepsTableEnum.NO_VALUE) {
//                table.setMaxWidth(c, max);
//            }
//
//            if (column.isHidden() || !column.isVisible()) {
//                table.setColumnHidden(c, true);
//            }
//        }
//
//        //Span columns for headers.
//        table.clearSpannedRanges();
//        if (isColumnLabelDisplayed()) {
//            spanLabels(table);
//        }
//
//        //Lable Row Sizes.
//        for (int r = -1; r < getLabelRowCount(); r++) {
//            table.setPixelHeight(r, 20);
//        }
//        table.setRepaintEnabled(true);
//
//    }
//
//    /**
//     * Returns the CellStyle of the given name.  If there is no style by that name it will check the parent's list of styles.  Returns <B>null</B> if it still can't be found.
//     * @param id The id of the CellStyle.
//     * @return The CellStyle that was found or <B>null</B> if one could not be found.
//     */
//    public CellStyle getCellStyle(String id) {
//        if (id == null) {
//            return null;
//        //Inherit styles from the parent.
//        }
//        CellStyle style = c_cellStyleMap.get(id);
//        if (style != null) {
//            return style;
//        } else if (c_parent != null) {
//            return c_parent.getCellStyle(id);
//        } else {
//            return null;
//        }
//    }
//
//    /**
//     * Returns all CellStyles available.
//     * @return A Collection of all available CellStyles.
//     */
//    public Collection<CellStyle> getCellStyles() {
//        return c_cellStyleMap.values();
//    }
//
//    /**
//     * Adds the given CellStyle to the list of available CellStyles.
//     * @param style The style to be added to the list.
//     */
//    public void putCellStyle(CellStyle style) {
//        if (style.isAnonymous()) {
//            return;        //No reason to store an anonymous cellstyle.
//        }
//        c_cellStyleMap.put(style.getId(), style);
//    }
//
    /**
     * Returns the specified Column.  Checks the parent if the column can't be found.  Returns <B>null</B> if it still can't be found.
     * @param id The name of the desired Column.
     * @return The Column found or null if no column found.
     */
    public Column getColumn(String id) {
        if (id == null) {
            return null;
        //Inherit columns from the parent.
        }
        Column column = c_columnMap.get(id);
        if (column != null) {
            return column;
        } else if (c_parent != null) {
            return c_parent.getColumn(id);
        } else {
            return null;
        }
    }

    /**
     * Adds the given Column to the list of Columns
     * @param column The Column to be added.
     */
    public void putColumn(Column column) {
        if (column.isAnonymous()) {
            return;       //No reason to store an anonymous column for lookup
        }
        c_columnMap.put(column.getId(), column);
    }

    /**
     * Returns a unique ID.
     * @return A unique ID.
     */
    public String getAnonymousId() {
        return Integer.toString(c_anonymousId++);
    }

    /**
     * Load configuration settings from an XML file.
     * @param file The file containing configuration settings for the table.
     */
    public void fromFile(File file) {
        Element root = Helper.getRootNode(file);
        //TODO: Notify of fail.
        if (root == null) {
            return;
        }
        fromXml(root);
    }

    private void loadParent(Element node) {
        String parentPath = node.getAttributeValue(WepsTableEnum.XML_parent);
        if (parentPath == null) {
            return;
        }
        File parentFile = new File(new File(parentPath).getAbsoluteFile().toString());
        WepsTableMeta parent = new WepsTableMeta();
        parent.fromFile(parentFile);
        setParentMeta(parent);
    }

    private void setParentMeta(WepsTableMeta meta) {
        c_parent = meta;
    }

//    @SuppressWarnings("unchecked")
//    private void loadCellStyles(Element node) {
//        //Load the defined cell styles
//        Element stylesNode = node.getChild(WepsTableEnum.XML_cellstyles);
//
//        if (stylesNode != null) //No cell styles to load.
//        {
//            for (Element element : (List<Element>) stylesNode.getChildren(WepsTableEnum.XML_style)) {
//                CellStyle cellStyle = new CellStyle(this);
//                cellStyle.fromXml(element);
//                putCellStyle(cellStyle);
//            }
//        }
//
//        //Load the default cell and label styles
//        c_defaultCellStyleId = node.getChildText(WepsTableEnum.XML_defaultcellstyle);
//        c_defaultCellStyle = getCellStyle(getDefaultCellStyleId());
//
//        c_defaultLabelStyleId = node.getChildText(WepsTableEnum.XML_defaultlabelstyle);
//        c_defaultLabelStyle = getCellStyle(getDefaultLabelStyleId());
//
//        if (c_parent != null) {
//            for (CellStyle style : c_parent.getCellStyles()) {
//                style.setMeta(this);
//                style.refreshParentStyle();
//            }
//        }
//    }
//
//    /**
//     * Return the default CellStyle ID.  If there is no default specified, it checks for the parents default CellStyle ID.
//     * @return The ID of the default CellStyle.
//     */
//    public String getDefaultCellStyleId() {
//        if (c_defaultCellStyleId == null && c_parent != null) {
//            return c_parent.getDefaultCellStyleId();
//        } else {
//            return c_defaultCellStyleId;
//        }
//    }
//
//    /**
//     * Return the default CellStyle.  If there is no default specified, it checks for the parents default CellStyle.
//     * @return The default CellStyle.
//     */
//    public CellStyle getDefaultCellStyle() {
//        if (c_defaultCellStyle == null && c_parent != null) {
//            return c_parent.getDefaultCellStyle();
//        } else {
//            return c_defaultCellStyle;
//        }
//    }
//
//    /**
//     * Return the default label CellStyle ID.  If there is no default specified, it checks for the parents default label CellStyle ID.
//     * @return The default label CellStyle ID.
//     */
//    public String getDefaultLabelStyleId() {
//        if (c_defaultLabelStyleId == null && c_parent != null) {
//            return c_parent.getDefaultLabelStyleId();
//        } else {
//            return c_defaultLabelStyleId;
//        }
//    }
//
//    /**
//     * Return the default label CellStyle.  If there is no default specified, it checks for the parents default label CellStyle.
//     * @return The default label CellStyle.
//     */
//    public CellStyle getDefaultLabelStyle() {
//        if (c_defaultLabelStyle == null && c_parent != null) {
//            return c_parent.getDefaultLabelStyle();
//        } else {
//            return c_defaultLabelStyle;
//        }
//    }
//
    private void loadColumnStyles(Element node) {
        Element columnStylesNode = node.getChild(WepsTableEnum.XML_columnstyles);
        if (columnStylesNode == null) {
            return;
        }
        List<Element> children = columnStylesNode.getChildren(WepsTableEnum.XML_column);
        for (Element columnNode : children) {
            Column column = new Column(this, false);
            column.fromXml(columnNode);
            putColumn(column);
        }
    }

    private void loadColumns(Element node) {
        Element columnsNode = node.getChild(WepsTableEnum.XML_columns);
        if (columnsNode != null) {
            ColumnGroup columns = new ColumnGroup(this);
            columns.fromXml(columnsNode);
//            buildLabelMatrix();
        }
    }

//    private void buildLabelMatrix() {
//        int numberOfColumns = c_columns.size();
//        int numberOfRows = 0;
//        for (Column column : c_columns) {
//            int depth = column.depthInHeader();
//            if (depth > numberOfRows) {
//                numberOfRows = depth;
//            }
//        }
////        numberOfRows = numberOfRows;
//        c_labelMatrix = new Label[numberOfRows][numberOfColumns];
//        for (int c = 0; c < numberOfColumns; c++) {
//            addToLabelMatrix(getColumn(c), numberOfRows - 1, c);
//        }
//    }
//
//    private void addToLabelMatrix(ColumnGroupOrColumn group, int rowIndex, int columnIndex) {
//        //printLabelMatrix();
//
//        int numberOfColumnsToSpan = group.getLabel().getSpanColumns();
//        //Is the column spanning set on the label?        
//        if (numberOfColumnsToSpan <= 0) {
//            //Determine the number of columns to span.
//            //This is the number of columns at the bottom of the tree which share this parent.
//            //Should always be one for a column, they have no children.
//            numberOfColumnsToSpan = group.bottomBreadth();
//        }
//
//        int numberOfRowsToSpan = group.getLabel().getSpanRows();
//        //Is the row spanning set on the label?
//        if (numberOfRowsToSpan <= 0) {
//            //Determine the number of rows to span.
//            //span = rows - depthInHeader + 1
//            int depth = group.depthInHeader() - 1;      //We subtract 1 because the top level parent is a root container.
//            numberOfRowsToSpan = rowIndex - depth + 1;
//        }
//
//        for (int c = 0; c < numberOfColumnsToSpan; c++) {
//            for (int r = 0; r < numberOfRowsToSpan; r++) {
//                if (c_labelMatrix[rowIndex - r][columnIndex + c] == null) {
//                    c_labelMatrix[rowIndex - r][columnIndex + c] = group.getLabel();
//                }
//            }
//        }
//
//        if (group.depthInHeader() > 1) {     //Has a parent and grandparent.          
//            if (group.getParentGroup().isFirstChild(group)) {
//                //This is the fist child, so it adds the parent.
//                addToLabelMatrix(group.getParentGroup(), rowIndex - numberOfRowsToSpan, columnIndex);
//            }
//        }
//
//    }
//
//    /**
//     * 
//     * @param table The WepsTable to be queried.
//     * @param rowIndex The row index of the Cell to be queried.
//     * @param columnIndex The column index of the cell to be queried.
//     * @return 
//     */
//    public Object getColumnLabel(WepsTable table, int rowIndex, int columnIndex) {
//        if (rowIndex < 0 || columnIndex < 0) {
//            return null;
//        }
//        Label label = getLabelMatrix()[rowIndex][columnIndex];
//        if (label == null) {
//            return null;
//        }
//        Object value = label.getObject(table, rowIndex, columnIndex);
//        if (value instanceof String) {
//            value = parse((String) value, table, rowIndex, columnIndex);
//        }
//        return value;
//    }
//
//    public void spanLabels(JCTable table) {
//        //Num rows = 2 ---> freeze 
//        //TODO: Adjust frozen rows to the user's number of frozen rows.  This will need to be handled by overiding setFrozenRows in WepsTable.
//        boolean[][] used = null;
//        try {
//            used = new boolean[getLabelMatrix().length][getLabelMatrix()[0].length];
//        } catch (ArrayIndexOutOfBoundsException aioobe) {
//            //TODO: Notify of fail.
//            return;
//        }
//
//        for (int r = 0; r < getLabelMatrix().length; r++) {
//            for (int c = 0; c < getLabelMatrix()[r].length; c++) {
//                if (!used[r][c]) {
//                    JCCellRange span = getSpan(r, c, used);
//                    if (span != null) {
//                        table.addSpannedRange(span);
//                    }
//                }
//            }
//        }
//    }
//
//    private JCCellRange getSpan(int rowIndex, int columnIndex, boolean[][] used) {
//        if (used[rowIndex][columnIndex]) {
//            return null;
//        }
//        JCCellRange span = new JCCellRange();
//        Label[][] labelMatrix = getLabelMatrix();
//        Label label = labelMatrix[rowIndex][columnIndex];
//
//        int endColumn = columnIndex;
//        Label test = labelMatrix[rowIndex][endColumn];
//
//        //How many columns are the same?  Catch if the entire row has the same header.
//        for (int cdx = columnIndex; cdx < used[rowIndex].length; cdx++) {
//            test = labelMatrix[rowIndex][cdx];
//            if (test != label) {
//                break;
//            }
//            endColumn = cdx;
//        }
//
//        int endRow = 0;
//        for (int rdx = rowIndex; rdx < used.length; rdx++) {
//            test = labelMatrix[rdx][columnIndex];
//            if (test != label) {
//                break;
//            }
//            endRow = rdx;
//            for (int cdx = columnIndex; cdx < endColumn; cdx++) {
//                used[rdx][cdx] = false;
//            }
//        }
//
//        span.start_column = columnIndex;
//        span.start_row = rowIndex - 1;
//        span.end_column = endColumn;
//        span.end_row = endRow - 1;
//
//        for (int ir = span.start_row + 1; ir <= span.end_row + 1; ir++) {
//            for (int ic = span.start_column; ic <= span.end_column; ic++) {
//                used[ir][ic] = true;
//            }
//        }
//
//        return span;
//    }
//
//    /**
//     * 
//     * @return 
//     */
//    public Label[][] getLabelMatrix() {
//        //TODO: Add more smarts to the tests, ie: is the number of columns correct...
//        if (c_labelMatrix == null) {
//            buildLabelMatrix();
//        }
//        return c_labelMatrix.clone();
//    }
//
//    private void loadRowLabelDisplay(Element node) {
//        String rowLabelDisplayText = node.getChildTextTrim(WepsTableEnum.XML_rowlabeldisplay);
//        if (rowLabelDisplayText == null) {
//            return;
//        }
//        boolean rowLabelDisplay = Helper.isTrue(rowLabelDisplayText);
//        if (rowLabelDisplay) {
//            c_rowLabelDisplay = 1;
//        }
//    }
//
//    /**
//     * 
//     * @return 
//     */
//    public boolean isRowLabelDisplayed() {
//        if (c_rowLabelDisplay == WepsTableEnum.NO_VALUE && c_parent != null) {
//            return c_parent.isRowLabelDisplayed();
//        } else {
//            return (c_rowLabelDisplay == 1);
//        }
//    }
//
//    private void loadColumnLabelDisplay(Element node) {
//        String columnLabelDisplayText = node.getChildTextTrim(WepsTableEnum.XML_columnlabeldisplay);
//        if (columnLabelDisplayText == null) {
//            return;
//        }
//        boolean columnLabelDisplay = Helper.isTrue(columnLabelDisplayText);
//        if (columnLabelDisplay) {
//            c_columnLabelDisplay = 1;
//        }
//    }
//
//    /**
//     * 
//     * @return 
//     */
//    public boolean isColumnLabelDisplayed() {
//        if (c_columnLabelDisplay == WepsTableEnum.NO_VALUE && c_parent != null) {
//            return c_parent.isColumnLabelDisplayed();
//        } else {
//            return (c_columnLabelDisplay == 1);
//        }
//    }
//
//    private void loadFrozenRows(Element node) {
//        String frozenRowsText = node.getChildTextTrim(WepsTableEnum.XML_frozenrows);
//        try {
//            int frozenRows = Integer.parseInt(frozenRowsText);
//            c_frozenRows = frozenRows;
//        } catch (NumberFormatException nfe) {
//            return;
//        }
//    }
//
//    /**
//     * Return the number of rows to be reserved as header rows.
//     * @return The number of rows used as header rows.
//     */
//    public int getFrozenRows() {
//        if (c_frozenRows == WepsTableEnum.NO_VALUE && c_parent != null) {
//            return c_parent.getFrozenRows();
//        } else if (c_frozenRows > 0) {
//            return c_frozenRows;
//        } else {
//            return 0;
//        }
//    }
//
//    private void loadFrozenColumns(Element node) {
//        String frozenColumnsText = node.getChildTextTrim(WepsTableEnum.XML_frozencolumns);
//        try {
//            int frozenColumns = Integer.parseInt(frozenColumnsText);
//            c_frozenColumns = frozenColumns;
//        } catch (NumberFormatException nfe) {
//            return;
//        }
//    }
//
//    /**
//     * Return the number of columns to be reserved as row headers.
//     * @return The number of columns used as header rows.
//     */
//    public int getFrozenColumns() {
//        if (c_frozenColumns == WepsTableEnum.NO_VALUE && c_parent != null) {
//            return c_parent.getFrozenColumns();
//        } else if (c_frozenColumns > 0) {
//            return c_frozenColumns;
//        } else {
//            return 0;
//        }
//    }
//
    /**
     * Returns the list of Columns available to the table.
     * @return The list of columns available to the table.
     */
    public Column[] getColumns() {
        Column[] temp = new Column[0];
        return c_columns.toArray(temp);
    }

    /**
     * Returns the number of Columns available to the table.
     * @return The number of columns available to the table.
     */
    public int getColumnCount() {
        if (c_columns == null) {
            return 0;
        }
        int count = c_columns.size();
        return count;
    }

    /**
     * Add the specified Column to the list of available Columns.
     * @param column The Column to be added.
     */
    public void addColumn(Column column) {
        c_columns.add(column);
        putColumn(column);
    }

    /**
     * Return the Column at the specified index in the table.
     * @param columnIndex The index of the desired Column.
     * @return The Column at the specified index.
     */
    public Column getColumn(int columnIndex) {
        try {
            return c_columns.get(columnIndex);
        } catch (IndexOutOfBoundsException ioobe) {
            return null;
        }
    }

//    /**
//     * Return the number of rows used as column headers.
//     * @return The number or rows used as column headers.
//     */
//    public int getLabelRowCount() {
//        if (isColumnLabelDisplayed() == false) {
//            return 0;
//        }
//        int count = getLabelMatrix().length;
//        return count;
//    }
//
//    /**
//     * Evaluate the supplied boolean expression.
//     * @param expression The expression to be evaluated.
//     * @param table 
//     * @param rowIndex 
//     * @param columnIndex 
//     * @return The result of the expression.
//     */
//    public boolean evaluate(String expression, WepsTable table, int rowIndex, int columnIndex) {
//        //Very very basic, only supports left of == equals right of == in a string comparison.
//        int operatorIndex = expression.indexOf("==");
//        if (operatorIndex < 0) {
//            return false;
//        }
//        String left = expression.substring(0, operatorIndex);
//        String right = expression.substring(operatorIndex + 2);
//
//        if (left == null || right == null) {
//            return false;
//        }
//        left = parse(left, table, rowIndex, columnIndex).toString();
//        right = parse(right, table, rowIndex, columnIndex).toString();
//
//        return left.equals(right);
//    }    //Regex =  (?:\$\{([.[^\}]]*)\.([.[^\}]]*)\})|(?:\$\{([.[^\}]]*)\})    
//    //This will match ${name.field}, name is in group 1 and field is in group 2, group 3 is a field without a name, ${field}
//    private static final String parsePatternString = "(?:\\$\\{([.[^\\}]]*)\\.([.[^\\}]]*)\\})|(?:\\$\\{([.[^\\}]]*)\\})";
//    private static final Pattern parsePattern = Pattern.compile(parsePatternString);    //Regex For Events =  (?:\$X\{([.[^\}]]*)\.([.[^\}]]*)\})|(?:\$X\{([.[^\}]]*)\})
//    private static final String parseXPatternString = "(?:\\$X\\{([.[^\\}]]*)\\.([.[^\\}]]*)\\})|(?:\\$X\\{([.[^\\}]]*)\\})";
//    private static final Pattern parseXPattern = Pattern.compile(parseXPatternString);
//
//    /**
//     * 
//     * @param text 
//     * @param table 
//     * @param rowIndex 
//     * @param columnIndex 
//     * @return 
//     */
//    public Object parse(String text, WepsTable table, int rowIndex, int columnIndex) {
//        Object value;
//        value = parseSpecial(text, table, rowIndex, columnIndex);
//        value = parseExternal(value.toString(), table, rowIndex, columnIndex);
//        value = parseInternal(value.toString(), table, rowIndex, columnIndex);
//        return value;
//    }
//
//    private Object parseSpecial(String text, WepsTable table, int rowIndex, int columnIndex) {
//        text = text.replace("${column.index}", String.valueOf(columnIndex));
//        return text;
//    }
//
//    private Object parseExternal(String text, WepsTable table, int rowIndex, int columnIndex) {
//        if (text == null) {
//            return null;
//        }
//        Object value = text;
//        if (text.indexOf("X") > 0) {
//            int a = 0;
//        }
//        Matcher matcher = parseXPattern.matcher(text);
//        while (matcher.find()) {
//            String expression = matcher.group();
//            String loneField = matcher.group(3);
//            String field;
//            String name;
//            if (loneField != null && loneField.length() > 0) {
//                name = null;
//                field = loneField;
//            } else {
//                name = matcher.group(1);
//                field = matcher.group(2);
//            }
//            ParseEvent event = new ParseEvent(text, table, rowIndex, columnIndex, expression, name, field);
//            table.fireParseEvent(event);
//            if (event.getParsed() != null) {
//                text = text.replace(expression, event.getParsed().toString());
//            }
//
//            value = text;
//        }
//        return value;
//    }
//
//    private Object parseInternal(String text, WepsTable table, int rowIndex, int columnIndex) {
//        if (text == null) {
//            return null;
//        }
//        Matcher matcher = parsePattern.matcher(text);
//        while (matcher.find()) {
//            String expression = matcher.group();
//            String loneField = matcher.group(3);
//            Column column = null;
//            String field;
//            String name = null;
//            if (loneField != null && loneField.length() > 0) {
//                //Assume the current column.
//                column = getColumn(columnIndex);
//                field = loneField;
//            } else {
//                name = matcher.group(1);
//                column = c_columnMap.get(name);
//                field = matcher.group(2);
//            }
//
//
//            if (column != null) {
//                ParseEvent event = new ParseEvent(text, table, rowIndex, columnIndex, expression, name, field);
//                column.parse(event);
//                Object value = event.getParsed();
//                if (value == null) {
//                    value = "#N/A#";
//                }
//                text = text.replace(expression, value.toString());
//            }
//        }
//        return text;
//    }
}