Ejemplo n.º 1
0
/**
 * This is the tab displaying the Catalogs in the database.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class CatalogsTab extends BaseDataSetTab {
  /**
   * This interface defines locale specific strings. This should be replaced with a property file.
   */
  private interface I18n {
    String TITLE = "Catalogs";
    String HINT = "Show all the catalogs";
  }

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

  /**
   * Return the title for the tab.
   *
   * @return The title for the tab.
   */
  public String getTitle() {
    return I18n.TITLE;
  }

  /**
   * Return the hint for the tab.
   *
   * @return The hint for the tab.
   */
  public String getHint() {
    return I18n.HINT;
  }

  /** Create the <TT>IDataSet</TT> to be displayed in this tab. */
  protected IDataSet createDataSet() throws DataSetException {
    final ISession session = getSession();
    try {
      final SQLDatabaseMetaData md = session.getSQLConnection().getSQLMetaData();
      return new ObjectArrayDataSet(md.getCatalogs());
    } catch (SQLException ex) {
      throw new DataSetException(ex);
    }
  }
}
Ejemplo n.º 2
0
/**
 * This action will &quot;quote&quot; an SQL string.
 *
 * @author Gerd Wagner
 */
class InQuotesAction extends SquirrelAction implements ISQLPanelAction {
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(InQuotesAction.class);

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

  /** Current session. */
  private ISession _session;

  private EditExtrasPlugin _plugin;

  InQuotesAction(IApplication app, EditExtrasPlugin plugin) {
    super(app, plugin.getResources());
    _plugin = plugin;
  }

  public void setSQLPanel(ISQLPanelAPI panel) {
    if (null != panel) {
      _session = panel.getSession();
    } else {
      _session = null;
    }
    setEnabled(null != _session);
  }

  public void actionPerformed(ActionEvent evt) {
    if (_session != null) {
      try {
        // new InQuotesCommand(_session.getSQLPanelAPI(_plugin)).execute();
        new InQuotesCommand(FrameWorkAcessor.getSQLPanelAPI(_session, _plugin)).execute();
      } catch (Throwable ex) {
        // i18n[editextras.errorQuoteSql=Error processing Quote SQL command: {0}]
        final String msg = s_stringMgr.getString("editextras.errorQuoteSql", ex);
        _session.showErrorMessage(msg);
        s_log.error(msg, ex);
      }
    }
  }
}
Ejemplo n.º 3
0
/**
 * The Look and Feel plugin class.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class LAFPlugin extends DefaultPlugin {
  /** Logger for this class. */
  private static final ILogger s_log = LoggerController.createLogger(LAFPlugin.class);

  /** Old name of file to store user prefs in. Replaced by USER_PREFS_FILE_NAME. */
  static final String OLD_USER_PREFS_FILE_NAME = "LAFPrefs.xml";

  /** Name of file to store user prefs in. */
  static final String USER_PREFS_FILE_NAME = "LAFPreferences.xml";

  /** Resources for this plugin. */
  private LAFPluginResources _resources;

  /** Plugin preferences. */
  private LAFPreferences _lafPrefs;

  /** A register of Look and Feels. */
  private LAFRegister _lafRegister;

  /** The folder that contains LAF jars. */
  private FileWrapper _lafFolder;

  /** Folder to store user settings in. */
  private FileWrapper _userSettingsFolder;

  /** Folder to store extra LAFs supplied by the user. */
  private FileWrapper _userExtraLAFFolder;

  /** Cache of settings for the plugin. */
  private final XMLObjectCache<LAFPreferences> _settingsCache =
      new XMLObjectCache<LAFPreferences>();

  /**
   * Return the internal name of this plugin.
   *
   * @return the internal name of this plugin.
   */
  public String getInternalName() {
    return "laf";
  }

  /**
   * Return the descriptive name of this plugin.
   *
   * @return the descriptive name of this plugin.
   */
  public String getDescriptiveName() {
    return "Look & Feel Plugin";
  }

  /**
   * Returns the current version of this plugin.
   *
   * @return the current version of this plugin.
   */
  public String getVersion() {
    return "1.1.1";
  }

  /**
   * Returns the authors name.
   *
   * @return the authors name.
   */
  public String getAuthor() {
    return "Colin Bell";
  }

  /**
   * Returns the name of the change log for the plugin. This should be a text or HTML file residing
   * in the <TT>getPluginAppSettingsFolder</TT> directory.
   *
   * @return the changelog file name or <TT>null</TT> if plugin doesn't have a change log.
   */
  public String getChangeLogFileName() {
    return "changes.txt";
  }

  /**
   * Returns the name of the Help file for the plugin. This should be a text or HTML file residing
   * in the <TT>getPluginAppSettingsFolder</TT> directory.
   *
   * @return the Help file name or <TT>null</TT> if plugin doesn't have a help file.
   */
  public String getHelpFileName() {
    return "readme.html";
  }

  /**
   * Returns the name of the Licence file for the plugin. This should be a text or HTML file
   * residing in the <TT>getPluginAppSettingsFolder</TT> directory.
   *
   * @return the Licence file name or <TT>null</TT> if plugin doesn't have a licence file.
   */
  public String getLicenceFileName() {
    return "licences.html";
  }

  /**
   * Load this plugin.
   *
   * @param app Application API.
   */
  public synchronized void load(IApplication app) throws PluginException {
    super.load(app);

    // Load resources.
    _resources = new LAFPluginResources(this);

    // Folder within plugins folder that belongs to this
    // plugin.
    FileWrapper pluginAppFolder = null;
    try {
      pluginAppFolder = getPluginAppSettingsFolder();
    } catch (IOException ex) {
      throw new PluginException(ex);
    }

    // Folder that stores Look and Feel jars.
    _lafFolder = fileWrapperFactory.create(pluginAppFolder, "lafs");
    if (!_lafFolder.exists()) {
      _lafFolder.mkdir();
    }

    // Folder to store user settings.
    try {
      _userSettingsFolder = getPluginUserSettingsFolder();
    } catch (IOException ex) {
      throw new PluginException(ex);
    }

    // Folder to contain extra LAFs supplied by the user.
    _userExtraLAFFolder =
        fileWrapperFactory.create(_userSettingsFolder, ILAFConstants.USER_EXTRA_LAFS_FOLDER);

    // Create empty required files in user settings directory.
    createEmptyRequiredUserFiles();

    // Load plugin preferences.
    loadPrefs();

    // Create the Look and Feel register.
    _lafRegister = new LAFRegister(app, this);

    // Listen for GUI components being created.
    UIFactory.getInstance().addListener(new UIFactoryListener());

    // Update font used for status bars.
    _lafRegister.updateStatusBarFont();
  }

  /** Application is shutting down so save preferences. */
  public void unload() {
    try {
      savePrefs(fileWrapperFactory.create(_userSettingsFolder, USER_PREFS_FILE_NAME));
    } catch (IOException ex) {
      s_log.error("Error occured writing to preferences file: " + USER_PREFS_FILE_NAME, ex);
    } catch (XMLException ex) {
      s_log.error("Error occured writing to preferences file: " + USER_PREFS_FILE_NAME, ex);
    }
    super.unload();
  }

  /**
   * Create Look and Feel preferences panels for the Global Preferences dialog.
   *
   * @return Look and Feel preferences panels.
   */
  public IGlobalPreferencesPanel[] getGlobalPreferencePanels() {
    return new IGlobalPreferencesPanel[] {
      new LAFPreferencesTab(this, _lafRegister), new LAFFontsTab(this, _lafRegister),
    };
  }

  /**
   * Return the folder that contains LAF jars.
   *
   * @return folder as <TT>File</TT> that contains LAF jars.
   */
  FileWrapper getLookAndFeelFolder() {
    return _lafFolder;
  }

  /**
   * Retrieve the directory that contains the extra LAFs supplied by the user.
   *
   * @return folder as <TT>File</TT> that contains the extra LAFs supplied by the user.
   */
  FileWrapper getUsersExtraLAFFolder() {
    return _userExtraLAFFolder;
  }

  /**
   * Get the preferences info object for this plugin.
   *
   * @return The preferences info object for this plugin.
   */
  LAFPreferences getLAFPreferences() {
    return _lafPrefs;
  }

  /**
   * Retrieve plugins resources.
   *
   * @return Plugins resources.
   */
  PluginResources getResources() {
    return _resources;
  }

  XMLObjectCache<LAFPreferences> getSettingsCache() {
    return _settingsCache;
  }

  /** Load from preferences file. */
  private void loadPrefs() {
    final FileWrapper oldPrefsFile =
        fileWrapperFactory.create(_userSettingsFolder, OLD_USER_PREFS_FILE_NAME);
    final FileWrapper newPrefsFile =
        fileWrapperFactory.create(_userSettingsFolder, USER_PREFS_FILE_NAME);
    final boolean oldExists = oldPrefsFile.exists();
    final boolean newExists = newPrefsFile.exists();

    try {
      if (oldExists) {
        loadOldPrefs(oldPrefsFile);
        try {
          _settingsCache.add(_lafPrefs);
        } catch (DuplicateObjectException ex) {
          s_log.error("LAFPreferences object already in cache", ex);
        }
        savePrefs(newPrefsFile);
        if (!oldPrefsFile.delete()) {
          s_log.error("Unable to delete old LAF preferences file");
        }

      } else if (newExists) {
        loadNewPrefs(newPrefsFile);
      }
    } catch (IOException ex) {
      s_log.error("Error occured in preferences file", ex);
    } catch (XMLException ex) {
      s_log.error("Error occured in preferences file", ex);
    }

    if (_lafPrefs == null) {
      _lafPrefs = new LAFPreferences(IdentifierFactory.getInstance().createIdentifier());
      _lafPrefs.setLookAndFeelClassName(MetalLookAndFeelController.METAL_LAF_CLASS_NAME);
      try {
        _settingsCache.add(_lafPrefs);
      } catch (DuplicateObjectException ex) {
        s_log.error("LAFPreferences object already in cache", ex);
      }
    }
  }

  /**
   * Load preferences from the old file format.
   *
   * @param oldPrefsFile FileWrapper containing the preferences info.
   * @throws XMLException Thrown if an error occurs eradign the rpeferences data.
   */
  private void loadOldPrefs(FileWrapper oldPrefsFile) throws XMLException {
    try {
      XMLBeanReader doc = new XMLBeanReader();
      doc.load(oldPrefsFile, getClass().getClassLoader());
      Iterator<?> it = doc.iterator();
      if (it.hasNext()) {
        _lafPrefs = (LAFPreferences) it.next();
      }
    } catch (FileNotFoundException ignore) {
      // property file not found for user - first time user ran pgm.
    }
  }

  /**
   * Load preferences from the new file format.
   *
   * @param newPerfsFile FileWrapper containing the preferences information.
   * @throws XMLException Thrown if error reading preferences file.
   */
  private void loadNewPrefs(FileWrapper newPrefsFile) throws XMLException {
    try {
      try {
        _settingsCache.load(newPrefsFile.getPath(), getClass().getClassLoader());
      } catch (DuplicateObjectException ex) {
        s_log.error("Cache should have been empty", ex);
      }
      Iterator<LAFPreferences> it = _settingsCache.getAllForClass(LAFPreferences.class);
      if (it.hasNext()) {
        _lafPrefs = it.next();
      } else {
        s_log.error("LAFPreferences object not loaded");
      }
    } catch (FileNotFoundException ignore) {
      // property file not found for user - first time user ran pgm.
    }
  }

  /**
   * Save preferences to disk.
   *
   * @param prefsFile File to save preferences to.
   */
  private void savePrefs(FileWrapper prefsFile) throws IOException, XMLException {
    _settingsCache.save(prefsFile.getPath());
  }

  private void createEmptyRequiredUserFiles() {
    _userExtraLAFFolder.mkdirs();

    FileWrapper file =
        fileWrapperFactory.create(_userExtraLAFFolder, ILAFConstants.USER_EXTRA_LAFS_PROPS_FILE);
    try {
      boolean result = file.createNewFile();
      if (!result) {
        s_log.warn("Failed to create empty required user files");
      }
    } catch (IOException ex) {
      s_log.error("Error creating file " + file.getAbsolutePath(), ex);
    }
  }

  private static class UIFactoryListener extends UIFactoryAdapter {
    /**
     * A tabbed panel object has been created.
     *
     * @param evt event object.
     */
    public void tabbedPaneCreated(UIFactoryComponentCreatedEvent evt) {
      final JTabbedPane pnl = (JTabbedPane) evt.getComponent();
      pnl.putClientProperty(Options.NO_CONTENT_BORDER_KEY, Boolean.TRUE);
    }
  }
}
public class DataSetUpdateableTableModelImpl implements IDataSetUpdateableTableModel {

  /** Internationalized strings for this class. */
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(DataSetUpdateableTableModelImpl.class);

  /** string to be passed to user when table name is not found or is ambiguous */
  // i18n[DataSetUpdateableTableModelImpl.error.tablenotfound=Cannot edit table because table cannot
  // be found\nor table name is not unique in DB.]
  private final String TI_ERROR_MESSAGE =
      s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.tablenotfound");

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

  /**
   * This is the long name of the current table including everything that might be able to
   * distinguish it from another table of the same name in a different DB.
   */
  private String fullTableName = null;

  private ITableInfo ti;
  private ISession _session;

  /**
   * Remember whether or not the user has forced us into editing mode when the SessionProperties
   * says to use read-only mode.
   */
  private boolean editModeForced = false;

  /**
   * We need to save the name of the SessionProperties display class at the time that the table was
   * forced into edit mode so that if the properties get changed while we are in forced edit mode,
   * we will change back to match the new Session Properties.
   */
  String sqlOutputClassNameAtTimeOfForcedEdit = "";

  private Vector<DataSetUpdateableTableModelListener> _dataSetUpdateableTableModelListener =
      new Vector<DataSetUpdateableTableModelListener>();

  /**
   * Remember which column contains the rowID; if no rowID, this is -1 which does not match any
   * legal column index. Note that for this class, since the list of columns to include is given by
   * the user, we never include any pseudo-column automatically in the ResultSet, and thus we never
   * have any legal column index here.
   */
  int _rowIDcol = -1;

  /** A util for handling parts of an where clause. */
  private IWhereClausePartUtil whereClausePartUtil = new WhereClausePartUtil();

  public void setTableInfo(ITableInfo ti) {
    this.ti = ti;
    // re-calculate fullTablename the next time it's requested.
    fullTableName = null;
  }

  public void setSession(ISession session) {
    this._session = session;
  }

  /**
   * return the name of the table that is unambiguous across DB accesses, including the same DB on
   * different machines. This function is static because it is used elsewhere to generate the same
   * name as is used within instances of this class.
   *
   * @return the name of the table that is unique for this DB access
   */
  public static String getUnambiguousTableName(ISession session, String name) {
    return session.getAlias().getUrl() + ":" + name;
  }

  /** Get the full name of this table, creating that name the first time we are called */
  public String getFullTableName() {
    if (fullTableName == null) {
      try {
        final String name = ti.getQualifiedName();
        fullTableName = getUnambiguousTableName(_session, name);
      } catch (Exception e) {
        s_log.error("getFullTableName: Unexpected exception - " + e.getMessage(), e);
      }
    }
    return fullTableName;
  }

  /** If the user forces us into edit mode, remember that they did so for this table. */
  public void forceEditMode(boolean mode) {
    editModeForced = mode;
    sqlOutputClassNameAtTimeOfForcedEdit =
        _session.getProperties().getTableContentsOutputClassName();

    DataSetUpdateableTableModelListener[] listeners =
        _dataSetUpdateableTableModelListener.toArray(new DataSetUpdateableTableModelListener[0]);

    for (int i = 0; i < listeners.length; i++) {
      listeners[i].forceEditMode(mode);
    }

    /**
     * Tell the GUI to rebuild itself. This is not a clean way to do that, since we are telling the
     * SessionProperties listeners that a property has changed when in reality none of them have
     * done so, but this does cause the GUI to be rebuilt.
     */
    //		_session.getProperties().forceTableContentsOutputClassNameChange();
  }

  /**
   * The fw needs to know whether we are in forced edit mode or not so it can decide whether or not
   * to let the user undo that mode.
   */
  public boolean editModeIsForced() {
    return editModeForced;
  }

  /**
   * If the user has forced us into editing mode, use the EDITABLE_TABLE form, but otherwise use
   * whatever form the user specified in the Session Preferences.
   */
  public String getDestinationClassName() {
    if (editModeForced) {
      if (_session
          .getProperties()
          .getTableContentsOutputClassName()
          .equals(sqlOutputClassNameAtTimeOfForcedEdit)) {
        return _session.getProperties().getEditableTableOutputClassName();
      }
      // forced edit mode ended because user changed the Session Properties
      editModeForced = false;
    }

    // if the user selected Editable Table in the Session Properties,
    // then the display will be an editable table; otherwise the display is read-only
    return _session.getProperties().getTableContentsOutputClassName();
  }

  /**
   * Link from fw to check on whether there are any unusual conditions in the current data that the
   * user needs to be aware of before updating.
   */
  public String getWarningOnCurrentData(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, Object oldValue) {

    // if we could not identify which table to edit, tell user
    if (ti == null) return TI_ERROR_MESSAGE;

    List<IWhereClausePart> whereClauseParts = getWhereClause(values, colDefs, col, oldValue);

    // It is possible for a table to contain only columns of types that
    // we cannot process or do selects on, so check for that.
    // Since this check is on the structure of the table rather than the contents,
    // we only need to do it once (ie: it is not needed in getWarningOnProjectedUpdate)
    if (whereClausePartUtil.hasUsableWhereClause(whereClauseParts) == false) {
      // i18n[DataSetUpdateableTableModelImpl.confirmupdateallrows=The table has no columns that can
      // be SELECTed on.\nAll rows will be updated.\nDo you wish to proceed?]
      return s_stringMgr.getString("DataSetUpdateableTableModelImpl.confirmupdateallrows");
    }

    final ISession session = _session;
    final ISQLConnection conn = session.getSQLConnection();

    int count = -1; // start with illegal number of rows matching query

    try {
      count = count(whereClauseParts, conn);
    } catch (SQLException ex) {
      // i18n[DataSetUpdateableTableModelImpl.error.exceptionduringcheck=Exception
      // seen during check on DB.  Exception was:\n{0}\nUpdate is probably not
      // safe to do.\nDo you wish to proceed?]
      String msg =
          s_stringMgr.getString(
              "DataSetUpdateableTableModelImpl.error.exceptionduringcheck", ex.getMessage());
      s_log.error(msg, ex);
      return msg;
    }

    if (count == -1) {
      // i18n[DataSetUpdateableTableModelImpl.error.unknownerror=Unknown error during check on DB.
      // Update is probably not safe.\nDo you wish to proceed?]
      return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.unknownerror");
    }
    if (count == 0) {
      // i18n[DataSetUpdateableTableModelImpl.error.staleupdaterow=This row in the Database has been
      // changed since you refreshed the data.\nNo rows will be updated by this operation.\nDo you
      // wish to proceed?]
      return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.staleupdaterow");
    }
    if (count > 1) {
      // i18n[DataSetUpdateableTableModelImpl.info.updateidenticalrows=This operation will update
      // {0} identical rows.\nDo you wish to proceed?]
      return s_stringMgr.getString(
          "DataSetUpdateableTableModelImpl.info.updateidenticalrows", Long.valueOf(count));
    }
    // no problems found, so do not return a warning message.
    return null; // nothing for user to worry about
  }

