Column.java [src/usda/weru/util/table] Revision: default  Date:
/*
 * Column.java
 *
 * Created on June 8, 2006, 10:54 AM
 *
 */

package usda.weru.util.table;

import java.text.DecimalFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import org.jdom.Element;
import usda.weru.util.ConversionCalculator;
import usda.weru.util.ConversionUnit;
import usda.weru.util.ConvertedValue;

/**
 * Wrapper class for a Column object in a WepsTable.
 * @author Joseph Levin
 */
public class Column implements ColumnGroupOrColumn, XmlObject{
    
//    protected Label c_label;
    protected WepsTableMeta c_meta;    
    
    protected ColumnGroup c_parentColumnGroup;
    protected ColumnGroupOrColumn c_tempChild;
    
    protected String c_id;
    protected boolean c_anonymous;
    protected boolean c_forDisplay = true;
    protected boolean quickplot = false;
    
    
    protected Column c_parent;
    //Inhertable junk
    protected String c_name;
    protected int c_dataType = WepsTableEnum.NO_VALUE;
    protected String c_dataKey;
    protected ConversionUnit c_dataUnits;
    protected Hashtable <String, ConversionUnit> c_dataDisplayUnits;      //System to unit
    protected Hashtable <String, Format> c_dataFormats;      //System to format
    protected Hashtable <String, DataLimit> c_dataLimits;      //System to format
    protected List <DataAdjustment> c_dataAdjustments;
    protected int c_minWidth = WepsTableEnum.NO_VALUE;;
    protected int c_width = WepsTableEnum.NO_VALUE;
    protected int c_maxWidth = WepsTableEnum.NO_VALUE;
    protected int c_hidden = WepsTableEnum.NO_VALUE;;
    protected int c_visible = WepsTableEnum.NO_VALUE;;
    
//    protected CellStyle c_cellStyle;
//    
//    
    /**
     * Create a Column object associated with the given WepsTableMeta.
     * @param meta The WepsTableMeta to which this Column will be associated.
     */
    public Column(WepsTableMeta meta) {
        this(meta, true);
    }
    
    /**
     * Create a Column object associated with the given WepsTableMeta and is either displayable or not.
     * @param meta The WepsTableMeta to which this Column will be associated.
     * @param forDisplay Whether or not the Column will be displayable.
     */
    public Column(WepsTableMeta meta, boolean forDisplay) {
        init();        
        c_meta = meta;
        c_forDisplay = forDisplay;
    }
    
    /**
     * Load configuration settings for this Column from a jdom Element.
     * @param node An org.jdom.Element containing configuration information about this Column.
     */
    public void fromXml(Element node){
        loadId(node); 
        loadParent(node);
        loadName(node);  
        loadVisibility(node);
//        loadCellStyle(node);
        loadData(node);
        loadWidth(node);
//        if (isForDisplay()){
//            loadLabels(node);
//        }
    }
    
    private void init(){
        
    }
    
    private boolean isForDisplay(){
        return c_forDisplay;
    }
    
    /**
     * Return whether or not this column is hidden.  If a parent is specified it will check the parent to see if it is hidden.
     * @return Whether or not the Column is hidden.
     */
    public boolean isHidden(){
        if (c_hidden == WepsTableEnum.NO_VALUE && c_parent != null){
            return c_parent.isHidden();
        }
        else if (c_hidden == WepsTableEnum.NO_VALUE){
            return false;
        }
        else{
            return (c_hidden == 1);
        }
    }
    
    /**
     * Return whether or not this column is visible.  If a parent is specified it will check the parent to see if it is visible.
     * @return Whether or not the column is visible.
     */
    public boolean isVisible(){
        if (c_visible == WepsTableEnum.NO_VALUE && c_parent != null){
            return c_parent.isVisible();
        }
        else if (c_visible == WepsTableEnum.NO_VALUE){
            return true;
        }
        else{
            return (c_visible == 1);
        }
    }
    
