001: /*
002: * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004,
003: * Refractions Research Inc. This library is free software; you can redistribute it and/or modify it
004: * under the terms of the GNU Lesser General Public License as published by the Free Software
005: * Foundation; version 2.1 of the License. This library is distributed in the hope that it will be
006: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
007: * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
008: */
009: package net.refractions.udig.catalog.internal.postgis.ui;
010:
011: import java.io.Serializable;
012: import java.lang.reflect.InvocationTargetException;
013: import java.sql.Connection;
014: import java.sql.DriverManager;
015: import java.sql.ResultSet;
016: import java.sql.SQLException;
017: import java.sql.Statement;
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.LinkedList;
023: import java.util.List;
024: import java.util.Map;
025:
026: import net.refractions.udig.catalog.internal.postgis.PostgisPlugin;
027: import net.refractions.udig.catalog.postgis.internal.Messages;
028: import net.refractions.udig.catalog.ui.CatalogUIPlugin;
029: import net.refractions.udig.catalog.ui.UDIGConnectionPage;
030: import net.refractions.udig.catalog.ui.wizard.DataBaseRegistryWizardPage;
031:
032: import org.eclipse.core.runtime.IProgressMonitor;
033: import org.eclipse.jface.dialogs.IDialogSettings;
034: import org.eclipse.jface.operation.IRunnableWithProgress;
035: import org.eclipse.jface.viewers.IStructuredSelection;
036: import org.eclipse.swt.SWT;
037: import org.eclipse.swt.custom.CCombo;
038: import org.eclipse.swt.events.ModifyEvent;
039: import org.eclipse.swt.events.SelectionEvent;
040: import org.eclipse.swt.events.SelectionListener;
041: import org.eclipse.swt.graphics.Point;
042: import org.eclipse.swt.layout.GridData;
043: import org.eclipse.swt.layout.GridLayout;
044: import org.eclipse.swt.widgets.Button;
045: import org.eclipse.swt.widgets.Composite;
046: import org.eclipse.swt.widgets.Event;
047: import org.eclipse.swt.widgets.Group;
048: import org.eclipse.ui.PlatformUI;
049: import org.geotools.data.DataStoreFactorySpi;
050: import org.geotools.data.postgis.PostgisConnectionFactory;
051: import org.geotools.data.postgis.PostgisDataStoreFactory;
052:
053: /**
054: * Enter Postgis connection parameters.
055: *
056: * @author dzwiers
057: * @since 0.3
058: */
059: public class PostGisWizardPage extends DataBaseRegistryWizardPage
060: implements UDIGConnectionPage {
061:
062: private static final String POSTGIS_WIZARD = "POSTGIS_WIZARD"; //$NON-NLS-1$
063: private static final String POSTGIS_RECENT = "POSTGIS_RECENT"; //$NON-NLS-1$
064: private IDialogSettings settings;
065: private static final int COMBO_HISTORY_LENGTH = 15;
066: private static final DataBaseConnInfo NULL = new DataBaseConnInfo(
067: "", "5432", "", "", "", "public"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
068:
069: public final String IMAGE_KEY = "PostGisWizardPageImage"; //$NON-NLS-1$
070: PostGisConnectionFactory pgcFactory = new PostGisConnectionFactory();
071:
072: /** <code>wkb</code> field */
073: protected Button wkb = null;
074: /** <code>looseBBox</code> field */
075: protected Button looseBBox = null;
076:
077: private String connectionDB = null;
078:
079: public PostGisWizardPage() {
080: super (Messages.PostGisWizardPage_title);
081: settings = PostgisPlugin.getDefault().getDialogSettings()
082: .getSection(POSTGIS_WIZARD);
083: if (settings == null) {
084: settings = PostgisPlugin.getDefault().getDialogSettings()
085: .addNewSection(POSTGIS_WIZARD);
086: }
087: }
088:
089: @Override
090: public void createControl(Composite arg0) {
091: super .createControl(arg0);
092: final List<DataBaseConnInfo> dbData = getSavedConnInfo();
093:
094: List<String> hosts = new ArrayList<String>();
095: for (DataBaseConnInfo info : dbData) {
096: hosts.add(info.getHost() + "/" + info.getDb()); //$NON-NLS-1$
097: }
098:
099: if (hosts.size() > 0) {
100: ((CCombo) host).setItems(hosts.toArray(new String[0]));
101: ((CCombo) host)
102: .addSelectionListener(new SelectionListener() {
103:
104: public void widgetDefaultSelected(
105: SelectionEvent e) {
106: widgetSelected(e);
107: }
108:
109: public void widgetSelected(SelectionEvent e) {
110: CCombo combo = (CCombo) host;
111: String item = combo.getText();
112: for (DataBaseConnInfo db : dbData) {
113: if (item.equals(db.getHost()
114: + "/" + db.getDb())) { //$NON-NLS-1$
115: setConnectionInfo(db, true);
116: break;
117: }
118: }
119: }
120:
121: });
122: }
123:
124: //For Drag 'n Drop as well as for general selections
125: // look for a url as part of the selction
126: Map<String, Serializable> params = defaultParams(); // based on selection
127: String selectedHost = (String) params
128: .get(PostgisDataStoreFactory.HOST.key);
129:
130: if (selectedHost != null) {
131: ((CCombo) host).setText(params.get(
132: PostgisDataStoreFactory.HOST.key).toString());
133: port.setText(params.get(PostgisDataStoreFactory.PORT.key)
134: .toString());
135: ((CCombo) database).setText(params.get(
136: PostgisDataStoreFactory.DATABASE.key).toString());
137: ((CCombo) schema).setText(params.get(
138: PostgisDataStoreFactory.SCHEMA.key).toString());
139: user.setText(params.get(PostgisDataStoreFactory.USER.key)
140: .toString());
141: pass.setText(params.get(PostgisDataStoreFactory.PASSWD.key)
142: .toString());
143: }
144: }
145:
146: private void setConnectionInfo(DataBaseConnInfo db,
147: boolean updateButtons) {
148: setFireEvents(false);
149: if (db != NULL)
150: ((CCombo) host).setText(db.getHost());
151: port.setText(db.getPort());
152: user.setText(db.getUser());
153: pass.setText(db.getPass());
154: ((CCombo) database).setText(db.getDb());
155: schema.setText(db.getSchema());
156: if (updateButtons)
157: getContainer().updateButtons();
158: setFireEvents(true);
159: }
160:
161: private List<DataBaseConnInfo> getSavedConnInfo() {
162: String[] recentPostGiss = settings.getArray(POSTGIS_RECENT);
163: final List<DataBaseConnInfo> dbData = new LinkedList<DataBaseConnInfo>();
164: if (recentPostGiss != null) {
165: for (String recent : recentPostGiss) {
166: DataBaseConnInfo dbs = new DataBaseConnInfo(recent);
167: if (!dbData.contains(dbs))
168: dbData.add(dbs);
169: }
170: }
171: return dbData;
172: }
173:
174: @Override
175: protected Group createAdvancedControl(Composite arg0) {
176: advanced = new Group(arg0, SWT.SHADOW_NONE);
177: advanced.setLayout(new GridLayout(1, false));
178:
179: wkb = new Button(advanced, SWT.CHECK);
180: wkb.setLayoutData(new GridData(SWT.LEFT, SWT.DEFAULT, false,
181: false));
182: wkb.setSelection(false);
183: wkb.addSelectionListener(this );
184: wkb.setText(Messages.PostGisWizardPage_button_wkb_text);
185: wkb
186: .setToolTipText(Messages.PostGisWizardPage_button_wkb_tooltip);
187: wkb.setSelection(true);
188:
189: looseBBox = new Button(advanced, SWT.CHECK);
190: looseBBox.setLayoutData(new GridData(SWT.LEFT, SWT.DEFAULT,
191: false, false));
192: looseBBox.setSelection(false);
193: looseBBox.addSelectionListener(this );
194: looseBBox
195: .setText(Messages.PostGisWizardPage_button_looseBBox_text);
196: looseBBox
197: .setToolTipText(Messages.PostGisWizardPage_button_looseBBox_tooltip);
198: looseBBox.setSelection(true);
199:
200: port.setText("5432"); //$NON-NLS-1$
201:
202: return advanced;
203: }
204:
205: @Override
206: protected DataStoreFactorySpi getDataStoreFactorySpi() {
207: return factory;
208: }
209:
210: Connection realConnection;
211:
212: public String getId() {
213: return "net.refractions.udig.catalog.ui.postgis"; //$NON-NLS-1$
214: }
215:
216: /** Can be called during createControl */
217: protected Map<String, Serializable> defaultParams() {
218: IStructuredSelection selection = (IStructuredSelection) PlatformUI
219: .getWorkbench().getActiveWorkbenchWindow()
220: .getSelectionService().getSelection();
221: return toParams(selection);
222: }
223:
224: /** Retrieve "best" PostGIS guess of parameters based on provided context */
225: protected Map<String, Serializable> toParams(
226: IStructuredSelection context) {
227: if (context == null)
228: return Collections.emptyMap();
229: for (Iterator itr = context.iterator(); itr.hasNext();) {
230: Map<String, Serializable> params = pgcFactory
231: .createConnectionParameters(itr.next());
232: if (params != null && !params.isEmpty())
233: return params;
234: }
235: return Collections.emptyMap();
236: }
237:
238: @Override
239: protected boolean excludeDB(String db) {
240: return "template0".equals(db) || "template1".equals(db); //$NON-NLS-1$ //$NON-NLS-2$
241: }
242:
243: @Override
244: protected boolean excludeSchema(String schema) {
245: return "information_schema".equals(schema) || //$NON-NLS-1$
246: "pg_catalog".equals(schema); //$NON-NLS-1$
247: }
248:
249: @Override
250: protected Connection getConnection() {
251: final String portText = port.getText();
252: final String hostText = ((CCombo) host).getText();
253: final String userText = user.getText();
254: final String passText = pass.getText();
255: final String olddb = ((CCombo) database).getText();
256:
257: if (dirty || realConnection == null) {
258: dirty = false;
259:
260: try {
261: getContainer().run(true, true,
262: new IRunnableWithProgress() {
263:
264: public void run(IProgressMonitor monitor)
265: throws InvocationTargetException,
266: InterruptedException {
267: monitor.beginTask(
268: Messages.PostGisWizardPage_0,
269: IProgressMonitor.UNKNOWN);
270: if (realConnection != null)
271: try {
272: realConnection.close();
273: } catch (SQLException e1) {
274: // it's dead anyhow
275: }
276:
277: String db = olddb == null
278: || "".equals(olddb) ? "template1" : olddb; //$NON-NLS-1$ //$NON-NLS-2$
279:
280: PostgisConnectionFactory conFac = new PostgisConnectionFactory(
281: hostText, portText, db);
282: conFac.setLogin(userText, passText);
283: DriverManager.setLoginTimeout(3);
284: try {
285: if (monitor.isCanceled())
286: return;
287: realConnection = conFac
288: .getConnection();
289: if (realConnection != null
290: && !monitor.isCanceled()) {
291: database.getDisplay()
292: .asyncExec(
293: new Runnable() {
294: public void run() {
295: database
296: .notifyListeners(
297: SWT.FocusIn,
298: new Event());
299: }
300: });
301: connectionDB = db;
302: }
303: } catch (SQLException e) {
304: throw (InvocationTargetException) new InvocationTargetException(
305: e, e.getLocalizedMessage());
306: }
307: if (monitor.isCanceled())
308: realConnection = null;
309: monitor.done();
310: }
311: });
312: } catch (InvocationTargetException e2) {
313: throw new RuntimeException(e2.getLocalizedMessage(), e2);
314: } catch (InterruptedException e2) {
315:
316: }
317: }
318:
319: host.setEnabled(true);
320: user.setEnabled(true);
321: pass.setEnabled(true);
322: database.setEnabled(true);
323: schema.setEnabled(true);
324:
325: return realConnection;
326: }
327:
328: private boolean dirty = true;
329: private boolean dbInitialized = false;
330:
331: @Override
332: protected void widgetSelectedInternal(SelectionEvent e) {
333: if (isFireEvents() && e.widget != null
334: && e.widget.equals(database)) {
335: dirty = true;
336: populateSchema();
337: }
338: }
339:
340: @Override
341: public void modifyText(ModifyEvent e) {
342: if (e.widget == null) {
343: return;
344: }
345: if (e.widget.equals(host)) {
346: hostModified(e);
347: }
348: if (e.widget.equals(database)) {
349: databaseModified(e);
350: }
351:
352: if (isFireEvents()) {
353: if (e.widget.equals(host) || e.widget.equals(port)
354: || e.widget.equals(user) || e.widget.equals(pass)) {
355: dirty = true;
356: setErrorMessage(null);
357: }
358: if (e.widget.equals(database)) {
359: getWizard().getContainer().updateButtons();
360: }
361: }
362: }
363:
364: private void hostModified(ModifyEvent e) {
365: dbInitialized = false;
366: if (isFireEvents() && e.widget != null) {
367: boolean match = false;
368: CCombo combo = (CCombo) host;
369: String text = combo.getText();
370: for (DataBaseConnInfo db : getSavedConnInfo()) {
371: if (db.getHost() != null
372: && db.getHost().toLowerCase().startsWith(
373: text.toLowerCase())) {
374: match = true;
375: setConnectionInfo(db, false); //fireEvents --> false
376: combo.setSelection(new Point(text.length(), combo
377: .getText().length()));
378: break;
379: }
380: }
381: if (!match) {
382: setConnectionInfo(NULL, false); //fireEvents --> false
383: }
384: }
385: }
386:
387: private void databaseModified(ModifyEvent e) {
388: if (isFireEvents() && e.widget != null) {
389: CCombo combo = (CCombo) database;
390: String text = combo.getText();
391: if (text != null && text.length() > 0) {
392: String[] items = combo.getItems();
393: for (String item : items) {
394: if (item != null
395: && item.toLowerCase().startsWith(
396: text.toLowerCase())) {
397: //match
398: setFireEvents(false);
399: combo.select(combo.indexOf(item));
400: combo.setSelection(new Point(text.length(),
401: combo.getText().length()));
402: setFireEvents(true);
403: break;
404: }
405: }
406: }
407: }
408: }
409:
410: @Override
411: protected boolean isDBCombo() {
412: return true;
413: }
414:
415: @Override
416: protected boolean isHostCombo() {
417: return true;
418: }
419:
420: @Override
421: protected boolean hasSchema() {
422: return true;
423: }
424:
425: protected String getHostText() {
426: return ((CCombo) host).getText();
427: }
428:
429: protected String getPortText() {
430: return port.getText();
431: }
432:
433: protected String getUserText() {
434: return user.getText();
435: }
436:
437: protected String getPassText() {
438: return pass.getText();
439: }
440:
441: protected String getDBText() {
442: return ((CCombo) database).getText();
443: }
444:
445: protected String getSchema() {
446: return schema != null ? schema.getText() : ""; //$NON-NLS-1$
447: }
448:
449: @Override
450: public boolean isPageComplete() {
451: boolean complete = isPageCompleteInternal()
452: && getConnection() != null;
453: // this is a little bit crazy still, as it is difficult to determine if we should save the
454: // widget values without repeatedly reconnecting
455: if (complete
456: && connectionDB != null
457: && (connectionDB.equals(getDBText()) || (excludeDB(connectionDB)))) // saves the values if we have a connection to the
458: // template but have changed the database
459: saveWidgetValues();
460: return complete;
461: }
462:
463: private boolean isPageCompleteInternal() {
464: return getHostText().trim().length() != 0
465: && getDBText().trim().length() != 0
466: && factory.canProcess(getParams());
467: }
468:
469: private static PostgisDataStoreFactory factory = new PostgisDataStoreFactory();
470:
471: /**
472: * Returns the parameters
473: */
474: @Override
475: public Map<String, Serializable> getParams() {
476: Map<String, Serializable> params = new HashMap<String, Serializable>();
477: params.put(PostgisDataStoreFactory.DBTYPE.key, "postgis"); //$NON-NLS-1$
478: params.put(PostgisDataStoreFactory.HOST.key, getHostText());
479: String dbport = getPortText();
480: try {
481: params.put(PostgisDataStoreFactory.PORT.key, new Integer(
482: dbport));
483: } catch (NumberFormatException e) {
484: params.put(PostgisDataStoreFactory.PORT.key, new Integer(
485: 5432));
486: }
487:
488: params.put(PostgisDataStoreFactory.SCHEMA.key, getSchema());
489:
490: String db = getDBText();
491: params.put(PostgisDataStoreFactory.DATABASE.key, db);
492:
493: String userName = getUserText();
494: params.put(PostgisDataStoreFactory.USER.key, userName);
495: String password = getPassText();
496: params.put(PostgisDataStoreFactory.PASSWD.key, password);
497:
498: if (wkb.getSelection())
499: params.put(PostgisDataStoreFactory.WKBENABLED.key,
500: Boolean.TRUE);
501: if (looseBBox.getSelection())
502: params.put(PostgisDataStoreFactory.LOOSEBBOX.key,
503: Boolean.TRUE);
504:
505: params.put(PostgisDataStoreFactory.NAMESPACE.key, ""); //$NON-NLS-1$
506:
507: return params;
508: }
509:
510: /**
511: * Saves the widget values
512: */
513: private void saveWidgetValues() {
514: // Update history
515: if (settings != null) {
516: List<DataBaseConnInfo> info = getSavedConnInfo();
517: DataBaseConnInfo dbs = new DataBaseConnInfo(((CCombo) host)
518: .getText(), port.getText(), user.getText(), pass
519: .getText(), ((CCombo) database).getText(), schema
520: .getText());
521: info.remove(dbs);
522: info.add(0, dbs);
523: List<String> recentPOSTGISs = new ArrayList<String>(info
524: .size());
525: for (DataBaseConnInfo info2 : info) {
526: recentPOSTGISs.add(info2.toString());
527: }
528: int size = Math.min(recentPOSTGISs.size(),
529: COMBO_HISTORY_LENGTH);
530: settings.put(POSTGIS_RECENT, recentPOSTGISs
531: .subList(0, size).toArray(new String[size]));
532: }
533: }
534:
535: /**
536: * Populates the database drop-down list. Implementation is identical to
537: * base class implementation in all regards except one: we can't use getCatalogs()
538: * because the JDBC driver for PostgreSQL only returns one catalog (see
539: * http://archives.postgresql.org/pgsql-jdbc/2005-11/msg00224.php for discussion on this change).
540: */
541: protected void populateDB() {
542: if (dbInitialized)
543: return;
544: dbInitialized = true;
545: // will need a connection ...
546: CCombo db = (CCombo) this .database;
547:
548: //save current state
549: String string = db.getText();
550:
551: db.removeAll();
552: db.setText(""); //$NON-NLS-1$
553:
554: Connection con = null;
555: try {
556: con = getConnection();
557: } catch (Exception e) {
558: CatalogUIPlugin.log(e.getLocalizedMessage(), e);
559: }
560:
561: if (con == null)
562: return;
563:
564: //connection ok, reset any previous error messages
565: setErrorMessage(null);
566:
567: ResultSet rs = null;
568: if (con != null)
569: try {
570: Statement statement = con.createStatement();
571: rs = statement
572: .executeQuery("SELECT datname from pg_database ORDER BY datname"); //$NON-NLS-1$
573: while (rs.next()) {
574: String dbName = rs.getString(1);
575: if (!excludeDB(dbName)) {
576: ((CCombo) database).add(dbName);
577: }
578: }
579: statement.close();
580: if (db.getItemCount() > 0)
581: db.select(0);
582: } catch (SQLException e) {
583: setErrorMessage(e.getLocalizedMessage());
584: db.removeAll();
585: db.setText(""); //$NON-NLS-1$
586: return;
587: }
588:
589: if (string != null && string.length() > 0) {
590: String[] items = db.getItems();
591: for (int i = 0; items != null && i < items.length; i++) {
592: if (string.equals(items[i])) {
593: db.select(i);
594: return;
595: }
596: }
597: }
598: }
599:
600: @Override
601: protected void populateSchema() {
602: if (connectionDB != null && !connectionDB.equals(getDBText())) {
603: dirty = true;
604: }
605: super .populateSchema();
606: }
607:
608: @Override
609: public void dispose() {
610: if (realConnection != null) {
611: try {
612: if (!realConnection.isClosed()) {
613: realConnection.close();
614: }
615: } catch (SQLException e) {
616: //couldn't close connection, no matter -- we are exiting
617: }
618: }
619: super.dispose();
620: }
621: }
|