/** * 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); } } }
/** * This action will "quote" 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); } } } }
/** * 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; } } } }
/** * 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]"><[email protected]></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 } } } } }
/** * 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()); } }
/** @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; } } }
/** * 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"); } } }
/** * 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(); } }
/** * 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(); } } } }
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"); } }
/** @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); } } }
/** * @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]; } } }
/** * 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); } }