    public void setQuickPlot(boolean quickplot){
        this.quickplot = quickplot;
    }
    
    public boolean isQuickPlot(){
        return quickplot;
    }
    
//    /**
//     * Return the Label used for this Column.
//     * @return The Label for this Column.
//     */
//    public Label getLabel(){
//        return c_label;
//    }
//    
    /**
     * Return whether or not the Column is anonymous.
     * @return <b>true</b> if the column is anonymous, <b>false</b> otherwise.
     */
    public boolean isAnonymous(){
        return c_anonymous;
    }
    
    /**
     * Set the ColumnGroup this Column will be a part of.
     * @param parent The ColumnGroup this Column will be a part of.
     * @return 
     */
    public ColumnGroupOrColumn setParent(ColumnGroup parent){  
        if (c_tempChild == null){
            c_parentColumnGroup = parent;
            return this;
        }
        else{
            c_tempChild.setParent(parent);
            return c_tempChild;
        }
    }
    
    /**
     * Return the ColumnGroup this Column is a part of.
     * @return The ColumnGroup this Column is a part of.
     */
    public ColumnGroup getParentGroup(){
        return c_parentColumnGroup;
    }
    
    /**
     * Set the parent Column this Column will inherit configuration settings from.
     * @param parent The parent Column this Column will inherit configuration settings from.
     */
    public void setParent(Column parent){
        c_parent = parent;
    }
    
    /**
     * Return the depth in the header that this Columns Label will appear.
     * @return The depth in the header.
     */
    public int depthInHeader(){
        int depth = 0;                
//        if (getLabel() != null && getLabel().getSpanRows() > 1){
//            depth = depth + getLabel().getSpanRows() - 1;
//        }
        if (getParentGroup() != null){
            depth++;
            depth = depth + getParentGroup().depthInHeader();
        }
        return depth;
    }
    
    /**
     * Returns the number of Columns under this Column (inclusive).
     * @return 1
     */
    public int bottomBreadth(){
        return 1;
    }
    
    /**
     * Not implemented.
     * @param child 
     * @return <b>false</b>
     */
    public boolean isFirstChild(ColumnGroupOrColumn child){
        return false;
    }
    
   
    
    private void loadId(Element node){
        //Get Id                
        String id = node.getAttributeValue(WepsTableEnum.XML_id);
        if (id != null){
            c_id = id;
        }
        else{
            //Get Id from counter
            c_id = c_meta.getAnonymousId();
            c_anonymous = true;
        }
    }
    
    private void loadParent(Element node){
        //Set Parent Column
        String parentId = node.getAttributeValue(WepsTableEnum.XML_parent);
        if (parentId != null){              
            Column parentColumn = c_meta.getColumn(parentId);
            //Has the parent been loaded?
            if(parentColumn != null) setParent(parentColumn);                    
        } 
    }
    
    private void loadName(Element node){
        c_name = node.getChildTextTrim(WepsTableEnum.XML_name);
    }
    