  /**
   * Counts the number of affected rows, using this where clause.
   *
   * @param whereClauseParts where clause to use
   * @param conn connection to use
   * @return number of rows in the database, which will be selected by the given whereClauseParts
   * @throws SQLException if an SQLExcetpion occurs.
   */
  private int count(List<IWhereClausePart> whereClauseParts, final ISQLConnection conn)
      throws SQLException {
    int count;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      String whereClause = whereClausePartUtil.createWhereClause(whereClauseParts);
      String countSql = "select count(*) from " + ti.getQualifiedName() + whereClause;
      pstmt = conn.prepareStatement(countSql);
      whereClausePartUtil.setParameters(pstmt, whereClauseParts, 1);

      rs = pstmt.executeQuery();
      rs.next();
      count = rs.getInt(1);
    } finally {
      // We don't care if these throw an SQLException.  Just squelch them
      // and report to the user what the outcome of the previous statements
      // were.
      SQLUtilities.closeResultSet(rs);
      SQLUtilities.closeStatement(pstmt);
    }
    return count;
  }

  /**
   * Link from fw to check on whether there are any unusual conditions that will occur after the
   * update has been done.
   */
  public String getWarningOnProjectedUpdate(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, Object newValue) {
    try {
      // if we could not identify which table to edit, tell user
      if (ti == null) return TI_ERROR_MESSAGE;

      List<IWhereClausePart> whereClauseParts = getWhereClause(values, colDefs, col, newValue);

      final ISession session = _session;
      final ISQLConnection conn = session.getSQLConnection();

      int count = -1; // start with illegal number of rows matching query

      try {
        count = count(whereClauseParts, conn);

      } catch (SQLException ex) {
        // i18n[DataSetUpdateableTableModelImpl.error.exceptionduringcheck=Exception seen during
        // check on DB.  Exception was:\n{0}\nUpdate is probably not safe to do.\nDo you wish to
        // proceed?]
        s_stringMgr.getString(
            "DataSetUpdateableTableModelImpl.error.exceptionduringcheck", ex.getMessage());
      }

      if (count == -1) {
        // i18n[DataSetUpdateableTableModelImpl.error.unknownerror=Unknown error during check on DB.
        //  Update is probably not safe.\nDo you wish to proceed?]
        return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.unknownerror");
      }
      // There are some fields that cannot be used in a WHERE clause, either
      // because there cannot be an exact match (e.g. REAL, FLOAT), or
      // because we may not have the actual data in hand (BLOB/CLOB), or
      // because the data cannot be expressed in a string form (e.g. BINARY).
      // An update to one of those fields
      // will look like we are replacing one row with an identical row (because
      // we can only "see" the fields that we know how to do WHEREs on).  Therefore,
      // when we are updating them, there should be exactly one row that matches
      // all of our other fields, and when we are not updating one of these
      // special types of fields, there should be
      // no rows that exactly match our criteria (we hope).
      //
      // We determine whether this field is one that cannot be used in the WHERE
      // clause by checking the value returned for that field to use in the
      // WHERE clause.  Any field that can be used there will return something
      // of the form "<fieldName> = <value>", and a field that cannot be
      // used will return a null or zero-length string.

      if (count > 1) {
        // i18n[DataSetUpdateableTableModelImpl.info.identicalrows=This
        // operation will result in {0} identical rows.\nDo you wish
        // to proceed?]
        return s_stringMgr.getString(
            "DataSetUpdateableTableModelImpl.info.identicalrows", Long.valueOf(count));
      }

      // no problems found, so do not return a warning message.
      return null; // nothing for user to worry about
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Re-read the value for a single cell in the table, if possible. If there is a problem, the
   * message has a non-zero length when this returns.
   */
  public Object reReadDatum(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, StringBuffer message) {

    // if we could not identify which table to edit, tell user
    if (ti == null) return TI_ERROR_MESSAGE;

    // get WHERE clause
    // The -1 says to ignore the last arg and use the contents of the values array
    // for the column that we care about.  However, since the data in
    // that column has been limited, when getWhereClause calls that
    // DataType with that value, the DataType will see that the data has
    // been limited and therefore cannnot be used in the WHERE clause.
    // In some cases it may be possible for the DataType to use the
    // partial data, such as "matches <data>*", but that may not be
    // standard accross all Databases and thus may be risky.

    List<IWhereClausePart> whereClauseParts = getWhereClause(values, colDefs, -1, null);
    String whereClause = whereClausePartUtil.createWhereClause(whereClauseParts);
    final ISession session = _session;
    final ISQLConnection conn = session.getSQLConnection();

    Object wholeDatum = null;

    try {
      final String queryString =
          "SELECT " + colDefs[col].getColumnName() + " FROM " + ti.getQualifiedName() + whereClause;

      final PreparedStatement pstmt = conn.prepareStatement(queryString);
      whereClausePartUtil.setParameters(pstmt, whereClauseParts, 1);

      try {
        ResultSet rs = pstmt.executeQuery(queryString);

        // There should be one row in the data, so try to move to it
        if (rs.next() == false) {
          // no first row, so we cannot retrieve the data
          // i18n[DataSetUpdateableTableModelImpl.error.nomatchingrow=Could not find any row in DB
          // matching current row in table]
          throw new SQLException(
              s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.nomatchingrow"));
        }

        // we have at least one row, so try to retrieve the object
        // Do Not limit the read of this data
        wholeDatum = CellComponentFactory.readResultSet(colDefs[col], rs, 1, false);

        //  There should not be more than one row in the DB that matches
        // the table, and if there is we cannot determine which one to read,
        // so check that there are no more
        if (rs.next() == true) {
          // multiple rows - not good
          wholeDatum = null;
          // i18n[DataSetUpdateableTableModelImpl.error.multimatchingrows=Muliple rows in DB match
          // current row in table - cannot re-read data.]
          throw new SQLException(
              s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.multimatchingrows"));
        }
      } finally {
        pstmt.close();
      }
    } catch (Exception ex) {
      // i18n[DataSetUpdateableTableModelImpl.error.rereadingdb=There was a problem reported while
      // re-reading the DB.  The DB message was:\n{0}]
      message.append(
          s_stringMgr.getString(
              "DataSetUpdateableTableModelImpl.error.rereadingdb", ex.getMessage()));

      // It would be nice to tell the user what happened, but if we try to
      // put up a dialog box at this point, we run into trouble in some
      // cases where the field continually tries to re-read after the dialog
      // closes (because it is being re-painted).
    }

    // return the whole contents of this column in the DB
    return wholeDatum;
  };

  /** link from fw to this for updating data */
  public String updateTableComponent(
      Object[] values,
      ColumnDisplayDefinition[] colDefs,
      int col,
      Object oldValue,
      Object newValue) {
    // if we could not identify which table to edit, tell user
    if (ti == null) return TI_ERROR_MESSAGE;

    // get WHERE clause using original value
    List<IWhereClausePart> whereClauseParts = getWhereClause(values, colDefs, col, oldValue);
    String whereClause = whereClausePartUtil.createWhereClause(whereClauseParts);
    if (s_log.isDebugEnabled()) {
      s_log.debug("updateTableComponent: whereClause = " + whereClause);
    }

    final ISession session = _session;
    final ISQLConnection conn = session.getSQLConnection();

    int count = -1;

    final String sql =
        constructUpdateSql(ti.getQualifiedName(), colDefs[col].getColumnName(), whereClause);

    if (s_log.isDebugEnabled()) {
      s_log.debug("updateTableComponent: executing SQL - " + sql);
    }
    PreparedStatement pstmt = null;
    try {
      pstmt = conn.prepareStatement(sql);

      /*
       * have the DataType object fill in the appropriate kind of value of the changed data
       * into the first variable position in the prepared stmt
       */
      CellComponentFactory.setPreparedStatementValue(colDefs[col], pstmt, newValue, 1);

      // Fill the parameters of the where clause - start at position 2 because the data which is
      // updated is at position 1
      whereClausePartUtil.setParameters(pstmt, whereClauseParts, 2);
      count = pstmt.executeUpdate();
    } catch (SQLException ex) {
      // i18n[DataSetUpdateableTableModelImpl.error.updateproblem=There
      // was a problem reported during the update.
      // The DB message was:\n{0}\nThis may or may not be serious depending
      // on the above message.\nThe data was probably not changed in the
      // database.\nYou may need to refresh the table to get an accurate
      // view of the current data.]
      String errMsg =
          s_stringMgr.getString(
              "DataSetUpdateableTableModelImpl.error.updateproblem", ex.getMessage());
      s_log.error(
          "updateTableComponent: unexpected exception - "
              + ex.getMessage()
              + " while executing SQL: "
              + sql);

      return errMsg;
    } finally {
      SQLUtilities.closeStatement(pstmt);
    }

    if (count == -1) {
      // i18n[DataSetUpdateableTableModelImpl.error.unknownupdateerror=Unknown problem during
      // update.\nNo count of updated rows was returned.\nDatabase may be corrupted!]
      return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.unknownupdateerror");
    }
    if (count == 0) {
      // i18n[DataSetUpdateableTableModelImpl.info.norowsupdated=No rows updated.]
      return s_stringMgr.getString("DataSetUpdateableTableModelImpl.info.norowsupdated");
    }
    // everything seems to have worked ok
    return null;
  }

  /**
   * Build the update SQL from the specified components.
   *
   * @param table the fully qualified name of the table
   * @param column the name of the column to update
   * @param whereClause the where clause that restricts the update to one row.
   * @return the SQL to execute
   */
  private String constructUpdateSql(String table, String column, String whereClause) {
    StringBuilder result = new StringBuilder();
    result.append("UPDATE ");
    result.append(table);
    result.append(" SET ");
    result.append(column);
    result.append(" = ? ");
    result.append(whereClause);
    return result.toString();
  }

  /** Let fw get the rowIDcol */
  public int getRowidCol() {
    return _rowIDcol;
  }

  /**
   * helper function to create a WHERE clause to search the DB for matching rows. If the col number
   * is < 0, then the colValue is ignored and the WHERE clause is constructed using only the
   * values[].
   */
  private List<IWhereClausePart> getWhereClause(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, Object colValue) {
    try {

      // For tables that have a lot of columns, the user may have limited the set of columns
      // to use in the where clause, so see if there is a table of col names
      HashMap<String, String> colNames = (EditWhereCols.get(getFullTableName()));

      ColumnDisplayDefinition editedCol = null;
      if (-1 != col) {
        editedCol = colDefs[col];
      }

      List<IWhereClausePart> clauseParts = new ArrayList<IWhereClausePart>();

      for (int i = 0; i < colDefs.length; i++) {

        if (i != col
            && null != editedCol
            && colDefs[i]
                .getFullTableColumnName()
                .equalsIgnoreCase(editedCol.getFullTableColumnName())) {
          // The edited column is in the resultset twice (example: SELECT MyName,* FROM MyTable).
          // We won't add the this col to the where clause.
          continue;
        }

        // if the user has said to not use this column, then skip it
        if (colNames != null) {
          // the user has restricted the set of columns to use.
          // If this name is NOT in the list, then skip it; otherwise we fall through
          // and use the column in the WHERE clause
          if (colNames.get(colDefs[i].getColumnName()) == null) continue; // go on to the next item
        }

        // for the column that is being changed, use the value
        // passed in by the caller (which may be either the
        // current value or the new replacement value)
        Object value = values[i];
        if (i == col) value = colValue;

        // convert user representation of null into an actual null
        if (value != null && value.toString().equals("<null>")) value = null;

        // do different things depending on data type
        ISQLDatabaseMetaData md = _session.getMetaData();
        IWhereClausePart clausePart =
            CellComponentFactory.getWhereClauseValue(colDefs[i], value, md);

        if (clausePart.shouldBeUsed())
          // Now we know that the part should not we ignoredshould
          clauseParts.add(clausePart);
      }

      return clauseParts;

    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Delete a set of rows from the DB. If the delete succeeded this returns a null string. The
   * deletes are done within a transaction so they are either all done or all not done.
   */
  public String deleteRows(Object[][] rowData, ColumnDisplayDefinition[] colDefs) {

    // if we could not identify which table to edit, tell user
    if (ti == null) return TI_ERROR_MESSAGE;

    // get the SQL session
    final ISession session = _session;
    final ISQLConnection conn = session.getSQLConnection();

    // string used as error indicator and description of problems seen
    // when checking for 0 or mulitple matches in DB
    String rowCountErrorMessage = "";

    // for each row in table, count how many rows match where clause
    // if not exactly one, generate message describing situation
    for (int i = 0; i < rowData.length; i++) {
      // get WHERE clause for the selected row
      // the -1 says to just use the contents of the values without
      // any substitutions
      List<IWhereClausePart> whereClauseParts = getWhereClause(rowData[i], colDefs, -1, null);

      // count how many rows this WHERE matches
      try {

        int count = count(whereClauseParts, conn);
        if (count != 1) {
          if (count == 0) {
            // i18n[DataSetUpdateableTableModelImpl.error.rownotmatch=\n   Row {0}  did not match
            // any row in DB]
            rowCountErrorMessage +=
                s_stringMgr.getString(
                    "DataSetUpdateableTableModelImpl.error.rownotmatch", Integer.valueOf(i + 1));
          } else {
            // i18n[DataSetUpdateableTableModelImpl.error.rowmatched=\n   Row {0} matched {1} rows
            // in DB]
            rowCountErrorMessage +=
                s_stringMgr.getString(
                    "DataSetUpdateableTableModelImpl.error.rowmatched",
                    new Object[] {Integer.valueOf(i + 1), Integer.valueOf(count)});
          }
        }
      } catch (Exception e) {
        // some kind of problem - tell user
        // i18n[DataSetUpdateableTableModelImpl.error.preparingdelete=While preparing for delete,
        // saw exception:\n{0}]
        return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.preparingdelete", e);
      }
    }

    // if the rows do not match 1-for-1 to DB, ask user if they
    // really want to do delete
    if (rowCountErrorMessage.length() > 0) {
      // i18n[DataSetUpdateableTableModelImpl.error.tabledbmismatch=There may be a mismatch between
      // the table and the DB:\n{0}\nDo you wish to proceed with the deletes anyway?]
      String msg =
          s_stringMgr.getString(
              "DataSetUpdateableTableModelImpl.error.tabledbmismatch", rowCountErrorMessage);

      int option =
          JOptionPane.showConfirmDialog(
              null, msg, "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);

      if (option != JOptionPane.YES_OPTION) {
        // i18n[DataSetUpdateableTableModelImpl.info.deletecancelled=Delete canceled at user
        // request.]
        return s_stringMgr.getString("DataSetUpdateableTableModelImpl.info.deletecancelled");
      }
    }

    // for each row in table, do delete and add to number of rows deleted from DB
    for (int i = 0; i < rowData.length; i++) {
      // get WHERE clause for the selected row
      // the -1 says to just use the contents of the values without
      // any substitutions
      List<IWhereClausePart> whereClauseParts = getWhereClause(rowData[i], colDefs, -1, null);
      String whereClause = whereClausePartUtil.createWhereClause(whereClauseParts);
      // try to delete
      try {
        // do the delete and add the number of rows deleted to the count
        String sql = "DELETE FROM " + ti.getQualifiedName() + whereClause;
        final PreparedStatement pstmt = conn.prepareStatement(sql);
        whereClausePartUtil.setParameters(pstmt, whereClauseParts, 1);
        try {
          pstmt.executeUpdate();
        } finally {
          pstmt.close();
        }
      } catch (Exception e) {
        // some kind of problem - tell user
        // i18n[DataSetUpdateableTableModelImpl.error.deleteFailed=One of the delete operations
        // failed with exception:\n{0}\nDatabase is in an unknown state and may be corrupted.]
        return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.deleteFailed", e);
      }
    }

    return null; // hear no evil, see no evil
  }

  /** Let fw get the list of default values for the columns to be used when creating a new row */
  public String[] getDefaultValues(ColumnDisplayDefinition[] colDefs) {

    // we return something valid even if there is a DB error
    final String[] defaultValues = new String[colDefs.length];

    // if we could not identify which table to edit, just return
    if (ti == null) {
      return defaultValues;
    }

    final ISession session = _session;
    final ISQLConnection conn = session.getSQLConnection();

    try {
      SQLDatabaseMetaData md = conn.getSQLMetaData();
      TableColumnInfo[] infos = md.getColumnInfo(ti);

      // read the DB MetaData info and fill in the value, if any
      // Note that the ResultSet info and the colDefs should be
      // in the same order, but we cannot guarantee that.
      int expectedColDefIndex = 0;

      for (int idx = 0; idx < infos.length; idx++) {
        String colName = infos[idx].getColumnName();
        String defValue = infos[idx].getDefaultValue();

        // if value was null, we do not need to do
        // anything else with this column.
        // Also assume that a value of "" is equivilent to null
        if (defValue != null && defValue.length() > 0) {
          // find the entry in colDefs matching this column
          if (colDefs[expectedColDefIndex].getColumnName().equals(colName)) {
            // DB cols are in same order as colDefs
            defaultValues[expectedColDefIndex] = defValue;
          } else {
            // colDefs not in same order as DB, so search for
            // matching colDef entry
            // Note: linear search here will NORMALLY be not too bad
            // because most tables do not have huge numbers of columns.
            for (int i = 0; i < colDefs.length; i++) {
              if (colDefs[i].getColumnName().equals(colName)) {
                defaultValues[i] = defValue;
                break;
              }
            }
          }
        }

        // assuming that the columns in table match colDefs,
        // bump the index to point to the next colDef entry
        expectedColDefIndex++;
      }
    } catch (Exception ex) {
      // i18n[DataSetUpdateableTableModelImpl.error.retrievingdefaultvalues=Error retrieving default
      // column values]
      s_log.error(
          s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.retrievingdefaultvalues"),
          ex);
    }

    return defaultValues;
  }

  /** Insert a row into the DB. If the insert succeeds this returns a null string. */
  public String insertRow(Object[] values, ColumnDisplayDefinition[] colDefs) {

    // if we could not identify which table to edit, tell user
    if (ti == null) {
      return TI_ERROR_MESSAGE;
    }

    final ISession session = _session;
    final ISQLConnection conn = session.getSQLConnection();

    int count = -1;

    try {
      // start the string for use in the prepared statment
      StringBuilder buf = new StringBuilder("INSERT INTO ");
      buf.append(ti.getQualifiedName());

      // Add the list of column names we will be inserting into - be sure
      // to skip the rowId column and any auto increment columns.
      buf.append(" ( ");
      for (int i = 0; i < colDefs.length; i++) {
        if (i == _rowIDcol) {
          continue;
        }
        if (colDefs[i].isAutoIncrement()) {
          if (s_log.isInfoEnabled()) {
            s_log.info("insertRow: skipping auto-increment column " + colDefs[i].getColumnName());
          }
          continue;
        }
        buf.append(colDefs[i].getColumnName());
        buf.append(",");
      }
      buf.setCharAt(buf.length() - 1, ')');
      buf.append(" VALUES (");

      // add a variable position for each of the columns
      for (int i = 0; i < colDefs.length; i++) {
        if (i != _rowIDcol && !colDefs[i].isAutoIncrement()) buf.append(" ?,");
      }

      // replace the last "," with ")"
      buf.setCharAt(buf.length() - 1, ')');

      String pstmtSQL = buf.toString();
      if (s_log.isInfoEnabled()) {
        s_log.info("insertRow: pstmt sql = " + pstmtSQL);
      }
      final PreparedStatement pstmt = conn.prepareStatement(pstmtSQL);

      try {
        // We need to keep track of the bind var index separately, since
        // the number of column defs may not be the number of bind vars
        // (For example: auto-increment columns are excluded)
        int bindVarIdx = 1;

        // have the DataType object fill in the appropriate kind of value
        // into the appropriate variable position in the prepared stmt
        for (int i = 0; i < colDefs.length; i++) {
          if (i != _rowIDcol && !colDefs[i].isAutoIncrement()) {
            CellComponentFactory.setPreparedStatementValue(
                colDefs[i], pstmt, values[i], bindVarIdx);
            bindVarIdx++;
          }
        }
        count = pstmt.executeUpdate();
      } finally {
        pstmt.close();
      }
    } catch (SQLException ex) {
      // i18n[DataSetUpdateableTableModelImpl.error.duringInsert=Exception seen during check on DB.
      // Exception was:\n{0}\nInsert was probably not completed correctly.  DB may be corrupted!]
      return s_stringMgr.getString(
          "DataSetUpdateableTableModelImpl.error.duringInsert", ex.getMessage());
    }

    if (count != 1)
      // i18n[DataSetUpdateableTableModelImpl.error.unknownerrorupdate=Unknown problem during
      // update.\nNo count of inserted rows was returned.\nDatabase may be corrupted!]
      return s_stringMgr.getString("DataSetUpdateableTableModelImpl.error.unknownerrorupdate");

    // insert succeeded
    try {
      IObjectTreeAPI api = _session.getObjectTreeAPIOfActiveSessionWindow();
      api.refreshSelectedTab();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return null;
  }

  public void addListener(DataSetUpdateableTableModelListener l) {
    _dataSetUpdateableTableModelListener.add(l);
  }

  public void removeListener(DataSetUpdateableTableModelListener l) {
    _dataSetUpdateableTableModelListener.remove(l);
  }

  public void setEditModeForced(boolean b) {
    editModeForced = b;
  }

  public void setRowIDCol(int rowIDCol) {
    _rowIDcol = rowIDCol;
  }

  public void setWhereClausePartUtil(IWhereClausePartUtil whereClausePartUtil) {
    this.whereClausePartUtil = whereClausePartUtil;
  }
}
public class ObjectResultController {
  private static ILogger s_log = LoggerController.createLogger(ObjectResultController.class);
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(ObjectResultController.class);

  private JTabbedPane _objectResultTabbedPane;
  private ISession _session;
  private HibernatePluginResources _resource;

  public ObjectResultController(ISession session, HibernatePluginResources resource) {
    _session = session;
    _resource = resource;
    final SessionProperties props = session.getProperties();
    _objectResultTabbedPane =
        UIFactory.getInstance().createTabbedPane(props.getSQLExecutionTabPlacement());
  }

  public JTabbedPane getPanel() {
    return _objectResultTabbedPane;
  }

  public void displayObjects(
      HibernateConnection con, String hqlQuery, boolean limitObjectCount, int limitObjectCountVal) {

    int maxNumResults = -1;
    if (limitObjectCount) {
      maxNumResults = limitObjectCountVal;
    }

    QueryListCreatorListener queryListCreatorListener =
        new QueryListCreatorListener() {
          @Override
          public void listRead(QueryListCreator queryListCreator) {
            onListRead(queryListCreator);
          }
        };

    WaitPanel waitPanel = new WaitPanel(hqlQuery);
    _objectResultTabbedPane.addTab(waitPanel.getTitle(), waitPanel);
    _objectResultTabbedPane.setSelectedComponent(waitPanel);
    new QueryListCreator(
            queryListCreatorListener, hqlQuery, maxNumResults, con, _session, waitPanel)
        .execute();
  }

  private void onListRead(QueryListCreator queryListCreator) {

    for (int i = 0; i < _objectResultTabbedPane.getTabCount(); i++) {
      if (_objectResultTabbedPane.getComponentAt(i) == queryListCreator.getWaitPanel()) {
        _objectResultTabbedPane.removeTabAt(i);
        break;
      }
    }

    List list = queryListCreator.getList();

    if (null == list) {
      return;
    }

    ObjectResultTabControllerListener l =
        new ObjectResultTabControllerListener() {
          @Override
          public void closeTab(ObjectResultTabController toClose) {
            onCloseTab(toClose);
          }
        };

    String hqlQuery = queryListCreator.getHqlQuery();
    int maxNumResults = queryListCreator.getMaxNumResults();
    HibernateConnection con = queryListCreator.getConnection();

    ObjectResultTabController ortc =
        new ObjectResultTabController(list, maxNumResults, con, hqlQuery, _resource, l, _session);
    int titelLen = Math.min(hqlQuery.length(), 14);
    String title = hqlQuery.trim().substring(0, titelLen).replaceAll("\n", " ");
    _objectResultTabbedPane.addTab(title, ortc.getPanel());
    _objectResultTabbedPane.setSelectedComponent(ortc.getPanel());
  }

  private void onCloseTab(ObjectResultTabController toClose) {
    for (int i = 0; i < _objectResultTabbedPane.getTabCount(); i++) {
      if (_objectResultTabbedPane.getComponentAt(i) == toClose.getPanel()) {
        _objectResultTabbedPane.removeTabAt(i);
        break;
      }
    }
  }
}
Ejemplo n.º 6
0
/**
 * This class manages the string bundle. It is to be used to externalize strings to ease
 * multi-lingual versions based on translation property files.
 *
 * @author Andrea Mazzolini
 * @author Rocco Rutte <a href="mailto:[email protected]">&lt;[email protected]&gt;</a>.
 */
public class Messages {

  private static final String BUNDLE_NAME = ".text";

  private static ResourceBundle[] resources = null;

  private static final ILogger logger = LoggerController.createLogger(Messages.class);

  private Messages() {}

  /**
   * Get a translated string given its key. This loads the property file for sqlexplorer as well as
   * all known extensions prior to first lookup.
   *
   * @param key Message's key.
   * @return The translated message or !key! if not found.
   */
  public static String getString(String key) {
    init();
    for (int i = 0; i < resources.length; i++) {

      try {
        if (resources[i] != null) return resources[i].getString(key);
      } catch (MissingResourceException e) {
        // noop
      }
    }

    return '!' + key + '!';
  }

  /**
   * Get the translated string given its key and required parameter. This is wrapper around the java
   * {@link MessageFormat} API.
   *
   * @param key Message's key.
   * @param param The message's only parameter.
   * @return The translated message or !key! if not found.
   * @see MessageFormat#format(String, Object[])
   */
  public static String getString(String key, Object param) {
    return getString(key, new Object[] {param});
  }

  /**
   * Get the translated string given its key and required parameters. This is wrapper around the
   * java {@link MessageFormat} API.
   *
   * @param key Message's key.
   * @param params The message's parameters.
   * @return The translated message or !key! if not found.
   * @see MessageFormat#format(String, Object[])
   */
  public static String getString(String key, Object[] params) {
    String pattern = getString(key);
    return MessageFormat.format(pattern, params);
  }

  /**
   * Parse some input string and translate all special tokens. This is similar to a string template
   * engine: it extracts <em>key</em> from <tt>${key}</tt> sequences in the input string and uses
   * {@link #getString(String)} to translate it. Contents in between <tt>${}</tt> sequences are
   * copied as-is.
   *
   * @param input The input string.
   * @return String with all special tokens replaced.
   */
  public static String processTemplate(String input) {
    if (input == null || input.trim().length() == 0) return input;
    StringBuilder sb = new StringBuilder();
    int last = 0;
    for (int i = 0; i < input.length(); i++) {
      if (i <= input.length() - 3 && input.charAt(i) == '$' && input.charAt(i + 1) == '{') {
        int j;
        for (j = i + 2; j < input.length() && input.charAt(j) != '}'; j++) ;
        if (i > 0) sb.append(input.substring(last, i));
        String key = input.substring(i + 2, j);
        String text = null;
        try {
          text = getString(key);
        } catch (Exception e) {
        }
        if (text == null || text.contains(key)) {
          logger.error("Failed to lookup key [" + key + "]");
          sb.append("${" + key + "}");
        } else sb.append(text);
        last = j + 1;
      }
    }
    sb.append(input.substring(last));
    logger.debug("Template string [" + input + "] turns out as [" + sb + "]");
    return sb.toString();
  }

  private static void init() {
    if (resources == null) {

      // initialize resources

      Bundle mainPlugin = SQLExplorerPlugin.getDefault().getBundle();
      Bundle[] fragments = Platform.getFragments(mainPlugin);

      if (fragments == null) {
        fragments = new Bundle[0];
      }

      resources = new ResourceBundle[fragments.length + 1];

      resources[0] = ResourceBundle.getBundle(mainPlugin.getSymbolicName() + BUNDLE_NAME);

      for (int i = 0; i < fragments.length; i++) {
        try {
          resources[i + 1] = ResourceBundle.getBundle(fragments[i].getSymbolicName() + BUNDLE_NAME);
        } catch (Exception ignored) {
          // ignore it
        }
      }
    }
  }
}
Ejemplo n.º 7
0
/**
 * This action will validate the current SQL.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class ValidateSQLAction extends SquirrelAction implements ISessionAction {
  /** Logger for this class. */
  private static final ILogger s_log = LoggerController.createLogger(ValidateSQLAction.class);

  /** Preferences. */
  private final WebServicePreferences _prefs;

  /** Current plugin. */
  private final SQLValidatorPlugin _plugin;

  /** Current session. */
  private ISession _session;

  /**
   * Ctor.
   *
   * @param app Application API.
   * @param rsrc Resources to build this action from.
   * @param prefs Plugin preferences.
   * @param plugin Plugin
   * @throws IllegalArgumentException Thrown if <TT>null</TT>WebServicePreferences</TT> passed.
   */
  public ValidateSQLAction(
      IApplication app, Resources rsrc, WebServicePreferences prefs, SQLValidatorPlugin plugin) {
    super(app, rsrc);
    if (prefs == null) {
      throw new IllegalArgumentException("WebServicePreferences == null");
    }
    _prefs = prefs;
    _plugin = plugin;
  }

  public void actionPerformed(ActionEvent evt) {
    if (_session != null) {
      final WebServiceSessionProperties sessionProps =
          _plugin.getWebServiceSessionProperties(_session);
      if (!sessionProps.getWebServiceSession().isOpen()) {
        JDialog dlog = new LogonDialog(_session, _prefs, sessionProps);
        dlog.setVisible(true);
      }

      if (sessionProps.getWebServiceSession().isOpen()) {
        validateSQL();
      }
    }
  }

  public void setSession(ISession session) {
    _session = session;
  }

  private void validateSQL() {
    final ISQLPanelAPI api = _session.getSQLPanelAPI(_plugin);
    final String sql = api.getSQLScriptToBeExecuted();
    if (sql != null && sql.trim().length() > 0) {
      final SessionProperties sessionProps = _session.getProperties();
      final WebServiceSessionProperties wssProps = _plugin.getWebServiceSessionProperties(_session);
      final char stmtSepChar = sessionProps.getSQLStatementSeparatorChar();
      final String solComment = sessionProps.getStartOfLineComment();
      final ValidationProps valProps =
          new ValidationProps(
              _prefs, wssProps, _session.getMessageHandler(), sql, stmtSepChar, solComment);
      new Executor(_session.getApplication(), valProps).execute();
    } else {
      _session.getMessageHandler().showErrorMessage("No SQL specified");
    }
  }

  static final class ValidationProps {
    final WebServicePreferences _prefs;
    final WebServiceSessionProperties _sessionProps;
    final IMessageHandler _msgHandler;
    final String _sql;
    final char _stmtSepChar;
    final String _solComment;

    ValidationProps(
        WebServicePreferences prefs,
        WebServiceSessionProperties sessionProps,
        IMessageHandler msgHandler,
        String sql,
        char stmtSepChar,
        String solComment) {
      super();
      _prefs = prefs;
      _sessionProps = sessionProps;
      _msgHandler = msgHandler;
      _sql = sql;
      _stmtSepChar = stmtSepChar;
      _solComment = solComment;
    }
  }

  static class Executor implements ICommand {
    private final IApplication _app;
    private final ValidationProps _valProps;

    Executor(IApplication app, ValidationProps valProps) {
      super();
      _app = app;
      _valProps = valProps;
    }

    public void execute() {
      ValidateSQLCommand cmd =
          new ValidateSQLCommand(
              _valProps._prefs,
              _valProps._sessionProps,
              _valProps._sql,
              _valProps._stmtSepChar,
              _valProps._solComment);
      try {
        cmd.execute();
        _valProps._msgHandler.showMessage(cmd.getResults());
      } catch (Throwable th) {
        final String msg = "Error occured when talking to the web service";
        s_log.error(msg, th);
        _app.showErrorDialog(msg, th);
      }
    }
  }
}
/**
 * This is intended to be used by plugins that have custom IQueryTokenizers, which require some sort
 * of preference management (loading/storing preference beans to XML file and vice-versa). In
 * reality this functionality isn't specific to IQueryTokenizer preferences, so this could be
 * further generalized. There is no need at the present time to do so.
 *
 * @author manningr
 */
public class PluginQueryTokenizerPreferencesManager {

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

  /** Name of preferences file. */
  private static final String USER_PREFS_FILE_NAME = "prefs.xml";

  /** Folder to store user settings in. */
  private FileWrapper _userSettingsFolder;

  /** The bean we will be loading from / storing to */
  private IQueryTokenizerPreferenceBean _prefs = null;

  /** Tells us which file to store the preference bean in */
  private IPlugin plugin = null;

  /** whether or not we've been initialized */
  private boolean _initialized = false;

  /**
   * factory for creating FileWrappers which insulate the application from direct reference to File
   */
  private static FileWrapperFactory fileWrapperFactory = new FileWrapperFactoryImpl();

  /** Constructor. */
  public PluginQueryTokenizerPreferencesManager() {
    /* Do Nothing */
  }

  /**
   * Initializes this preference manager. This must be done prior to calling other methods.
   *
   * @param thePlugin which plugin we are handling preferences for.
   * @param defaultPrefsBean the bean to use if no preference file currently exists.
   * @throws PluginException
   */
  public void initialize(IPlugin thePlugin, IQueryTokenizerPreferenceBean defaultPrefsBean)
      throws PluginException {
    if (thePlugin == null) {
      throw new IllegalArgumentException("IPlugin arguement cannot be null");
    }
    if (defaultPrefsBean == null) {
      throw new IllegalArgumentException("IQueryTokenizerPreferenceBean arguement cannot be null");
    }
    plugin = thePlugin;

    // Folder to store user settings.
    try {
      _userSettingsFolder = plugin.getPluginUserSettingsFolder();
    } catch (IOException ex) {
      throw new PluginException(ex);
    }
    _prefs = defaultPrefsBean;
    loadPrefs();
    _initialized = true;
  }

  /**
   * Returns the preferences bean that this class manages.
   *
   * @return an implementation instance of IQueryTokenizerPreferenceBean
   */
  public IQueryTokenizerPreferenceBean getPreferences() {
    if (!_initialized) {
      throw new IllegalStateException("initialize() must be called first");
    }
    return _prefs;
  }

  /** Saves the preferences */
  public void unload() {
    savePrefs();
  }

  /** Save preferences to disk. */
  public void savePrefs() {
    if (!_initialized) {
      throw new IllegalStateException("initialize() must be called first");
    }
    try {
      XMLBeanWriter wtr = new XMLBeanWriter(_prefs);
      wtr.save(fileWrapperFactory.create(_userSettingsFolder, USER_PREFS_FILE_NAME));
    } catch (Exception ex) {
      s_log.error("Error occurred writing to preferences file: " + USER_PREFS_FILE_NAME, ex);
    }
  }

  /** @param fileWrapperFactory the fileWrapperFactory to set */
  public void setFileWrapperFactory(FileWrapperFactory fileWrapperFactory) {
    Utilities.checkNull("setFileWrapperFactory", "fileWrapperFactory", fileWrapperFactory);
    PluginQueryTokenizerPreferencesManager.fileWrapperFactory = fileWrapperFactory;
  }

  /** Load from preferences file. */
  private void loadPrefs() {
    try {
      XMLBeanReader doc = new XMLBeanReader();

      FileWrapper prefFile = PreferenceUtil.getPreferenceFileToReadFrom(plugin);

      doc.load(prefFile, _prefs.getClass().getClassLoader());

      Iterator<Object> it = doc.iterator();

      if (it.hasNext()) {
        _prefs = (IQueryTokenizerPreferenceBean) it.next();
      }
    } catch (FileNotFoundException ignore) {
      s_log.info(USER_PREFS_FILE_NAME + " not found - will be created");
    } catch (Exception ex) {
      s_log.error("Error occurred reading from preferences file: " + USER_PREFS_FILE_NAME, ex);
    }

    _prefs.setClientName(Version.getApplicationName() + "/" + plugin.getDescriptiveName());
    _prefs.setClientVersion(Version.getShortVersion() + "/" + plugin.getVersion());
  }
}
Ejemplo n.º 9
0
/** @author <A HREF="mailto:[email protected]">Colin Bell</A> */
public class ToolBar extends JToolBar {
  /** Logger for this class. */
  private static ILogger s_log = LoggerController.createLogger(ToolBar.class);

  public ToolBar() {
    super();
  }

  public ToolBar(int orientation) {
    super(orientation);
  }

  public JButton add(Action action) {
    JButton btn = super.add(action);
    initialiseButton(action, btn);
    return btn;
  }

  public JToggleButton addToggleAction(IToggleAction action) {
    JToggleButton tglBtn = new JToggleButton();
    tglBtn.setAction(action);
    super.add(tglBtn);
    action.getToggleComponentHolder().addToggleableComponent(tglBtn);
    initialiseButton(action, tglBtn);
    return tglBtn;
  }

  public AbstractButton add(Action action, AbstractButton btn) {
    btn.setAction(action);
    super.add(btn);
    initialiseButton(action, btn);
    return btn;
  }

  public void setUseRolloverButtons(boolean value) {
    putClientProperty("JToolBar.isRollover", value ? Boolean.TRUE : Boolean.FALSE);
  }

  protected void initialiseButton(Action action, AbstractButton btn) {
    if (btn != null) {
      btn.setRequestFocusEnabled(false);
      btn.setText("");
      String tt = null;
      if (action != null) {
        tt = (String) action.getValue(Action.SHORT_DESCRIPTION);
      }
      btn.setToolTipText(tt != null ? tt : "");
      if (action != null) {
        Icon icon = getIconFromAction(action, BaseAction.IBaseActionPropertyNames.ROLLOVER_ICON);
        if (icon != null) {
          btn.setRolloverIcon(icon);
          btn.setRolloverSelectedIcon(icon);
        }
        icon = getIconFromAction(action, BaseAction.IBaseActionPropertyNames.DISABLED_ICON);
        if (icon != null) {
          btn.setDisabledIcon(icon);
        }
      }
    }
  }

  /**
   * Retrieve an icon from the passed action for the specified key.
   *
   * @param action Action to retrieve icon from.
   * @param key Key that specified the icon.
   * @return The requested Icon or null.
   */
  protected Icon getIconFromAction(Action action, String key) {
    // Object obj = action.getValue(BaseAction.IBaseActionPropertyNames.ROLLOVER_ICON);
    Object obj = action.getValue(key);
    if (obj != null) {
      if (obj instanceof Icon) {
        return (Icon) obj;
      }
      StringBuffer msg = new StringBuffer();
      msg.append("Non icon object of type ")
          .append(obj.getClass().getName())
          .append(" was stored in an Action of type ")
          .append(action.getClass().getName())
          .append(" using the key ")
          .append(key)
          .append(".");
      s_log.error(msg.toString());
    }
    return null;
  }
}
/** This is the panel where SQL scripts are executed and results presented. */
public class SQLResultExecuterPanel extends JPanel implements ISQLResultExecuter {
  /** Logger for this class. */
  private static final ILogger s_log = LoggerController.createLogger(SQLResultExecuterPanel.class);

  /** Internationalized strings for this class. */
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(SQLResultExecuterPanel.class);

  static interface i18n {
    // i18n[SQLResultExecuterPanel.exec=Executing SQL]
    String EXEC_SQL_MSG = s_stringMgr.getString("SQLResultExecuterPanel.exec");
    // i18n[SQLResultExecuterPanel.cancelMsg=Press Cancel to Stop]
    String CANCEL_SQL_MSG = s_stringMgr.getString("SQLResultExecuterPanel.cancelMsg");
  }

  private ISession _session;

  private MyPropertiesListener _propsListener;

  /** Each tab is a <TT>ResultTab</TT> showing the results of a query. */
  private JTabbedPane _tabbedExecutionsPanel;

  private ArrayList<ResultFrame> _sqlResultFrames = new ArrayList<ResultFrame>();

  /** Listeners */
  private EventListenerList _listeners = new EventListenerList();

  private ResultTabFactory _resultTabFactory;

  private IResultTab _stickyTab;

  /**
   * Ctor.
   *
   * @param session Current session.
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>ISession</TT> passed.
   */
  public SQLResultExecuterPanel(ISession session) {
    _resultTabFactory = new ResultTabFactory(session, createSQLResultExecuterPanelFacade());
    setSession(session);
    createGUI();
    propertiesHaveChanged(null);
  }

  private SQLResultExecuterPanelFacade createSQLResultExecuterPanelFacade() {
    return new SQLResultExecuterPanelFacade() {
      @Override
      public void closeResultTab(ResultTab resultTab) {
        SQLResultExecuterPanel.this.closeResultTab(resultTab);
      }

      @Override
      public void returnToTabbedPane(ResultTab resultTab) {
        SQLResultExecuterPanel.this.returnToTabbedPane(resultTab);
      }

      @Override
      public void createSQLResultFrame(ResultTab resultTab) {
        SQLResultExecuterPanel.this.createSQLResultFrame(resultTab);
      }

      @Override
      public void rerunSQL(String sql, IResultTab resultTab) {
        SQLResultExecuterPanel.this.rerunSQL(sql, resultTab);
      }

      @Override
      public void removeErrorPanel(ErrorPanel errorPanel) {
        SQLResultExecuterPanel.this.removeErrorPanel(errorPanel);
      }
    };
  }

  public String getTitle() {
    // i18n[SQLResultExecuterPanel.title=Results]
    return s_stringMgr.getString("SQLResultExecuterPanel.title");
  }

  public JComponent getComponent() {
    return this;
  }

  /**
   * Set the current session.
   *
   * @param session Current session.
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>ISession</TT> passed.
   */
  public synchronized void setSession(ISession session) {
    if (session == null) {
      throw new IllegalArgumentException("Null ISession passed");
    }
    sessionClosing();
    _session = session;
    _propsListener = new MyPropertiesListener();
    _session.getProperties().addPropertyChangeListener(_propsListener);
  }

  /** Current session. */
  public ISession getSession() {
    return _session;
  }

  /**
   * Add a listener listening for SQL Execution.
   *
   * @param lis Listener
   * @throws IllegalArgumentException If a null <TT>ISQLExecutionListener</TT> passed.
   */
  public synchronized void addSQLExecutionListener(ISQLExecutionListener lis) {
    if (lis == null) {
      throw new IllegalArgumentException("ISQLExecutionListener == null");
    }
    _listeners.add(ISQLExecutionListener.class, lis);
  }

  /**
   * Remove an SQL execution listener.
   *
   * @param lis Listener
   * @throws IllegalArgumentException If a null <TT>ISQLExecutionListener</TT> passed.
   */
  public synchronized void removeSQLExecutionListener(ISQLExecutionListener lis) {
    if (lis == null) {
      throw new IllegalArgumentException("ISQLExecutionListener == null");
    }
    _listeners.remove(ISQLExecutionListener.class, lis);
  }

  public void execute(ISQLEntryPanel sqlPanel) {
    removeErrorPanels();

    String sql = sqlPanel.getSQLToBeExecuted();
    if (sql != null && sql.length() > 0) {
      executeSQL(sql);
    } else {
      // i18n[SQLResultExecuterPanel.nosqlselected=No SQL selected for execution.]
      String msg = s_stringMgr.getString("SQLResultExecuterPanel.nosqlselected");
      _session.showErrorMessage(msg);
    }
  }

  private void removeErrorPanels() {
    ArrayList<ErrorPanel> toRemove = new ArrayList<ErrorPanel>();

    for (int i = 0; i < _tabbedExecutionsPanel.getTabCount(); i++) {
      Component tab = _tabbedExecutionsPanel.getComponentAt(i);
      if (tab instanceof ErrorPanel) {
        toRemove.add((ErrorPanel) tab);
      }
    }

    for (ErrorPanel errorPanel : toRemove) {
      closeTab(errorPanel);
    }
  }

  public void executeSQL(String sql) {
    if (sql != null && sql.trim().length() > 0) {
      removeErrorPanels();

      String origSQL = sql;
      sql = fireSQLToBeExecutedEvent(sql);

      // This can happen if an impl of ISQLExecutionListener returns null
      // from the statementExecuting API method, to indicate that the SQL
      // shouldn't be executed.
      if (sql == null) {
        s_log.info(
            "executeSQL: An ISQLExecutionListener veto'd execution of "
                + "the following SQL: "
                + origSQL);
        return;
      }

      ISQLExecutionListener[] executionListeners =
          _listeners.getListeners(ISQLExecutionListener.class);

      ISQLExecutionHandlerListener executionHandlerListener = createSQLExecutionHandlerListener();

      new SQLExecutionHandler(
          (IResultTab) null, _session, sql, executionHandlerListener, executionListeners);
    }
  }

  private ISQLExecutionHandlerListener createSQLExecutionHandlerListener() {
    return new ISQLExecutionHandlerListener() {
      @Override
      public void addResultsTab(
          SQLExecutionInfo info,
          ResultSetDataSet rsds,
          ResultSetMetaDataDataSet rsmdds,
          IDataSetUpdateableTableModel model,
          IResultTab resultTabToReplace) {
        onAddResultsTab(info, rsds, rsmdds, model, resultTabToReplace);
      }

      @Override
      public void removeCancelPanel(
          CancelPanelCtrl cancelPanelCtrl, IResultTab resultTabToReplace) {
        onRemoveCancelPanel(cancelPanelCtrl, resultTabToReplace);
      }

      @Override
      public void setCancelPanel(CancelPanelCtrl cancelPanelCtrl) {
        onSetCancelPanel(cancelPanelCtrl);
      }

      @Override
      public void displayErrors(ArrayList<String> sqlExecErrorMsgs, String lastExecutedStatement) {
        onDisplayErrors(sqlExecErrorMsgs, lastExecutedStatement);
      }
    };
  }

  private void onDisplayErrors(
      final ArrayList<String> sqlExecErrorMsgs, final String lastExecutedStatement) {
    Runnable runnable =
        new Runnable() {
          public void run() {
            showErrorPanel(sqlExecErrorMsgs, lastExecutedStatement);
          }
        };

    SwingUtilities.invokeLater(runnable);
  }

  private void showErrorPanel(ArrayList<String> sqlExecErrorMsgs, String lastExecutedStatement) {
    ErrorPanel errorPanel =
        _resultTabFactory.createErrorPanel(sqlExecErrorMsgs, lastExecutedStatement);
    _tabbedExecutionsPanel.add(
        s_stringMgr.getString("SQLResultExecuterPanel.ErrorTabHeader"), errorPanel);
    _tabbedExecutionsPanel.setSelectedComponent(errorPanel);
  }

  private void removeErrorPanel(ErrorPanel errorPanel) {
    _tabbedExecutionsPanel.remove(errorPanel);
  }

  private void rerunSQL(String sql, IResultTab resultTab) {
    new SQLExecutionHandler(
        resultTab,
        _session,
        sql,
        createSQLExecutionHandlerListener(),
        new ISQLExecutionListener[0]);
  }

  /** Close all the Results frames. */
  public synchronized void closeAllSQLResultFrames() {
    for (ResultFrame sqlResultFrame : _sqlResultFrames) {
      sqlResultFrame.dispose();
    }
  }

  /** Close all the Results tabs. */
  public synchronized void closeAllSQLResultTabs() {
    ArrayList<Component> allTabs = getAllTabs();

    for (Component tab : allTabs) {
      closeTab(tab);
    }
  }

  private ArrayList<Component> getAllTabs() {
    ArrayList<Component> allTabs = new ArrayList<Component>();
    for (int i = 0; i < _tabbedExecutionsPanel.getTabCount(); i++) {
      Component component = _tabbedExecutionsPanel.getComponentAt(i);
      if (false == component instanceof CancelPanel) {
        allTabs.add(component);
      }
    }
    return allTabs;
  }

  public synchronized void closeAllButCurrentResultTabs() {
    Component selectedTab = _tabbedExecutionsPanel.getSelectedComponent();

    ArrayList<Component> allTabs = getAllTabs();

    for (Component tab : allTabs) {
      if (tab != selectedTab) {
        closeTab(tab);
      }
    }
  }

  public synchronized void toggleCurrentSQLResultTabSticky() {
    if (null != _stickyTab) {
      if (_stickyTab.equals(_tabbedExecutionsPanel.getSelectedComponent())) {
        // Sticky is turned off. Just remove sticky and return.
        _stickyTab = null;
        _tabbedExecutionsPanel.setIconAt(_tabbedExecutionsPanel.getSelectedIndex(), null);
        return;

      } else {
        // remove old sticky tab
        int indexOfStickyTab = getIndexOfTab(_stickyTab);
        if (-1 != indexOfStickyTab) {
          _tabbedExecutionsPanel.setIconAt(indexOfStickyTab, null);
        }
        _stickyTab = null;
      }
    }

    if (false == _tabbedExecutionsPanel.getSelectedComponent() instanceof IResultTab) {
      // i18n[SQLResultExecuterPanel.nonStickyPanel=Cannot make a cancel panel sticky]
      String msg = s_stringMgr.getString("SQLResultExecuterPanel.nonStickyPanel");
      JOptionPane.showMessageDialog(_session.getApplication().getMainFrame(), msg);
      return;
    }

    _stickyTab = (IResultTab) _tabbedExecutionsPanel.getSelectedComponent();
    int selectedIndex = _tabbedExecutionsPanel.getSelectedIndex();

    ImageIcon icon = getStickyIcon();

    _tabbedExecutionsPanel.setIconAt(selectedIndex, icon);
  }

  private ImageIcon getStickyIcon() {
    ActionCollection actionCollection = _session.getApplication().getActionCollection();

    ImageIcon icon =
        (ImageIcon)
            actionCollection
                .get(ToggleCurrentSQLResultTabStickyAction.class)
                .getValue(Action.SMALL_ICON);
    return icon;
  }

  private int getIndexOfTab(IResultTab resultTab) {
    return getIndexOfTab((JComponent) resultTab);
  }

  private int getIndexOfTab(JComponent tab) {
    if (null == tab) {
      return -1;
    }

    for (int i = 0; i < _tabbedExecutionsPanel.getTabCount(); i++) {
      if (tab == _tabbedExecutionsPanel.getComponentAt(i)) {
        return i;
      }
    }
    return -1;
  }

  public synchronized void closeCurrentResultTab() {
    Component selectedTab = _tabbedExecutionsPanel.getSelectedComponent();
    closeTab(selectedTab);
  }

  /**
   * Sesssion is ending. Remove all listeners that this component has setup. Close all torn off
   * result tab windows.
   */
  void sessionClosing() {
    if (_propsListener != null) {
      _session.getProperties().removePropertyChangeListener(_propsListener);
      _propsListener = null;
    }

    closeAllSQLResultFrames();
  }

  private void closeTab(Component tab) {
    if (tab instanceof ErrorPanel) {
      _tabbedExecutionsPanel.remove(tab);
    } else if (tab instanceof ResultTab) {
      closeResultTab((ResultTab) tab);
    } else if (tab instanceof CancelPanel) {
      ((CancelPanel) tab).closeBtn.doClick();
    }
  }

  private void closeResultTab(ResultTab tab) {
    if (tab == null) {
      throw new IllegalArgumentException("Null ResultTab passed");
    }
    s_log.debug("SQLPanel.closeResultTab(" + tab.getIdentifier().toString() + ")");
    tab.closeTab();
    _tabbedExecutionsPanel.remove(tab);
  }

  /** Display the next tab in the SQL results. */
  public void gotoNextResultsTab() {
    final int tabCount = _tabbedExecutionsPanel.getTabCount();
    if (tabCount > 1) {
      int nextTabIdx = _tabbedExecutionsPanel.getSelectedIndex() + 1;
      if (nextTabIdx >= tabCount) {
        nextTabIdx = 0;
      }
      _tabbedExecutionsPanel.setSelectedIndex(nextTabIdx);
    }
  }

  /** Display the previous tab in the SQL results. */
  public void gotoPreviousResultsTab() {
    final int tabCount = _tabbedExecutionsPanel.getTabCount();
    if (tabCount > 1) {
      int prevTabIdx = _tabbedExecutionsPanel.getSelectedIndex() - 1;
      if (prevTabIdx < 0) {
        prevTabIdx = tabCount - 1;
      }
      _tabbedExecutionsPanel.setSelectedIndex(prevTabIdx);
    }
  }

  protected String fireSQLToBeExecutedEvent(String sql) {
    // Guaranteed to be non-null.
    Object[] listeners = _listeners.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event.
    for (int i = listeners.length - 2; i >= 0; i -= 2) {
      if (listeners[i] == ISQLExecutionListener.class) {
        sql = ((ISQLExecutionListener) listeners[i + 1]).statementExecuting(sql);
        if (sql == null) {
          break;
        }
      }
    }
    return sql;
  }

  /**
   * Create an internal frame for the specified tab and display the tab in the internal frame after
   * removing it from the tabbed pane.
   *
   * @param tab <TT>ResultTab</TT> to be displayed in an internal frame.
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>ResultTab</TT> passed.
   */
  private void createSQLResultFrame(ResultTab tab) {
    if (tab == null) {
      throw new IllegalArgumentException("Null ResultTab passed");
    }
    s_log.debug("SQLPanel.createSQLResultFrame(" + tab.getIdentifier().toString() + ")");
    _tabbedExecutionsPanel.remove(tab);

    ResultFrameListener resultFrameListener =
        new ResultFrameListener() {
          @Override
          public void frameReplaced(ResultFrame oldFrame, ResultFrame newFrame) {
            onFrameReplaced(oldFrame, newFrame);
          }
        };

    ResultFrame frame =
        new ResultFrame(_session, tab, _resultTabFactory, resultFrameListener, true, false);
    _sqlResultFrames.add(frame);
  }

  private void onFrameReplaced(ResultFrame oldFrame, ResultFrame newFrame) {
    _sqlResultFrames.remove(oldFrame);
    _sqlResultFrames.add(newFrame);
  }

  /**
   * Return the passed tab back into the tabbed pane.
   *
   * @param tab <TT>Resulttab</TT> to be returned
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>ResultTab</TT> passed.
   */
  private void returnToTabbedPane(ResultTab tab) {
    if (tab == null) {
      throw new IllegalArgumentException("Null ResultTab passed");
    }

    s_log.debug("SQLPanel.returnToTabbedPane(" + tab.getIdentifier().toString() + ")");

    for (ResultFrame sqlResultFrame : _sqlResultFrames) {
      if (tab == sqlResultFrame.getTab()) {
        _sqlResultFrames.remove(sqlResultFrame);
        break;
      }
    }

    addResultsTab(tab, null);
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.client.session.mainpanel.ISQLResultExecuter#getSelectedResultTab()
   */
  public IResultTab getSelectedResultTab() {
    return (IResultTab) _tabbedExecutionsPanel.getSelectedComponent();
  }

  private void onAddResultsTab(
      final SQLExecutionInfo exInfo,
      final ResultSetDataSet rsds,
      final ResultSetMetaDataDataSet mdds,
      final IDataSetUpdateableTableModel creator,
      final IResultTab resultTabToReplace) {
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            try {
              ResultTab tab = _resultTabFactory.createResultTab(exInfo, creator, rsds, mdds);
              addResultsTab(tab, resultTabToReplace);
              _tabbedExecutionsPanel.setSelectedComponent(tab);
            } catch (Throwable t) {
              _session.showErrorMessage(t);
            }
          }
        });
  }

  private void onRemoveCancelPanel(
      final CancelPanelCtrl cancelPanelCtrl, final IResultTab resultTabToReplace) {
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            _tabbedExecutionsPanel.remove(cancelPanelCtrl.getPanel());

            int indexToSelect = -1;
            if (null == resultTabToReplace) {
              indexToSelect = getIndexOfTab(_stickyTab);
            } else {
              indexToSelect = getIndexOfTab(resultTabToReplace);
            }

            if (-1 != indexToSelect) {
              _tabbedExecutionsPanel.setSelectedIndex(indexToSelect);
            }

            cancelPanelCtrl.wasRemoved();
          }
        });
  }

  private void onSetCancelPanel(final CancelPanelCtrl cancelPanelCtrl) {
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            _tabbedExecutionsPanel.addTab(
                SQLResultExecuterPanel.i18n.EXEC_SQL_MSG,
                null,
                cancelPanelCtrl.getPanel(),
                SQLResultExecuterPanel.i18n.CANCEL_SQL_MSG);
            _tabbedExecutionsPanel.setSelectedComponent(cancelPanelCtrl.getPanel());
          }
        });
  }

  private void addResultsTab(ResultTab tab, IResultTab resultTabToReplace) {
    if (null == resultTabToReplace && null == _stickyTab) {
      _tabbedExecutionsPanel.addTab(tab.getTitle(), null, tab, tab.getViewableSqlString());
      checkResultTabLimit();
    } else {
      if (null != resultTabToReplace && _session.getProperties().getKeepTableLayoutOnRerun()) {
        TableState sortableTableState = resultTabToReplace.getResultSortableTableState();
        if (null != sortableTableState) {
          tab.applyResultSortableTableState(sortableTableState);
        }
      }

      int indexToReplace = -1;
      ImageIcon tabIcon = null;

      // Either resultTabToReplace or _stickyTab must be not null here
      if (null != resultTabToReplace && _stickyTab != resultTabToReplace) {
        indexToReplace = getIndexOfTab(resultTabToReplace);
      } else {
        indexToReplace = getIndexOfTab(_stickyTab);
        if (-1 == indexToReplace) {
          // sticky tab was closed
          _stickyTab = null;
        } else {
          tabIcon = getStickyIcon();
          _stickyTab = tab;
        }
      }

      if (-1 == indexToReplace) {
        // Just add the tab
        addResultsTab(tab, null);
        return;
      }

      closeTabAt(indexToReplace);
      _tabbedExecutionsPanel.insertTab(
          tab.getTitle(), tabIcon, tab, tab.getViewableSqlString(), indexToReplace);
    }
  }

  private void checkResultTabLimit() {
    SessionProperties props = _session.getProperties();

    while (props.getLimitSQLResultTabs()
        && props.getSqlResultTabLimit() < _tabbedExecutionsPanel.getTabCount()) {
      if (_tabbedExecutionsPanel.getComponentAt(0) instanceof CancelPanel) {
        break;
      }
      closeTabAt(0);
    }
  }

  private void closeTabAt(int index) {
    Component selectedTab = _tabbedExecutionsPanel.getComponentAt(index);
    closeTab(selectedTab);
  }

  private void propertiesHaveChanged(String propName) {
    final SessionProperties props = _session.getProperties();

    if (propName == null || propName.equals(SessionProperties.IPropertyNames.AUTO_COMMIT)) {
      SetAutoCommitTask task = new SetAutoCommitTask();
      if (SwingUtilities.isEventDispatchThread()) {
        _session.getApplication().getThreadPool().addTask(task);
      } else {
        task.run();
      }
    }

    if (propName == null
        || propName.equals(SessionProperties.IPropertyNames.SQL_EXECUTION_TAB_PLACEMENT)) {
      _tabbedExecutionsPanel.setTabPlacement(props.getSQLExecutionTabPlacement());
    }
  }

  private class SetAutoCommitTask implements Runnable {

    public void run() {
      final ISQLConnection conn = _session.getSQLConnection();
      final SessionProperties props = _session.getProperties();
      if (conn != null) {
        boolean auto = true;
        try {
          auto = conn.getAutoCommit();
        } catch (SQLException ex) {
          s_log.error("Error with transaction control", ex);
          _session.showErrorMessage(ex);
        }
        try {
          conn.setAutoCommit(props.getAutoCommit());
        } catch (SQLException ex) {
          props.setAutoCommit(auto);
          _session.showErrorMessage(ex);
        }
      }
    }
  }

  private void createGUI() {
    final SessionProperties props = _session.getProperties();
    _tabbedExecutionsPanel =
        UIFactory.getInstance().createTabbedPane(props.getSQLExecutionTabPlacement(), true);

    createTabPopup();

    setLayout(new BorderLayout());

    add(_tabbedExecutionsPanel, BorderLayout.CENTER);
  }

  /**
   * Due to JDK 1.4 Bug 4465870 this doesn't work with JDK 1.4. when scrollable tabbed pane is used.
   */
  private void createTabPopup() {
    final JPopupMenu popup = new JPopupMenu();

    // i18n[SQLResultExecuterPanel.close=Close]
    String closeLabel = s_stringMgr.getString("SQLResultExecuterPanel.close");
    JMenuItem mnuClose = new JMenuItem(closeLabel);
    initAccelerator(CloseCurrentSQLResultTabAction.class, mnuClose);
    mnuClose.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            closeCurrentResultTab();
          }
        });
    popup.add(mnuClose);

    // i18n[SQLResultExecuterPanel.closeAllButThis=Close all but this]
    String cabtLabel = s_stringMgr.getString("SQLResultExecuterPanel.closeAllButThis");
    JMenuItem mnuCloseAllButThis = new JMenuItem(cabtLabel);
    initAccelerator(CloseAllSQLResultTabsButCurrentAction.class, mnuCloseAllButThis);
    mnuCloseAllButThis.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            closeAllButCurrentResultTabs();
          }
        });
    popup.add(mnuCloseAllButThis);

    // i18n[SQLResultExecuterPanel.closeAll=Close all]
    String caLabel = s_stringMgr.getString("SQLResultExecuterPanel.closeAll");
    JMenuItem mnuCloseAll = new JMenuItem(caLabel);
    initAccelerator(CloseAllSQLResultTabsAction.class, mnuCloseAll);
    mnuCloseAll.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            closeAllSQLResultTabs();
          }
        });
    popup.add(mnuCloseAll);

    // i18n[SQLResultExecuterPanel.toggleSticky=Toggle sticky]
    String tsLabel = s_stringMgr.getString("SQLResultExecuterPanel.toggleSticky");
    JMenuItem mnuToggleSticky = new JMenuItem(tsLabel);
    initAccelerator(ToggleCurrentSQLResultTabStickyAction.class, mnuToggleSticky);
    mnuToggleSticky.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            toggleCurrentSQLResultTabSticky();
          }
        });
    popup.add(mnuToggleSticky);

    _tabbedExecutionsPanel.addMouseListener(
        new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            maybeShowPopup(e, popup);
          }

          public void mouseReleased(MouseEvent e) {
            maybeShowPopup(e, popup);
          }
        });
  }

  private void initAccelerator(Class<? extends Action> actionClass, JMenuItem mnuItem) {
    Action action = _session.getApplication().getActionCollection().get(actionClass);

    String accel = (String) action.getValue(Resources.ACCELERATOR_STRING);
    if (null != accel && 0 != accel.trim().length()) {
      mnuItem.setAccelerator(KeyStroke.getKeyStroke(accel));
    }
  }

  private void maybeShowPopup(MouseEvent e, JPopupMenu popup) {
    if (e.isPopupTrigger()) {
      int tab =
          _tabbedExecutionsPanel
              .getUI()
              .tabForCoordinate(_tabbedExecutionsPanel, e.getX(), e.getY());
      if (-1 != tab) {
        popup.show(e.getComponent(), e.getX(), e.getY());
      }
    }
  }

  private class MyPropertiesListener implements PropertyChangeListener {
    private boolean _listening = true;

    void stopListening() {
      _listening = false;
    }

    void startListening() {
      _listening = true;
    }

    public void propertyChange(PropertyChangeEvent evt) {
      if (_listening) {
        propertiesHaveChanged(evt.getPropertyName());
      }
    }
  }

  private static final class ResultTabInfo {
    final ResultTab _tab;
    ResultFrame _resultFrame;

    ResultTabInfo(ResultTab tab) {
      if (tab == null) {
        throw new IllegalArgumentException("Null ResultTab passed");
      }
      _tab = tab;
    }
  }
}
Ejemplo n.º 11
0
/**
 * This class manages the windows relating to JDBC drivers.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 * @author <A HREF="mailto:[email protected]">Jason Height</A>
 */
