001: /*
002: * Copyright (C) 2007 Rob Manning
003: * manningr@users.sourceforge.net
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent;
020:
021: import java.awt.event.KeyEvent;
022: import java.awt.event.KeyListener;
023: import java.awt.event.MouseAdapter;
024: import java.awt.event.MouseEvent;
025: import java.io.FileInputStream;
026: import java.io.FileOutputStream;
027: import java.io.IOException;
028: import java.io.InputStreamReader;
029: import java.io.OutputStreamWriter;
030:
031: import javax.swing.JTable;
032: import javax.swing.JTextArea;
033: import javax.swing.JTextField;
034: import javax.swing.SwingUtilities;
035:
036: import net.sourceforge.squirrel_sql.fw.datasetviewer.CellDataPopup;
037: import net.sourceforge.squirrel_sql.fw.datasetviewer.ColumnDisplayDefinition;
038: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
039: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
040:
041: /**
042: * A base class for DataTypeComponents with common behavior.
043: *
044: * @author manningr
045: */
046: public class BaseDataTypeComponent {
047:
048: /** Logger for this class. */
049: private static ILogger s_log = LoggerController
050: .createLogger(BaseDataTypeComponent.class);
051:
052: /** the whole column definition */
053: protected ColumnDisplayDefinition _colDef;
054:
055: /** table of which we are part (needed for creating popup dialog) */
056: protected JTable _table;
057:
058: /** A default renderer */
059: protected DefaultColumnRenderer _renderer = DefaultColumnRenderer
060: .getInstance();
061:
062: /** The JTextComponent that is being used for editing */
063: protected RestorableJTextField _textField;
064:
065: /** The JTextComponent that is being used for editing */
066: protected RestorableJTextArea _textArea;
067:
068: /** The text value that is placed in the cell to indicate a null value */
069: public static final String NULL_VALUE_PATTERN = "<null>";
070:
071: /**
072: * Sets the display definition of the Column being operated upon.
073: *
074: * @param def the ColumnDisplayDefinition that describes the column in the
075: * db table.
076: */
077: public void setColumnDisplayDefinition(ColumnDisplayDefinition def) {
078: this ._colDef = def;
079: }
080:
081: /**
082: * Sets the JTable of which holds data rendered by this DataTypeComponent.
083: *
084: * @param table a JTable component
085: */
086: public void setTable(JTable table) {
087: _table = table;
088: }
089:
090: /**
091: * Return a JTextArea usable in the CellPopupDialog. This will use the
092: * renderer to render the value as text and set this as the text for the
093: * JTextArea that is returned.
094: *
095: * @param value
096: * the value to set as text in the JTextArea.
097: */
098: public JTextArea getJTextArea(Object value) {
099: _textArea = new RestorableJTextArea();
100: _textArea.setText((String) _renderer.renderObject(value));
101:
102: // special handling of operations while editing this data type
103: KeyListener keyListener = getKeyListener();
104: if (keyListener != null) {
105: _textArea.addKeyListener(keyListener);
106: }
107:
108: return _textArea;
109: }
110:
111: /**
112: * Return a JTextField usable in a CellEditor.
113: */
114: public JTextField getJTextField() {
115: _textField = new RestorableJTextField();
116:
117: KeyListener keyListener = getKeyListener();
118: if (keyListener != null) {
119: // special handling of operations while editing this data type
120: _textField.addKeyListener(keyListener);
121: }
122:
123: // handle mouse events for double-click creation of popup dialog.
124: // This happens only in the JTextField, not the JTextArea, so we can
125: // make this an inner class within this method rather than a separate
126: // inner class as is done with the KeyTextHandler class.
127: //
128: _textField.addMouseListener(new MouseAdapter() {
129: public void mousePressed(MouseEvent evt) {
130: if (evt.getClickCount() == 2) {
131: MouseEvent tableEvt = SwingUtilities
132: .convertMouseEvent(_textField, evt, _table);
133: CellDataPopup.showDialog(_table, _colDef, tableEvt,
134: true);
135: }
136: }
137: }); // end of mouse listener
138:
139: return _textField;
140: }
141:
142: /**
143: * Render a value into text for this DataType.
144: */
145: public String renderObject(final Object value) {
146: String text = (String) _renderer.renderObject(value);
147: return text;
148: }
149:
150: /**
151: * Implement the interface for validating and converting to internal object.
152: * Null is a valid successful return, so errors are indicated only by
153: * existance or not of a message in the messageBuffer.
154: */
155: @SuppressWarnings("unused")
156: public Object validateAndConvert(final String value,
157: final Object originalValue, final StringBuffer messageBuffer) {
158: // handle null, which is shown as the special string "<null>"
159: if (value.equals(NULL_VALUE_PATTERN)) {
160: return null;
161: }
162:
163: // Special case: the input is exactly the output
164: return value;
165: }
166:
167: /**
168: * Validating and converting in Popup is identical to cell-related operation.
169: */
170: public Object validateAndConvertInPopup(String value,
171: Object originalValue, StringBuffer messageBuffer) {
172: return validateAndConvert(value, originalValue, messageBuffer);
173: }
174:
175: /**
176: * If any custom key handling behavior is required, this can be set by
177: * sub-class implementations
178: */
179: protected KeyListener getKeyListener() {
180: return null;
181: }
182:
183: /**
184: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#exportObject(java.io.FileOutputStream, java.lang.String)
185: */
186: public void exportObject(FileOutputStream outStream, String text)
187: throws IOException {
188: OutputStreamWriter outWriter = null;
189: try {
190: outWriter = new OutputStreamWriter(outStream);
191: outWriter.write(text);
192: outWriter.flush();
193: outWriter.close();
194: } finally {
195: if (outWriter != null) {
196: try {
197: outWriter.close();
198: } catch (IOException e) {
199: s_log.error("exportObject: Unexpected exception: "
200: + e, e);
201: }
202: }
203: }
204: }
205:
206: /**
207: * Read a file and construct a valid object from its contents.
208: * Errors are returned by throwing an IOException containing the
209: * cause of the problem as its message.
210: * <P>
211: * DataType is responsible for validating that the imported
212: * data can be converted to an object, and then must return
213: * a text string that can be used in the Popup window text area.
214: * This object-to-text conversion is the same as is done by
215: * the DataType object internally in the getJTextArea() method.
216: *
217: * <P>
218: * File is assumed to be and ASCII string of digits
219: * representing a value of this data type.
220: */
221: public String importObject(FileInputStream inStream)
222: throws IOException {
223:
224: InputStreamReader inReader = new InputStreamReader(inStream);
225:
226: int fileSize = inStream.available();
227:
228: char charBuf[] = new char[fileSize];
229:
230: int count = inReader.read(charBuf, 0, fileSize);
231:
232: if (count != fileSize)
233: throw new IOException("Could read only " + count
234: + " chars from a total file size of " + fileSize
235: + ". Import failed.");
236:
237: // convert file text into a string
238: // Special case: some systems tack a newline at the end of
239: // the text read. Assume that if last char is a newline that
240: // we want everything else in the line.
241: String fileText;
242: if (charBuf[count - 1] == KeyEvent.VK_ENTER)
243: fileText = new String(charBuf, 0, count - 1);
244: else
245: fileText = new String(charBuf);
246:
247: // test that the string is valid by converting it into an
248: // object of this data type
249: StringBuffer messageBuffer = new StringBuffer();
250: validateAndConvertInPopup(fileText, null, messageBuffer);
251: if (messageBuffer.length() > 0) {
252: // convert number conversion issue into IO issue for consistancy
253: throw new IOException(
254: "Text does not represent data of type "
255: + getClassName() + ". Text was:\n"
256: + fileText);
257: }
258:
259: // return the text from the file since it does
260: // represent a valid data value
261: return fileText;
262: }
263:
264: /**
265: * Sub-classes should override this if the data they handle cannot be
266: * represented as a String.
267: *
268: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getClassName()
269: */
270: public String getClassName() {
271: return "java.lang.String";
272: }
273:
274: }
|