Source code: com/dghda/kent/ReportData.java
1 /* Copyright (C) 2001 Duane Griffin <duanegriffin@users.sourceforge.net>
2 This file is part of Kent.
3
4 Kent is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 Kent is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public
15 License along with Kent; see the file COPYING. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18 */
19
20 package com.dghda.kent;
21
22 import java.util.*;
23
24 import org.w3c.dom.*;
25
26 import org.xml.sax.*;
27
28 /**
29 This class provides a convenient method of creating a report data document.
30 Report data documents are formatted based on their report template.
31 */
32 public class ReportData {
33
34 /** A class which contains encapsulates a data set. */
35 public static class DataSet {
36
37 /**
38 Creates a new data set with the given name.
39 @param name The new data set's name.
40 @param columns Names of the data columns.
41 @throws InvalidColumnException If there are no columns given.
42 */
43 public DataSet (String name, String [] columns) throws InvalidColumnException {
44
45 // Sanity check columns
46 if (columns == null || columns.length == 0)
47 throw new InvalidColumnException ("Data set must include at least one column");
48
49 m_Name = name;
50 m_Columns = columns;
51 for (int index = 0; index != m_Columns.length; ++index)
52 m_ColumnIndex.put (m_Columns[index], new Integer (index));
53 }
54
55 /**
56 Creates a new data set by parsing the given DOM element.
57 @throws InvalidColumnException If the column info is invalid or inconsistent.
58 */
59 public DataSet (Element elem) throws InvalidColumnException {
60
61 // Get & sanity check the name
62 m_Name = elem.getAttribute ("Name");
63 if (m_Name == null)
64 throw new DOMException (DOMException.SYNTAX_ERR, "No data set name specified");
65
66 // Parse columns info
67 NodeList columns = elem.getElementsByTagName ("Column");
68 if (columns.getLength() == 0)
69 throw new InvalidColumnException ("No columns specified in data set " + m_Name);
70
71 m_Columns = new String [columns.getLength()];
72 for (int index = 0; index != columns.getLength(); ++index) {
73 String column = XMLConstructor.getNodeText (columns.item (index));
74 m_Columns[index] = column;
75 m_ColumnIndex.put (column, new Integer (index));
76 }
77
78 // Parse rows
79 NodeList rows = elem.getElementsByTagName ("Row");
80 for (int index = 0; index != rows.getLength(); ++index)
81 parseRow ((Element) rows.item (index));
82 }
83
84 /** Get the data set's name. */
85 public String getName() {
86 return m_Name;
87 }
88
89 /** Get the column headings. */
90 public String [] getColumns() {
91 return m_Columns;
92 }
93
94 /** Returns the number of rows in the data set. */
95 public int getNumberRows() {
96 return m_Rows.size();
97 }
98
99 /** Returns an iterator over the rows in the data set. */
100 public Iterator getRowIterator() {
101 return m_Rows.iterator();
102 }
103
104 /**
105 Returns the value of the given column/row.
106 @param field The column to access.
107 @param row The row number to access.
108 @throws InvalidColumnException If the given field is not a column in the specified data set.
109 @throws ArrayIndexOutOfBounds If the specified row is invalid.
110 */
111 public String getDatum (String field, int row) {
112
113 // Find the column
114 Integer index = (Integer) m_ColumnIndex.get (field);
115 if (index == null)
116 throw new InvalidColumnException ("Field " + field + " doesn't exist");
117
118 // Get the data
119 String [] data = (String []) m_Rows.get (row);
120 return data[index.intValue()];
121 }
122
123 /**
124 Adds a row.
125 @param data Values for the row.
126 @throws InvalidColumnException If the data doesn't match the column info for the given data set.
127 */
128 public void addRow (String [] data) throws InvalidColumnException {
129
130 // Check the number of columns is correct
131 if (data.length != m_Columns.length)
132 throw new InvalidColumnException ("Row data must contain " + m_Columns.length + " columns");
133
134 // Add the data
135 m_Rows.add (data);
136 }
137
138 /** Parse a row. */
139 protected void parseRow (Element row) throws InvalidColumnException {
140
141 // Sanity check the data matches that specified
142 NamedNodeMap attrs = row.getAttributes();
143 if (attrs.getLength() != m_Columns.length)
144 throw new InvalidColumnException ("Row data must contain " + m_Columns.length + " columns");
145
146 // Get & add the data
147 String [] data = new String[m_Columns.length];
148 for (int index = 0; index != m_Columns.length; ++index)
149 data[index] = attrs.getNamedItem (m_Columns[index]).getNodeValue();
150 m_Rows.add (data);
151 }
152
153 private String m_Name;
154 private String [] m_Columns;
155 private HashMap m_ColumnIndex = new HashMap();
156 private ArrayList m_Rows = new ArrayList();
157 }
158
159 /** Constructs a new report data object. */
160 public ReportData() {
161 }
162
163 /**
164 Constructs a new report data object by parsing the given document.
165 @param data The report data document to parse
166 @throws SAXException If the document is not valid report data
167 */
168 protected ReportData (Document data) throws SAXException {
169
170 // Check the document is actually a report template
171 DocumentType type = data.getDoctype();
172 if (type != null && (!type.getName().equals ("KentData")))
173 throw new SAXException ("Document must be of type KentData");
174
175 // Parse the data set elements
176 NodeList dataSets = data.getDocumentElement().getElementsByTagName ("DataSet");
177 for (int index = 0; index != dataSets.getLength(); ++index) {
178 DataSet dataSet = new DataSet ((Element) dataSets.item (index));
179 m_DataSets.put (dataSet.getName(), dataSet);
180 }
181 }
182
183 /**
184 Constructs a new report data object by parsing the given XML document.
185 @param data The report data document to parse
186 @throws javax.xml.parsers.ParserConfigurationException If there was a configuration error creating the XML parser
187 @throws SAXException If the document is not valid report data
188 */
189 public static ReportData constructReportData (String data) throws javax.xml.parsers.ParserConfigurationException, SAXException {
190 Document doc = XMLConstructor.parseDocument (data);
191 return new ReportData (doc);
192 }
193
194 /**
195 Adds a new data set to the report.
196 @param name The new data set's name.
197 @param columns Names of the data columns.
198 @throws InvalidColumnException If there are no columns given.
199 @throws InvalidDataSetException If the specified data set already exists.
200 */
201 public synchronized void addDataSet (String name, String [] columns) throws InvalidColumnException, InvalidDataSetException {
202
203 // Check the data set specified doesn't already exist
204 if (m_DataSets.get (name) != null)
205 throw new InvalidDataSetException ("Data set " + name + " already exists");
206
207 // Add the data set
208 m_DataSets.put (name, new DataSet (name, columns));
209 }
210
211 /**
212 Adds a new, already constructed, data set to the report.
213 @param data The new data set.
214 @throws InvalidDataSetException If the specified data set already exists.
215 */
216 public synchronized void addDataSet (DataSet data) throws InvalidDataSetException {
217
218 // Check the data set specified doesn't already exist
219 if (m_DataSets.get (data.getName()) != null)
220 throw new InvalidDataSetException ("Data set " + data.getName() + " already exists");
221
222 // Add the data set
223 m_DataSets.put (data.getName(), data);
224 }
225
226 /**
227 Adds a row to the specified data set.
228 @param name The data set to add the row to
229 @param data Values for the row.
230 @throws InvalidColumnException If the data doesn't match the column info for the given data set.
231 @throws InvalidDataSetException If the specified data set doesn't exist.
232 */
233 public synchronized void addRow (String name, String [] data) throws InvalidColumnException, InvalidDataSetException {
234
235 // Get the data set
236 DataSet dataSet = (DataSet) m_DataSets.get (name);
237 if (dataSet == null)
238 throw new InvalidDataSetException ("Data set " + name + " doesn't exist");
239
240 dataSet.addRow (data);
241 }
242
243 /** Returns the given data set. */
244 public synchronized DataSet getDataSet (String name) {
245 return (DataSet) m_DataSets.get (name);
246 }
247
248 /**
249 An iterator over the data sets.
250 The iterator returns a data set object.
251 */
252 public synchronized Iterator getDataSetIterator() {
253 return m_DataSets.values().iterator();
254 }
255
256 /** Gets the number of rows in a given data set. */
257 public synchronized int getNumberRows (String dataSet) throws InvalidDataSetException {
258
259 // Get the data set
260 DataSet data = (DataSet) m_DataSets.get (dataSet);
261 if (data == null)
262 throw new InvalidDataSetException ("Data set " + dataSet + " doesn't exist");
263
264 return data.getNumberRows();
265 }
266
267 /**
268 Gets the value of a specific column.
269 @param dataSet The name of the data set to get the data from.
270 @param field The column to access.
271 @param which The row number to access.
272 @returns null if the specified column doesn't exist.
273 @throws InvalidColumnException If the given field is not a column in the specified data set.
274 @throws InvalidDataSetException If the specified data set doesn't exist.
275 @throws ArrayIndexOutOfBounds If the specified row is invalid.
276 */
277 public synchronized String getColumn (String dataSet, String field, int which) throws InvalidColumnException, InvalidDataSetException {
278
279 // Get the data set
280 DataSet data = (DataSet) m_DataSets.get (dataSet);
281 if (data == null)
282 throw new InvalidDataSetException ("Data set " + dataSet + " doesn't exist");
283
284 return data.getDatum (field, which);
285 }
286
287 /** Returns a document containing the report data constructed. */
288 public synchronized String getData() {
289 StringBuffer result = new StringBuffer();
290
291 // Pre-amble
292 result.append ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
293 result.append ("<KentData>\n");
294
295 // Create each data set
296 Iterator dataSets = m_DataSets.values().iterator();
297 while (dataSets.hasNext()) {
298 DataSet dataSet = (DataSet) dataSets.next();
299
300 // Create & add data set element
301 result.append (" <DataSet");
302 XMLConstructor.addAttribute (result, "Name", dataSet.getName());
303 result.append (">\n");
304
305 // Add the column info
306 String [] columns = dataSet.getColumns();
307 for (int index = 0; index != columns.length; ++index) {
308
309 // Create & add new column element
310 result.append (" <Column>");
311 result.append (columns[index]);
312 result.append ("</Column>\n");
313 }
314
315 // Add each row
316 Iterator values = dataSet.getRowIterator();
317 while (values.hasNext()) {
318 String [] rowData = (String []) values.next();
319
320 // Create new row element
321 result.append (" <Row");
322
323 // Add column data
324 for (int index = 0; index != columns.length; ++index)
325 XMLConstructor.addAttribute (result, columns[index], rowData[index]);
326
327 // Add row to data set
328 result.append ("/>\n");
329 }
330
331 result.append (" </DataSet>\n");
332 }
333 result.append ("</KentData>\n");
334
335 return result.toString();
336 }
337
338 private TreeMap m_DataSets = new TreeMap();
339 }