public class DriverWindowManager {
  /** Logger for this class. */
  private static final ILogger s_log = LoggerController.createLogger(WindowManager.class);

  /** Application API. */
  private final IApplication _app;

  /** Window Factory for driver maintenace windows. */
  private final DriverWindowFactory _driverWinFactory;

  /**
   * Ctor.
   *
   * @param app Application API.
   * @throws IllegalArgumentException Thrown if <TT>null</TT> <TT>IApplication</TT> passed.
   */
  public DriverWindowManager(IApplication app) {
    super();
    if (app == null) {
      throw new IllegalArgumentException("IApplication == null");
    }

    _app = app;
    _driverWinFactory = new DriverWindowFactory(_app);
  }

  /**
   * Get a maintenance sheet for the passed driver. If a maintenance sheet already exists it will be
   * brought to the front. If one doesn't exist it will be created.
   *
   * @param driver The driver that user has requested to modify.
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>ISQLDriver</TT> passed.
   */
  public void showModifyDriverInternalFrame(final ISQLDriver driver) {
    if (driver == null) {
      throw new IllegalArgumentException("ISQLDriver == null");
    }

    _driverWinFactory.getModifySheet(driver).moveToFront();
  }

  /** Create and show a new maintenance window to allow the user to create a new driver. */
  public void showNewDriverInternalFrame() {
    _driverWinFactory.getCreateSheet().moveToFront();
  }