    private void loadVisibility(Element node){
        String visibleText = node.getChildTextTrim(WepsTableEnum.XML_visible);
        String hiddenText = node.getChildTextTrim(WepsTableEnum.XML_hidden);
        
        if (visibleText != null){
            if (Helper.isTrue(visibleText)){
                c_visible = 1;
            }
            else{
                c_visible = 0;
            }
        }
        
        if (hiddenText != null){
            if (Helper.isTrue(hiddenText)){
                c_hidden = 1;
            }
            else{
                c_hidden = 0;
            }
        }
        
    }
    
//    private void loadCellStyle(Element node){
//        Element styleNode = node.getChild(WepsTableEnum.XML_style);
//        if (styleNode == null) return;
//        String cellId = null;
//        cellId = styleNode.getTextTrim();
//
//        if (cellId != null && cellId.length() > 0){
//            c_cellStyle = c_meta.getCellStyle(cellId);
//        }
//        else{
//            c_cellStyle = new CellStyle(c_meta);
//            c_cellStyle.fromXml(styleNode);
//        }    
//    }
//    
//    private void loadLabel(Element node){        
//        c_label = new Label(c_meta);
//        Element labelElement = node.getChild(WepsTableEnum.XML_label);
//        c_label.fromXml(labelElement);
//    }
//    
//    private void loadLabels(Element node){
//        List <Element> labelNodes = (List <Element>) node.getChildren(WepsTableEnum.XML_label);
//        c_label = new Label(c_meta);
//        
//        boolean first = true;
//        boolean last = false;
//        ColumnGroup parent = null;
//        ColumnGroupOrColumn tempChild = null;
//        for (Element child : labelNodes){
//            last = labelNodes.indexOf(child) == labelNodes.size() - 1;      //Is the child the last element?
//            if (last){
//                c_label.fromXml(child);
//                if (parent != null){
//                    parent.add(this);
//                    c_tempChild = tempChild;
//                }                    
//            }
//            else{                
//                //We have more labels which we will create unique ColumnGroup objects for.
//                ColumnGroup tempColumns = new ColumnGroup(c_meta);     
//                if(parent != null){
//                    parent.add(tempColumns);
//                }                
//                
//                if (first){
//                    first = false;
//                    tempChild = tempColumns;    
//                }
//                Label tempLabel = new Label(c_meta);
//                tempLabel.fromXml(child);
//                tempColumns.setLabel(tempLabel);                
//                parent = tempColumns;
//            }
//        }     
//    }
//    
    private void loadData(Element node){
        Element data = node.getChild(WepsTableEnum.XML_data);
        if (data == null) return;
        String typeText = data.getAttributeValue(WepsTableEnum.XML_type);
        int type = WepsTableEnum.getEnumFromTag(typeText);
        if (type > 0 ) c_dataType = type;
        
        loadFormats(data);
        
        switch(getDataType()){
            case WepsTableEnum.DATA_numeric:
                loadUnits(data);
                loadDisplayUnits(data);
                loadAdjustments(data);
                loadLimits(data);
                break;
            case WepsTableEnum.DATA_list:
                loadChoiceList(data);
                break;
        }
        
        
    }
    
    private void loadUnits(Element node){
        try{             
            String unitsTag = node.getChildTextTrim(WepsTableEnum.XML_units);             
            //No tag, stop
            if (unitsTag == null) return;
            c_dataUnits = ConversionCalculator.getUnitFromTag(unitsTag);             
        }
        catch(Exception e){}
        
    }
    
    private void loadDisplayUnits(Element node){      
        if (getUnits() == null) return;     //We don't need display units if we don't have units to convert from.
        
        List <Element> displayUnits = (List <Element>) node.getChildren(WepsTableEnum.XML_displayunits);            
        //Load display units linked to a measurement system (SI, or US, etc...)
        boolean loadedDefault = false;
        for (Element unitElement : displayUnits){                
            String system = unitElement.getAttributeValue(WepsTableEnum.XML_system);
            if (system == null || system.length() == 0){
                if (loadedDefault) continue;
                system = WepsTableEnum.NO_UNITS_SYSTEM;
                loadedDefault = true;
            }            
            if (unitElement.getText().length() > 0){
                try{
                ConversionUnit unit = ConversionCalculator.getUnitFromTag(unitElement.getText());
                    if (system != null && system.length() > 0 && unit != null){
                        if (c_dataDisplayUnits == null){
                            c_dataDisplayUnits = new Hashtable <String, ConversionUnit> ();
                        }
                        c_dataDisplayUnits.put(system, unit);    
                    }
                }
                catch(Exception e){
                    //TODO:Notify of conversion unit lookup failure.
                }
            }

        }
    }
    
        
    private void loadFormats(Element node){        
        List <Element> formatNodeList = (List <Element>) node.getChildren(WepsTableEnum.XML_format);            
        //Load formats linked to a measurement system (SI, or US, etc...)
        boolean loadedDefault = false;
        for (Element formatNode : formatNodeList){                 
            String system = formatNode.getAttributeValue(WepsTableEnum.XML_system);    
            if (system == null || system.length() == 0){
                if (loadedDefault) continue;
                system = WepsTableEnum.NO_UNITS_SYSTEM;
                loadedDefault = true;
            }
            String pattern = formatNode.getText();
            if (pattern != null && pattern.length() > 0){                                    
                Format format = null;
                switch(getDataType()){
                    case WepsTableEnum.DATA_numeric:
                        format = new DecimalFormat(pattern);                        
                        break;
                    case WepsTableEnum.DATA_date:
                        format = new SimpleDateFormat(pattern);
                }
                
                if (format != null){
                    if (c_dataFormats == null){
                        c_dataFormats = new Hashtable <String, Format> ();    
                    }
                    c_dataFormats.put(system, format);
                }                
            }                            
        }
    }
    
