Package net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent

Source Code of net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.DataTypeDouble

package net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent;

/*
* Copyright (C) 2001-2003 Colin Bell
* colbell@users.sourceforge.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.NumberFormat;

import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;

import net.sourceforge.squirrel_sql.fw.datasetviewer.CellDataPopup;
import net.sourceforge.squirrel_sql.fw.datasetviewer.ColumnDisplayDefinition;
import net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;

/**
* @author gwg This class provides the display components for handling Double data types, specifically SQL
*         types REAL. Note: The java.sun.com site recommends using float for SQL type REAL and data type
*         double for SQL type FLOAT. The display components are for:
*         <UL>
*         <LI>read-only display within a table cell
*         <LI>editing within a table cell
*         <LI>read-only or editing display within a separate window
*         </UL>
*         The class also contains
*         <UL>
*         <LI>a function to compare two display values to see if they are equal. This is needed because the
*         display format may not be the same as the internal format, and all internal object types may not
*         provide an appropriate equals() function.
*         <LI>a function to return a printable text form of the cell contents, which is used in the text
*         version of the table.
*         </UL>
*         <P>
*         The components returned from this class extend RestorableJTextField and RestorableJTextArea for use
*         in editing table cells that contain values of this data type. It provides the special behavior for
*         null handling and resetting the cell to the original value.
*/

public class DataTypeDouble extends FloatingPointBase implements IDataTypeComponent
{

  /** Logger for this class. */
  private static ILogger s_log = LoggerController.createLogger(DataTypeBigDecimal.class);

  /* whether nulls are allowed or not */
  private boolean _isNullable;

  /* table of which we are part (needed for creating popup dialog) */
  private JTable _table;

  /* The JTextComponent that is being used for editing */
  private IRestorableTextComponent _textComponent;

  /* The CellRenderer used for this data type */
  // ??? For now, use the same renderer as everyone else.
  // ??
  // ?? IN FUTURE: change this to use a new instance of renederer
  // ?? for this data type.
  private DefaultColumnRenderer _renderer = DefaultColumnRenderer.getInstance();

  // The NumberFormat object to use for all locale-dependent formatting.
  private NumberFormat _numberFormat;

  private boolean _renderExceptionHasBeenLogged;

  /**
   * Constructor - save the data needed by this data type.
   */
  public DataTypeDouble(JTable table, ColumnDisplayDefinition colDef)
  {
    _table = table;
    _colDef = colDef;
    _isNullable = colDef.isNullable();
    _numberFormat = NumberFormat.getInstance();

    // This is a bit hard coded but if we use _scale here
    // some number displays go crazy.
    _numberFormat.setMaximumFractionDigits(5);

    _numberFormat.setMinimumFractionDigits(0);

  }

  /**
   * Return the name of the java class used to hold this data type.
   */
  public String getClassName()
  {
    return "java.lang.Double";
  }

  /*
   * First we have the methods for in-cell and Text-table operations
   */

  /**
   * Render a value into text for this DataType.
   */
  public String renderObject(Object value)
  {

    // return (String)_renderer.renderObject(value);

    if (value == null || useJavaDefaultFormat)
    {
      return (String) _renderer.renderObject(value);
    }
    else
    {

      try
      {
        return (String) _renderer.renderObject(_numberFormat.format(value));
      }
      catch (Exception e)
      {
        if (false == _renderExceptionHasBeenLogged)
        {
          _renderExceptionHasBeenLogged = true;
          s_log.error("Could not format \"" + value + "\" as number type", e);
        }
        return (String) _renderer.renderObject(value);
      }

    }

  }

  /**
   * This Data Type can be edited in a table cell.
   */
  public boolean isEditableInCell(Object originalValue)
  {
    return true;
  }

  /**
   * See if a value in a column has been limited in some way and needs to be re-read before being used for
   * editing. For read-only tables this may actually return true since we want to be able to view the entire
   * contents of the cell even if it was not completely loaded during the initial table setup.
   */
  public boolean needToReRead(Object originalValue)
  {
    // this DataType does not limit the data read during the initial load of the table,
    // so there is no need to re-read the complete data later
    return false;
  }

