001: package net.refractions.udig.project.ui.controls;
002:
003: import java.io.IOException;
004: import java.util.ArrayList;
005: import java.util.List;
006:
007: import net.refractions.udig.project.ui.internal.ProjectUIPlugin;
008:
009: import org.eclipse.jface.viewers.IStructuredContentProvider;
010: import org.eclipse.jface.viewers.ITableLabelProvider;
011: import org.eclipse.jface.viewers.LabelProvider;
012: import org.eclipse.jface.viewers.TableViewer;
013: import org.eclipse.jface.viewers.Viewer;
014: import org.eclipse.swt.SWT;
015: import org.eclipse.swt.graphics.Image;
016: import org.eclipse.swt.widgets.Composite;
017: import org.eclipse.swt.widgets.Control;
018: import org.eclipse.swt.widgets.Table;
019: import org.eclipse.swt.widgets.TableColumn;
020: import org.eclipse.ui.part.PageBook;
021: import org.geotools.data.DataUtilities;
022: import org.geotools.data.FeatureReader;
023: import org.geotools.data.FeatureResults;
024: import org.geotools.feature.AttributeType;
025: import org.geotools.feature.DefaultFeatureCollection;
026: import org.geotools.feature.Feature;
027: import org.geotools.feature.FeatureType;
028: import org.geotools.feature.GeometryAttributeType;
029: import org.geotools.feature.SchemaException;
030:
031: /**
032: * A TreeViewer control for viewing a table of Feature attributes.
033: * <p>
034: * This object can be created in two ways. The first is using a FeatureReader. This method reads in
035: * Features a page at a time and populates the table entries. The page size can be set by calling
036: * FeatureTableControl#setPageSize(int).
037: * </p>
038: * <p>
039: * The second way is using a DefaultFeatureCollection. In this case the control hangs on to a
040: * reference to the DefaultFeatureCollection and populates the table entries directory from it. This
041: * method results in a single page containing all features.
042: * </p>
043: * TODO: Create next and first action
044: *
045: * @author jdeolive
046: * @since 0.3
047: */
048: public class FeatureTableControl {
049:
050: /** results per page * */
051: private int pageSize = 10; // XXX: actual put this as a user pref
052:
053: /** feature types and reader to get the actual feature data * */
054: private FeatureReader fReader;
055: FeatureType fType;
056:
057: /** feature collection for holding features in memory * */
058: private List<Feature> features;
059:
060: /** table viewer control * */
061: private TableViewer tableViewer;
062: private Table table;
063:
064: private FeatureResults results;
065:
066: private PageBook control;
067:
068: /**
069: * Construct <code>FeatureTableControl</code>.
070: * <p>
071: * Must call setFeatures before use.
072: * </p>
073: */
074: public FeatureTableControl() {
075: clear();
076: }
077:
078: /**
079: * Construct <code>FeatureTableControl</code>.
080: *
081: * @param features The DefaultFeatureCollection containing the features to be displayed.
082: */
083: public FeatureTableControl(DefaultFeatureCollection features) {
084: setFeatures(features);
085: }
086:
087: /**
088: * Construct a <code>FeatureTableControl</code>.
089: *
090: * @param fReader The FeatureReader that returns the actual features.
091: */
092: public FeatureTableControl(FeatureReader fReader) {
093: this (fReader, 10);
094: }
095:
096: /**
097: * Construct a <code>FeatureTableControl</code>.
098: *
099: * @param fReader The FeatureReader that returns the actual features.
100: * @param resPerPage Results per page to be shown in the table.
101: */
102: public FeatureTableControl(FeatureReader fReader, int resPerPage) {
103: this .fType = fReader.getFeatureType();
104: this .fReader = fReader;
105: this .pageSize = resPerPage;
106:
107: features = new ArrayList<Feature>();
108:
109: // /features = (DefaultFeatureCollection) DefaultFeatureCollections.newCollection();
110:
111: // initial page
112: next();
113: }
114:
115: /**
116: * Sets the number of features viewed in the table per page.
117: *
118: * @param resPerPage positive integer.
119: */
120: public void setPageSize(int resPerPage) {
121: this .pageSize = resPerPage;
122: }
123:
124: /**
125: * Returns the number of features viewed in the table per page.
126: *
127: * @return positive integer.
128: */
129: public int getPageSize() {
130: return pageSize;
131: }
132:
133: /**
134: * Returns the control representing the table control.
135: *
136: * @return The internal table viewer control.
137: * @see Control
138: */
139: public Control getControl() {
140: return control;
141: }
142:
143: /**
144: * Creates the table control.
145: *
146: * @param parent The to be parent of the control.
147: * @see Composite
148: */
149: public void createTableControl(Composite parent) {
150: control = new PageBook(parent, SWT.NONE);
151: createTableViewer(control);
152: }
153:
154: /**
155: * Moves the control to the next page of features. This method should only be called if the
156: * control was created from a <code>org.geotools.data.FeatureReader</code>.
157: */
158: public void next() {
159: // if the control was created directory from a feature collection
160: // fReader will be null
161: // TODO: Throw an exception here instead ??
162: if (fReader != null) {
163: nextInternal();
164: }
165: }
166:
167: /**
168: * Moves the control to the next page of features.
169: */
170: protected void nextInternal() {
171:
172: // clear the current features
173: features.clear();
174:
175: try {
176: // read in the new features
177: for (int i = 0; i < pageSize && fReader.hasNext(); i++) {
178: features.add(fReader.next());
179: }
180: } catch (Exception e) {
181: e.printStackTrace();
182: }
183:
184: }
185:
186: /**
187: * Updates the table control with the current set of features.
188: * <p>
189: * This method will ensure that the column information gets updated
190: * </p>
191: */
192: public void update() {
193: // System.out.println("Page size:" + pageSize);
194: updateTable();
195: tableViewer.setInput(features);
196: }
197:
198: /**
199: * Updates the table by adding/removing columns. TableViewer doesn't support updating columns
200: * based on the data set supplied so this is a way around it that works nicely.
201: */
202: protected void updateTable() {
203: table = tableViewer.getTable();
204: TableColumn tableColumn;
205:
206: // aa: For some reason this column is hard coded in here??!!
207: // if it's removed, all the loops should be rechecked to remove the +/- 1
208: if (table.getColumnCount() > 0) {
209: tableColumn = table.getColumn(0);
210: } else {
211: tableColumn = new TableColumn(table, SWT.CENTER
212: | SWT.BORDER);
213: }
214: tableColumn.setText("FID"); //$NON-NLS-1$
215: tableColumn.setWidth(100);
216:
217: if (fType != null) {
218: for (int i = 0; i < fType.getAttributeCount(); i++) {
219: AttributeType aType = fType.getAttributeType(i);
220: if (table.getColumnCount() > i + 1) {
221: tableColumn = table.getColumn(i + 1);
222: } else {
223: tableColumn = new TableColumn(table, SWT.CENTER
224: | SWT.BORDER);
225: }
226:
227: if (aType instanceof GeometryAttributeType) {
228: // jg: wot is this maddness? jd: paul said so
229: tableColumn.setText("GEOMETRY"); //$NON-NLS-1$
230: } else {
231: tableColumn.setText(aType.getName());
232: }
233: tableColumn.setWidth(100);
234: }
235: for (int i = fType.getAttributeCount() + 1; i < table
236: .getColumnCount(); i++) {
237: tableColumn = table.getColumn(i);
238: tableColumn.dispose();
239: }
240: }
241: }
242:
243: /**
244: * Creates the table control itself.
245: *
246: * @param parent Makes A TableViewer which diplays the features.
247: * @see Composite
248: */
249: protected void createTableViewer(Composite parent) {
250: int style = SWT.MULTI; // | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL;
251: table = new Table(parent, style);
252: // TableColumn tableColumn = new TableColumn(table, SWT.CENTER | SWT.BORDER);
253: // tableColumn.setText("FID"); //$NON-NLS-1$
254: // tableColumn.setWidth(100);
255: //
256: // for( int i = 0; i < fType.getAttributeCount(); i++ ) {
257: // AttributeType aType = fType.getAttributeType(i);
258: // tableColumn = new TableColumn(table, SWT.CENTER | SWT.BORDER);
259: // if (aType.isGeometry()) {
260: // // jg: wot is this maddness? jd: paul said so
261: // tableColumn.setText("GEOMETRY"); //$NON-NLS-1$
262: // } else {
263: // tableColumn.setText(aType.getName());
264: // }
265: // tableColumn.setWidth(100);
266: // }
267: table.setHeaderVisible(true);
268:
269: FeatureTableProvider ftp = new FeatureTableProvider();
270: tableViewer = new TableViewer(table);
271: tableViewer.setContentProvider(ftp);
272: tableViewer.setLabelProvider(ftp);
273: tableViewer.setInput(features);
274: control.showPage(tableViewer.getControl());
275: updateTable();
276:
277: // tableViewer.refresh();
278: // tableViewer.getControl().redraw();
279: // tableViewer.getControl().update();
280: }
281:
282: /**
283: * The TableViewer used is returned... would be used for adding listeners and various other
284: * things to the tableViewer
285: *
286: * @return TableViewer
287: */
288: public TableViewer getTableViewer() {
289: return tableViewer;
290: }
291:
292: /**
293: * API use?
294: *
295: * @author jdeolive
296: * @see LabelProvider
297: * @see ITableLabelProvider
298: * @see IStructuredContentProvider
299: */
300: class FeatureTableProvider extends LabelProvider implements
301: ITableLabelProvider, IStructuredContentProvider {
302:
303: /**
304: * Returns the elemetns for the viewer. Input to this method should be at type of
305: * <code>org.geotools.feature.DefaultFeatureCollection</code>
306: *
307: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
308: * @param inputElement A <code>org.geotools.feature.DefaultFeatureCollection.</code>
309: * @return An array of <code>org.geotools.feature.Feature</code>
310: * @throws ClassCastException when inputElement not DefaultFeatureCollection
311: */
312: public Object[] getElements(Object inputElement)
313: throws ClassCastException {
314: @SuppressWarnings("unchecked")
315: List<Feature> features = (List<Feature>) inputElement;
316: return features.toArray();
317: }
318:
319: /**
320: * Does nothing.
321: *
322: * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
323: * java.lang.Object, java.lang.Object)
324: * @param viewer
325: * @param oldInput
326: * @param newInput
327: */
328: public void inputChanged(Viewer viewer, Object oldInput,
329: Object newInput) {
330: // do nothing.
331: ((TableViewer) viewer).getTable().layout();
332: }
333:
334: /**
335: * Does nothing.
336: *
337: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
338: * @param element
339: * @param columnIndex
340: * @return null
341: */
342: public Image getColumnImage(Object element, int columnIndex) {
343: return null;
344: }
345:
346: /**
347: * Returns the value of the column / feature attribute.
348: *
349: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
350: * @param element the array of feature attributes.
351: * @param columnIndex the column index / feature attribute.
352: * @return the string representation of the feature attribute, except for attributes of type
353: * Geometry in which a string representing the type of Geometry is returned.
354: * @throws ClassCastException when element not Feature
355: */
356: public String getColumnText(Object element, int columnIndex)
357: throws ClassCastException {
358: Feature f = (Feature) element;
359: if (f == null) {
360: ProjectUIPlugin
361: .trace(
362: getClass(),
363: "Feature was null in FeatureTableControl", (Exception) null); //$NON-NLS-1$
364: return null;
365: }
366: if (columnIndex == 0)
367: return f.getID();
368:
369: if (columnIndex >= fType.getAttributeCount()) {
370: return null;
371: }
372: AttributeType at = fType.getAttributeType(columnIndex - 1);
373: if (at == null)
374: return null;
375: if (at instanceof GeometryAttributeType) {
376: String s = f.getAttribute(columnIndex - 1).getClass()
377: .getName();
378: return s.substring(s.lastIndexOf('.') + 1);
379: }
380: if (f.getAttribute(columnIndex - 1) == null)
381: return null;
382: return f.getAttribute(columnIndex - 1).toString();
383:
384: }
385: }
386:
387: /**
388: * Does nothing.
389: *
390: * @see org.eclipse.ui.IWorkbenchPart#setFocus()
391: */
392: public void setFocus() {
393: // do nothing.
394: }
395:
396: /**
397: * Contents of the current page of features
398: *
399: * @return DefaultFeatureCollection
400: * @see DefaultFeatureCollection
401: */
402: public List<Feature> getFeatures() {
403: return features;
404: }
405:
406: /**
407: * Set up for a one time only paged access
408: *
409: * @param reader
410: * @see Featurereader API should this throw exceptions? the answer also potentially propagates
411: * the issue and the question ... API is there a memory issue here?
412: */
413: public void setFeatures(FeatureReader reader) {
414: this .fReader = reader;
415: this .fType = reader.getFeatureType();
416: features = new ArrayList<Feature>();
417: next(); // load the first page into the collection
418: update();
419: }
420:
421: /**
422: * Set up for a single page of content
423: *
424: * @param features
425: * @see DefaultFeatureCollection
426: */
427: public void setFeatures(List<Feature> features) {
428: this .features = features;
429: if (features.size() != 0) {
430: this .fType = features.get(0).getFeatureType();
431: } else {
432: this .fType = null;
433: }
434: // this.pageSize = features.size();
435: this .fReader = null;
436: update();
437: }
438:
439: /**
440: * Set up for pages access with a next and a reset button. API should this throw exceptions? the
441: * answer also potentially propagates the issue and the question ... API is there a memory issue
442: * here?
443: *
444: * @param results
445: * @see FeatureResults
446: */
447: public void setFeatures(FeatureResults results) {
448: this .results = results;
449: reset();
450: update();
451: }
452:
453: /** Reset to start of FeatureResutls */
454: public void reset() {
455: int count = pageSize + 1;
456: try {
457: count = results.getCount();
458: // System.out.println("results count " + count );
459: } catch (IOException e) {
460: // System.out.println("results count " + e );
461: e.printStackTrace();
462: }
463:
464: if (count < this .pageSize) {
465: ArrayList<Feature> list = new ArrayList<Feature>(count);
466: try {
467: list.addAll(results.collection());
468: setFeatures(list);
469: } catch (IOException e) {
470: e.printStackTrace();
471: clear();
472: }
473: } else {
474: try {
475: setFeatures(results.reader());
476: } catch (IOException e1) {
477: e1.printStackTrace();
478: clear();
479: }
480: }
481: }
482:
483: /**
484: * Don't display nothing :-)
485: */
486: public void clear() {
487: features = new ArrayList<Feature>();
488: try {
489: fType = DataUtilities.createType("empty", "message:String"); //$NON-NLS-1$ //$NON-NLS-2$
490: } catch (SchemaException e) {
491: // TODO Catch e
492: }
493: fReader = null;
494: this.results = null;
495: }
496: }
|