    /**
     * Return the format used for the unit system this Column uses or the parents format if none is declared.
     * @param unitsSystem The unit system for the format required.
     * @return The format used by this Column for the unit system specified.
     */
    public Format getFormat(String unitsSystem){                
        if (c_dataFormats == null && c_parent != null){
            return c_parent.getFormat(unitsSystem);
        }
        else{
            if (c_dataFormats == null) return null;
            return c_dataFormats.get(unitsSystem);
        }
    }
    
    private void loadAdjustments(Element node){
        List <Element> adjustmentNodes = node.getChildren(WepsTableEnum.XML_adjust);
        for (Element adjustNode : adjustmentNodes){
            if (c_dataAdjustments == null){
                c_dataAdjustments = new Vector <DataAdjustment> ();
            }
            DataAdjustment adjust = new DataAdjustment();
            adjust.fromXml(adjustNode);
            c_dataAdjustments.add(adjust);
            
        }
    }
    
    /**
     * Return a List of adjustments applied to the Columns data or a List from the parent if there is none specified.
     * @return The List of adjustments to be applied to the Columns data.
     */
    public List <DataAdjustment> getAdjustmentList(){
        if (c_dataAdjustments == null && c_parent != null){
            return c_parent.getAdjustmentList();
        }
        else{
            return c_dataAdjustments;
        }
    }
    
    
    
    private void loadLimits(Element node){
        List <Element> limitNodes = node.getChildren(WepsTableEnum.XML_limit);
        
        for (Element limitNode : limitNodes){
            DataLimit limit = new DataLimit();
            limit.fromXml(limitNode);
            if (c_dataLimits == null){
                c_dataLimits = new Hashtable <String, DataLimit> ();
            }
            c_dataLimits.put(limit.getSystem(), limit);
        }
    }
    
    /**
     * Return the DataLimit to be applied to this Column in the unit system specified.
     * @param unitsSystem The unit system to be used.
     * @return The DataLimit to be applied.
     */
    public DataLimit getLimit(String unitsSystem){                
        if (c_dataLimits == null && c_parent != null){
            return c_parent.getLimit(unitsSystem);
        }
        else{
            if (c_dataLimits == null) return null;
            return c_dataLimits.get(unitsSystem);
        }
    }
    
    
    private void loadChoiceList(Element node){
        
    }
    
    private void loadWidth(Element node){
        Element widthNode = node.getChild(WepsTableEnum.XML_width);
        if (widthNode == null) return;
        
        String widthText = widthNode.getValue();
        String minText = widthNode.getAttributeValue(WepsTableEnum.XML_min);
        String maxText = widthNode.getAttributeValue(WepsTableEnum.XML_max);
        
        int width = WepsTableEnum.getEnumFromTag(widthText);
        if (width != WepsTableEnum.NOT_FOUND){
            c_width = width;
        }
        else{
            try{
                c_width = Integer.parseInt(widthText);
            }
            catch(NumberFormatException nfe){
                return;
            }
        }
        
        try{
                c_minWidth = Integer.parseInt(minText);
            }
            catch(NumberFormatException nfe){}
        
        try{
                c_maxWidth = Integer.parseInt(maxText);
            }
            catch(NumberFormatException nfe){}
    }
    