  /**
   * Create and show a new maintenance sheet that will allow the user to create a new driver that is
   * a copy of the passed one.
   *
   * @return The new maintenance sheet.
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>ISQLDriver</TT> passed.
   */
  public void showCopyDriverInternalFrame(final ISQLDriver driver) {
    if (driver == null) {
      throw new IllegalArgumentException("ISQLDriver == null");
    }

    _driverWinFactory.showCopySheet(driver).moveToFront();
  }

  public void moveToFront(final JInternalFrame fr) {
    if (fr != null) {
      GUIUtils.processOnSwingEventThread(
          new Runnable() {
            public void run() {
              GUIUtils.moveToFront(fr);
            }
          });
    } else {
      s_log.debug("JInternalFrame == null");
    }
  }
}
Ejemplo n.º 12
0
/**
 * This is the tab showing the contents (data) of the table.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class ContentsTab extends BaseTableTab implements IDataSetUpdateableTableModel {
  private DataSetUpdateableTableModelImpl _dataSetUpdateableTableModel =
      new DataSetUpdateableTableModelImpl();

  /** Internationalized strings for this class. */
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(ContentsTab.class);

  /**
   * Name of the table that this tab displayed last time it was loaded. This is needed to prevent an
   * on-demand edit operation from turning all data into editable tables. The initial value of ""
   * allows us to dispense with a check for null on the first pass.
   */
  String previousTableName = "";

  private SQLFilterClauses _sqlFilterClauses = new SQLFilterClauses();

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

  private ObjectTreePanel _treePanel = null;

  private PleaseWaitDialog _waitDialog = null;

  private SquirrelPreferences _prefs = null;

  public ContentsTab(ObjectTreePanel treePanel) {
    _treePanel = treePanel;
    _prefs = _treePanel.getSession().getApplication().getSquirrelPreferences();
  }

  /**
   * Return the title for the tab.
   *
   * @return The title for the tab.
   */
  public String getTitle() {
    return getContentsTabTitle();
  }

  /**
   * Return the title for the tab.
   *
   * @return The title for the tab.
   */
  public static String getContentsTabTitle() {
    // i18n[ContentsTab.title=Content]
    return s_stringMgr.getString("ContentsTab.title");
  }

  /**
   * Return the hint for the tab.
   *
   * @return The hint for the tab.
   */
  public String getHint() {
    // i18n[ContentsTab.hint=View the contents of the selected table]
    return s_stringMgr.getString("ContentsTab.hint");
  }

  public SQLFilterClauses getSQLFilterClauses() {
    return _sqlFilterClauses;
  }

  /** Create the <TT>IDataSet</TT> to be displayed in this tab. */
  protected IDataSet createDataSet() throws DataSetException {
    final ISession session = getSession();
    final ISQLConnection conn = session.getSQLConnection();
    ISQLDatabaseMetaData md = session.getMetaData();

    try {
      final Statement stmt = conn.createStatement();
      try {
        final SessionProperties props = session.getProperties();
        if (props.getContentsLimitRows()) {
          try {
            stmt.setMaxRows(props.getContentsNbrRowsToShow());
          } catch (Exception ex) {
            s_log.error("Error on Statement.setMaxRows()", ex);
          }
        }
        final ITableInfo ti = getTableInfo();

        /**
         * When the SessionProperties are set to read-only (either table or text) but the user has
         * selected "Make Editable" on the Popup menu, we want to limit the edit capability to only
         * that table, and only for as long as the user is looking at that one table. When the user
         * switches away to another table, that new table should not be editable.
         */
        final String currentTableName = ti.getQualifiedName();
        if (!currentTableName.equals(previousTableName)) {
          previousTableName = currentTableName; // needed to prevent an infinite loop
          _dataSetUpdateableTableModel.setEditModeForced(false);

          /**
           * Tell the GUI to rebuild itself. Unfortunately, this has the side effect of calling this
           * same function another time. The second call does not seem to be a problem, but we need
           * to have reset the previousTableName before makeing this call or we will be in an
           * infinite loop.
           */
          // props.forceTableContentsOutputClassNameChange();
        }

        /**
         * If the table has a pseudo-column that is the best unique identifier for the rows (like
         * Oracle's rowid), then we want to include that field in the query so that it will be
         * available if the user wants to edit the data later.
         */
        String pseudoColumn = "";

        try {
          BestRowIdentifier[] rowIDs = md.getBestRowIdentifier(ti);
          for (int i = 0; i < rowIDs.length; ++i) {
            short pseudo = rowIDs[i].getPseudoColumn();
            if (pseudo == DatabaseMetaData.bestRowPseudo) {
              pseudoColumn = " ," + rowIDs[i].getColumnName();
              break;
            }
          }
        }

        // Some DBMS's (EG Think SQL) throw an exception on a call to
        // getBestRowIdentifier.
        catch (Throwable th) {
          if (s_log.isDebugEnabled()) {
            s_log.debug("getBestRowIdentifier not supported for table " + currentTableName, th);
          }
        }

        // TODO: - Col - Add method to Databasemetadata that returns array
        // of objects for getBestRowIdentifier. For PostgreSQL put this kludge in
        // the new function. THis way all the kludges are kept in one place.
        //
        // KLUDGE!!!!!!
        //
        // For some DBs (e.g. PostgreSQL) there is actually a pseudo-column
        // providing the rowId, but the getBestRowIdentifier function is not
        // implemented.  This kludge hardcodes the knowledge that specific
        // DBs use a specific pseudo-column.  Additionally, as of pg 8.1,
        // you must create the table using "WITH OID" appended to the create
        // statement.  Otherwise, OID column is not available by default.
        //
        if (pseudoColumn.length() == 0) {
          if (DialectFactory.isPostgreSQL(md)) {
            pseudoColumn = ", oid";
          }
          if (DialectFactory.isOracle(md)) {
            pseudoColumn = ", ROWID";
          }
        }

        ResultSet rs = null;
        try {
          // Note. Some DBMSs such as Oracle do not allow:
          // "select *, rowid from table"
          // You cannot have any column name in the columns clause
          // if you have * in there. Aliasing the table name seems to
          // be the best way to get around the problem.
          final StringBuffer buf = new StringBuffer();
          buf.append("select tbl.*")
              .append(pseudoColumn)
              .append(" from ")
              .append(ti.getQualifiedName())
              .append(" tbl");

          String clause =
              _sqlFilterClauses.get(WhereClausePanel.getClauseIdentifier(), ti.getQualifiedName());
          if ((clause != null) && (clause.length() > 0)) {
            buf.append(" where ").append(clause);
          }
          clause =
              _sqlFilterClauses.get(
                  OrderByClausePanel.getClauseIdentifier(), ti.getQualifiedName());
          if ((clause != null) && (clause.length() > 0)) {
            buf.append(" order by ").append(clause);
          }

          if (s_log.isDebugEnabled()) {
            s_log.debug("createDataSet running SQL: " + buf.toString());
          }

          showWaitDialog(stmt);

          rs = stmt.executeQuery(buf.toString());

        } catch (SQLException ex) {
          if (s_log.isDebugEnabled()) {
            s_log.debug("createDataSet: exception from pseudocolumn query - " + ex, ex);
          }
          // We assume here that if the pseudoColumn was used in the query,
          // then it was likely to have caused the SQLException.  If not,
          // (length == 0), then retrying the query won't help - just throw
          // the exception.
          if (pseudoColumn.length() == 0) {
            throw ex;
          }
          // pseudocolumn query failed, so reset it.  Otherwise, we'll
          // mistake the last column for a pseudocolumn and make it
          // uneditable
          pseudoColumn = "";

          // Some tables have pseudo column primary keys and others
          // do not.  JDBC on some DBMSs does not handle pseudo
          // columns 'correctly'.  Also, getTables returns 'views' as
          // well as tables, so the thing we are looking at might not
          // be a table. (JDBC does not give a simple way to
          // determine what we are looking at since the type of
          // object is described in a DBMS-specific encoding.)  For
          // these reasons, rather than testing for all these
          // conditions, we just try using the pseudo column info to
          // get the table data, and if that fails, we try to get the
          // table data without using the pseudo column.
          // TODO: Should we change the mode from editable to
          // non-editable?
          final StringBuffer buf = new StringBuffer();
          buf.append("select *").append(" from ").append(ti.getQualifiedName()).append(" tbl");

          String clause =
              _sqlFilterClauses.get(WhereClausePanel.getClauseIdentifier(), ti.getQualifiedName());
          if ((clause != null) && (clause.length() > 0)) {
            buf.append(" where ").append(clause);
          }
          clause =
              _sqlFilterClauses.get(
                  OrderByClausePanel.getClauseIdentifier(), ti.getQualifiedName());
          if ((clause != null) && (clause.length() > 0)) {
            buf.append(" order by ").append(clause);
          }

          rs = stmt.executeQuery(buf.toString());
        }

        final ResultSetDataSet rsds = new ResultSetDataSet();

        // to allow the fw to save and reload user options related to
        // specific columns, we construct a unique name for the table
        // so the column can be associcated with only that table.
        // Some drivers do not provide the catalog or schema info, so
        // those parts of the name will end up as null.  That's ok since
        // this string is never viewed by the user and is just used to
        // distinguish this table from other tables in the DB.
        // We also include the URL used to connect to the DB so that
        // the same table/DB on different machines is treated differently.
        rsds.setContentsTabResultSet(
            rs, _dataSetUpdateableTableModel.getFullTableName(), DialectFactory.getDialectType(md));
        if (rs != null) {
          try {
            rs.close();
          } catch (SQLException e) {
          }
        }
        // KLUDGE:
        // We want some info about the columns to be available for validating the
        // user input during cell editing operations.  Ideally we would get that
        // info inside the ResultSetDataSet class during the creation of the
        // columnDefinition objects by using various functions in ResultSetMetaData
        // such as isNullable(idx).  Unfortunately, in at least some DBMSs (e.g.
        // Postgres, HSDB) the results of those calls are not the same (and are less accurate
        // than) the SQLMetaData.getColumns() call used in ColumnsTab to get the column info.
        // Even more unfortunate is the fact that the set of attributes reported on by the two
        // calls is not the same, with the ResultSetMetadata listing things not provided by
        // getColumns.  Most of the data provided by the ResultSetMetaData calls is correct.
        // However, the nullable/not-nullable property is not set correctly in at least two
        // DBMSs, while it is correct for those DBMSs in the getColumns() info.  Therefore,
        // we collect the collumn nullability information from getColumns() and pass that
        // info to the ResultSet to override what it got from the ResultSetMetaData.
        TableColumnInfo[] columnInfos = md.getColumnInfo(getTableInfo());
        final ColumnDisplayDefinition[] colDefs =
            rsds.getDataSetDefinition().getColumnDefinitions();

        // get the nullability information and pass it into the ResultSet
        // Unfortunately, not all DBMSs provide the column number in object 17 as stated in the
        // SQL documentation, so we have to guess that the result set is in column order
        for (int i = 0; i < columnInfos.length; i++) {
          boolean isNullable = true;
          TableColumnInfo info = columnInfos[i];
          if (info.isNullAllowed() == DatabaseMetaData.columnNoNulls) {
            isNullable = false;
          }
          if (i < colDefs.length) {
            colDefs[i].setIsNullable(isNullable);
          }
        }

        // ?? remember which column is the rowID (if any) so we can
        // ?? prevent editing on it
        if (pseudoColumn.length() > 0) {
          _dataSetUpdateableTableModel.setRowIDCol(rsds.getColumnCount() - 1);
        }

        return rsds;
      } finally {
        SQLUtilities.closeStatement(stmt);
      }

    } catch (SQLException ex) {
      throw new DataSetException(ex);
    } finally {
      disposeWaitDialog();
    }
  }

  /**
   * Returns true if the ObjectTree tab is selected.
   *
   * @return Returns true if the ObjectTree tab is selected. false is returned otherwise.
   */
  private boolean objectTreeTabIsSelected() {
    boolean result = false;
    ISession session = _treePanel.getSession();
    if (session != null) {
      SessionPanel sessionPanel = session.getSessionSheet();
      if (sessionPanel != null) {
        result = sessionPanel.isObjectTreeTabSelected();
      }
    }
    return result;
  }

  /**
   * Initialize the dialog to ask the user to wait, because the query can take a while, but only if
   * the ObjectTreeTab is selected.
   *
   * @param stmt the Statement to cancel.
   */
  private void showWaitDialog(final Statement stmt) {

    if (!_prefs.getShowPleaseWaitDialog()) return;

    // Only do this if the object tree
    // (and hence this contents tab) is visible.
    if (objectTreeTabIsSelected()) {

      // Save off selections so that selection/focus can be restored
      // later.
      _treePanel.saveSelectedPaths();

      GUIUtils.processOnSwingEventThread(
          new Runnable() {
            public void run() {
              _waitDialog = new PleaseWaitDialog(stmt, _app);
              _waitDialog.showDialog(_app);
              // Restore the paths
              _treePanel.restoreSavedSelectedPaths();
            }
          });
    }
  }

  /**
   * Hide the dialog if one is shown
   *
   * @param _waitDialog the PleaseWaitDialog to close - can be null.
   */
  private void disposeWaitDialog() {
    if (!_prefs.getShowPleaseWaitDialog()) return;
    GUIUtils.processOnSwingEventThread(
        new Runnable() {
          public void run() {
            if (_waitDialog != null) {
              _waitDialog.dispose();
            }
          }
        });
  }

  public void setDatabaseObjectInfo(IDatabaseObjectInfo value) {
    super.setDatabaseObjectInfo(value);
    _dataSetUpdateableTableModel.setTableInfo(getTableInfo());
  }

  public void setSession(ISession session) throws IllegalArgumentException {
    super.setSession(session);
    _dataSetUpdateableTableModel.setSession(session);
  }

  /**
   * return the name of the table that is unambiguous across DB accesses, including the same DB on
   * different machines. This function is static because it is used elsewhere to generate the same
   * name as is used within instances of this class.
   *
   * @return the name of the table that is unique for this DB access
   */
  public static String getUnambiguousTableName(ISession session, String name) {
    return DataSetUpdateableTableModelImpl.getUnambiguousTableName(session, name);
  }

  ////////////////////////////////////////////////////////
  // Implementataion of IDataSetUpdateableTableModel:
  // Delegation to _dataSetUpdateableTableModel
  public String getWarningOnCurrentData(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, Object oldValue) {
    return _dataSetUpdateableTableModel.getWarningOnCurrentData(values, colDefs, col, oldValue);
  }

  public String getWarningOnProjectedUpdate(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, Object newValue) {
    return _dataSetUpdateableTableModel.getWarningOnProjectedUpdate(values, colDefs, col, newValue);
  }

  public Object reReadDatum(
      Object[] values, ColumnDisplayDefinition[] colDefs, int col, StringBuffer message) {
    return _dataSetUpdateableTableModel.reReadDatum(values, colDefs, col, message);
  }

  public String updateTableComponent(
      Object[] values,
      ColumnDisplayDefinition[] colDefs,
      int col,
      Object oldValue,
      Object newValue) {
    return _dataSetUpdateableTableModel.updateTableComponent(
        values, colDefs, col, oldValue, newValue);
  }

  public int getRowidCol() {
    return _dataSetUpdateableTableModel.getRowidCol();
  }

  public String deleteRows(Object[][] rowData, ColumnDisplayDefinition[] colDefs) {
    return _dataSetUpdateableTableModel.deleteRows(rowData, colDefs);
  }

  public String[] getDefaultValues(ColumnDisplayDefinition[] colDefs) {
    return _dataSetUpdateableTableModel.getDefaultValues(colDefs);
  }

  public String insertRow(Object[] values, ColumnDisplayDefinition[] colDefs) {
    return _dataSetUpdateableTableModel.insertRow(values, colDefs);
  }

  public void addListener(DataSetUpdateableTableModelListener l) {
    _dataSetUpdateableTableModel.addListener(l);
  }

  public void removeListener(DataSetUpdateableTableModelListener l) {
    _dataSetUpdateableTableModel.removeListener(l);
  }

  public void forceEditMode(boolean mode) {
    _dataSetUpdateableTableModel.forceEditMode(mode);
  }

  public boolean editModeIsForced() {
    return _dataSetUpdateableTableModel.editModeIsForced();
  }

  public IDataModelImplementationDetails getDataModelImplementationDetails() {
    return new IDataModelImplementationDetails() {
      public String getStatementSeparator() {
        return getSession().getQueryTokenizer().getSQLStatementSeparator();
      }
    };
  }

  protected String getDestinationClassName() {
    return _dataSetUpdateableTableModel.getDestinationClassName();
  }
  //
  //////////////////////////////////////////////////////////////////////////////////
}
public class DataSetViewerTablePanel extends BaseDataSetViewerDestination
    implements IDataSetTableControls, Printable {

  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(DataSetViewerTablePanel.class);

  private ILogger s_log = LoggerController.createLogger(DataSetViewerTablePanel.class);

  private MyJTable _table = null;
  private DataSetViewerTableModel _typedModel;
  private IDataSetUpdateableModel _updateableModel;
  private DataSetViewerTableListSelectionHandler _selectionHandler;

  private IDataModelImplementationDetails _dataModelImplementationDetails =
      new IDataModelImplementationDetails() {
        @Override
        public String getStatementSeparator() {
          return ";";
        }
      };
  private ContinueReadHandler _continueReadHandler;
  private RowColSelectedCountListener _rowColSelectedCountListener;

  public DataSetViewerTablePanel() {}

  public void init(IDataSetUpdateableModel updateableModel, ISession session) {
    init(updateableModel, ListSelectionModel.SINGLE_INTERVAL_SELECTION, session);
  }

  public void init(
      IDataSetUpdateableModel updateableModel, int listSelectionMode, ISession session) {
    init(updateableModel, listSelectionMode, null, session);
  }

  public void init(
      IDataSetUpdateableModel updateableModel,
      IDataModelImplementationDetails dataModelImplementationDetails,
      ISession session) {
    init(
        updateableModel,
        ListSelectionModel.SINGLE_INTERVAL_SELECTION,
        dataModelImplementationDetails,
        session);
  }

  public void init(
      IDataSetUpdateableModel updateableModel,
      int listSelectionMode,
      IDataModelImplementationDetails dataModelImplementationDetails,
      ISession session) {
    if (null != dataModelImplementationDetails) {
      _dataModelImplementationDetails = dataModelImplementationDetails;
    }

    _table = new MyJTable(this, updateableModel, listSelectionMode, session);
    _continueReadHandler = new ContinueReadHandler(_table);
    _selectionHandler = new DataSetViewerTableListSelectionHandler(_table);
    _updateableModel = updateableModel;

    _table
        .getSelectionModel()
        .addListSelectionListener(
            new ListSelectionListener() {
              @Override
              public void valueChanged(ListSelectionEvent e) {
                onSelectionChanged();
              }
            });
  }

  private void onSelectionChanged() {
    if (null != _rowColSelectedCountListener) {
      _rowColSelectedCountListener.rowColSelectedCountChanged(
          _table.getSelectedRowCount(), _table.getSelectedColumnCount());
    }
  }

  @Override
  public void setRowColSelectedCountListener(
      RowColSelectedCountListener rowColSelectedCountListener) {
    _rowColSelectedCountListener = rowColSelectedCountListener;
  }

  public IDataSetUpdateableModel getUpdateableModel() {
    return _updateableModel;
  }

  public IDataModelImplementationDetails getDataModelImplementationDetails() {
    return _dataModelImplementationDetails;
  }

  public void clear() {
    _typedModel.clear();
    _typedModel.fireTableDataChanged();
  }

  public void setColumnDefinitions(ColumnDisplayDefinition[] colDefs) {
    super.setColumnDefinitions(colDefs);
    _table.setColumnDefinitions(colDefs);
  }

  public void moveToTop() {
    if (_table.getRowCount() > 0) {
      _table.setRowSelectionInterval(0, 0);
    }
  }

  /**
   * Get the component for this viewer.
   *
   * @return The component for this viewer.
   */
  public Component getComponent() {
    return _table;
  }

  /*
   * @see BaseDataSetViewerDestination#addRow(Object[])
   */
  protected void addRow(Object[] row) {
    _typedModel.addRow(row);
  }

  /*
   * @see BaseDataSetViewerDestination#getRow(row)
   */
  protected Object[] getRow(int row) {
    Object values[] = new Object[_typedModel.getColumnCount()];
    for (int i = 0; i < values.length; i++) values[i] = _typedModel.getValueAt(row, i);
    return values;
  }

  /*
   * @see BaseDataSetViewerDestination#allRowsAdded()
   */
  protected void allRowsAdded() {
    _typedModel.fireTableStructureChanged();
    _table.initColWidths();
  }

  /*
   * @see IDataSetViewer#getRowCount()
   */
  public int getRowCount() {
    return _typedModel.getRowCount();
  }

  public void setShowRowNumbers(boolean showRowNumbers) {
    _table.setShowRowNumbers(showRowNumbers);
  }

  public void addRowSelectionListener(RowSelectionListener rowSelectionListener) {
    _selectionHandler.addRowSelectionListener(rowSelectionListener);
  }

  public void removeRowSelectionListener(RowSelectionListener rowSelectionListener) {
    _selectionHandler.removeRowSelectionListener(rowSelectionListener);
  }

  public int[] getSeletedRows() {
    return _table.getSelectedRows();
  }

  public int[] getSeletedModelRows() {
    int[] selectedViewRows = _table.getSelectedRows();

    int[] ret = new int[selectedViewRows.length];

    for (int i = 0; i < selectedViewRows.length; i++) {
      ret[i] = (((SortableTableModel) _table.getModel()).transfromToModelRow(selectedViewRows[i]));
    }

    return ret;
  }

  public int getColumnWidthForHeader(String header) {
    TableColumnModel columnModel = _table.getColumnModel();

    for (int i = 0; i < columnModel.getColumnCount(); i++) {
      if (columnModel.getColumn(i).getHeaderValue().equals(header)) {
        return columnModel.getColumn(i).getWidth();
      }
    }

    throw new IllegalStateException("No col with header: " + header);
  }

  public FindService createFindService() {
    return new DefaultFindService(_table, getColumnDefinitions(), _table.getTypedModel());
  }

  @Override
  public void switchColumnHeader(ColumnHeaderDisplay columnHeaderDisplay) {
    for (Enumeration e = _table.getColumnModel().getColumns(); e.hasMoreElements(); ) {
      ExtTableColumn col = (ExtTableColumn) e.nextElement();

      if (ColumnHeaderDisplay.COLUMN_NAME == columnHeaderDisplay) {
        col.setHeaderValue(col.getColumnDisplayDefinition().getColumnName());
      } else if (ColumnHeaderDisplay.COLUMN_LABEL == columnHeaderDisplay) {
        col.setHeaderValue(col.getColumnDisplayDefinition().getLabel());
      }
    }

    _table.getTableHeader().repaint();
  }

  /*
   * The JTable used for displaying all DB ResultSet info.
   */
  protected final class MyJTable extends JTable {
    private final int _multiplier;
    private static final String data = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG";

    private TablePopupMenu _tablePopupMenu;
    private IDataSetTableControls _creator;

    private RectangleSelectionHandler _rectangleSelectionHandler =
        new RectangleSelectionHandler(this);
    private RowNumberTableColumn _rntc;
    private ButtonTableHeader _tableHeader = new ButtonTableHeader();

    MyJTable(
        IDataSetTableControls creator,
        IDataSetUpdateableModel updateableObject,
        int listSelectionMode,
        ISession session) {
      super(new SortableTableModel(new DataSetViewerTableModel(creator)));
      _creator = creator;
      _typedModel = (DataSetViewerTableModel) ((SortableTableModel) getModel()).getActualModel();
      _multiplier = getFontMetrics(getFont()).stringWidth(data) / data.length();
      setRowHeight(getFontMetrics(getFont()).getHeight());
      boolean allowUpdate = false;
      // we want to allow editing of read-only tables on-demand, but
      // it would be confusing to include the "Make Editable" option
      // when we are already in edit mode, so only allow that option when
      // the background model is updateable AND we are not already editing
      if (updateableObject != null && !creator.isTableEditable()) allowUpdate = true;
      createGUI(allowUpdate, updateableObject, listSelectionMode, session);

      // just in case table is editable, call creator to set up cell editors
      _creator.setCellEditors(this);

      /*
       * TODO: When 1.4 is the earliest version supported, add the following line:
       *		setSurrendersFocusOnKeystroke(true);
       * This should help handle some problems with navigation using tab & return
       * to move through cells.
       */

    }

    public void paint(Graphics g) {
      super.paint(g);
      _rectangleSelectionHandler.paintRectWhenNeeded(g);
    }

    public IDataSetTableControls getCreator() {
      return _creator;
    }

    /*
     * override the JTable method so that whenever something asks for
     * the cellEditor, we save a reference to that cell editor.
     * Our ASSUMPTION is that the cell editor is only requested
     * when it is about to be activated.
     */
    public TableCellEditor getCellEditor(int row, int col) {
      TableCellEditor cellEditor = super.getCellEditor(row, col);
      currentCellEditor = (DefaultCellEditor) cellEditor;
      return cellEditor;
    }

    /**
     * There are two special cases where we need to override the default behavior when we begin cell
     * editing. For some reason, when you use the keyboard to enter a cell (tab, enter, arrow keys,
     * etc), the first character that you type after entering the field is NOT passed through the
     * KeyListener mechanism where we have the special handling in the DataTypes. Instead, it is
     * passed through the KeyMap and Action mechanism, and the default Action on the JTextField is
     * to add the character to the end of the existing text, or if it is delete to delete the last
     * character of the existing text. In most cases, this is ok, but there are three special cases
     * of which we only handle two here: - If the data field currently contains "<null>" and the
     * user types a character, we want that character to replace the string "<null>", which
     * represents the null value. In this case we process the event normally, which usually adds the
     * char to the end of the string, then remove the char afterwards. We take this approach rather
     * than just immediately replacing the "<null>" with the char because there are some chars that
     * should not be put into the editable text, such as control-characters. - If the data field
     * contains "<null>" and the user types a delete, we do not want to delete the last character
     * from the string "<null>" since that string represents the null value. In this case we simply
     * ignore the user input. - Whether or not the field initially contains null, we do not run the
     * input validation function for the DataType on the input character. This means that the user
     * can type an illegal character into the field. For example, after entering an Integer field by
     * typing a tab, the user can enter a letter (e.g. "a") into that field. The normal keyListener
     * processing prevents that, but we cannot call it from this point. (More accurately, I cannot
     * figure out how to do it easilly.) Thus the user may enter one character of invalid data into
     * the field. This is not too serious a problem, however, because the normal validation is still
     * done when the user leaves the field and it SQuirreL tries to convert the text into an object
     * of the correct type, so errors of this nature will still be caught. They just won't be
     * prevented.
     */
    public void processKeyEvent(KeyEvent e) {

      // handle special case of delete with <null> contents
      if (e.getKeyChar() == '\b'
          && getEditorComponent() != null
          && ((RestorableJTextField) getEditorComponent()).getText().equals("<null>")) {
        // ignore the user input
        return;
      }

      // generally for KEY_TYPED this means add the typed char to the end of the text,
      // but there are some things (e.g. control chars) that are ignored, so let the
      // normal processing do its thing
      super.processKeyEvent(e);

      // now check to see if the original contents were <null>
      // and we have actually added the input char to the end of it
      if (getEditorComponent() != null) {
        if (e.getID() == KeyEvent.KEY_TYPED
            && ((RestorableJTextField) getEditorComponent()).getText().length() == 7) {
          // check that we did not just add a char to a <null>
          if (((RestorableJTextField) getEditorComponent())
              .getText()
              .equals("<null>" + e.getKeyChar())) {
            // replace the null with just the char
            ((RestorableJTextField) getEditorComponent()).updateText("" + e.getKeyChar());
          }
        }
      }
    }

    /*
     * When user leaves a cell after editing it, the contents of
     * that cell need to be converted from a string into an
     * object of the appropriate type before updating the table.
     * However, when the call comes from the Popup window, the data
     * has already been converted and validated.
     * We assume that a String being passed in here is a value from
     * a text field that needs to be converted to an object, and
     * a non-string object has already been validated and converted.
     */
    public void setValueAt(Object newValueString, int row, int col) {
      if (!(newValueString instanceof java.lang.String)) {
        // data is an object - assume already validated
        super.setValueAt(newValueString, row, col);
        return;
      }

      // data is a String, so we need to convert to real object
      StringBuffer messageBuffer = new StringBuffer();

      int modelIndex = getColumnModel().getColumn(col).getModelIndex();
      ColumnDisplayDefinition colDef = getColumnDefinitions()[modelIndex];
      Object newValueObject =
          CellComponentFactory.validateAndConvert(
              colDef, getValueAt(row, col), (String) newValueString, messageBuffer);

      if (messageBuffer.length() > 0) {

        // i18n[dataSetViewerTablePanel.textCantBeConverted=The given text cannot be converted into
        // the internal object.\nThe database has not been changed.\nThe conversion error was:\n{0}]
        String msg =
            s_stringMgr.getString("dataSetViewerTablePanel.textCantBeConverted", messageBuffer);

        if (s_log.isDebugEnabled()) {
          s_log.debug("setValueAt: msg from DataTypeComponent was: " + msg);
        }

        // display error message and do not update the table
        JOptionPane.showMessageDialog(
            this,
            msg,
            // i18n[dataSetViewerTablePanel.conversionError=Conversion Error]
            s_stringMgr.getString("dataSetViewerTablePanel.conversionError"),
            JOptionPane.ERROR_MESSAGE);

      } else {
        // data converted ok, so update the table
        super.setValueAt(newValueObject, row, col);
      }
    }

    public void setColumnDefinitions(ColumnDisplayDefinition[] colDefs) {
      TableColumnModel tcm = createColumnModel(colDefs);
      setColumnModel(tcm);
      _typedModel.setHeadings(colDefs);

      // just in case table is editable, call creator to set up cell editors
      _creator.setCellEditors(this);
      _tablePopupMenu.reset();
    }

    DataSetViewerTableModel getTypedModel() {
      return _typedModel;
    }

    /** Display the popup menu for this component. */
    private void displayPopupMenu(MouseEvent evt) {
      _tablePopupMenu.show(evt.getComponent(), evt.getX(), evt.getY());
    }

    private TableColumnModel createColumnModel(ColumnDisplayDefinition[] colDefs) {
      // _colDefs = hdgs;
      TableColumnModel cm = new DefaultTableColumnModel();

      _rntc = new RowNumberTableColumn();

      for (int i = 0; i < colDefs.length; ++i) {
        ColumnDisplayDefinition colDef = colDefs[i];

        int colWidth;

        if (null == colDef.getAbsoluteWidth()) {
          colWidth = colDef.getDisplayWidth() * _multiplier;
          if (colWidth > MAX_COLUMN_WIDTH * _multiplier) {
            colWidth = MAX_COLUMN_WIDTH * _multiplier;
          } else if (colWidth < MIN_COLUMN_WIDTH * _multiplier) {
            colWidth = MIN_COLUMN_WIDTH * _multiplier;
          }
        } else {
          colWidth = colDef.getAbsoluteWidth();
        }

        ExtTableColumn col =
            new ExtTableColumn(
                i, colWidth, CellComponentFactory.getTableCellRenderer(colDefs[i]), null);

        String headerValue = colDef.getColumnHeading();
        col.setHeaderValue(headerValue);
        col.setColumnDisplayDefinition(colDef);
        cm.addColumn(col);
      }

      return cm;
    }

    void setShowRowNumbers(boolean show) {
      try {
        int rowNumColIx =
            getColumnModel().getColumnIndex(RowNumberTableColumn.ROW_NUMBER_COL_IDENTIFIER);
        _tableHeader.columnIndexWillBeRemoved(rowNumColIx);
      } catch (IllegalArgumentException e) {
        // Column not in model
      }

      getColumnModel().removeColumn(_rntc);
      if (show) {
        _tableHeader.columnIndexWillBeAdded(0);
        getColumnModel().addColumn(_rntc);
        getColumnModel().moveColumn(getColumnModel().getColumnCount() - 1, 0);
      }
    }

    private void createGUI(
        boolean allowUpdate,
        IDataSetUpdateableModel updateableObject,
        int selectionMode,
        ISession session) {
      setSelectionMode(selectionMode);
      setRowSelectionAllowed(false);
      setColumnSelectionAllowed(false);
      setCellSelectionEnabled(true);
      getTableHeader().setResizingAllowed(true);
      getTableHeader().setReorderingAllowed(true);
      setAutoCreateColumnsFromModel(false);
      setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      setTableHeader(_tableHeader);
      _tableHeader.setTable(this);

      _tablePopupMenu =
          new TablePopupMenu(
              allowUpdate,
              updateableObject,
              DataSetViewerTablePanel.this,
              getDataModelImplementationDetails(),
              session);
      _tablePopupMenu.setTable(this);

      addMouseListener(
          new MouseAdapter() {
            public void mousePressed(MouseEvent evt) {
              onMousePressed(evt, false);
            }

            public void mouseReleased(MouseEvent evt) {
              onMouseReleased(evt);
            }
          });

      getTableHeader()
          .addMouseListener(
              new MouseAdapter() {
                public void mousePressed(MouseEvent evt) {
                  onMousePressed(evt, true);
                }

                public void mouseReleased(MouseEvent evt) {
                  onMouseReleased(evt);
                }
              });
    }

    private void onMouseReleased(MouseEvent evt) {
      if (evt.isPopupTrigger()) {
        this.displayPopupMenu(evt);
      }
    }

    private void onMousePressed(MouseEvent evt, boolean clickedOnTableHeader) {
      if (evt.isPopupTrigger()) {
        this.displayPopupMenu(evt);
      } else if (evt.getClickCount() == 2 && false == clickedOnTableHeader) {
        // If this was done when the header was clicked
        // it prevents MS Excel like adopition of column
        // sizes by double click. See class ButtonTableHeader.

        // figure out which column the user clicked on
        // so we can pass in the right column description
        Point pt = evt.getPoint();
        TableColumnModel cm = this.getColumnModel();
        int columnIndexAtX = cm.getColumnIndexAtX(pt.x);

        int modelIndex = cm.getColumn(columnIndexAtX).getModelIndex();

        if (RowNumberTableColumn.ROW_NUMBER_MODEL_INDEX != modelIndex) {
          ColumnDisplayDefinition colDefs[] = getColumnDefinitions();
          CellDataPopup.showDialog(this, colDefs[modelIndex], evt, this._creator.isTableEditable());
        }
      }
    }

    public void initColWidths() {
      _tableHeader.initColWidths();
    }
  }

  /////////////////////////////////////////////////////////////////////////
  //
  // Implement the IDataSetTableControls interface,
  // functions needed to support table operations
  //
  // These functions are called from within MyJTable and MyTable to tell
  // those classes how to operate.  The code in these functions will be
  // different depending on whether the table is read-only or editable.
  //
  // The definitions below are for read-only operation.  The editable
  // table panel overrides these functions with the versions that tell the
  // tables how to set up for editing operations.
  //
  //
  /////////////////////////////////////////////////////////////////////////

  /**
   * Tell the table that it is editable. This is called from within MyTable.isCellEditable(). We do
   * not bother to distinguish between editable and non-editable cells within the same table.
   */
  public boolean isTableEditable() {
    return false;
  }

  /** Tell the table whether particular columns are editable. */
  public boolean isColumnEditable(int col, Object originalValue) {
    return false;
  }

  /**
   * 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(int col, Object originalValue) {
    // call the DataType object for this column and have it check the current value
    return CellComponentFactory.needToReRead(_colDefs[col], originalValue);
  }

  /**
   * Re-read the contents of this cell from the database. If there is a problem, the message will
   * have a non-zero length after return.
   */
  public Object reReadDatum(Object[] values, int col, StringBuffer message) {
    // call the underlying model to get the whole data, if possible
    return ((IDataSetUpdateableTableModel) _updateableModel)
        .reReadDatum(values, _colDefs, col, message);
  }

  /** Function to set up CellEditors. Null for read-only tables. */
  public void setCellEditors(JTable table) {}

  /**
   * Change the data in the permanent store that is represented by the JTable. Does nothing in
   * read-only table.
   */
  public int[] changeUnderlyingValueAt(int row, int col, Object newValue, Object oldValue) {
    return new int[0]; // underlaying data cannot be changed
  }

  /** Delete a set of rows from the table. The indexes are the row indexes in the SortableModel. */
  public void deleteRows(int[] rows) {} // cannot delete rows in read-only table

  /** Initiate operations to insert a new row into the table. */
  public void insertRow() {} // cannot insert row into read-only table

  //
  // Begin code related to printing
  //

  //
  // variables used in printing
  //
  JTableHeader tableHeader;
  int[] subTableSplit = null;
  boolean pageinfoCalculated = false;
  int totalNumPages = 0;
  int prevPageIndex = 0;
  int subPageIndex = 0;
  int subTableSplitSize = 0;
  double tableHeightOnFullPage, headerHeight;
  double pageWidth, pageHeight;
  int fontHeight, fontDesent;
  double tableHeight, rowHeight;
  double scale = 8.0 / 12.0; // default is 12 point, so define font relative to that

  /**
   * Print the table contents. This was copied from a tutorial paper on the Sun Java site: paper:
   * http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/advprint.html code:
   * http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/Code/SalesReport.java
   */
  public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {

    Graphics2D g2 = (Graphics2D) g;

    // reset each time we start a new print
    if (pageIndex == 0) pageinfoCalculated = false;

    if (!pageinfoCalculated) {
      getPageInfo(g, pageFormat);
    }

    g2.setColor(Color.black);
    if (pageIndex >= totalNumPages) {
      return NO_SUCH_PAGE;
    }
    if (prevPageIndex != pageIndex) {
      subPageIndex++;
      if (subPageIndex == subTableSplitSize - 1) {
        subPageIndex = 0;
      }
    }

    g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());

    int rowIndex = pageIndex / (subTableSplitSize - 1);

    printTablePart(g2, pageFormat, rowIndex, subPageIndex);
    prevPageIndex = pageIndex;

    return Printable.PAGE_EXISTS;
  }

  /** Part of print code coped from Sun */
  public void getPageInfo(Graphics g, PageFormat pageFormat) {

    subTableSplit = null;
    subTableSplitSize = 0;
    subPageIndex = 0;
    prevPageIndex = 0;

    fontHeight = (int) (g.getFontMetrics().getHeight() * scale);
    fontDesent = (int) (g.getFontMetrics().getDescent() * scale);

    tableHeader = _table.getTableHeader();
    //		double headerWidth = tableHeader.getWidth() * scale;
    headerHeight = tableHeader.getHeight() + _table.getRowMargin() * scale;

    pageHeight = pageFormat.getImageableHeight();
    pageWidth = pageFormat.getImageableWidth();

    //		double tableWidth =_table.getColumnModel().getTotalColumnWidth() * scale;
    tableHeight = _table.getHeight() * scale;
    rowHeight = _table.getRowHeight() + _table.getRowMargin() * scale;

    tableHeightOnFullPage = (int) (pageHeight - headerHeight - fontHeight * 2);
    tableHeightOnFullPage = tableHeightOnFullPage / rowHeight * rowHeight;

    TableColumnModel tableColumnModel = tableHeader.getColumnModel();
    int columns = tableColumnModel.getColumnCount();
    int columnMargin = (int) (tableColumnModel.getColumnMargin() * scale);

    int[] temp = new int[columns];
    int columnIndex = 0;
    temp[0] = 0;
    int columnWidth;
    int length = 0;
    subTableSplitSize = 0;
    while (columnIndex < columns) {

      columnWidth = (int) (tableColumnModel.getColumn(columnIndex).getWidth() * scale);

      if (length + columnWidth + columnMargin > pageWidth) {
        temp[subTableSplitSize + 1] = temp[subTableSplitSize] + length;
        length = columnWidth;
        subTableSplitSize++;
      } else {
        length += columnWidth + columnMargin;
      }
      columnIndex++;
    } // while

    if (length > 0) { // if are more columns left, part page
      temp[subTableSplitSize + 1] = temp[subTableSplitSize] + length;
      subTableSplitSize++;
    }

    subTableSplitSize++;
    subTableSplit = new int[subTableSplitSize];
    for (int i = 0; i < subTableSplitSize; i++) {
      subTableSplit[i] = temp[i];
    }
    totalNumPages = (int) (tableHeight / tableHeightOnFullPage);
    if (tableHeight % tableHeightOnFullPage >= rowHeight) { // at least 1 more row left
      totalNumPages++;
    }

    totalNumPages *= (subTableSplitSize - 1);
    pageinfoCalculated = true;
  }

  /** Part of print code coped from Sun */
  public void printTablePart(Graphics2D g2, PageFormat pageFormat, int rowIndex, int columnIndex) {

    String pageNumber = "Page: " + (rowIndex + 1);
    if (subTableSplitSize > 1) {
      pageNumber += "-" + (columnIndex + 1);
    }

    int pageLeft = subTableSplit[columnIndex];
    int pageRight = subTableSplit[columnIndex + 1];

    int pageWidth = pageRight - pageLeft;

    // page number message (in smaller type)
    g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), 8));
    g2.drawString(pageNumber, pageWidth / 2 - 35, (int) (pageHeight - fontHeight));

    double clipHeight =
        Math.min(tableHeightOnFullPage, tableHeight - rowIndex * tableHeightOnFullPage);

    g2.translate(-subTableSplit[columnIndex], 0);
    g2.setClip(pageLeft, 0, pageWidth, (int) headerHeight);

    g2.scale(scale, scale);
    tableHeader.paint(g2); // draw the header on every page
    g2.scale(1 / scale, 1 / scale);
    g2.translate(0, headerHeight);
    g2.translate(0, -tableHeightOnFullPage * rowIndex);

    // cut table image and draw on the page

    g2.setClip(pageLeft, (int) tableHeightOnFullPage * rowIndex, pageWidth, (int) clipHeight);
    g2.scale(scale, scale);
    _table.paint(g2);
    g2.scale(1 / scale, 1 / scale);

    double pageTop = tableHeightOnFullPage * rowIndex - headerHeight;
    //		double pageBottom = pageTop +  clipHeight + headerHeight;
    g2.drawRect(pageLeft, (int) pageTop, pageWidth, (int) (clipHeight + headerHeight));
  }

  //
  // End of code related to printing
  //

  @Override
  public TableState getResultSortableTableState() {
    return new TableState(_table);
  }

  @Override
  public void applyResultSortableTableState(TableState sortableTableState) {
    sortableTableState.apply(_table);
  }

  @Override
  public void setContinueReadChannel(ContinueReadChannel continueReadChannel) {
    _continueReadHandler.setContinueReadChannel(continueReadChannel);
  }

  @Override
  public void disableContinueRead() {
    _continueReadHandler.disableContinueRead();
  }
}
Ejemplo n.º 14
0
/**
 * The Ostermiller plugin class. This plugin adds syntax highlighting to the SQL entry area.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class SyntaxPlugin extends DefaultSessionPlugin {
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(SyntaxPlugin.class);

  static interface i18n {
    // i18n[SyntaxPlugin.touppercase=touppercase]
    String TO_UPPER_CASE = s_stringMgr.getString("SyntaxPlugin.touppercase");

    // i18n[SyntaxPlugin.tolowercase=tolowercase]
    String TO_LOWER_CASE = s_stringMgr.getString("SyntaxPlugin.tolowercase");

    // i18n[SyntaxPlugin.find=find]
    String FIND = s_stringMgr.getString("SyntaxPlugin.find");

    // i18n[SyntaxPlugin.findSelected=findselected]
    String FIND_SELECTED = s_stringMgr.getString("SyntaxPlugin.findselected");

    // i18n[SyntaxPlugin.repeatLastFind=findrepeatlast]
    String REPEAT_LAST_FIND = s_stringMgr.getString("SyntaxPlugin.repeatLastFind");

    // i18n[SyntaxPlugin.markSelected=markselected]
    String MARK_SELECTED = s_stringMgr.getString("SyntaxPlugin.markSelected");

    // i18n[SyntaxPlugin.replace=replace]
    String REPLACE = s_stringMgr.getString("SyntaxPlugin.replace");

    // i18n[SyntaxPlugin.unmark=unmark]
    String UNMARK = s_stringMgr.getString("SyntaxPlugin.unmark");

    // i18n[SyntaxPlugin.gotoline=gotoline]
    String GO_TO_LINE = s_stringMgr.getString("SyntaxPlugin.gotoline");

    // i18n[SyntaxPlugin.autocorr=autocorr]
    String AUTO_CORR = s_stringMgr.getString("SyntaxPlugin.autocorr");

    // i18n[SyntaxPlugin.duplicateline=duplicateline]
    String DUP_LINE = s_stringMgr.getString("SyntaxPlugin.duplicateline");

    // i18n[SyntaxPlugin.comment=comment]
    String COMMENT = s_stringMgr.getString("SyntaxPlugin.comment");

    // i18n[SyntaxPlugin.uncomment=uncomment]
    String UNCOMMENT = s_stringMgr.getString("SyntaxPlugin.uncomment");

    // i18n[SyntaxPlugin.copyasrtf=copyasrtf]
    String COPY_AS_RTF = s_stringMgr.getString("SyntaxPlugin.copyasrtf");;
  }

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

  /** SyntaxPreferences for new sessions. */
  private SyntaxPreferences _newSessionPrefs;

  /** Folder to store user settings in. */
  private FileWrapper _userSettingsFolder;

  /** Factory that creates text controls. */
  private SQLEntryPanelFactoryProxy _sqlEntryFactoryProxy;

  /** Listeners to the preferences object in each open session. */
  private Map<IIdentifier, SessionPreferencesListener> _prefListeners =
      new HashMap<IIdentifier, SessionPreferencesListener>();

  /** Resources for this plugin. */
  private SyntaxPluginResources _resources;

  private AutoCorrectProviderImpl _autoCorrectProvider;

  private interface IMenuResourceKeys {
    String MENU = "syntax";
  }

  /**
   * Return the internal name of this plugin.
   *
   * @return the internal name of this plugin.
   */
  public String getInternalName() {
    return "syntax";
  }

  /**
   * Return the descriptive name of this plugin.
   *
   * @return the descriptive name of this plugin.
   */
  public String getDescriptiveName() {
    return "Syntax Highlighting Plugin";
  }

  /**
   * Returns the current version of this plugin.
   *
   * @return the current version of this plugin.
   */
  public String getVersion() {
    return "1.0";
  }

  /**
   * Returns the authors name.
   *
   * @return the authors name.
   */
  public String getAuthor() {
    return "Gerd Wagner, Colin Bell";
  }

  /**
   * Returns the name of the change log for the plugin. This should be a text or HTML file residing
   * in the <TT>getPluginAppSettingsFolder</TT> directory.
   *
   * @return the changelog file name or <TT>null</TT> if plugin doesn't have a change log.
   */
  public String getChangeLogFileName() {
    return "changes.txt";
  }

  /**
   * Returns the name of the Help file for the plugin. This should be a text or HTML file residing
   * in the <TT>getPluginAppSettingsFolder</TT> directory.
   *
   * @return the Help file name or <TT>null</TT> if plugin doesn't have a help file.
   */
  public String getHelpFileName() {
    return "readme.html";
  }

  /**
   * Returns the name of the Licence file for the plugin. This should be a text or HTML file
   * residing in the <TT>getPluginAppSettingsFolder</TT> directory.
   *
   * @return the Licence file name or <TT>null</TT> if plugin doesn't have a licence file.
   */
  public String getLicenceFileName() {
    return "licence.txt";
  }

  /** Initialize this plugin. */
  public synchronized void initialize() throws PluginException {
    super.initialize();

    _resources = new SyntaxPluginResources(this);

    // Folder to store user settings.
    try {
      _userSettingsFolder = getPluginUserSettingsFolder();
    } catch (IOException ex) {
      throw new PluginException(ex);
    }

    // Load plugin preferences.
    loadPrefs();

    // Install the factory for creating SQL entry text controls.
    final IApplication app = getApplication();
    final ISQLEntryPanelFactory originalFactory = app.getSQLEntryPanelFactory();
    // _sqlEntryFactoryProxy = new OsterSQLEntryAreaFactory(this, originalFactory);

    _sqlEntryFactoryProxy = new SQLEntryPanelFactoryProxy(this, originalFactory);

    app.setSQLEntryPanelFactory(_sqlEntryFactoryProxy);

    _autoCorrectProvider = new AutoCorrectProviderImpl(_userSettingsFolder);

    createMenu();

    createAdditionalActions();
  }

  /**
   * Create some additional actions and add them to the application. These actions are not part of
   * the menu, but needs to be initialized with the resources of the syntax plugin. Some of these
   * actions may be depend on a concrete editor.
   */
  private void createAdditionalActions() {
    IApplication app = getApplication();
    ActionCollection coll = app.getActionCollection();

    coll.add(new SquirrelCopyAsRtfAction(getApplication(), _resources));
  }

  private void createMenu() {
    IApplication app = getApplication();
    ActionCollection coll = app.getActionCollection();

    JMenu menu = _resources.createMenu(IMenuResourceKeys.MENU);
    app.addToMenu(IApplication.IMenuIDs.SESSION_MENU, menu);

    Action act = new ConfigureAutoCorrectAction(app, _resources, this);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new FindAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new FindSelectedAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new RepeatLastFindAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new MarkSelectedAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new ReplaceAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new UnmarkAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new GoToLineAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new DuplicateLineAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new CommentAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);

    act = new UncommentAction(getApplication(), _resources);
    coll.add(act);
    _resources.addToMenu(act, menu);
  }

  /** Application is shutting down so save preferences. */
  public void unload() {
    savePrefs();
    super.unload();
  }

  /**
   * Called when a session created but the UI hasn't been built for the session.
   *
   * @param session The session that is starting.
   */
  public void sessionCreated(ISession session) {
    SyntaxPreferences prefs = null;

    try {
      prefs = (SyntaxPreferences) _newSessionPrefs.clone();
    } catch (CloneNotSupportedException ex) {
      throw new InternalError("CloneNotSupportedException for SyntaxPreferences");
    }

    session.putPluginObject(this, IConstants.ISessionKeys.PREFS, prefs);

    SessionPreferencesListener lis = new SessionPreferencesListener(this, session);
    prefs.addPropertyChangeListener(lis);
    _prefListeners.put(session.getIdentifier(), lis);
  }

  public PluginSessionCallback sessionStarted(final ISession session) {
    PluginSessionCallback ret =
        new PluginSessionCallback() {
          public void sqlInternalFrameOpened(SQLInternalFrame sqlInternalFrame, ISession sess) {
            initSqlInternalFrame(sqlInternalFrame);
          }

          public void objectTreeInternalFrameOpened(
              ObjectTreeInternalFrame objectTreeInternalFrame, ISession sess) {}
        };

    initSessionSheet(session);

    return ret;
  }

  private void initSessionSheet(ISession session) {
    ActionCollection coll = getApplication().getActionCollection();
    session.addSeparatorToToolbar();
    session.addToToolbar(coll.get(FindAction.class));
    session.addToToolbar(coll.get(ReplaceAction.class));
    session.addToToolbar(coll.get(ConfigureAutoCorrectAction.class));

    SessionInternalFrame sif = session.getSessionInternalFrame();

    ISQLPanelAPI sqlPanelAPI = sif.getSQLPanelAPI();

    new ToolsPopupHandler(this).initToolsPopup(sif, coll);

    completeSqlPanelEntryAreaMenu(coll, sqlPanelAPI);
  }

  private void initSqlInternalFrame(SQLInternalFrame sqlInternalFrame) {
    ActionCollection coll = getApplication().getActionCollection();
    FindAction findAction = ((FindAction) coll.get(FindAction.class));
    ReplaceAction replaceAction = (ReplaceAction) coll.get(ReplaceAction.class);

    sqlInternalFrame.addSeparatorToToolbar();
    sqlInternalFrame.addToToolbar(findAction);
    sqlInternalFrame.addToToolbar(replaceAction);
    sqlInternalFrame.addToToolbar(coll.get(ConfigureAutoCorrectAction.class));

    new ToolsPopupHandler(this).initToolsPopup(sqlInternalFrame, coll);

    ISQLPanelAPI sqlPanelAPI = sqlInternalFrame.getSQLPanelAPI();

    completeSqlPanelEntryAreaMenu(coll, sqlPanelAPI);
  }

  private void completeSqlPanelEntryAreaMenu(ActionCollection coll, ISQLPanelAPI sqlPanelAPI) {
    JMenuItem mnuUnmark = sqlPanelAPI.addToSQLEntryAreaMenu(coll.get(UnmarkAction.class));
    _resources.configureMenuItem(coll.get(UnmarkAction.class), mnuUnmark);

    JMenuItem mnuComment = sqlPanelAPI.addToSQLEntryAreaMenu(coll.get(CommentAction.class));
    _resources.configureMenuItem(coll.get(CommentAction.class), mnuComment);
    JMenuItem mnuUncomment = sqlPanelAPI.addToSQLEntryAreaMenu(coll.get(UncommentAction.class));
    _resources.configureMenuItem(coll.get(UncommentAction.class), mnuUncomment);

    if (sqlPanelAPI.getSQLEntryPanel().getTextComponent() instanceof SquirrelRSyntaxTextArea) {

      JMenuItem mnuCopyToRtf =
          sqlPanelAPI.addToSQLEntryAreaMenu(coll.get(SquirrelCopyAsRtfAction.class));
      _resources.configureMenuItem(coll.get(SquirrelCopyAsRtfAction.class), mnuCopyToRtf);

      SquirrelRSyntaxTextArea rsEdit =
          (SquirrelRSyntaxTextArea) sqlPanelAPI.getSQLEntryPanel().getTextComponent();

      configureRichTextAction(
          sqlPanelAPI,
          rsEdit,
          RTextAreaEditorKit.rtaUpperSelectionCaseAction,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_KEY_STROKE_TO_UPPER_CASE,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_STRING_TO_UPPER_CASE,
          s_stringMgr.getString("SyntaxPlugin.ToUpperShortDescription"));
      configureRichTextAction(
          sqlPanelAPI,
          rsEdit,
          RTextAreaEditorKit.rtaLowerSelectionCaseAction,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_KEY_STROKE_TO_LOWER_CASE,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_STRING_TO_LOWER_CASE,
          s_stringMgr.getString("SyntaxPlugin.ToLowerShortDescription"));

      configureRichTextAction(
          sqlPanelAPI,
          rsEdit,
          RTextAreaEditorKit.rtaLineUpAction,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_KEY_STROKE_LINE_UP,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_LINE_UP,
          s_stringMgr.getString("SyntaxPlugin.LineUpShortDescription"));
      configureRichTextAction(
          sqlPanelAPI,
          rsEdit,
          RTextAreaEditorKit.rtaLineDownAction,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_KEY_STROKE_LINE_DOWN,
          SquirreLRSyntaxTextAreaUI.RS_ACCELERATOR_LINE_DOWN,
          s_stringMgr.getString("SyntaxPlugin.LineDownShortDescription"));
    }
  }

  private void configureRichTextAction(
      ISQLPanelAPI sqlPanelAPI,
      SquirrelRSyntaxTextArea rsEdit,
      String rtaKey,
      KeyStroke acceleratorKeyStroke,
      String acceleratorDescription,
      String shortDescription) {
    Action action = SquirreLRSyntaxTextAreaUI.getActionForName(rsEdit, rtaKey);
    action.putValue(Resources.ACCELERATOR_STRING, acceleratorDescription);

    action.putValue(Action.SHORT_DESCRIPTION, shortDescription);
    action.putValue(Action.MNEMONIC_KEY, 0);
    action.putValue(Action.ACCELERATOR_KEY, acceleratorKeyStroke);

    JMenuItem mnu = sqlPanelAPI.addToSQLEntryAreaMenu(action);
    mnu.setText((String) action.getValue(Action.SHORT_DESCRIPTION));
    _resources.configureMenuItem(action, mnu);
  }

  /**
   * Called when a session shutdown.
   *
   * @param session The session that is ending.
   */
  public void sessionEnding(ISession session) {
    super.sessionEnding(session);

    session.removePluginObject(this, IConstants.ISessionKeys.PREFS);
    _prefListeners.remove(session.getIdentifier());
    _sqlEntryFactoryProxy.sessionEnding(session);
  }

  /**
   * Create preferences panel for the New Session Properties dialog.
   *
   * @return preferences panel.
   */
  public INewSessionPropertiesPanel[] getNewSessionPropertiesPanels() {
    return new INewSessionPropertiesPanel[] {
      new SyntaxPreferencesPanel(_newSessionPrefs, _resources)
    };
  }

  /**
   * Create panels for the Session Properties dialog.
   *
   * @return Array of panels for the properties dialog.
   */
  public ISessionPropertiesPanel[] getSessionPropertiesPanels(ISession session) {
    SyntaxPreferences sessionPrefs =
        (SyntaxPreferences) session.getPluginObject(this, IConstants.ISessionKeys.PREFS);

    return new ISessionPropertiesPanel[] {new SyntaxPreferencesPanel(sessionPrefs, _resources)};
  }

  SyntaxPluginResources getResources() {
    return _resources;
  }

  ISQLEntryPanelFactory getSQLEntryAreaFactory() {
    return _sqlEntryFactoryProxy;
  }

  /** Load from preferences file. */
  private void loadPrefs() {
    try {
      final XMLBeanReader doc = new XMLBeanReader();
      final FileWrapper file =
          fileWrapperFactory.create(_userSettingsFolder, IConstants.USER_PREFS_FILE_NAME);
      doc.load(file, getClass().getClassLoader());

      Iterator<?> it = doc.iterator();

      if (it.hasNext()) {
        _newSessionPrefs = (SyntaxPreferences) it.next();
      }
    } catch (FileNotFoundException ignore) {
      // property file not found for user - first time user ran pgm.
    } catch (Exception ex) {
      final String msg =
          "Error occured reading from preferences file: " + IConstants.USER_PREFS_FILE_NAME;
      s_log.error(msg, ex);
    }

    if (_newSessionPrefs == null) {
      _newSessionPrefs = new SyntaxPreferences();
    }
  }

  /** Save preferences to disk. */
  private void savePrefs() {
    try {
      final XMLBeanWriter wtr = new XMLBeanWriter(_newSessionPrefs);
      wtr.save(fileWrapperFactory.create(_userSettingsFolder, IConstants.USER_PREFS_FILE_NAME));
    } catch (Exception ex) {
      final String msg =
          "Error occured writing to preferences file: " + IConstants.USER_PREFS_FILE_NAME;
      s_log.error(msg, ex);
    }
  }

  public Object getExternalService() {
    return getAutoCorrectProviderImpl();
  }

  public AutoCorrectProviderImpl getAutoCorrectProviderImpl() {
    return _autoCorrectProvider;
  }

  private static final class SessionPreferencesListener implements PropertyChangeListener {
    private SyntaxPlugin _plugin;

    private ISession _session;

    SessionPreferencesListener(SyntaxPlugin plugin, ISession session) {
      super();
      _plugin = plugin;
      _session = session;
    }

    public void propertyChange(PropertyChangeEvent evt) {
      String propName = evt.getPropertyName();

      if (false == SyntaxPreferences.IPropertyNames.USE_RSYNTAX_CONTROL.equals(propName)) {

        // Not the Textcontrol itself changed but some other of the Syntax Preferences, for example
        // a
        // color.
        // So we tell the current control to update the preferences.
        Object pluginObject =
            _session.getPluginObject(_plugin, IConstants.ISessionKeys.SQL_ENTRY_CONTROL);

        if (pluginObject instanceof RSyntaxSQLEntryPanel) {
          ((RSyntaxSQLEntryPanel) pluginObject).updateFromPreferences();
        }
      } else {
        /*
        We don't support switching the entry control during a session
        because serveral things, that are attached to the entry control
        from outside this plugin would need to reinitialze too.
        For example code completion and edit extras.

        synchronized (_session)
        {
        	ISQLEntryPanelFactory factory = _plugin.getSQLEntryAreaFactory();
        	ISQLEntryPanel pnl = factory.createSQLEntryPanel(_session);
        	_session.getSQLPanelAPI(_plugin).installSQLEntryPanel(pnl);
        }
        */

        String msg =
            // i18n[syntax.switchingNotSupported=Switching the editor of a runninig session is not
            // supported.\nYou may switch the entry area in the New Session Properties dialog]
            s_stringMgr.getString("syntax.switchingNotSupported");

        JOptionPane.showMessageDialog(_session.getApplication().getMainFrame(), msg);

        throw new SyntaxPrefChangeNotSupportedException();
      }
    }
  }
}
Ejemplo n.º 15
0
public class ExplainExecuterPanel extends JPanel implements ISQLResultExecuter {
  static final String EXPLAIN_PREFIX = "EXPLAIN ANALYZE ";