  /**
   * Return a JTextField usable in a CellEditor.
   */
  public JTextField getJTextField()
  {
    _textComponent = new RestorableJTextField();

    // special handling of operations while editing this data type
    ((RestorableJTextField) _textComponent).addKeyListener(new KeyTextHandler());

    //
    // handle mouse events for double-click creation of popup dialog.
    // This happens only in the JTextField, not the JTextArea, so we can
    // make this an inner class within this method rather than a separate
    // inner class as is done with the KeyTextHandler class.
    //
    ((RestorableJTextField) _textComponent).addMouseListener(new MouseAdapter()
    {
      public void mousePressed(MouseEvent evt)
      {
        if (evt.getClickCount() == 2)
        {
          MouseEvent tableEvt =
            SwingUtilities.convertMouseEvent((RestorableJTextField) DataTypeDouble.this._textComponent,
              evt, DataTypeDouble.this._table);
          CellDataPopup.showDialog(DataTypeDouble.this._table, DataTypeDouble.this._colDef, tableEvt,
            true);
        }
      }
    }); // end of mouse listener

    return (JTextField) _textComponent;
  }

  /**
   * Implement the interface for validating and converting to internal object. Null is a valid successful
   * return, so errors are indicated only by existance or not of a message in the messageBuffer.
   */
  public Object validateAndConvert(String value, Object originalValue, StringBuffer messageBuffer)
  {
    // handle null, which is shown as the special string "<null>"
    if (value.equals("<null>") || value.equals("")) return null;

    // Do the conversion into the object in a safe manner
    try
    {
      Double obj;

      if (useJavaDefaultFormat)
      {
        obj = new Double(value);
      }
      else
      {
        obj = new Double("" + _numberFormat.parse(value));
      }

      return obj;
    }
    catch (Exception e)
    {
      messageBuffer.append(e.toString() + "\n");
      // ?? do we need the message also, or is it automatically part of the toString()?
      // messageBuffer.append(e.getMessage());
      return null;
    }
  }

  /**
   * If true, this tells the PopupEditableIOPanel to use the binary editing panel rather than a pure text
   * panel. The binary editing panel assumes the data is an array of bytes, converts it into text form,
   * allows the user to change how that data is displayed (e.g. Hex, Decimal, etc.), and converts the data
   * back from text to bytes when the user editing is completed. If this returns false, this DataType class
   * must convert the internal data into a text string that can be displayed (and edited, if allowed) in a
   * TextField or TextArea, and must handle all user key strokes related to editing of that data.
   */
  public boolean useBinaryEditingPanel()
  {
    return false;
  }

  /*
    * Now the functions for the Popup-related operations.
    */

  /**
   * Returns true if data type may be edited in the popup, false if not.
   */
  public boolean isEditableInPopup(Object originalValue)
  {
    return true;
  }

  /*
    * Return a JTextArea usable in the CellPopupDialog
    * and fill in the value.
    */
  public JTextArea getJTextArea(Object value)
  {
    _textComponent = new RestorableJTextArea();

    // value is a simple string representation of the data,
    // the same one used in Text and in-cell operations.
    ((RestorableJTextArea) _textComponent).setText(renderObject(value));

    // special handling of operations while editing this data type
    ((RestorableJTextArea) _textComponent).addKeyListener(new KeyTextHandler());

    return (RestorableJTextArea) _textComponent;
  }

  /**
   * Validating and converting in Popup is identical to cell-related operation.
   */
  public Object validateAndConvertInPopup(String value, Object originalValue, StringBuffer messageBuffer)
  {
    return validateAndConvert(value, originalValue, messageBuffer);
  }

  /*
    * The following is used in both cell and popup operations.
    */