    /**
     * Return the ID of this Column.
     * @return The ID for this Column.
     */
    public String getId(){
        return c_id;
    }
    
    /**
     * Return the name of the Column or the name of the parent Column if none is specified.
     * @return The name of the Column.
     */
    public String getName(){
        if (c_name == null && c_parent != null){
            return c_parent.getName();
        }
        else if (c_name == null){
            return getId();
        }
        else{
            return c_name;
        }
    }
    
    /**
     * Return the data key used by this Column.
     * @return The data key used by this Column.
     */
    public String getDataKey(){
        if (c_dataKey != null){
            return c_dataKey;
        }
        else{
            if (isAnonymous() && c_parent != null){
                return c_parent.getDataKey();
            }
            else{
                return getId();
            }
        }
    }
    
//    /**
//     * Return the CellStyle used by this Column, or the CellStyle used by the parent.
//     * @return The CellStyle used by this Column.
//     */
//    public CellStyle getCellStyle(){
//        if (c_cellStyle == null && c_parent != null){
//            return c_parent.getCellStyle();
//        }
//        else{
//            return c_cellStyle;
//        }        
//    }
//    
//    
//    
    /**
     * Return a the Columns ID.
     * @return The Columns ID.
     */
    public String toString(){
        return getId();
    }
    
    
//    /**
//     * ParseEvent handler.
//     * @param event The ParseEvent to be handled.
//     */
//    public void parse(ParseEvent event){
//        String field = event.getField();
//        WepsTable table = event.getTable();
//        String unitsSystem = table.getUnitsSystem();
//        int rowIndex = event.getRowIndex();
//        
//        if (field == null) return;
//        field = field.toLowerCase().trim();
//        
//        if (field.equals(WepsTableEnum.PARSE_units)){
//            if (getUnits() != null){
//                event.setParsedExpression(getUnits().getName());
//            }
//        }
//        else if (field.equals(WepsTableEnum.PARSE_displayunits)){
//            ConversionUnit displayUnits = getDisplayUnits(unitsSystem);
//            if (displayUnits != null){
//                event.setParsedExpression(displayUnits.getName());
//            }
//        }
//        else if (field.equals(WepsTableEnum.PARSE_displayunitsabbreviation)){
//            ConversionUnit displayUnits = getDisplayUnits(unitsSystem);
//            if (displayUnits != null){
//                event.setParsedExpression(displayUnits.getAbbreviation());
//            }
//        }
//        else if (field.equals(WepsTableEnum.PARSE_value)){
//            event.setParsedExpression(table.getDataView().getObject(rowIndex, this));
//        }
//        else if (field.equals("quickplot")){
//            event.setParsedExpression(true);
//        }
//    }
//    
    /**
     * Returns the units used by this Column or the units of the parent column if none are specified.
     * @return The units being used by this Column.
     */
    public ConversionUnit getUnits(){
        if (c_dataUnits == null && c_parent != null){
            return c_parent.getUnits();
        }
        else{
            return c_dataUnits;
        }
    }
    