  private static final long serialVersionUID = 9155604319585792834L;

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

  /** Internationalized strings for this class. */
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(ExplainExecuterPanel.class);

  static interface i18n {
    String TITLE = s_stringMgr.getString("ExplainExecuterPanel.title");

    String CLOSE = s_stringMgr.getString("ExplainExecuterPanel.close");

    String CLOSE_ALL_BUT_THIS = s_stringMgr.getString("ExplainExecuterPanel.closeAllButThis");

    String CLOSE_ALL = s_stringMgr.getString("ExplainExecuterPanel.closeAll");
  }

  /** Current session. */
  private final ISession _session;

  /** Current session's dialect type */
  private final DialectType _dialectType;

  /** Each tab is an <TT>ExplainTab</TT> showing the explain result of a query. */
  private JTabbedPane _tabbedExecutionsPanel;

  public ExplainExecuterPanel(ISession session) {
    _session = session;
    _dialectType = DialectFactory.getDialectType(_session.getMetaData());
    _session
        .getProperties()
        .addPropertyChangeListener(
            new PropertyChangeListener() {
              @Override
              public void propertyChange(PropertyChangeEvent evt) {
                onPropertyChange(evt.getPropertyName());
              }
            });

    createGUI();
  }

  private void createGUI() {
    setLayout(new BorderLayout());

    _tabbedExecutionsPanel =
        UIFactory.getInstance()
            .createTabbedPane(_session.getProperties().getSQLExecutionTabPlacement());

    initTabPopup();

    add(_tabbedExecutionsPanel, BorderLayout.CENTER);
  }

