001    package net.sf.jolene.dom;
002    
003    import net.sf.jolene.constants.StyleClasses;
004    import net.sf.jolene.constants.Tags;
005    import org.apache.commons.beanutils.PropertyUtils;
006    import org.apache.log4j.LogManager;
007    import org.apache.log4j.Logger;
008    
009    import java.util.ArrayList;
010    import java.util.Iterator;
011    import java.util.List;
012    
013    /**
014     * A renderable data bound grid object. A grid is detected by the parser by having a table using an ID tag
015     * that starts with 'grid'.  The grid object can be accessed in the document just like any other object.
016     * See the bind method to see how to 'datalink' the grid to a list of data objects.
017     *
018     * @author Dan Howard
019     * @since Aug 1, 2005 5:54:59 PM
020     */
021    public final class Grid extends HTMLElement {
022    
023        private static final Logger log = LogManager.getLogger(Grid.class);
024    
025        /**
026         * List of row objects created when a document is read.
027         */
028        List<GridRow> rows;
029    
030        /**
031         * Default constructor.
032         */
033        public Grid() {
034            tag = Tags.table;
035            rows = new ArrayList<GridRow>(0);
036        }
037    
038        /**
039         * Binds the grid object to the specified data.
040         *
041         * @param header List of GridColumn objects defining the column information for the grid.
042         * @param fields List of fields (property names) which are accessed via PropertyUtils to set the values for each cell.
043         * @param data   List of data objects used to retrieve the values.
044         * @throws GridBindException if PropertyUtils fails.
045         */
046        public void bind(List<GridColumn> header, List<String> fields, List data) throws GridBindException {
047            rows = new ArrayList<GridRow>(data.size());
048    
049            if (header.size() > 0) {
050                Iterator<GridColumn> it = header.iterator();
051                rows.add(new GridRow());
052                rows.get(0).setHeader(true);
053                while (it.hasNext()) {
054                    rows.get(0).cells.add(new GridCell(it.next()));
055                }
056            }
057    
058            if (data == null || data.size() == 0) {
059                return;
060            }
061    
062            int rowPosition = 1;
063            Iterator it = data.iterator();
064    
065            while (it.hasNext()) {
066                Object row;
067                row = it.next();
068                rows.add(new GridRow());
069    
070                Iterator<String> fit = fields.iterator();
071    
072                int colPosition = 0;
073                while (fit.hasNext()) {
074                    String fieldName = fit.next();
075    
076                    // Here we get the associated grid column header since it might
077                    // have width and align properties defined which need be set on
078                    // each cell.
079                    GridColumn col = header.get(colPosition);
080                    GridCell cell;
081                    cell = new GridCell(col);
082                    if (col.getCellObject() != null) {
083                        cell.setCellObject(col.getCellObject().clone());
084    
085                        int n1;
086                        String cellName = cell.getCellObject().getName();
087                        n1 = cellName.indexOf("${");
088                        if (n1 > -1) {
089                            // Means the user has a indexed property needs to be in a format propertyName(${indexerFieldName})
090                            // parse out the field name.
091                            int n2 = cellName.indexOf("}");
092                            String propertyName = cellName.substring(n1 + 2, n2).trim();
093    
094                            try {
095                                cellName = cellName.substring(0, n1) + PropertyUtils.getProperty(row, propertyName) + cellName.substring(n2 + 1);
096                            } catch (Exception e) {
097                                throw new GridBindException(e);
098                            }
099                            cell.getCellObject().setName(cellName);
100                        }
101                    }
102    
103                    log.debug(fieldName + " " + row.getClass().getName());
104                    try {
105                        cell.setValue("" + PropertyUtils.getProperty(row, fieldName));
106                    } catch (Exception e) {
107                        throw new GridBindException(e);
108                    }
109                    rows.get(rowPosition).cells.add(cell);
110    
111                    if (rowPosition % 2 == 0) {
112                        rows.get(rowPosition).setAttribute("class", StyleClasses.EvenRow.toString());
113                    } else {
114                        rows.get(rowPosition).setAttribute("class", StyleClasses.OddRow.toString());
115                    }
116                    colPosition++;
117                }
118                rowPosition++;
119            }
120        }
121    
122    
123        /**
124         * Returns a clone of the grid object.
125         *
126         * @return Grid object.
127         */
128        @Override
129        public Grid clone() {
130            Grid grid = (Grid) super.clone();
131    
132            for (int j = 0; j < rows.size(); j++) {
133                GridRow r = rows(j).clone();
134                for (int i = 0; i < rows(j).cells.size(); i++) {
135                    r.cells.add(rows(j).cells(i).clone());
136                }
137                grid.rows.add(r);
138            }
139            return grid;
140        }
141    
142        /**
143         * Renders the grid.
144         *
145         * @return Grid as html string.
146         */
147        @Override
148        public String toString() {
149    
150            if (swapWith != null) {
151                return swapWith.toString();
152            }
153    
154            setAttribute("class", StyleClasses.Grid.toString());
155    
156            StringBuilder sb = new StringBuilder(super.toString());
157            String lf = System.getProperty("line.separator");
158    
159            sb.append(lf);
160    
161            Iterator it = rows.iterator();
162            while (it.hasNext()) {
163                sb.append(it.next().toString()).append(lf);
164            }
165            sb.append("</").append(tag).append(">").append(lf);
166            return sb.toString();
167        }
168    
169        /**
170         * @param row row index to return.
171         * @return specified GridRow.
172         */
173        GridRow rows(int row) {
174            return rows.get(row);
175        }
176    }