    /**
     * Returns the display units used by this Column given the system provided.
     * @param system The system of units being used.
     * @return The Columns units converted to the units of the system.
     */
    public ConversionUnit getDisplayUnits(String system){
        if (system == null) return null;
        if (c_dataDisplayUnits == null && c_parent != null){
            return c_parent.getDisplayUnits(system);
        }
        else{
            if (c_dataDisplayUnits == null) return null;
            return c_dataDisplayUnits.get(system);
        }

    }
    
    
    /**
     * Apply all available adjustments to the Object supplied.
     * @param o The Object to be adjusted.
     * @param unitsSystem Not used.
     * @return The adjusted Object.
     */
    public Object applyAdjustments(Object o, String unitsSystem){
        //Adjust the data
        List <DataAdjustment> adjustments = getAdjustmentList();
        if (adjustments != null){
            for (DataAdjustment adjust : adjustments){
                o = adjust.adjust(o);
            }
        }
        return o;
    }
    
    /**
     * Convert this Columns data to use a different unit system for display.
     * @param o The Object to be converted.
     * @param unitsSystem The unit System to convert to.
     * @return The converted value.
     */
        public Object applyDisplayUnits(Object o, String unitsSystem){
        if (o == null){
            return null;
        }
        try{
            double d = Double.parseDouble(o.toString());
            ConversionUnit displayUnits = getDisplayUnits(unitsSystem);
            ConversionUnit units = getUnits();
            if (displayUnits != null && units != null){
                ConvertedValue cValue = new ConvertedValue();
                cValue.setBaseUnits(units);
                cValue.setDisplayUnits(displayUnits);
                cValue.setValue(d);
                d = cValue.getDisplayValue();
                return d;
            }
            else return o;
        }
        catch(NumberFormatException nfe){
            return o;
        }
        catch(Exception e){
            return "#ERR#";
        } 
    }

    /**
     * Apply available limits to this Column.
     * @param o The Object to apply the limits to.
     * @param unitsSystem The unit system specifying the set of limits to be used.
     * @return The adjusted value.
     */
    public Object applyLimits(Object o, String unitsSystem){
        //Limit the data
        DataLimit limit = getLimit(unitsSystem);
        if (limit != null){
            return limit.limit(o);
        }
        else{
            return o;
        }
    }
        
    /**
     * Apply formating to the Object passed.
     * @param o The Object to be formatted.
     * @param unitsSystem The unit system whos formatting will be used.
     * @return The formatted Object.
     */
    public Object applyFormat(Object o, String unitsSystem){
        //Format the data
        Format format = getFormat(unitsSystem);
        if (format != null){
            try{
                return format.format(o);
            }
            catch(Exception e){
                return o;
            }
        }    
        else{
            return o;
        }
    }
    

    

    
    /**
     * Return the data type for this Column or the data type of the parent if there is none specified.
     * @return The data type used by this Column.
     */
    public int getDataType(){
        if (c_dataType < 0 && c_parent != null){
            return c_parent.getDataType();
        }
        else{
            return c_dataType;
        }
    }
    
//    /**
//     * Return the width of this Column or the width of the parent if there is none specified.
//     * @return The width of the Column or JCTableEnum.VARIABLE_ESTIMATE if there is no value available.
//     */
//    public int getWidth(){
//        if (c_width == WepsTableEnum.NO_VALUE && c_parent != null){
//            return c_parent.getWidth();
//        }
//        else{
//            if (c_width == WepsTableEnum.NO_VALUE){
//                return JCTableEnum.VARIABLE_ESTIMATE;
//            }
//            else{
//                return c_width;
//            }
//        }
//    }
//    
    /**
     * Return the minimum width or the minimum width of the parent if no minimum width is specified.
     * @return The minimum width of the Column.
     */
    public int getMinWidth(){
        if (c_minWidth == WepsTableEnum.NO_VALUE && c_parent != null){
            return c_parent.getMinWidth();
        }
        else{
            return c_minWidth;
        }
    }
    
    /**
     * Return the maximum width or the maximum width of the parent if no maximum width is specified.
     * @return The maximum width of the Column.
     */
    public int getMaxWidth(){
        if (c_maxWidth == WepsTableEnum.NO_VALUE && c_parent != null){
            return c_parent.getMaxWidth();
        }
        else{
            return c_maxWidth;
        }
    }
   
}