001: /* ====================================================================
002: * Copyright (c) 1998 - 2003 David F. Glasser. All rights
003: * reserved.
004: *
005: * This file is part of the QueryForm Database Tool.
006: *
007: * The QueryForm Database Tool is free software; you can redistribute it
008: * and/or modify it under the terms of the GNU General Public License as
009: * published by the Free Software Foundation; either version 2 of the
010: * License, or (at your option) any later version.
011: *
012: * The QueryForm Database Tool is distributed in the hope that it will
013: * be useful, but WITHOUT ANY WARRANTY; without even the implied
014: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
015: * See the GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with the QueryForm Database Tool; if not, write to:
019: *
020: * The Free Software Foundation, Inc.,
021: * 59 Temple Place, Suite 330
022: * Boston, MA 02111-1307 USA
023: *
024: * or visit http://www.gnu.org.
025: *
026: * ====================================================================
027: *
028: * This product includes software developed by the
029: * Apache Software Foundation (http://www.apache.org/).
030: *
031: * ====================================================================
032: */
033: package org.glasser.qform;
034:
035: import java.sql.*;
036: import java.awt.*;
037: import javax.swing.*;
038: import javax.swing.border.*;
039: import java.util.*;
040: import org.glasser.sql.*;
041: import org.glasser.util.*;
042: import org.glasser.swing.PopupMenuManager;
043: import org.glasser.swing.GUIHelper;
044:
045: /**
046: * An object of this class provides the form through which query parameters are input
047: * and query results are displayed (one record at a time) in the QueryForm app.
048: */
049: public class BaseForm extends JPanel {
050:
051: protected int textRowHeight = 28;
052:
053: protected int margin = 2;
054:
055: protected TextBox[] fields = null;
056:
057: protected ResultSetBuffer resultSetBuffer = null;
058:
059: protected boolean hasPK = false;
060:
061: protected boolean endReached = false;
062:
063: int minWidth = 0;
064:
065: public ResultSetBuffer getResultSetBuffer() {
066: return resultSetBuffer;
067: }
068:
069: public boolean setResultSetBuffer(ResultSetBuffer resultSetBuffer)
070: throws SQLException {
071:
072: this .resultSetBuffer = resultSetBuffer;
073:
074: endReached = false;
075: // this.setFieldsEditable(false);
076: // clearFields();
077:
078: if (resultSetBuffer.isEndOfResultsReached()
079: && resultSetBuffer.size() == 0) {
080: endReached = true;
081: return false;
082: }
083:
084: this .setFieldsEditable(false);
085: clearFields();
086: this .current();
087: return true;
088:
089: }
090:
091: public void removeResultSetBuffer() {
092: this .resultSetBuffer = null;
093: }
094:
095: public BaseForm(Vector fieldList) {
096:
097: fields = (TextBox[]) fieldList.toArray(new TextBox[fieldList
098: .size()]);
099:
100: int vgap = 5;
101:
102: GridBagLayout gbLayout = new GridBagLayout();
103: this .setLayout(gbLayout);
104: GridBagConstraints gc = new GridBagConstraints();
105:
106: Insets labelInsets = new Insets(0, 0, 0, 0);
107: Insets fieldInsets = new Insets(0, 5, vgap, 0);
108: gc.anchor = gc.NORTH;
109: gc.fill = gc.HORIZONTAL;
110:
111: this .setBorder(new EmptyBorder(10, 10, 10, 10));
112: for (int row = 0; row < fields.length; row++) {
113:
114: TextBox textBox = fields[row];
115: JLabel label = textBox.getLabel();
116:
117: if (fields[row].isPkComponent()) {
118: hasPK = true;
119: }
120:
121: // add the label
122: gc.gridx = 0;
123: gc.gridy = row;
124: gc.weightx = 0;
125: gc.ipadx = 0;
126: gc.ipady = 0;
127: gc.insets = labelInsets;
128:
129: this .add(label, gc);
130: label.setForeground(java.awt.Color.black);
131: label.setHorizontalAlignment(JLabel.RIGHT);
132:
133: // now add the textfield
134: gc.gridx = 1;
135: gc.weightx = 1;
136: gc.ipadx = 300;
137: gc.insets = fieldInsets;
138:
139: // we need to see how many lines high the JTextArea needs to be to display
140: // the field's contents, assuming that it is 40 characters wide. This only
141: // applys to fields with displayable (non-binary) data types.
142: int numlines = 1;
143: if (textBox.getLength() > 40 && textBox.isTypeDisplayable()) {
144: numlines = (textBox.getLength() / 40) + 1;
145: }
146:
147: // if the number of lines required to display this field is more than
148: // 7, we'll only make it 7 high, but put it in a scrollpane so all of
149: // the field's data can be viewed.
150: if (numlines > 7) {
151: numlines = 7;
152: gc.ipady = (textRowHeight * numlines) / 2;
153:
154: JScrollPane sp = new JScrollPane(textBox);
155: sp.setBorder(null); // use the JTextArea's border
156: sp.getHorizontalScrollBar().setUnitIncrement(
157: textRowHeight + margin);
158: this .add(sp, gc);
159: } else if (numlines > 1) {
160: gc.ipady = (textRowHeight * numlines) / 2;
161: this .add(textBox, gc);
162: } else {
163: // otherwise
164: this .add(textBox, gc);
165: }
166: }
167:
168: // now add a row to the bottom of the form for some
169: // padding, and make it vertically stretchable to
170: // absorb any excess height
171: gc.gridx = 0;
172: gc.gridy = fields.length;
173: gc.weightx = 0;
174: gc.weighty = 1;
175: gc.ipadx = 0;
176: gc.ipady = 0;
177: gc.insets = labelInsets;
178:
179: this .add(new JLabel(" "), gc);
180:
181: fieldInsets.bottom = 0;
182: gc.gridx = 1;
183: gc.weightx = 1;
184: gc.insets = fieldInsets;
185: this .add(new JLabel(" "), gc);
186:
187: for (int k = 0; k < fields.length - 1; k++) {
188: fields[k].setNextFocusableComponent(fields[k + 1]);
189: }
190:
191: if (fields.length > 0) {
192: fields[fields.length - 1]
193: .setNextFocusableComponent(fields[0]);
194: }
195:
196: // constrain the width to make sure the scrollpane does not let the
197: // this panel grow to a huge width, and it will also
198: // make it so that vertical scrolling occurs (instead of clipping).
199: minWidth = gbLayout.minimumLayoutSize(this ).width;
200: }
201:
202: /**
203: * Overriden to constrain the preferred width.
204: */
205: public Dimension getPreferredSize() {
206: Dimension d = super .getPreferredSize();
207: return new Dimension(minWidth, d.height);
208: }
209:
210: public boolean getHasPK() {
211: return hasPK;
212: }
213:
214: public void focusFirstTextBox() {
215: if (fields != null && fields.length > 0) {
216: fields[0].requestFocus();
217: }
218: }
219:
220: public void setFieldsEditable(boolean b) {
221: for (int j = 0; j < fields.length; j++) {
222: fields[j].setEditable(b);
223: // this works around Java Bug #4838730 that occurs in version 1.4.1.
224: // See the Java bug database at java.sun.com for details.
225: if (b)
226: GUIHelper.setTabTraversal(fields[j]);
227: }
228: }
229:
230: public void clearFields() {
231: for (int j = 0; j < fields.length; j++) {
232: fields[j].clear();
233: }
234: }
235:
236: void showRequiredFields(boolean b) {
237: for (int j = 0; j < fields.length; j++) {
238: fields[j].setRequiredFieldBorder(b);
239: }
240: }
241:
242: public boolean isAtBeginning() {
243: if (resultSetBuffer != null)
244: return resultSetBuffer.isAtBeginning();
245: return false;
246: }
247:
248: public int getCurrentRowNum() {
249: if (resultSetBuffer == null)
250: return -1;
251: return resultSetBuffer.getCursor();
252: }
253:
254: public int getCursorVal() {
255: if (resultSetBuffer == null)
256: return -1;
257: return resultSetBuffer.getCursor();
258: }
259:
260: public String getPkWhereClause() throws UnknownPrimaryKeyException {
261: return getPkWhereClause(true);
262: }
263:
264: public String getPkWhereClause(boolean includeWhere)
265: throws UnknownPrimaryKeyException {
266: if (resultSetBuffer == null)
267: throw new RuntimeException("There is no current record.");
268:
269: // collect the indices of all of the PK fields.
270: ArrayList pkFieldIndices = new ArrayList();
271: for (int j = 0; j < fields.length; j++) {
272: if (fields[j].isPkComponent()) {
273: pkFieldIndices.add(new Integer(j));
274: } else if (hasPK == false) {
275: // see if this is a candidate key.
276: int dataType = fields[j].getDataType().intValue();
277: if (fields[j].isTypeDisplayable()
278: && dataType != Types.BIT
279: && fields[j].isNullable() == false
280: && fields[j].getLength() > 0
281: && ((DBUtil.isCharType(dataType) && fields[j]
282: .getLength() < 51) || DBUtil
283: .isNumericType(dataType))) {
284: pkFieldIndices.add(new Integer(j));
285: }
286: }
287: }
288:
289: if (pkFieldIndices.size() == 0) {
290: throw new UnknownPrimaryKeyException();
291: }
292: Vector row = resultSetBuffer.getCurrentRow();
293: StringBuffer buffer = new StringBuffer(
294: 25 + (40 * pkFieldIndices.size()));
295: if (includeWhere)
296: buffer.append("WHERE ");
297: for (int j = 0; j < pkFieldIndices.size(); j++) {
298:
299: int fieldIndex = ((Integer) pkFieldIndices.get(j))
300: .intValue();
301:
302: TextBox field = fields[fieldIndex];
303:
304: if (j > 0) {
305: buffer.append(" AND ");
306: }
307: buffer.append(field.getColumnNameForQuery());
308: buffer.append(" = ");
309:
310: // if this field is null, something's wrong
311: // so let it fail.
312: String val = row.get(fieldIndex).toString().trim();
313: if (field.isTypeNumeric()) {
314: buffer.append(val);
315: } else {
316:
317: buffer.append("'");
318: buffer.append(DBUtil.escape(val));
319: buffer.append("'");
320: }
321: }
322: return buffer.toString();
323: }
324:
325: public String getSetClause() {
326: if (resultSetBuffer == null)
327: throw new RuntimeException("There is no current record.");
328: Vector row = resultSetBuffer.getCurrentRow();
329: StringBuffer buffer = new StringBuffer(fields.length * 30);
330: boolean dirtyFieldFound = false;
331: for (int j = 0; j < fields.length; j++) {
332: if (fields[j].isDirty()) {
333: if (dirtyFieldFound)
334: buffer.append(", ");
335: dirtyFieldFound = true;
336: buffer.append(fields[j].getSetClause());
337: }
338: }
339: if (dirtyFieldFound == false)
340: return null;
341: return buffer.toString();
342: }
343:
344: /**
345: * Copies the values from the textboxes into the current row's Vector.
346: */
347: public int updateCurrentRow() {
348: if (resultSetBuffer == null)
349: return -1;
350: Vector cachedRow = resultSetBuffer.getCurrentRow();
351:
352: if (cachedRow != null) {
353: for (int j = 0; j < fields.length; j++) {
354: String fieldValue = fields[j].getText();
355: cachedRow.set(j, fieldValue);
356: fields[j].setDirty(false);
357: }
358: return resultSetBuffer.getCursor();
359: }
360:
361: else {
362: // System.out.println("cachedRow is null.");
363: clearFields();
364: return -1;
365: }
366:
367: }
368:
369: public int current() {
370:
371: if (resultSetBuffer == null)
372: return -1;
373:
374: Vector cachedRow = resultSetBuffer.getCurrentRow();
375:
376: if (cachedRow != null) {
377: for (int j = 0; j < fields.length; j++) {
378: Object fieldValue = cachedRow.elementAt(j);
379: fields[j].setValue(fieldValue);
380: }
381: return resultSetBuffer.getCursor();
382: }
383:
384: else {
385: // System.out.println("cachedRow is null.");
386: clearFields();
387: return -1;
388: }
389:
390: }
391:
392: public int next() throws SQLException {
393:
394: Vector cachedRow = resultSetBuffer.getNextRow();
395:
396: // if we're at the end of the backbuffer...
397: if (cachedRow == null) {
398:
399: endReached = true;
400: return (0 - resultSetBuffer.getCursor());
401: } else {
402: // if we're not at the end of the backbuffer, read in the next row from it.
403: for (int j = 0; j < fields.length; j++) {
404: Object fieldValue = cachedRow.elementAt(j);
405: fields[j].setValue(fieldValue);
406: }
407: return resultSetBuffer.getCursor();
408: }
409: }
410:
411: /////////////////////////////////////////////////////////////////////
412: /////////////////////////////////////////////////////////////////////
413: //
414: // display the previous record
415: //
416: public int previous() {
417:
418: Vector cachedRow = resultSetBuffer.getPreviousRow();
419:
420: if (cachedRow != null) {
421: for (int j = 0; j < fields.length; j++) {
422: Object fieldValue = cachedRow.elementAt(j);
423: fields[j].setValue(fieldValue);
424: }
425: return resultSetBuffer.getCursor();
426: } else {
427: return -1;
428: }
429: }
430:
431: public boolean first() {
432:
433: if (resultSetBuffer.isAtBeginning())
434: return false;
435:
436: Vector cachedRow = resultSetBuffer.getFirstRow();
437:
438: if (cachedRow != null) {
439: for (int j = 0; j < fields.length; j++) {
440: Object fieldValue = cachedRow.get(j);
441: fields[j].setValue(fieldValue);
442: }
443: return true;
444: } else {
445: return false;
446: }
447: }
448:
449: public int removeCurrentRow() throws SQLException {
450:
451: if (resultSetBuffer == null)
452: return -1;
453:
454: resultSetBuffer.removeCurrentRow();
455: int cur = resultSetBuffer.getCursor();
456: // System.out.println("CURSOR IS NOW " + cur);
457: return cur;
458:
459: }
460:
461: public void showRow(int row) {
462: resultSetBuffer.setCursor(row);
463: current();
464: }
465:
466: public boolean maybeShowRow(int row) {
467: boolean b = resultSetBuffer.maybeSetCursor(row);
468: if (false == b)
469: return false;
470: current();
471: return true;
472: }
473:
474: public boolean isEndOfResultsReached() {
475: return resultSetBuffer.isEndOfResultsReached();
476: }
477:
478: public boolean hasCurrentResultSet() {
479: return resultSetBuffer != null && resultSetBuffer.size() > 0;
480: }
481:
482: public int getRowsRead() {
483: if (resultSetBuffer == null)
484: return 0;
485: return resultSetBuffer.size();
486: }
487:
488: public void replaceCurrentRow(ResultSet rs) throws SQLException {
489:
490: // replace the current row in the buffer
491: resultSetBuffer.replaceCurrentRow(rs);
492:
493: // now display the newly read row in the form
494: current();
495: }
496:
497: public Vector[] getCurrentRowset() {
498: if (resultSetBuffer == null)
499: return null;
500: return resultSetBuffer.getCurrentRowset();
501: }
502:
503: public Vector getCurrentRowClone() {
504: if (resultSetBuffer == null)
505: return null;
506: return resultSetBuffer.getCurrentRowClone();
507: }
508:
509: public void populateFields(Vector v) {
510: for (int j = 0; j < fields.length; j++) {
511: if (fields[j].isTypeDisplayable()) {
512: fields[j].setValue(v.get(j));
513: }
514: }
515: }
516:
517: public Vector getContentsOfFields() {
518: Vector v = new Vector();
519: for (int j = 0; j < fields.length; j++) {
520: v.add(fields[j].getText());
521: }
522: return v;
523: }
524:
525: public void addPopupMenuManager(PopupMenuManager popupManager) {
526: this .addMouseListener(popupManager);
527: Component[] comps = this .getComponents();
528: for (int j = 0; j < comps.length; j++) {
529: if (comps[j] instanceof JScrollPane) {
530: ((JScrollPane) comps[j]).getViewport().getView()
531: .addMouseListener(popupManager);
532: } else {
533: comps[j].addMouseListener(popupManager);
534: }
535: }
536: }
537:
538: }
|