  @Override
  public void execute(ISQLEntryPanel parent, ExecutionScope executionScope) {
    String sql = parent.getSQLToBeExecuted();
    if (sql == null || sql.length() == 0) return;

    SQLExecuterTask executer =
        new SQLExecuterTask(_session, getExplainSql(sql), new SQLExecutionHandler());
    _session.getApplication().getThreadPool().addTask(executer);
  }

  private String getExplainSql(String sql) {
    StringBuilder result = new StringBuilder();
    IQueryTokenizer tokenizer = _session.getQueryTokenizer();
    tokenizer.setScriptToTokenize(sql);
    while (tokenizer.hasQuery()) {
      String query = tokenizer.nextQuery();
      result.append("BEGIN").append(tokenizer.getSQLStatementSeparator());
      result.append(EXPLAIN_PREFIX).append(query).append(tokenizer.getSQLStatementSeparator());
      result.append("ROLLBACK").append(tokenizer.getSQLStatementSeparator());
    }
    if (s_log.isDebugEnabled()) {
      s_log.debug("getExplainSql - Input: " + sql);
      s_log.debug("getExplainSql - Querys: " + tokenizer.getQueryCount());
      s_log.debug("getExplainSql - Result: " + result);
    }
    return result.toString();
  }

  private void addExplainTab(
      ResultSetDataSet rsds, SQLExecutionInfo info, IDataSetUpdateableTableModel model) {
    final ExplainTab tab = new ExplainTab(_session, this, rsds, info, model);
    SwingUtilities.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            secureAddTab(tab);
          }
        });
  }

  public void closeTab(final ExplainTab tab) {
    SwingUtilities.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            _tabbedExecutionsPanel.removeTabAt(_tabbedExecutionsPanel.indexOfComponent(tab));
          }
        });
  }

  public void closeTabAt(final int index) {
    SwingUtilities.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            _tabbedExecutionsPanel.removeTabAt(index);
          }
        });
  }

  private void secureAddTab(ExplainTab tab) {
    SessionProperties props = _session.getProperties();

    if (s_log.isDebugEnabled()) {
      s_log.debug("secureAddTab - TabCount: " + _tabbedExecutionsPanel.getTabCount());
      s_log.debug("secureAddTab - Limited?: " + props.getLimitSQLResultTabs());
      s_log.debug("secureAddTab - TabLimit: " + props.getSqlResultTabLimit());
    }

    if (props.getLimitSQLResultTabs()
        && props.getSqlResultTabLimit() <= _tabbedExecutionsPanel.getTabCount()) {
      closeTabAt(0);
    }
    _tabbedExecutionsPanel.addTab(tab.getTitle(), null, tab, tab.getToolTip());
    _tabbedExecutionsPanel.setSelectedComponent(tab);
  }

  private void onPropertyChange(String propertyName) {
    if (propertyName.equals(SessionProperties.IPropertyNames.SQL_EXECUTION_TAB_PLACEMENT)) {
      _tabbedExecutionsPanel.setTabPlacement(
          _session.getProperties().getSQLExecutionTabPlacement());
    }
  }

  private void initTabPopup() {
    final JPopupMenu popup = new JPopupMenu();

    JMenuItem close = new JMenuItem(i18n.CLOSE);
    close.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            _tabbedExecutionsPanel.remove(_tabbedExecutionsPanel.getSelectedIndex());
          }
        });
    popup.add(close);

    JMenuItem closeAllButThis = new JMenuItem(i18n.CLOSE_ALL_BUT_THIS);
    closeAllButThis.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            for (Component c : _tabbedExecutionsPanel.getComponents()) {
              if (c != _tabbedExecutionsPanel.getSelectedComponent())
                _tabbedExecutionsPanel.remove(c);
            }
          }
        });
    popup.add(closeAllButThis);

    JMenuItem closeAll = new JMenuItem(i18n.CLOSE_ALL);
    closeAll.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            _tabbedExecutionsPanel.removeAll();
          }
        });
    popup.add(closeAll);

    _tabbedExecutionsPanel.addMouseListener(
        new MouseAdapter() {
          @Override
          public void mousePressed(MouseEvent e) {
            showPopup(e, popup);
          }

          @Override
          public void mouseReleased(MouseEvent e) {
            showPopup(e, popup);
          }
        });
  }

  private void showPopup(MouseEvent e, JPopupMenu popup) {
    if (e.isPopupTrigger()) {
      int index =
          _tabbedExecutionsPanel
              .getUI()
              .tabForCoordinate(_tabbedExecutionsPanel, e.getX(), e.getY());
      if (-1 != index) {
        popup.show(e.getComponent(), e.getX(), e.getY());
      }
    }
  }

  public void reRunTab(ExplainTab tab) {
    SQLExecuterTask executer =
        new SQLExecuterTask(_session, getExplainSql(tab.getQuery()), new SQLExecutionHandler(tab));
    _session.getApplication().getThreadPool().addTask(executer);
  }

  /** This class is the handler for the execution of sql against the ExplainPanel */
  private class SQLExecutionHandler implements ISQLExecuterHandler {
    ExplainTab _tabToReplace;

    public SQLExecutionHandler() {}

    public SQLExecutionHandler(ExplainTab tabToReplace) {
      _tabToReplace = tabToReplace;
    }

    @Override
    public void sqlToBeExecuted(final String sql) {}

    @Override
    public void sqlExecutionComplete(
        SQLExecutionInfo exInfo, int processedStatementCount, int statementCount) {
      double executionLength = ((double) exInfo.getSQLExecutionElapsedMillis()) / 1000;
      double outputLength = ((double) exInfo.getResultsProcessingElapsedMillis()) / 1000;
      double totalLength = executionLength + outputLength;

      final NumberFormat numberFormat = NumberFormat.getNumberInstance();
      Object[] args =
          new Object[] {
            processedStatementCount,
            statementCount,
            numberFormat.format(totalLength),
            numberFormat.format(executionLength),
            numberFormat.format(outputLength)
          };

      _session.showMessage(s_stringMgr.getString("ExplainExecuterPanel.queryStatistics", args));
    }

    @Override
    public void sqlExecutionCancelled() {}

    @Override
    public void sqlDataUpdated(int updateCount) {}

    @Override
    public void sqlResultSetAvailable(
        ResultSetWrapper rs, final SQLExecutionInfo info, final IDataSetUpdateableTableModel model)
        throws DataSetException {

      final ResultSetDataSet rsds = new ResultSetDataSet();
      rsds.setResultSet(rs.getResultSet(), _dialectType);

      GUIUtils.processOnSwingEventThread(
          new Runnable() {
            public void run() {
              updateExplainTab(info, model, rsds);
            }
          });
    }

    private void updateExplainTab(
        SQLExecutionInfo info, IDataSetUpdateableTableModel model, ResultSetDataSet rsds) {
      if (_tabToReplace != null) {
        _tabToReplace.reInit(rsds, info, model);
      } else {
        addExplainTab(rsds, info, model);
      }
    }

    @Override
    public void sqlExecutionWarning(SQLWarning warn) {
      _session.showMessage(warn);
    }

    @Override
    public void sqlStatementCount(int statementCount) {}

    @Override
    public String sqlExecutionException(Throwable th, String postErrorString) {
      String message = _session.formatException(new SQLExecutionException(th, postErrorString));
      try {
        _session.getSQLConnection().createStatement().executeQuery("ROLLBACK;");
      } catch (SQLException rollbackException) {
        message += rollbackException.getMessage();
      }

      _session.showErrorMessage(message);

      if (_session.getProperties().getWriteSQLErrorsToLog()) {
        s_log.info(message);
      }

      return message;
    }

    @Override
    public void sqlCloseExecutionHandler(
        ArrayList<String> sqlExecErrorMsgs, String lastExecutedStatement) {
      // To change body of implemented methods use File | Settings | File Templates.
    }
  }

  @Override
  public String getTitle() {
    return i18n.TITLE;
  }

  @Override
  public JComponent getComponent() {
    return this;
  }

  @Override
  public IResultTab getSelectedResultTab() {
    throw new UnsupportedOperationException("ExplainExecuter has no ResultTabs");
  }
}
Ejemplo n.º 16
0
/** @author rowen */
public class MacOSPlugin extends DefaultPlugin {

