001: package jimm.datavision.source;
002:
003: import jimm.datavision.*;
004: import jimm.util.XMLWriter;
005: import jimm.util.I18N;
006: import java.io.IOException;
007: import java.io.FileNotFoundException;
008: import java.util.Iterator;
009: import java.util.ArrayList;
010: import org.xml.sax.InputSource;
011:
012: /**
013: * An abstract data source.
014: *
015: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
016: * @see Column
017: * @see Table
018: * @see Query
019: */
020: public abstract class DataSource implements Writeable {
021:
022: protected Report report;
023: protected Query query;
024: protected String metadataURL;
025:
026: public DataSource(Report r, Query q) {
027: report = r;
028: query = q;
029: }
030:
031: /**
032: * Used to enable/disable the "Table Linker" menu item.
033: *
034: * @return <code>true</code> if the "Table Linker" menu item
035: * should be enabled.
036: */
037: public abstract boolean canJoinTables();
038:
039: /**
040: * Used to enable/disable the "SQL Query Text" menu item.
041: *
042: * @return <code>true</code> if the "SQL Query Text" menu item
043: * should be enabled.
044: */
045: public abstract boolean isSQLGenerated();
046:
047: /**
048: * Used to enable/disable the "Connection" menu item.
049: *
050: * @return <code>true</code> if the "Connection" menu item
051: * should be enabled.
052: */
053: public abstract boolean isConnectionEditable();
054:
055: /**
056: * Used to enable/disable the "Select Records" menu item.
057: *
058: * @return <code>true</code> if the "Select Records" menu item
059: * should be enabled.
060: */
061: public abstract boolean areRecordsSelectable();
062:
063: /**
064: * Used to enable/disable the "Sort By" menu item.
065: *
066: * @return <code>true</code> if the "Sort By" menu item
067: * should be enabled.
068: */
069: public abstract boolean areRecordsSortable();
070:
071: /**
072: * Used to enable/disable the "Group By" menu item.
073: *
074: * @return <code>true</code> if the "Group By" menu item
075: * should be enabled.
076: */
077: public abstract boolean canGroupRecords();
078:
079: /**
080: * Used to enable/disable the "Run" and "Export" menu
081: * items. Most data sources will enable these, of course.
082: *
083: * @return <code>true</code> if the "Run" and "Export"
084: * menu items should be enabled.
085: */
086: public boolean canRunReports() {
087: return true;
088: }
089:
090: /**
091: * Returns <code>true</code> if this data source uses a file to retrieve
092: * data. The default implementation returns false.
093: *
094: * @return <code>true</code> if this data source uses a file to retrieve
095: * data
096: */
097: public boolean usesSourceFile() {
098: return false;
099: }
100:
101: /**
102: * Returns <code>true</code> if this data source uses a file to retrieve
103: * data and does not yet have one. The default implementation returns false.
104: *
105: * @return <code>true</code> if this data source uses a file to retrieve
106: * data and doesn't yet have one
107: */
108: public boolean needsSourceFile() {
109: return false;
110: }
111:
112: /**
113: * Returns <code>true</code> if this data source uses a file to retrieve
114: * data and and has already done so. The default implementation returns false.
115: *
116: * @return <code>true</code> if this data source uses a file to retrieve
117: * data
118: */
119: public boolean alreadyUsedSourceFile() {
120: return false;
121: }
122:
123: /**
124: * Accepts the path to a data source file. The default implementation does
125: * nothing.
126: *
127: * @param filePath the full path to a file
128: */
129: public void setSourceFile(String filePath)
130: throws FileNotFoundException {
131: }
132:
133: /**
134: * Tells this data source to re-use (perhaps re-open) the current data
135: * source file. The default implementation does nothing.
136: */
137: public void reuseSourceFile() throws FileNotFoundException {
138: }
139:
140: public Report getReport() {
141: return report;
142: }
143:
144: public Query getQuery() {
145: return query;
146: }
147:
148: public abstract DataCursor execute() throws Exception;
149:
150: /**
151: * Called from <code>ReportReader.column</code> to add a column to a
152: * data source.
153: * <p>
154: * The default implementation does nothing.
155: *
156: * @param col a column
157: */
158: public void addColumn(Column col) {
159: }
160:
161: /**
162: * Called from <code>Report.reloadColumns/code>, this method gives the
163: * data source a chance to tell its ancillary objects (such as the query)
164: * to reload column objects.
165: * <p>
166: * This is necessary, for example, after a SQL database data source has
167: * reloaded all of its table and column information. The old column
168: * objects no longer exist. New ones (with the same ids, we assume) have
169: * taken their place.
170: */
171: public void reloadColumns() {
172: if (query != null)
173: query.reloadColumns(this );
174: }
175:
176: /**
177: * Reads metadata from a URL. We save the URL string so we can write it
178: * back out when we write to XML.
179: *
180: * @param urlString where to get the metadata
181: * @see MetadataReader
182: */
183: public void readMetadataFrom(String urlString) throws Exception {
184: try {
185: metadataURL = urlString;
186: new MetadataReader(this ).read(new InputSource(urlString));
187: } catch (IOException ioe) {
188: ErrorHandler.error(I18N.get("DataSource.metadata_err"),
189: ioe, I18N.get("DataSource.metadata_err_title"));
190: }
191: }
192:
193: /**
194: * Given an id, returns the column that has that id. If no column with the
195: * specified id exists, returns <code>null</code>.
196: *
197: * @return a column, or <code>null</code> if no column with the specified
198: * id exists
199: */
200: public abstract Column findColumn(Object id);
201:
202: /**
203: * Returns the index of the specified selectable.
204: *
205: * @param sel a selectable
206: */
207: public int indexOfSelectable(Selectable sel) {
208: return query.indexOfSelectable(sel);
209: }
210:
211: /**
212: * Returns an iterator over all tables, or <code>null</code> if the
213: * data source does not have tables (for example, a character-separated
214: * file data source).
215: *
216: * @return a possibly <code>null</code> iterator over all tables
217: */
218: public abstract Iterator tables();
219:
220: /**
221: * Returns an iterator over all tables actually used in the report, or
222: * <code>null</code> if the data source does not have tables (for example,
223: * a character-separated file data source).
224: *
225: * @return a possibly <code>null</code> iterator over all tables used
226: * in the report
227: */
228: public abstract Iterator tablesUsedInReport();
229:
230: /**
231: * Returns an iterator over all columns.
232: *
233: * @return an iterator over all columns
234: */
235: public abstract Iterator columns();
236:
237: /**
238: * Returns an iterator over all the columns in only the tables used by the
239: * report, or over all columns if this data source does not have tables.
240: *
241: * @return an iterator over all columns in only tables used by the
242: * report, or over all columns if this data source does not have tables
243: */
244: public Iterator columnsInTablesUsedInReport() {
245: Iterator iter = tablesUsedInReport();
246: if (iter == null)
247: return columns();
248:
249: // Create a new collection and return an iterator over it.
250: ArrayList list = new ArrayList();
251: while (iter.hasNext()) {
252: Table t = (Table) iter.next();
253: list.addAll(t.columns.values());
254: }
255: return list.iterator();
256: }
257:
258: public void removeSort(Selectable s) {
259: query.removeSort(s);
260: }
261:
262: /**
263: * Returns <code>true</code> if the specified parameter exists within this
264: * data source's query.
265: *
266: * @param p a parameter
267: * @return <code>true</code> if the specified parameter exists within
268: * the query
269: * @see Query#containsReferenceTo
270: */
271: public boolean containsReferenceTo(Parameter p) {
272: return query.containsReferenceTo(p);
273: }
274:
275: /**
276: * Writes this data source and its query as an XML tag.
277: *
278: * @param out a writer that knows how to write XML
279: */
280: public void writeXML(XMLWriter out) {
281: out.startElement("source");
282: doWriteXML(out);
283: query.writeXML(out);
284: out.endElement();
285: }
286:
287: protected abstract void doWriteXML(XMLWriter out);
288:
289: }
|