001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.sql.framework.ui.view.graph;
042:
043: import java.awt.BorderLayout;
044: import java.awt.Container;
045: import java.awt.FlowLayout;
046: import java.awt.Frame;
047: import java.awt.GridBagConstraints;
048: import java.awt.GridBagLayout;
049: import java.awt.Insets;
050: import java.awt.event.ActionEvent;
051: import java.awt.event.ActionListener;
052: import java.awt.event.KeyAdapter;
053: import java.awt.event.KeyEvent;
054: import java.awt.event.WindowAdapter;
055: import java.awt.event.WindowEvent;
056: import java.sql.Types;
057: import java.text.DateFormat;
058: import java.text.ParseException;
059: import java.util.List;
060:
061: import javax.swing.JButton;
062: import javax.swing.JComboBox;
063: import javax.swing.JDialog;
064: import javax.swing.JFrame;
065: import javax.swing.JLabel;
066: import javax.swing.JPanel;
067: import javax.swing.JTextField;
068: import javax.swing.SwingUtilities;
069: import javax.swing.WindowConstants;
070: import javax.swing.border.EmptyBorder;
071:
072: import org.netbeans.modules.sql.framework.common.jdbc.SQLUtils;
073: import org.netbeans.modules.sql.framework.model.SQLLiteral;
074: import org.openide.DialogDisplayer;
075: import org.openide.NotifyDescriptor;
076:
077: import com.sun.sql.framework.utils.StringUtil;
078: import net.java.hulp.i18n.Logger;
079: import org.netbeans.modules.etl.logger.Localizer;
080: import org.netbeans.modules.etl.logger.LogUtil;
081:
082: /**
083: * Configures type and value of an SQLBuilder literal element.
084: *
085: * @author Jonathan Giron
086: * @version $Revision$
087: */
088: public class LiteralDialog extends JDialog implements ActionListener {
089:
090: /* Array of Strings representing available SQL datatypes */
091: public static final String[] DISPLAY_NAMES;
092:
093: /* Action command string representing OK user option */
094: private static final String CMD_OK = "ok"; //NOI18N
095:
096: /* Action command string representing Cancel user option */
097: private static final String CMD_CANCEL = "cancel"; //NOI18N
098:
099: static {
100: List<String> types = SQLLiteral.VALID_TYPE_NAMES;
101:
102: // Now populated DISPLAY_NAMES with contents of the restricted list.
103: DISPLAY_NAMES = types.toArray(new String[types.size()]);
104: }
105:
106: /* Holds available SQL types */
107: private JComboBox mTypesBox = new JComboBox(DISPLAY_NAMES);
108:
109: /* Holds value of literal */
110: private JTextField mInput = new JTextField();
111:
112: /*
113: * If date literals are supported, delete the JTextField declaration and uncomment
114: * this field. Also uncomment the inner class FormatTypesChangeListener to
115: * enable/disable the secondary dialog button.
116: */
117: // private CalendarComboBox mInput = new CalendarComboBox(false);
118: /* OK dialog button */
119: private JButton mOkButton;
120:
121: /* Cancel dialog button */
122: private JButton mCancelButton;
123:
124: /* Indicates whether user cancelled dialog box */
125: private boolean mIsCanceled = true;
126: private static transient final Logger mLogger = LogUtil
127: .getLogger(LiteralDialog.class.getName());
128: private static transient final Localizer mLoc = Localizer.get();
129:
130: /**
131: * Creates a new LiteralDialog object.
132: *
133: * @param title
134: * @param modal
135: */
136: public LiteralDialog(Frame parent, String title, boolean modal) {
137: super (parent, title, modal);
138:
139: try {
140: String nbBundle1 = mLoc.t("PRSR001: Ok");
141: mOkButton = new JButton(Localizer.parse(nbBundle1)); //NOI18N
142: mOkButton.getAccessibleContext().setAccessibleName(
143: Localizer.parse(nbBundle1));
144: String nbBundle2 = mLoc.t("PRSR001: Cancel");
145: mCancelButton = new JButton(Localizer.parse(nbBundle2)); //NOI18N
146: mCancelButton.getAccessibleContext().setAccessibleName(
147: Localizer.parse(nbBundle2));
148: initComponents();
149: } catch (Exception e) {
150: e.printStackTrace();
151: }
152: }
153:
154: /**
155: * Indicates whether the given String value is a valid representation of a literal of
156: * the given type, displaying a GUI error message dialog if it is not.
157: *
158: * @param literalVal value to test
159: * @param type asserted JDBC type of literal
160: * @return true if <code>literalVal</code> is valid; false otherwise
161: */
162: public static final boolean isValidLiteral(String literalVal,
163: int type) {
164: boolean returnVal = true;
165:
166: String errorMessage = evaluateIfLiteralValid(literalVal, type);
167: if (errorMessage != null) {
168: returnVal = false;
169: showMessage(errorMessage);
170: }
171:
172: return returnVal;
173: }
174:
175: public static final String evaluateIfLiteralValid(
176: String literalVal, int type) {
177: String errorMsg = null;
178:
179: switch (type) {
180: // We'll accept blank text for char-like literals, so treat
181: // them differently from other types.
182: case Types.CHAR:
183: case Types.VARCHAR:
184: case Types.LONGVARCHAR:
185: break;
186:
187: case Types.BOOLEAN:
188: if (!(Boolean.TRUE.toString().equals(literalVal) || Boolean.FALSE
189: .toString().equalsIgnoreCase(literalVal))) {
190: String nbBundle3 = mLoc
191: .t("PRSR001: Please enter either true or false.");
192: errorMsg = Localizer.parse(nbBundle3);//NOI18N
193: }
194: break;
195:
196: case Types.DECIMAL:
197: case Types.DOUBLE:
198: case Types.FLOAT:
199: case Types.NUMERIC:
200: case Types.REAL:
201: try {
202: Double.valueOf(literalVal);
203: } catch (NumberFormatException e) {
204: String nbBundle4 = mLoc
205: .t("PRSR001: Please enter a valid number value.");
206: errorMsg = Localizer.parse(nbBundle4);//NOI18N
207: }
208: break;
209:
210: case Types.BIGINT:
211: case Types.INTEGER:
212: case Types.SMALLINT:
213: case Types.TINYINT:
214: if (!StringUtil.isValid(literalVal, "[0-9]+")) {
215: String nbBundle5 = mLoc
216: .t("PRSR001: Please enter a valid whole number value.");
217: errorMsg = Localizer.parse(nbBundle5);//NOI18N
218: }
219: break;
220:
221: case Types.DATE:
222: try {
223: DateFormat.getDateInstance().parse(literalVal);
224: } catch (ParseException e) {
225: String nbBundle6 = mLoc
226: .t("PRSR001: Please enter a valid date string.");
227: errorMsg = Localizer.parse(nbBundle6);//NOI18N
228: }
229: break;
230:
231: case Types.TIME:
232: if (!StringUtil.isValid(literalVal,
233: "[0-9]?[0-9]:[0-9][0-9].?[AP]?[M]?")) { //NOI18N
234: String nbBundle7 = mLoc
235: .t("PRSR001: Please enter a valid time string.");
236: errorMsg = Localizer.parse(nbBundle7); //NOI18N
237: }
238: break;
239:
240: case Types.TIMESTAMP:
241: try {
242: java.sql.Timestamp.valueOf(literalVal);
243: } catch (IllegalArgumentException e) {
244: String nbBundle8 = mLoc
245: .t("PRSR001: Please enter a valid timestamp string, in the form yyyy-mm-dd hh:mm:ss.fffffffff (fractional seconds optional).");
246: errorMsg = Localizer.parse(nbBundle8);
247: }
248: break;
249: default:
250: if (StringUtil.isNullString(literalVal)) {
251: String nbBundle9 = mLoc
252: .t("PRSR001: Please enter a value.");
253: errorMsg = Localizer.parse(nbBundle9);
254: }
255: }
256:
257: return errorMsg;
258: }
259:
260: public static void showMessage(String msg) {
261: DialogDisplayer.getDefault().notify(
262: new NotifyDescriptor.Message(msg,
263: NotifyDescriptor.WARNING_MESSAGE));
264: }
265:
266: @Override
267: @SuppressWarnings("deprecation")
268: public void show() {
269: pack();
270: this .setResizable(false);
271: this .setLocationRelativeTo(null);
272: mOkButton.requestFocus();
273: super .show();
274: }
275:
276: /**
277: * Exposes standalone test entry-point.
278: *
279: * @param args command-line arguments
280: */
281: public static void main(String args[]) {
282: JFrame win = new JFrame("main"); //NOI18N
283: win.setSize(100, 100);
284: win.setVisible(true);
285: win
286: .setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
287:
288: LiteralDialog dia = new LiteralDialog(win, "Testing", true); //NOI18N
289: dia.setVisible(true);
290: dia
291: .setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
292: }
293:
294: /**
295: * Gets user-defined literal value.
296: *
297: * @return String representing literal value
298: */
299: public String getLiteral() {
300: switch (getType()) {
301: // We'll accept blank text for char-like literals, so treat
302: // them differently from other types.
303: case Types.VARCHAR:
304: case Types.CHAR:
305: case Types.LONGVARCHAR:
306: return mInput.getText();
307:
308: default:
309: return mInput.getText().trim();
310: }
311: }
312:
313: /**
314: * Gets user-selected JDBC datatype of this literal
315: *
316: * @return JDBC datatype, as enumerated in java.sql.Types
317: */
318: public int getType() {
319: return SQLUtils.getStdJdbcType((String) mTypesBox
320: .getSelectedItem());
321: }
322:
323: /**
324: * Indicates whether user cancelled this dialog box.
325: *
326: * @return true if user cancelled dialog box, false otherwise.
327: */
328: public boolean isCanceled() {
329: return mIsCanceled;
330: }
331:
332: /**
333: * @param e ActionEvent to handle.
334: */
335: public void actionPerformed(ActionEvent e) {
336: if (CMD_CANCEL.equals(e.getActionCommand())) { //NOI18N
337: mIsCanceled = true;
338: this .setVisible(false);
339: } else if (CMD_OK.equals(e.getActionCommand())) { //NOI18N
340: if (checkForm()) {
341: mIsCanceled = false;
342: this .setVisible(false);
343: } else {
344: SwingUtilities.invokeLater(new Runnable() {
345:
346: public void run() {
347: String text = mInput.getText();
348: int len = (text == null) ? -1 : text.length();
349: if (len != -1) {
350: mInput.setCaretPosition(len);
351: }
352:
353: mInput.requestFocusInWindow();
354: }
355: });
356: }
357: }
358: }
359:
360: private void buttonActionPerformed(Object source) {
361: if (source.equals(mCancelButton)) {
362: mIsCanceled = true;
363: this .setVisible(false);
364: } else if (source.equals(mOkButton)) {
365: if (checkForm()) {
366: mIsCanceled = false;
367: this .setVisible(false);
368: }
369: }
370: }
371:
372: /*
373: * Creates button pane for this dialog. @return Container containing control buttons.
374: */
375: private Container getButtonPane() {
376: JPanel buttonPanel = new JPanel();
377: buttonPanel.setLayout(new FlowLayout(FlowLayout.TRAILING, 10,
378: 10));
379: mOkButton.requestFocus();
380: buttonPanel.add(mOkButton);
381: buttonPanel.add(mCancelButton);
382:
383: return buttonPanel;
384: }
385:
386: private boolean checkForm() {
387: return LiteralDialog.isValidLiteral(mInput.getText(), SQLUtils
388: .getStdJdbcType((String) mTypesBox.getSelectedItem()));
389: }
390:
391: private void initEventHandle() {
392: mOkButton.setActionCommand(CMD_OK); // NOI18N
393: mCancelButton.setActionCommand(CMD_CANCEL); // NOI18N
394:
395: mOkButton.addActionListener(this );
396: mCancelButton.addActionListener(this );
397:
398: ButtonKeyAdapter bKeyAdapter = new ButtonKeyAdapter();
399: mOkButton.addKeyListener(bKeyAdapter);
400: mCancelButton.addKeyListener(bKeyAdapter);
401:
402: mInput.setActionCommand(CMD_OK); // NOI18N
403: mInput.addActionListener(this );
404:
405: //FIXME: Uncomment the following line if date literals are supported.
406: // mTypesBox.addItemListener(new FormatTypesChangeListener());
407: this .addWindowListener(new WindowAdapter() {
408:
409: @Override
410: public void windowClosing(WindowEvent e) {
411: mIsCanceled = true;
412: }
413: });
414: }
415:
416: private void initComponents() throws Exception {
417: Container contentPane = this .getContentPane();
418: contentPane.setLayout(new BorderLayout(0, 0));
419:
420: JPanel formPanel = new JPanel();
421: formPanel.setBorder(new EmptyBorder(25, 25, 0, 25));
422:
423: GridBagLayout gridBag = new GridBagLayout();
424: GridBagConstraints constraints = new GridBagConstraints();
425: formPanel.setLayout(gridBag);
426:
427: Insets leftInsets = new Insets(0, 0, 5, 5);
428: Insets rightInsets = new Insets(0, 5, 5, 0);
429:
430: constraints.fill = GridBagConstraints.HORIZONTAL;
431: constraints.anchor = GridBagConstraints.WEST;
432: constraints.gridx = 0;
433: constraints.gridy = 0;
434: constraints.weightx = 0.0;
435: constraints.weighty = 0.0;
436: constraints.gridheight = 1;
437: constraints.gridwidth = 1;
438: constraints.insets = leftInsets;
439:
440: String nbBundle10 = mLoc.t("PRSR001: Type:");
441: JLabel typeLabel = new JLabel(Localizer.parse(nbBundle10)); //NOI18N
442: typeLabel.getAccessibleContext().setAccessibleName(
443: Localizer.parse(nbBundle10));
444: gridBag.setConstraints(typeLabel, constraints);
445: formPanel.add(typeLabel);
446:
447: constraints.gridx = 1;
448: constraints.insets = rightInsets;
449: constraints.weightx = 1.0;
450: gridBag.setConstraints(mTypesBox, constraints);
451: formPanel.add(mTypesBox);
452:
453: constraints.gridx = 0;
454: constraints.gridy = 1;
455: constraints.insets = leftInsets;
456: constraints.weightx = 0.0;
457: constraints.gridwidth = 2;
458:
459: String nbBundle11 = mLoc.t("PRSR001: Value:");
460: JLabel valueLabel = new JLabel(Localizer.parse(nbBundle11)); //NOI18N
461: valueLabel.getAccessibleContext().setAccessibleName(
462: Localizer.parse(nbBundle11));
463: gridBag.setConstraints(valueLabel, constraints);
464: formPanel.add(valueLabel);
465:
466: mInput.setColumns(20);
467: constraints.gridx = 1;
468: constraints.insets = rightInsets;
469: constraints.weightx = 1.0;
470: gridBag.setConstraints(mInput, constraints);
471: formPanel.add(mInput);
472:
473: initEventHandle();
474: contentPane.add(formPanel, BorderLayout.CENTER);
475: contentPane.add(getButtonPane(), BorderLayout.SOUTH);
476: this .setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
477: }
478:
479: /*
480: * Uncomment this inner class if and when literal dates are again supported. This
481: * would imply that mInput is an instance of CalendarComboBox instead of JTextInput.
482: */
483: // class FormatTypesChangeListener implements ItemListener {
484: // /**
485: // * Invoked when an item has been selected or deselected by the user. The code
486: // * written for this method performs the operations that need to occur when an
487: // * item is selected (or deselected).
488: // */
489: // public void itemStateChanged(ItemEvent e) {
490: // int type = SQLUtils.getStdJdbcType((String) mTypesBox.getSelectedItem());
491: // if (type == Types.DATE) {
492: // mInput.showCalendarPopUp();
493: // } else {
494: // mInput.hideCalendarPopUp();
495: // }
496: // }
497: // }
498: class ButtonKeyAdapter extends KeyAdapter {
499:
500: @Override
501: public void keyPressed(KeyEvent evt) {
502: if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
503: buttonActionPerformed(evt.getSource());
504: }
505: }
506: }
507: }
|