  /*
    * Internal class for handling key events during editing
    * of both JTextField and JTextArea.
    */
  private class KeyTextHandler extends BaseKeyTextHandler
  {
    public void keyTyped(KeyEvent e)
    {
      char c = e.getKeyChar();

      // as a coding convenience, create a reference to the text component
      // that is typecast to JTextComponent. this is not essential, as we
      // could typecast every reference, but this makes the code cleaner
      JTextComponent _theComponent = (JTextComponent) DataTypeDouble.this._textComponent;
      String text = _theComponent.getText();

      // tabs and newlines get put into the text before this check,
      // so remove them
      // This only applies to Popup editing since these chars are
      // not passed to this level by the in-cell editor.
      if (c == KeyEvent.VK_TAB || c == KeyEvent.VK_ENTER)
      {
        // remove all instances of the offending char
        int index = text.indexOf(c);
        if (index != -1)
        {
          if (index == text.length() - 1)
          {
            text = text.substring(0, text.length() - 1); // truncate string
          }
          else
          {
            text = text.substring(0, index) + text.substring(index + 1);
          }
          ((IRestorableTextComponent) _theComponent).updateText(text);
          _beepHelper.beep(_theComponent);
        }
        e.consume();
      }

      if (!(Character.isDigit(c) || (c == '-') || (c == '+') || (c == 'e') || (c == 'E') || (c == 'f')
        || (c == 'F') || (c == 'd') || (c == 'D') || (c == '.') || (c == ',') || // several number formats
                                                          // use '.' as decimal
                                                          // separator, others
                                                          // use ','
        (c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE)))
      {
        _beepHelper.beep(_theComponent);
        e.consume();
      }

      // handle cases of null
      // The processing is different when nulls are allowed and when they are not.
      //

      if (DataTypeDouble.this._isNullable)
      {

        // user enters something when field is null
        if (text.equals("<null>"))
        {
          if ((c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE))
          {
            // delete when null => original value
            DataTypeDouble.this._textComponent.restoreText();
            e.consume();
          }
          else
          {
            // non-delete when null => clear field and add text
            DataTypeDouble.this._textComponent.updateText("");
            // fall through to normal processing of this key stroke
          }
        }
        else
        {
          // check for user deletes last thing in field
          if ((c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE))
          {
            if (text.length() <= 1)
            {
              // about to delete last thing in field, so replace with null
              DataTypeDouble.this._textComponent.updateText("<null>");
              e.consume();
            }
          }
        }
      }
      else
      {
        // field is not nullable
        //
        handleNotNullableField(text, c, e, _textComponent);
      }
    }
  }

  /*
    * DataBase-related functions
    */

  /**
   * On input from the DB, read the data from the ResultSet into the appropriate type of object to be stored
   * in the table cell.
   */
  public Object readResultSet(ResultSet rs, int index, boolean limitDataRead) throws java.sql.SQLException
  {

    double data = rs.getDouble(index);
    if (rs.wasNull()) return null;
    else return new Double(data);
  }

  /**
   * When updating the database, generate a string form of this object value that can be used in the WHERE
   * clause to match the value in the database. A return value of null means that this column cannot be used
   * in the WHERE clause, while a return of "null" (or "is null", etc) means that the column can be used in
   * the WHERE clause and the value is actually a null value. This function must also include the column
   * label so that its output is of the form: "columnName = value" or "columnName is null" or whatever is
   * appropriate for this column in the database.
   */
  public String getWhereClauseValue(Object value, ISQLDatabaseMetaData md)
  {
    if (value == null || value.toString() == null || value.toString().length() == 0)
    {
      return _colDef.getColumnName() + " IS NULL";
    }
    else
    {
      // since we cannot do exact matches on floating point
      // numbers, we cannot use this field in the WHERE clause.
      return null;
    }
  }

  /**
   * When updating the database, insert the appropriate datatype into the prepared statment at the given
   * variable position.
   */
  public void setPreparedStatementValue(PreparedStatement pstmt, Object value, int position)
    throws java.sql.SQLException
  {
    if (value == null)
    {
      pstmt.setNull(position, _colDef.getSqlType());
    }
    else
    {
      pstmt.setDouble(position, ((Double) value).floatValue());
    }
  }