  private static final String COM_APPLE_EAWT_APPLICATION_CLASSNAME = "com.apple.eawt.Application";
  private static final ILogger s_log = LoggerController.createLogger(MacOSPlugin.class);

  public String getAuthor() {
    return ("Neville Rowe");
  }

  public String getDescriptiveName() {
    return ("Mac OS User Interface");
  }

  public String getInternalName() {
    return ("macosx");
  }

  public String getVersion() {
    return ("0.1");
  }

  public String getChangeLogFileName() {
    return "changes.txt";
  }

  public String getHelpFileName() {
    return "readme.txt";
  }

  public String getLicenceFileName() {
    return "licence.txt";
  }

  public synchronized void load(IApplication app) throws PluginException {
    super.load(app);
  }

  public synchronized void initialize() throws PluginException {
    try {
      super.initialize();
      final IApplication app = getApplication();

      Class<?> com_apple_eawt_Application;
      try {
        com_apple_eawt_Application = Class.forName(COM_APPLE_EAWT_APPLICATION_CLASSNAME);
      } catch (ClassNotFoundException e) {
        s_log.error(
            "MacOSX plugin is loaded, but Apple support class isn't in the Classpath: "
                + e.getMessage(),
            e);
        return;
      }

      Method getApplication = com_apple_eawt_Application.getMethod("getApplication", new Class[0]);

      Object applicationInstance = getApplication.invoke(com_apple_eawt_Application, new Object[0]);

      Class<?> com_apple_eawt_ApplicationListener =
          Class.forName("com.apple.eawt.ApplicationListener");

      ApplicationListenerInvocationHandler handler = new ApplicationListenerInvocationHandler(app);
      Object proxy =
          Proxy.newProxyInstance(
              IApplication.class.getClassLoader(),
              new Class[] {com_apple_eawt_ApplicationListener},
              handler);

      //          com.apple.eawt.ApplicationAdapter applicationAdapter = new
      // com.apple.eawt.ApplicationAdapter()
      //                 {
      //                    public void handleAbout(ApplicationEvent e)
      //                    {
      //                       e.setHandled(true);
      //                       new AboutCommand(app).execute();
      //                    }
      //
      //                    public void handleOpenApplication(ApplicationEvent e)
      //                    {
      //                    }
      //
      //                    public void handleOpenFile(ApplicationEvent e)
      //                    {
      //                    }
      //
      //                    public void handlePreferences(ApplicationEvent e)
      //                    {
      //                       e.setHandled(true);
      //                       new GlobalPreferencesCommand(app).execute();
      //                    }
      //
      //                    public void handlePrintFile(ApplicationEvent e)
      //                    {
      //                    }
      //
      //                    public void handleQuit(ApplicationEvent e)
      //                    {
      //                       e.setHandled(true);
      //                       app.getMainFrame().dispose();
      //                    }
      //                 };

      Method addApplicationListener =
          com_apple_eawt_Application.getMethod(
              "addApplicationListener", new Class[] {com_apple_eawt_ApplicationListener});
      addApplicationListener.invoke(applicationInstance, new Object[] {proxy});

      Method addPreferencesMenuItem =
          com_apple_eawt_Application.getMethod("addPreferencesMenuItem", new Class[0]);
      addPreferencesMenuItem.invoke(applicationInstance, new Object[0]);

      Method setEnabledPreferencesMenu =
          com_apple_eawt_Application.getMethod(
              "setEnabledPreferencesMenu", new Class[] {Boolean.TYPE});
      setEnabledPreferencesMenu.invoke(applicationInstance, new Object[] {Boolean.TRUE});

      //          fApplication.addApplicationListener(applicationAdapter);
      //          fApplication.addPreferencesMenuItem();
      //          fApplication.setEnabledPreferencesMenu(true);
    } catch (Exception e) {
      s_log.error("initialize: encountered exception: " + e.getMessage(), e);
      throw new RuntimeException(e);
    }
  }
}
Ejemplo n.º 17
0
/**
 * @author gwg
 *     <p>This class provides the display components for handling Time data types, specifically SQL
 *     type TIME. 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 DataTypeTime extends BaseDataTypeComponent implements IDataTypeComponent {

  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(DataTypeTime.class);

  /** Logger for this class. */
  private static ILogger s_log = LoggerController.createLogger(DataTypeTime.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();

  /**
   * Name of this class, which is needed because the class name is needed by the static method
   * getControlPanel, so we cannot use something like getClass() to find this name.
   */
  private static final String thisClassName =
      "net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.DataTypeTime";

  /** Default date format */
  private static int DEFAULT_LOCALE_FORMAT = DateFormat.SHORT;

  /*
   * Properties settable by the user
   */
  // flag for whether we have already loaded the properties or not
  private static boolean propertiesAlreadyLoaded = false;

  // flag for whether to use the default Java format (true)
  // or the Locale-dependent format (false)
  private static boolean useJavaDefaultFormat = true;

  // which locale-dependent format to use; short, medium, long, or full
  private static int localeFormat = DEFAULT_LOCALE_FORMAT;

  // Whether to force user to enter dates in exact format or use heuristics to guess it
  private static boolean lenient = true;

  // The DateFormat object to use for all locale-dependent formatting.
  // This is reset each time the user changes the previous settings.
  private static ThreadSafeDateFormat dateFormat = new ThreadSafeDateFormat(localeFormat, true);
  private boolean _renderExceptionHasBeenLogged;

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

    loadProperties();
  }

  /**
   * Internal function to get the user-settable properties from the DTProperties, if they exist, and
   * to ensure that defaults are set if the properties have not yet been created.
   *
   * <p>This method may be called from different places depending on whether an instance of this
   * class is created before the user brings up the Session Properties window. In either case, the
   * data is static and is set only the first time we are called.
   */
  private static void loadProperties() {

    // set the property values
    // Note: this may have already been done by another instance of
    // this DataType created to handle a different column.
    if (propertiesAlreadyLoaded == false) {
      // get parameters previously set by user, or set default values
      useJavaDefaultFormat = true; // set to use the Java default
      String useJavaDefaultFormatString = DTProperties.get(thisClassName, "useJavaDefaultFormat");
      if (useJavaDefaultFormatString != null && useJavaDefaultFormatString.equals("false"))
        useJavaDefaultFormat = false;

      // get which locale-dependent format to use
      localeFormat = DateFormat.SHORT; // set to use the Java default
      String localeFormatString = DTProperties.get(thisClassName, "localeFormat");
      if (localeFormatString != null) localeFormat = Integer.parseInt(localeFormatString);

      // use lenient input or force user to enter exact format
      lenient = true; // set to allow less stringent input
      String lenientString = DTProperties.get(thisClassName, "lenient");
      if (lenientString != null && lenientString.equals("false")) lenient = false;

      /*
       * After loading the properties, we must initialize the dateFormat.
       * See Bug 3086444
       */
      initDateFormat(localeFormat, lenient);
    }
  }

  /** Defines the dateFormat with the specific format and lenient options */
  private static void initDateFormat(int format, boolean lenient) {
    dateFormat = new ThreadSafeDateFormat(format, true); // lenient is set next
    dateFormat.setLenient(lenient);
  }

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

  /*
   * 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) {
    // use the Java default date-to-string
    if (useJavaDefaultFormat == true || value == null)
      return (String) _renderer.renderObject(value);

    // use a date formatter
    try {
      return (String) _renderer.renderObject(dateFormat.format(value));
    } catch (Exception e) {
      if (false == _renderExceptionHasBeenLogged) {
        _renderExceptionHasBeenLogged = true;
        s_log.error("Could not format \"" + value + "\" as date 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) DataTypeTime.this._textComponent,
                          evt,
                          DataTypeTime.this._table);
                  CellDataPopup.showDialog(
                      DataTypeTime.this._table, DataTypeTime.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 {
      if (useJavaDefaultFormat) {
        // allow the user to enter just the hour or just hour and minute
        // and assume the un-entered values are 0
        int firstColon = value.indexOf(":");
        if (firstColon == -1) {
          // user just entered the hour, so append min & sec
          value = value + ":0:0";
        } else {
          // user entered hour an min. See if they also entered secs
          if (value.indexOf(":", firstColon + 1) == -1) {
            // user did not enter seconds
            value = value + ":0";
          }
        }
        Object obj = Time.valueOf(value);
        return obj;
      } else {
        // use the DateFormat to parse
        java.util.Date javaDate = dateFormat.parse(value);
        return new Time(javaDate.getTime());
      }
    } 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) DataTypeTime.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 == 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();
      }

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

      if (DataTypeTime.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
            DataTypeTime.this._textComponent.restoreText();
            e.consume();
          } else {
            // non-delete when null => clear field and add text
            DataTypeTime.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
              DataTypeTime.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 {

    Time data = rs.getTime(index);
    if (rs.wasNull()) return null;
    else return 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 IWhereClausePart getWhereClauseValue(Object value, ISQLDatabaseMetaData md) {
    if (value == null || value.toString() == null || value.toString().length() == 0)
      return new IsNullWhereClausePart(_colDef);
    else
      return new NoParameterWhereClausePart(
          _colDef, _colDef.getColumnName() + "={t '" + value.toString() + "'}");
  }

  /**
   * 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.setTime(position, ((Time) value));
    }
  }

  /**
   * 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 Time(new java.util.Date().getTime());
  }

  /*
   * 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();
  }

  /*
   * Property change control panel
   */

  /**
   * Generate a JPanel containing controls that allow the user to adjust the properties for this
   * DataType. All properties are static accross all instances of this DataType. However, the class
   * may choose to apply the information differentially, such as keeping a list (also entered by the
   * user) of table/column names for which certain properties should be used.
   *
   * <p>This is called ONLY if there is at least one property entered into the DTProperties for this
   * class.
   *
   * <p>Since this method is called by reflection on the Method object derived from this class, it
   * does not need to be included in the Interface. It would be nice to include this in the
   * Interface for consistancy, documentation, etc, but the Interface does not seem to like static
   * methods.
   */
  public static OkJPanel getControlPanel() {

    /*
     * If you add this method to one of the standard DataTypes in the
     * fw/datasetviewer/cellcomponent directory, you must also add the name
     * of that DataType class to the list in CellComponentFactory, method
     * getControlPanels, variable named initialClassNameList.
     * If the class is being registered with the factory using registerDataType,
     * then you should not include the class name in the list (it will be found
     * automatically), but if the DataType is part of the case statement in the
     * factory method getDataTypeObject, then it does need to be explicitly listed
     * in the getControlPanels method also.
     */

    // if this panel is called before any instances of the class have been
    // created, we need to load the properties from the DTProperties.
    loadProperties();

    return new TimeOkJPanel();
  }

  // Class that displays the various formats available for dates
  public static class DateFormatTypeCombo extends JComboBox {
    private static final long serialVersionUID = 1L;

    public DateFormatTypeCombo() {
      // i18n[dataTypeTime.full=Full ({0})]
      addItem(
          s_stringMgr.getString(
              "dataTypeTime.full",
              DateFormat.getTimeInstance(DateFormat.FULL).format(new java.util.Date())));
      // i18n[dataTypeTime.long=Long ({0})]
      addItem(
          s_stringMgr.getString(
              "dataTypeTime.long",
              DateFormat.getTimeInstance(DateFormat.LONG).format(new java.util.Date())));
      // i18n[dataTypeTime.medium=Medium ({0})]
      addItem(
          s_stringMgr.getString(
              "dataTypeTime.medium",
              DateFormat.getTimeInstance(DateFormat.MEDIUM).format(new java.util.Date())));
      // i18n[dataTypeTime.short=Short ({0})]
      addItem(
          s_stringMgr.getString(
              "dataTypeTime.short",
              DateFormat.getTimeInstance(DateFormat.SHORT).format(new java.util.Date())));
    }

    public void setSelectedIndex(int option) {
      if (option == DateFormat.SHORT) super.setSelectedIndex(3);
      else if (option == DateFormat.MEDIUM) super.setSelectedIndex(2);
      else if (option == DateFormat.LONG) super.setSelectedIndex(1);
      else super.setSelectedIndex(0);
    }

    public int getValue() {
      if (getSelectedIndex() == 3) return DateFormat.SHORT;
      else if (getSelectedIndex() == 2) return DateFormat.MEDIUM;
      else if (getSelectedIndex() == 1) return DateFormat.LONG;
      else return DateFormat.FULL;
    }
  }

  /**
   * Inner class that extends OkJPanel so that we can call the ok() method to save the data when the
   * user is happy with it.
   */
  private static class TimeOkJPanel extends OkJPanel {
    private static final long serialVersionUID = 1L;
    /*
     * GUI components - need to be here because they need to be
     * accessible from the event handlers to alter each other's state.
     */
    // check box for whether to use Java Default or a Locale-dependent format

    private JCheckBox useJavaDefaultFormatChk =
        // i18n[dataTypeTime.useDefaultFormat=Use default format ({0})]
        new JCheckBox(
            s_stringMgr.getString(
                "dataTypeTime.useDefaultFormat",
                new Time(new java.util.Date().getTime()).toString()));

    // label for the date format combo, used to enable/disable text
    // i18n[dataTypeTime.useDefaultFormat2= or locale-dependent format:]
    private RightLabel dateFormatTypeDropLabel =
        new RightLabel(s_stringMgr.getString("dataTypeTime.useDefaultFormat2"));

    // Combo box for read-all/read-part of blob
    private DateFormatTypeCombo dateFormatTypeDrop = new DateFormatTypeCombo();

    // checkbox for whether to interpret input leniently or not
    // i18n[dataTypeTime.inexact=allow inexact format on input]
    private JCheckBox lenientChk = new JCheckBox(s_stringMgr.getString("dataTypeTime.inexact"));

    public TimeOkJPanel() {

      /* set up the controls */
      // checkbox for Java default/non-default format
      useJavaDefaultFormatChk.setSelected(useJavaDefaultFormat);
      useJavaDefaultFormatChk.addChangeListener(
          new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
              dateFormatTypeDrop.setEnabled(!useJavaDefaultFormatChk.isSelected());
              dateFormatTypeDropLabel.setEnabled(!useJavaDefaultFormatChk.isSelected());
              lenientChk.setEnabled(!useJavaDefaultFormatChk.isSelected());
            }
          });

      // Combo box for read-all/read-part of blob
      dateFormatTypeDrop = new DateFormatTypeCombo();
      dateFormatTypeDrop.setSelectedIndex(localeFormat);

      // lenient checkbox
      lenientChk.setSelected(lenient);

      // handle cross-connection between fields
      dateFormatTypeDrop.setEnabled(!useJavaDefaultFormatChk.isSelected());
      dateFormatTypeDropLabel.setEnabled(!useJavaDefaultFormatChk.isSelected());
      lenientChk.setEnabled(!useJavaDefaultFormatChk.isSelected());

      /*
       * Create the panel and add the GUI items to it
       */

      setLayout(new GridBagLayout());

      // i18n[dataTypeTime.typeTime=Time   (SQL type 92)]
      setBorder(BorderFactory.createTitledBorder(s_stringMgr.getString("dataTypeTime.typeTime")));
      final GridBagConstraints gbc = new GridBagConstraints();
      gbc.fill = GridBagConstraints.HORIZONTAL;
      gbc.insets = new Insets(4, 4, 4, 4);
      gbc.anchor = GridBagConstraints.WEST;

      gbc.gridx = 0;
      gbc.gridy = 0;

      gbc.gridwidth = GridBagConstraints.REMAINDER;
      add(useJavaDefaultFormatChk, gbc);

      gbc.gridwidth = 1;
      gbc.gridx = 0;
      ++gbc.gridy;
      add(dateFormatTypeDropLabel, gbc);

      ++gbc.gridx;
      add(dateFormatTypeDrop, gbc);

      gbc.gridx = 0;
      ++gbc.gridy;
      add(lenientChk, gbc);
    } // end of constructor for inner class

    /** User has clicked OK in the surrounding JPanel, so save the current state of all variables */
    public void ok() {
      // get the values from the controls and set them in the static properties
      useJavaDefaultFormat = useJavaDefaultFormatChk.isSelected();
      DTProperties.put(
          thisClassName, "useJavaDefaultFormat", Boolean.valueOf(useJavaDefaultFormat).toString());

      localeFormat = dateFormatTypeDrop.getValue();
      dateFormat = new ThreadSafeDateFormat(localeFormat, true); // lenient is set next
      DTProperties.put(thisClassName, "localeFormat", Integer.toString(localeFormat));

      lenient = lenientChk.isSelected();
      dateFormat.setLenient(lenient);
      DTProperties.put(thisClassName, "lenient", Boolean.valueOf(lenient).toString());

      initDateFormat(localeFormat, lenient);
    }
  } // end of inner class
}
/**
 * This <CODE>ICommand</CODE> will dump the status of the application.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class DumpApplicationCommand implements ICommand {
  /** Logger for this class. */
  private final ILogger s_log = LoggerController.createLogger(DumpApplicationCommand.class);

  /** Prefix for temp file names. */
  private static final String PREFIX = "dump";

  /** Suffix for temp file names. */
  private static final String SUFFIX = "tmp";

  /** Used to separate lines of data in the dump file. */
  private static String SEP = "===================================================";

  /** Application. */
  private IApplication _app;

  /** File to dump application to. */
  private File _outFile;

  /** Message handler to write status/error info to. */
  private IMessageHandler _msgHandler;

  /** Internationalized strings for this class. */
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(DumpApplicationCommand.class);

  /**
   * Ctor.
   *
   * @param app Application
   * @param outFile File to dump session to.
   * @throws IllegalArgumentException Thrown if a <TT>null</TT> <TT>IApplication</TT> or
   *     <TT>File</TT> passed.
   */
  public DumpApplicationCommand(IApplication app, File outFile, IMessageHandler msgHandler) {
    super();
    if (app == null) {
      throw new IllegalArgumentException("Null IApplication passed");
    }
    if (outFile == null) {
      throw new IllegalArgumentException("Null File passed");
    }
    _app = app;
    _outFile = outFile;

    _msgHandler = msgHandler != null ? msgHandler : NullMessageHandler.getInstance();
  }

  /** Dump the application. */
  public void execute() {
    List<File> files = new ArrayList<File>();
    List<String> titles = new ArrayList<String>();
    synchronized (_app) {
      ApplicationStatusBean bean = new ApplicationStatusBean();
      bean.load(_app);
      try {
        files.add(createJavaBeanDumpFile(bean));
        // i18n[DumpApplicationCommand.title.status=Application Status Bean]
        titles.add(s_stringMgr.getString("DumpApplicationCommand.title.status"));
      } catch (Throwable th) {
        // i18n[DumpApplicationCommand.error.dumpingstatus=Error dumping Application Status bean]
        final String msg = s_stringMgr.getString("DumpApplicationCommand.error.dumpingstatus");
        _msgHandler.showMessage(msg);
        _msgHandler.showMessage(th, null);
        s_log.error(msg, th);
      }

      // Dump System Properties.
      try {
        File tempFile = File.createTempFile(PREFIX, SUFFIX);
        IDataSetViewer dest = new DataSetViewerTextFileDestination(tempFile);
        dest.show(new HashtableDataSet(System.getProperties()));
        files.add(tempFile);
        // i18n[DumpApplicationCommand.title.systemprops=System Properties]
        titles.add(s_stringMgr.getString("DumpApplicationCommand.title.systemprops"));
      } catch (Throwable th) {
        // i18n[DumpApplicationCommand.error.dumpingsystemprops=Error dumping metadata]
        final String msg = s_stringMgr.getString("DumpApplicationCommand.error.dumpingsystemprops");
        _msgHandler.showMessage(msg);
        _msgHandler.showMessage(th, null);
        s_log.error(msg, th);
      }

      // Dump drivers
      try {
        File tempFile = File.createTempFile(PREFIX, SUFFIX);
        _app.getDataCache().saveDrivers(tempFile);
        files.add(tempFile);
        // i18n[DumpApplicationCommand.title.drivers=Drivers]
        titles.add(s_stringMgr.getString("DumpApplicationCommand.title.drivers"));
      } catch (Throwable th) {
        // i18n[DumpApplicationCommand.error.dumpingdrivers=Error dumping drivers]
        final String msg = s_stringMgr.getString("DumpApplicationCommand.error.dumpingdrivers");
        _msgHandler.showMessage(msg);
        _msgHandler.showMessage(th, null);
        s_log.error(msg, th);
      }

      // Dump aliases.
      try {
        File tempFile = File.createTempFile(PREFIX, SUFFIX);
        _app.getDataCache().saveAliases(tempFile);
        files.add(tempFile);
        // i18n[DumpApplicationCommand.title.aliases=Aliases]
        titles.add(s_stringMgr.getString("DumpApplicationCommand.title.aliases"));
      } catch (Throwable th) {
        // i18n[DumpApplicationCommand.error.dumpingaliases=Error dumping aliases]
        final String msg = s_stringMgr.getString("DumpApplicationCommand.error.dumpingaliases");
        _msgHandler.showMessage(msg);
        _msgHandler.showMessage(th, null);
        s_log.error(msg, th);
      }

      // Dump sessions.
      final ISession[] sessions = _app.getSessionManager().getConnectedSessions();
      final DumpSessionCommand sessionCmd = new DumpSessionCommand();
      for (int i = 0; i < sessions.length; ++i) {
        try {
          File tempFile = File.createTempFile(PREFIX, SUFFIX);
          sessionCmd.setSession(sessions[i]);
          sessionCmd.setDumpFile(tempFile);
          sessionCmd.execute();
          files.add(tempFile);
          // i18n[DumpApplicationCommand.title.sessiondump=Session Dump: {0}]
          String title =
              s_stringMgr.getString(
                  "DumpApplicationCommand.title.sessiondump", sessions[i].getIdentifier());
          titles.add(title);
        } catch (Throwable th) {
          // i18n[DumpApplicationCommand.error.sessiondump=Error dumping sessions]
          final String msg = s_stringMgr.getString("DumpApplicationCommand.error.sessiondump");
          _msgHandler.showMessage(msg);
          _msgHandler.showMessage(th, null);
          s_log.error(msg, th);
        }
      }
    }

    combineTempFiles(titles, files);
    deleteTempFiles(files);
  }

  private void combineTempFiles(List<String> titles, List<File> files) {
    try {
      PrintWriter wtr = new PrintWriter(new FileWriter(_outFile));
      try {
        // i18n[DumpApplicationCommand.header=SQuirreL SQL Client Application Dump {0}]
        String header =
            s_stringMgr.getString(
                "DumpApplicationCommand.header", Calendar.getInstance().getTime());
        wtr.println(header);
        for (int i = 0, limit = files.size(); i < limit; ++i) {
          wtr.println();
          wtr.println();
          wtr.println(SEP);
          wtr.println(titles.get(i));
          wtr.println(SEP);
          BufferedReader rdr = new BufferedReader(new FileReader(files.get(i)));
          try {
            String line = null;
            while ((line = rdr.readLine()) != null) {
              wtr.println(line);
            }
          } finally {
            rdr.close();
          }
        }
      } finally {
        wtr.close();
      }
    } catch (IOException ex) {
      // i18n[DumpApplicationCommand.error.combiningtempfiles=Error combining temp files into dump
      // file]
      final String msg = s_stringMgr.getString("DumpApplicationCommand.error.combiningtempfiles");
      _msgHandler.showMessage(msg);
      _msgHandler.showMessage(ex.toString());
      s_log.error(msg, ex);
    }
  }

  private void deleteTempFiles(List<File> files) {
    for (int i = 0, limit = files.size(); i < limit; ++i) {
      if (!(files.get(i)).delete()) {
        // i18n[DumpApplicationCommand.error.deletetempfile=Couldn't delete temporary DumpSession
        // file]
        s_log.error(s_stringMgr.getString("DumpApplicationCommand.error.deletetempfile"));
      }
    }
  }

  private File createJavaBeanDumpFile(Object obj) throws IOException, XMLException {
    File tempFile = File.createTempFile(PREFIX, SUFFIX);
    XMLBeanWriter wtr = new XMLBeanWriter(obj);
    wtr.save(tempFile);

    return tempFile;
  }

  public static final class ApplicationStatusBean {
    private SquirrelPreferences _prefs;
    private PluginInfo[] _plugins;
    private String[] _appArgs;
    private String _version;
    private String _pluginLoc;
    private URLWrapper[] _pluginURLs;

    public ApplicationStatusBean() {
      super();
    }

    void load(IApplication app) {
      _prefs = app.getSquirrelPreferences();
      _plugins = app.getPluginManager().getPluginInformation();
      _appArgs = ApplicationArguments.getInstance().getRawArguments();
      _version = Version.getVersion();
      _pluginLoc = new ApplicationFiles().getPluginsDirectory().getAbsolutePath();
      URL[] urls = app.getPluginManager().getPluginURLs();
      _pluginURLs = new URLWrapper[urls.length];
      for (int i = 0; i < urls.length; ++i) {
        _pluginURLs[i] = new URLWrapper(urls[i]);
      }
    }

    public String getVersion() {
      return _version;
    }

    public SquirrelPreferences getPreferences() {
      return _prefs;
    }

    public String getPluginLocation() {
      return _pluginLoc;
    }

    public PluginInfo[] getPluginInfo() {
      return _plugins;
    }

    public URLWrapper[] getPluginURLs() {
      return _pluginURLs;
    }

    public PluginInfo getPluginInfo(int idx) throws ArrayIndexOutOfBoundsException {
      return _plugins[idx];
    }

    public String[] getApplicationArgument() {
      return _appArgs;
    }

    public String getApplicationArgument(int idx) throws ArrayIndexOutOfBoundsException {
      return _appArgs[idx];
    }
  }
}
Ejemplo n.º 19
0
/**
 * This class is only responsible for formatting source statements for Oracle views.
 *
 * @author manningr
 */