  /**
   * Get a default value for the table used to input data for a new row to be inserted into the DB.
   */
  public Object getDefaultValue(String dbDefaultValue)
  {
    if (dbDefaultValue != null)
    {
      // try to use the DB default value
      StringBuffer mbuf = new StringBuffer();
      Object newObject = validateAndConvert(dbDefaultValue, null, mbuf);

      // if there was a problem with converting, then just fall through
      // and continue as if there was no default given in the DB.
      // Otherwise, use the converted object
      if (mbuf.length() == 0) return newObject;
    }

    // no default in DB. If nullable, use null.
    if (_isNullable) return null;

    // field is not nullable, so create a reasonable default value
    return new Double(0);
  }

  /*
    * File IO related functions
    */

  /**
   * Say whether or not object can be exported to and imported from a file. We put both export and import
   * together in one test on the assumption that all conversions can be done both ways.
   */
  public boolean canDoFileIO()
  {
    return true;
  }

  /**
   * Read a file and construct a valid object from its contents. Errors are returned by throwing an
   * IOException containing the cause of the problem as its message.
   * <P>
   * DataType is responsible for validating that the imported data can be converted to an object, and then
   * must return a text string that can be used in the Popup window text area. This object-to-text conversion
   * is the same as is done by the DataType object internally in the getJTextArea() method.
   * <P>
   * File is assumed to be and ASCII string of digits representing a value of this data type.
   */
  public String importObject(FileInputStream inStream) throws IOException
  {

    InputStreamReader inReader = new InputStreamReader(inStream);

    int fileSize = inStream.available();

    char charBuf[] = new char[fileSize];

    int count = inReader.read(charBuf, 0, fileSize);

    if (count != fileSize) throw new IOException("Could read only " + count
      + " chars from a total file size of " + fileSize + ". Import failed.");

    // convert file text into a string
    // Special case: some systems tack a newline at the end of
    // the text read. Assume that if last char is a newline that
    // we want everything else in the line.
    String fileText;
    if (charBuf[count - 1] == KeyEvent.VK_ENTER) fileText = new String(charBuf, 0, count - 1);
    else fileText = new String(charBuf);

    // test that the string is valid by converting it into an
    // object of this data type
    StringBuffer messageBuffer = new StringBuffer();
    validateAndConvertInPopup(fileText, null, messageBuffer);
    if (messageBuffer.length() > 0)
    {
      // convert number conversion issue into IO issue for consistancy
      throw new IOException("Text does not represent data of type " + getClassName() + ".  Text was:\n"
        + fileText);
    }

    // return the text from the file since it does
    // represent a valid data value
    return fileText;
  }

  /**
   * Construct an appropriate external representation of the object and write it to a file. Errors are
   * returned by throwing an IOException containing the cause of the problem as its message.
   * <P>
   * DataType is responsible for validating that the given text text from a Popup JTextArea can be converted
   * to an object. This text-to-object conversion is the same as validateAndConvertInPopup, which may be used
   * internally by the object to do the validation.
   * <P>
   * The DataType object must flush and close the output stream before returning. Typically it will create
   * another object (e.g. an OutputWriter), and that is the object that must be flushed and closed.
   * <P>
   * File is assumed to be and ASCII string of digits representing a value of this data type.
   */
  public void exportObject(FileOutputStream outStream, String text) throws IOException
  {

    OutputStreamWriter outWriter = new OutputStreamWriter(outStream);

    // check that the text is a valid representation
    StringBuffer messageBuffer = new StringBuffer();
    validateAndConvertInPopup(text, null, messageBuffer);
    if (messageBuffer.length() > 0)
    {
      // there was an error in the conversion
      throw new IOException(new String(messageBuffer));
    }

    // just send the text to the output file
    outWriter.write(text);
    outWriter.flush();
    outWriter.close();
  }
}
TOP

Related Classes of net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.DataTypeDouble

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.