public abstract class OracleSourceTab extends BaseSourceTab {

  public static final int VIEW_TYPE = 0;
  public static final int STORED_PROC_TYPE = 1;
  public static final int TRIGGER_TYPE = 2;
  public static final int TABLE_TYPE = 3;

  protected int sourceType = VIEW_TYPE;

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

  private static CommentSpec[] commentSpecs =
      new CommentSpec[] {new CommentSpec("/*", "*/"), new CommentSpec("--", "\n")};

  private static CodeReformator formatter =
      new CodeReformator(CodeReformatorConfigFactory.createConfig(";", commentSpecs));

  public OracleSourceTab(String hint) {
    super(hint);
  }

  private final class OracleSourcePanel extends BaseSourcePanel {
    private static final long serialVersionUID = 7855991042669454322L;

    OracleSourcePanel(ISession session) {
      super(session);
    }

    public void load(ISession session, PreparedStatement stmt) {
      ResultSet rs = null;
      try {
        rs = stmt.executeQuery();
        StringBuffer buf = new StringBuffer(4096);
        while (rs.next()) {
          String line1 = rs.getString(1);
          String line2 = rs.getString(2);
          buf.append(line1.trim() + " ");
          buf.append(line2.trim() + " ");
        }
        String source = "";
        if (buf.length() == 0 && sourceType == TABLE_TYPE) {
          ISQLDatabaseMetaData md = session.getMetaData();
          // TODO: Need to define a better approach to getting dialects.
          // That is, we don't really want to ever prompt the user in this
          // case.  It's always Oracle.  Yet, we may have a new OracleDialect
          // at some point.
          HibernateDialect dialect = DialectFactory.getDialect("Oracle");

          // TODO: How to let the user customize this??
          CreateScriptPreferences prefs = new CreateScriptPreferences();

          ITableInfo[] tabs = new ITableInfo[] {(ITableInfo) getDatabaseObjectInfo()};
          List<ITableInfo> tables = Arrays.asList(tabs);
          // Handle table source
          List<String> sqls = dialect.getCreateTableSQL(tables, md, prefs, false);
          String sep = session.getQueryTokenizer().getSQLStatementSeparator();
          for (String sql : sqls) {
            buf.append(sql);
            buf.append(sep);
            buf.append("\n");
          }
          source = buf.toString();
        } else {
          if (s_log.isDebugEnabled()) {
            s_log.debug("View source before formatting: " + buf.toString());
          }
          source = formatter.reformat(buf.toString());
        }
        getTextArea().setText(source);
        getTextArea().setCaretPosition(0);
      } catch (SQLException ex) {
        s_log.error("Unexpected exception: " + ex.getMessage(), ex);
        session.showErrorMessage(ex);
      } finally {
        SQLUtilities.closeResultSet(rs);
      }
    }
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.client.session.mainpanel.objecttree.tabs.BaseSourceTab#createSourcePanel()
   */
  @Override
  protected BaseSourcePanel createSourcePanel() {
    return new OracleSourcePanel(getSession());
  }
}
/**
 * A custom DatatType implementation of IDataTypeComponent that can handle Postgres' xml type
 * (DataType value of 1111 and type name of 'xml').
 *
 * @author manningr
 */
public class PostgreSqlXmlTypeDataTypeComponent extends BaseDataTypeComponent
    implements IDataTypeComponent {

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

  /** Internationalized strings for this class. */
  private static final StringManager s_stringMgr =
      StringManagerFactory.getStringManager(PostgreSqlXmlTypeDataTypeComponent.class);

  /** I18n messages */
  static interface i18n {
    // i18n[PostgreSqlXmlTypeDataTypeComponent.cellErrorMsg=<Error: see log file>]
    String CELL_ERROR_MSG =
        s_stringMgr.getString("PostgreSqlXmlTypeDataTypeComponent.cellErrorMsg");
  }

  /* IDataTypeComponent interface methods

  /**
   * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#canDoFileIO()
   */
  public boolean canDoFileIO() {
    return true;
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getClassName()
   */
  public String getClassName() {
    return "java.lang.String";
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getDefaultValue(java.lang.String)
   */
  public Object getDefaultValue(String dbDefaultValue) {
    // At the moment, no default value
    if (s_log.isInfoEnabled()) {
      s_log.info("getDefaultValue: not yet implemented");
    }
    return null;
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getWhereClauseValue(java.lang.Object,
   *     net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData)
   */
  public IWhereClausePart getWhereClauseValue(Object value, ISQLDatabaseMetaData md) {
    // This results in "ERROR: operator does not exist: xml = unknown"
    // return _colDef.getLabel() + "='" + SQLUtilities.escapeLine(value.toString(), md) + "'";
    //
    // So don't use xml type column in where clauses if it has a non-null value for now (which is
    // what the
    // base class implementation provides).
    //
    return super.getWhereClauseValue(value, md);
  }

  /**
   * This Data Type can be edited in a table cell as long as there are no issues displaying the
   * data. If we detect our error message in the cell, then we should prevent the user from editing
   * the cell (our error message is not meant to be valid XML data; further, we don't want to let
   * the user whack their data with our tool accidentally)
   *
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#isEditableInCell(java.lang.Object)
   */
  public boolean isEditableInCell(Object originalValue) {
    return !i18n.CELL_ERROR_MSG.equals(originalValue);
  }

  /**
   * This Data Type can be edited in a popup as long as there are no issues displaying the data. If
   * we detect our error message in the cell, then we should prevent the user from editing the cell
   * (our error message is not meant to be valid XML data; further, we don't want to let the user
   * whack their data with our tool accidentally)
   *
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#isEditableInPopup(java.lang.Object)
   */
  public boolean isEditableInPopup(Object originalValue) {
    return !i18n.CELL_ERROR_MSG.equals(originalValue);
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#needToReRead(java.lang.Object)
   */
  public boolean needToReRead(Object originalValue) {
    return false;
  }

  /**
   * This class relies on reflection to get a handle to Oracle's XMLType which is made available
   * separately from the JDBC driver, so we cannot just assume the user will always have, nor can we
   * depend on it to compile SQuirreL code. So we remove this dependency here by using reflection
   * which doesn't require this library in order to just compile the code.
   *
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#readResultSet(java.sql.ResultSet,
   *     int, boolean)
   */
  public Object readResultSet(ResultSet rs, int idx, boolean limitDataRead) throws SQLException {
    Object result = null;
    try {
      result = rs.getString(idx);
      if (result == null || "".equals(result)) {
        return NULL_VALUE_PATTERN;
      }
    } catch (Exception e) {
      s_log.error("Unexpected exception while attempting to read PostgreSQL XML column", e);
    }
    if (result == null) {
      result = i18n.CELL_ERROR_MSG;
    }
    return result;
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#setPreparedStatementValue(java.sql.PreparedStatement,
   *     java.lang.Object, int)
   */
  public void setPreparedStatementValue(PreparedStatement pstmt, Object value, int position)
      throws SQLException {
    if (value == null || "".equals(value)) {
      pstmt.setNull(position, java.sql.Types.OTHER, "xml");
    } else {
      try {
        pstmt.setString(position, value.toString());
      } catch (Exception e) {
        s_log.error("setPreparedStatementValue: Unexpected exception - " + e.getMessage(), e);
      }
    }
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#useBinaryEditingPanel()
   */
  public boolean useBinaryEditingPanel() {
    return false;
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#areEqual(java.lang.Object,
   *     java.lang.Object)
   */
  public boolean areEqual(Object obj1, Object obj2) {
    return ((String) obj1).equals(obj2);
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.BaseDataTypeComponent#validateAndConvert(java.lang.String,
   *     java.lang.Object, java.lang.StringBuffer)
   */
  @Override
  public Object validateAndConvert(String value, Object originalValue, StringBuffer messageBuffer) {
    // handle null, which is shown as the special string "<null>"
    if (value.equals("<null>")) return null;

    // Do the conversion into the object in a safe manner
    return value; // Special case: the input is exactly the output
  }

  /**
   * @see
   *     net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.BaseDataTypeComponent#getKeyListener(IRestorableTextComponent)
   */
  @Override
  protected KeyListener getKeyListener(IRestorableTextComponent component) {
    boolean isNullable = false;
    int columnSize = -1;
    if (super._colDef != null) {
      isNullable = _colDef.isNullable();
      columnSize = _colDef.getColumnSize();
    }
    return new StringFieldKeyTextHandler(component, columnSize, isNullable, _beepHelper);
  }
}