<NME> DataSourcePropertyTester.java
<BEF> /*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2022 DBeaver Corp and others
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jkiss.dbeaver.ui.actions;

import org.eclipse.core.expressions.PropertyTester;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.*;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.exec.*;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.qm.QMUtils;
import org.jkiss.dbeaver.model.rm.RMConstants;
import org.jkiss.dbeaver.runtime.IPluginService;
import org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;
import org.jkiss.dbeaver.runtime.IPluginService;
import org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;
import org.jkiss.dbeaver.ui.ActionUtils;

/**
 * DatabaseEditorPropertyTester
 * DatabaseEditorPropertyTester
 */
public class DataSourcePropertyTester extends PropertyTester {

    static protected final Log log = Log.getLog(DataSourcePropertyTester.class);

    public static final String NAMESPACE = "org.jkiss.dbeaver.core.datasource";
    public static final String PROP_CONNECTED = "connected";
    public static final String PROP_TRANSACTIONAL = "transactional";
    public static final String PROP_SYNCHRONIZABLE = "synchronizable";
    public static final String PROP_SUPPORTS_TRANSACTIONS = "supportsTransactions";
    public static final String PROP_TRANSACTION_ACTIVE = "transactionActive";
    public static final String PROP_EDITABLE = "editable";
    public static final String PROP_PROJECT_RESOURCE_EDITABLE = "projectResourceEditable";
    public static final String PROP_PROJECT_RESOURCE_VIEWABLE = "projectResourceViewable";

    @Override
    public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
        try {
            // Get root datasource node (we don't want to
            while (receiver instanceof DBNDatabaseNode && !(receiver instanceof DBNDataSource)) {
                receiver = ((DBNDatabaseNode) receiver).getParentNode();
            }
            if (!(receiver instanceof DBPContextProvider)) {
                return false;
            }
            DBPContextProvider contextProvider = (DBPContextProvider) receiver;
            @Nullable
            DBCExecutionContext context = contextProvider.getExecutionContext();
            @Nullable
            DBPProject resourceProject = receiver instanceof IEditorPart
                ? EditorUtils.getFileProject(((IEditorPart) receiver).getEditorInput())
                : null;
            
            switch (property) {
                case PROP_CONNECTED:
                    boolean isConnected;
                    if (context != null) {
                        isConnected = context.getDataSource().getContainer().isConnected();
                    } else if (receiver instanceof IDataSourceContainerProvider) {
                        DBPDataSourceContainer container = ((IDataSourceContainerProvider) receiver).getDataSourceContainer();
                        isConnected = container != null && container.isConnected();
                    } else {
                        isConnected = false;
                    }
                    boolean checkConnected = Boolean.TRUE.equals(expectedValue);
                    return checkConnected == isConnected;
                case PROP_TRANSACTIONAL: {
                    if (context == null) {
                        return false;
                    }
                    if (!context.isConnected()) {
                        return Boolean.FALSE.equals(expectedValue);
                    }
                    DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
                    try {
                        return txnManager != null && Boolean.valueOf(!txnManager.isAutoCommit()).equals(expectedValue);
                    } catch (DBCException e) {
                        log.debug("Error checking auto-commit state", e);
                        return false;
                    }
                }
                case PROP_SYNCHRONIZABLE:
                    if (context == null || !context.isConnected()) {
                        return false;
                    } else {
                        return context.getDataSource().getContainer().getDriver()
                            .getDataSourceProvider() instanceof DBPDataSourceProviderSynchronizable;
                    }
                case PROP_SUPPORTS_TRANSACTIONS: {
                    if (context == null || !context.isConnected()) {
                        return false;
                    }
                    DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
                    return txnManager != null && txnManager.isSupportsTransactions();
                }
                case PROP_TRANSACTION_ACTIVE: {
                    if (context != null && context.isConnected()) {
                        DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
                        return txnManager != null && !txnManager.isAutoCommit();
//                        boolean active = QMUtils.isTransactionActive(context);
//                        return Boolean.valueOf(active).equals(expectedValue);
                    }
                    return Boolean.FALSE.equals(expectedValue);
                }
                case PROP_EDITABLE:
                    return resourceProject == null || resourceProject.hasRealmPermission(RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT);
                case PROP_PROJECT_RESOURCE_EDITABLE:
                    return resourceProject == null || resourceProject.hasRealmPermission(RMConstants.PERMISSION_PROJECT_RESOURCE_EDIT);
                case PROP_PROJECT_RESOURCE_VIEWABLE:
                    return resourceProject == null || resourceProject.hasRealmPermission(RMConstants.PERMISSION_PROJECT_RESOURCE_VIEW);
            }
            return false;
        } catch (Exception e) {
            log.debug("Error testing property " + property + ": " + e.getMessage());
            return false;
        }
    }

    public static void firePropertyChange(String propName)
    {
        ActionUtils.evaluatePropertyState(NAMESPACE + "." + propName);
    }

    public static class QMService implements IPluginService {

        private QMEventsHandler qmHandler;

        public synchronized void handleTransactionCommit(@NotNull DBCExecutionContext context)
        {
            DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);
        }

        @Override
        public synchronized void handleTransactionRollback(@NotNull DBCExecutionContext context, DBCSavepoint savepoint)
        {
            DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);
        }

        @Override
        @NotNull
        @Override
        public String getHandlerName() {
        }
    }

}
                DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);
                updateEditorsDirtyFlag();
            });
        }

        @Override
        public synchronized void handleStatementExecuteBegin(@NotNull DBCStatement statement) {
            updateUI(() -> DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE));
        }

        private void updateUI(Runnable runnable) {
            UIUtils.asyncExec(runnable);
        }
    }

    /**
     * This is a hack.
     * Editors should listen txn commit/rollback and update their dirty flag (active transaction makes SQL editor dirty).
     * Making each editor QM listener is too expensive.
     */
    private static void updateEditorsDirtyFlag() {
        IWorkbenchWindow workbenchWindow = UIUtils.findActiveWorkbenchWindow();
        if (workbenchWindow == null) {
            return;
        }
        IEditorReference[] editors = workbenchWindow.getActivePage().getEditorReferences();
        for (IEditorReference ref : editors) {
            final IEditorPart editor = ref.getEditor(false);
            if (editor instanceof SQLEditor) {
                UIUtils.asyncExec(((SQLEditor) editor)::updateDirtyFlag);
            }
        }
    }

}
<MSG> #1484 SQL Editor dirty flag update on commit/rollback


Former-commit-id: c003855b761017bcaea874db1c9a0a19ed98ae2b
<DFF> @@ -17,6 +17,8 @@
 package org.jkiss.dbeaver.ui.actions;
 
 import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.commands.ICommandService;
 import org.jkiss.code.NotNull;
@@ -33,6 +35,7 @@ import org.jkiss.dbeaver.model.qm.QMUtils;
 import org.jkiss.dbeaver.runtime.IPluginService;
 import org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;
 import org.jkiss.dbeaver.ui.ActionUtils;
+import org.jkiss.dbeaver.ui.editors.sql.SQLEditor;
 
 /**
  * DatabaseEditorPropertyTester
@@ -147,12 +150,14 @@ public class DataSourcePropertyTester extends PropertyTester
         public synchronized void handleTransactionCommit(@NotNull DBCExecutionContext context)
         {
             DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);
+            updateEditorsDirtyFlag();
         }
 
         @Override
         public synchronized void handleTransactionRollback(@NotNull DBCExecutionContext context, DBCSavepoint savepoint)
         {
             DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);
+            updateEditorsDirtyFlag();
         }
 
         @Override
@@ -162,4 +167,24 @@ public class DataSourcePropertyTester extends PropertyTester
         }
     }
 
+    /**
+     * This is a hack.
+     * Editors should listen txn commit/rollback and update their dirty flag (active transaction makes SQL editor dirty).
+     * Making each editor QM listener is too expensive.
+     */
+    private static void updateEditorsDirtyFlag() {
+        IEditorReference[] editors = DBeaverUI.getActiveWorkbenchWindow().getActivePage().getEditorReferences();
+        for (IEditorReference ref : editors) {
+            final IEditorPart editor = ref.getEditor(false);
+            if (editor instanceof SQLEditor) {
+                DBeaverUI.asyncExec(new Runnable() {
+                    @Override
+                    public void run() {
+                        ((SQLEditor) editor).updateDirtyFlag();
+                    }
+                });
+            }
+        }
+    }
+
 }
\ No newline at end of file

{'commit': 'diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/actions/DataSourcePropertyTester.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/actions/DataSourcePropertyTester.java\nindex 30e84aec2f..0ddae5276e 100644\n--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/actions/DataSourcePropertyTester.java\n+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/actions/DataSourcePropertyTester.java\n@@ -17,6 +17,8 @@\n package org.jkiss.dbeaver.ui.actions;\r\n \r\n import org.eclipse.core.expressions.PropertyTester;\r\n+import org.eclipse.ui.IEditorPart;\r\n+import org.eclipse.ui.IEditorReference;\r\n import org.eclipse.ui.PlatformUI;\r\n import org.eclipse.ui.commands.ICommandService;\r\n import org.jkiss.code.NotNull;\r\n@@ -33,6 +35,7 @@ import org.jkiss.dbeaver.model.qm.QMUtils;\n import org.jkiss.dbeaver.runtime.IPluginService;\r\n import org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;\r\n import org.jkiss.dbeaver.ui.ActionUtils;\r\n+import org.jkiss.dbeaver.ui.editors.sql.SQLEditor;\r\n \r\n /**\r\n  * DatabaseEditorPropertyTester\r\n@@ -147,12 +150,14 @@ public class DataSourcePropertyTester extends PropertyTester\n         public synchronized void handleTransactionCommit(@NotNull DBCExecutionContext context)\r\n         {\r\n             DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\r\n+            updateEditorsDirtyFlag();\r\n         }\r\n \r\n         @Override\r\n         public synchronized void handleTransactionRollback(@NotNull DBCExecutionContext context, DBCSavepoint savepoint)\r\n         {\r\n             DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\r\n+            updateEditorsDirtyFlag();\r\n         }\r\n \r\n         @Override\r\n@@ -162,4 +167,24 @@ public class DataSourcePropertyTester extends PropertyTester\n         }\r\n     }\r\n \r\n+    /**\r\n+     * This is a hack.\r\n+     * Editors should listen txn commit/rollback and update their dirty flag (active transaction makes SQL editor dirty).\r\n+     * Making each editor QM listener is too expensive.\r\n+     */\r\n+    private static void updateEditorsDirtyFlag() {\r\n+        IEditorReference[] editors = DBeaverUI.getActiveWorkbenchWindow().getActivePage().getEditorReferences();\r\n+        for (IEditorReference ref : editors) {\r\n+            final IEditorPart editor = ref.getEditor(false);\r\n+            if (editor instanceof SQLEditor) {\r\n+                DBeaverUI.asyncExec(new Runnable() {\r\n+                    @Override\r\n+                    public void run() {\r\n+                        ((SQLEditor) editor).updateDirtyFlag();\r\n+                    }\r\n+                });\r\n+            }\r\n+        }\r\n+    }\r\n+\r\n }\n\\ No newline at end of file\ndiff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditor.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditor.java\nindex f882662f1f..50a23ce488 100644\n--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditor.java\n+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/editors/sql/SQLEditor.java\n@@ -1928,7 +1928,7 @@ public class SQLEditor extends SQLEditorBase implements\n \n         @Override\n         public void handleResultSetChange() {\n-            firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);\n+            updateDirtyFlag();\n         }\n     }\n \n@@ -2012,7 +2012,7 @@ public class SQLEditor extends SQLEditorBase implements\n                     // Finish query\n                     processQueryResult(session, result);\n                     // Update dirty flag\n-                    firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);\n+                    updateDirtyFlag();\n                 }\n             });\n         }\n@@ -2090,6 +2090,10 @@ public class SQLEditor extends SQLEditorBase implements\n         }\n     }\n \n+    public void updateDirtyFlag() {\n+        firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);\n+    }\n+\n     private class FindReplaceTarget extends DynamicFindReplaceTarget {\n         private boolean lastFocusInEditor = true;\n         @Override\n', 'commit_subject': '#1484 SQL Editor dirty flag update on commit/rollback', 'repo_name': 'dbeaver/dbeaver', 'committer': {'name': 'Serge Rider', 'email': 'c9ddb6c25a8f77bd31ba26bb8332c4e1aa2c6778@jkiss.org', 'time_sec': '1491423133', 'tz_offset': '180', 'date': {'seconds': '1491423133'}}, 'commit_tree': 'a2ff6e03fbd84c52c254300a9d63280d2712c31e', 'commit_message': '#1484 SQL Editor dirty flag update on commit/rollback\n\n\nFormer-commit-id: c003855b761017bcaea874db1c9a0a19ed98ae2b', 'language_name': 'JavaScript', 'license': 'apache-2.0', 'hunks': '@@ -17,6 +17,8 @@\n package org.jkiss.dbeaver.ui.actions;\r\n \r\n import org.eclipse.core.expressions.PropertyTester;\r\n+import org.eclipse.ui.IEditorPart;\r\n+import org.eclipse.ui.IEditorReference;\r\n import org.eclipse.ui.PlatformUI;\r\n import org.eclipse.ui.commands.ICommandService;\r\n import org.jkiss.code.NotNull;\r\n@@ -33,6 +35,7 @@ import org.jkiss.dbeaver.model.qm.QMUtils;\n import org.jkiss.dbeaver.runtime.IPluginService;\r\n import org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;\r\n import org.jkiss.dbeaver.ui.ActionUtils;\r\n+import org.jkiss.dbeaver.ui.editors.sql.SQLEditor;\r\n \r\n /**\r\n  * DatabaseEditorPropertyTester\r\n@@ -147,12 +150,14 @@ public class DataSourcePropertyTester extends PropertyTester\n         public synchronized void handleTransactionCommit(@NotNull DBCExecutionContext context)\r\n         {\r\n             DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\r\n+            updateEditorsDirtyFlag();\r\n         }\r\n \r\n         @Override\r\n         public synchronized void handleTransactionRollback(@NotNull DBCExecutionContext context, DBCSavepoint savepoint)\r\n         {\r\n             DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\r\n+            updateEditorsDirtyFlag();\r\n         }\r\n \r\n         @Override\r\n@@ -162,4 +167,24 @@ public class DataSourcePropertyTester extends PropertyTester\n         }\r\n     }\r\n \r\n+    /**\r\n+     * This is a hack.\r\n+     * Editors should listen txn commit/rollback and update their dirty flag (active transaction makes SQL editor dirty).\r\n+     * Making each editor QM listener is too expensive.\r\n+     */\r\n+    private static void updateEditorsDirtyFlag() {\r\n+        IEditorReference[] editors = DBeaverUI.getActiveWorkbenchWindow().getActivePage().getEditorReferences();\r\n+        for (IEditorReference ref : editors) {\r\n+            final IEditorPart editor = ref.getEditor(false);\r\n+            if (editor instanceof SQLEditor) {\r\n+                DBeaverUI.asyncExec(new Runnable() {\r\n+                    @Override\r\n+                    public void run() {\r\n+                        ((SQLEditor) editor).updateDirtyFlag();\r\n+                    }\r\n+                });\r\n+            }\r\n+        }\r\n+    }\r\n+\r\n }\n\\ No newline at end of file\n', 'addition_count': 25, 'deletion_count': 0, 'src_file': 'a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/actions/DataSourcePropertyTester.java', 'tgt_file': 'b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/actions/DataSourcePropertyTester.java', 'file_extension': '.java', 'before_file': '/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2022 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jkiss.dbeaver.ui.actions;\r\n\r\nimport org.eclipse.core.expressions.PropertyTester;\r\nimport org.eclipse.ui.PlatformUI;\r\nimport org.eclipse.ui.commands.ICommandService;\r\nimport org.jkiss.code.NotNull;\r\nimport org.jkiss.dbeaver.Log;\nimport org.jkiss.dbeaver.model.*;\nimport org.jkiss.dbeaver.model.app.DBPProject;\nimport org.jkiss.dbeaver.model.exec.*;\nimport org.jkiss.dbeaver.model.navigator.DBNDataSource;\nimport org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;\nimport org.jkiss.dbeaver.model.qm.QMUtils;\nimport org.jkiss.dbeaver.model.rm.RMConstants;\nimport org.jkiss.dbeaver.runtime.IPluginService;\nimport org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;\nimport org.jkiss.dbeaver.runtime.IPluginService;\r\nimport org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler;\r\nimport org.jkiss.dbeaver.ui.ActionUtils;\r\n\r\n/**\r\n * DatabaseEditorPropertyTester\r\n * DatabaseEditorPropertyTester\n */\npublic class DataSourcePropertyTester extends PropertyTester {\n\n    static protected final Log log = Log.getLog(DataSourcePropertyTester.class);\n\n    public static final String NAMESPACE = "org.jkiss.dbeaver.core.datasource";\n    public static final String PROP_CONNECTED = "connected";\n    public static final String PROP_TRANSACTIONAL = "transactional";\n    public static final String PROP_SYNCHRONIZABLE = "synchronizable";\n    public static final String PROP_SUPPORTS_TRANSACTIONS = "supportsTransactions";\n    public static final String PROP_TRANSACTION_ACTIVE = "transactionActive";\n    public static final String PROP_EDITABLE = "editable";\n    public static final String PROP_PROJECT_RESOURCE_EDITABLE = "projectResourceEditable";\n    public static final String PROP_PROJECT_RESOURCE_VIEWABLE = "projectResourceViewable";\n\n    @Override\n    public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {\n        try {\n            // Get root datasource node (we don\'t want to\n            while (receiver instanceof DBNDatabaseNode && !(receiver instanceof DBNDataSource)) {\n                receiver = ((DBNDatabaseNode) receiver).getParentNode();\n            }\n            if (!(receiver instanceof DBPContextProvider)) {\n                return false;\n            }\n            DBPContextProvider contextProvider = (DBPContextProvider) receiver;\n            @Nullable\n            DBCExecutionContext context = contextProvider.getExecutionContext();\n            @Nullable\n            DBPProject resourceProject = receiver instanceof IEditorPart\n                ? EditorUtils.getFileProject(((IEditorPart) receiver).getEditorInput())\n                : null;\n            \n            switch (property) {\n                case PROP_CONNECTED:\n                    boolean isConnected;\n                    if (context != null) {\n                        isConnected = context.getDataSource().getContainer().isConnected();\n                    } else if (receiver instanceof IDataSourceContainerProvider) {\n                        DBPDataSourceContainer container = ((IDataSourceContainerProvider) receiver).getDataSourceContainer();\n                        isConnected = container != null && container.isConnected();\n                    } else {\n                        isConnected = false;\n                    }\n                    boolean checkConnected = Boolean.TRUE.equals(expectedValue);\n                    return checkConnected == isConnected;\n                case PROP_TRANSACTIONAL: {\n                    if (context == null) {\n                        return false;\n                    }\n                    if (!context.isConnected()) {\n                        return Boolean.FALSE.equals(expectedValue);\n                    }\n                    DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);\n                    try {\n                        return txnManager != null && Boolean.valueOf(!txnManager.isAutoCommit()).equals(expectedValue);\n                    } catch (DBCException e) {\n                        log.debug("Error checking auto-commit state", e);\n                        return false;\n                    }\n                }\n                case PROP_SYNCHRONIZABLE:\n                    if (context == null || !context.isConnected()) {\n                        return false;\n                    } else {\n                        return context.getDataSource().getContainer().getDriver()\n                            .getDataSourceProvider() instanceof DBPDataSourceProviderSynchronizable;\n                    }\n                case PROP_SUPPORTS_TRANSACTIONS: {\n                    if (context == null || !context.isConnected()) {\n                        return false;\n                    }\n                    DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);\n                    return txnManager != null && txnManager.isSupportsTransactions();\n                }\n                case PROP_TRANSACTION_ACTIVE: {\n                    if (context != null && context.isConnected()) {\n                        DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);\n                        return txnManager != null && !txnManager.isAutoCommit();\n//                        boolean active = QMUtils.isTransactionActive(context);\n//                        return Boolean.valueOf(active).equals(expectedValue);\n                    }\n                    return Boolean.FALSE.equals(expectedValue);\n                }\n                case PROP_EDITABLE:\n                    return resourceProject == null || resourceProject.hasRealmPermission(RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT);\n                case PROP_PROJECT_RESOURCE_EDITABLE:\n                    return resourceProject == null || resourceProject.hasRealmPermission(RMConstants.PERMISSION_PROJECT_RESOURCE_EDIT);\n                case PROP_PROJECT_RESOURCE_VIEWABLE:\n                    return resourceProject == null || resourceProject.hasRealmPermission(RMConstants.PERMISSION_PROJECT_RESOURCE_VIEW);\n            }\n            return false;\n        } catch (Exception e) {\n            log.debug("Error testing property " + property + ": " + e.getMessage());\n            return false;\n        }\n    }\n\n    public static void firePropertyChange(String propName)\n    {\n        ActionUtils.evaluatePropertyState(NAMESPACE + "." + propName);\n    }\n\n    public static class QMService implements IPluginService {\n\n        private QMEventsHandler qmHandler;\n\n        public synchronized void handleTransactionCommit(@NotNull DBCExecutionContext context)\r\n        {\r\n            DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\r\n        }\r\n\r\n        @Override\r\n        public synchronized void handleTransactionRollback(@NotNull DBCExecutionContext context, DBCSavepoint savepoint)\r\n        {\r\n            DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\r\n        }\r\n\r\n        @Override\r\n        @NotNull\n        @Override\n        public String getHandlerName() {\n        }\r\n    }\r\n\r\n}\n                DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE);\n                updateEditorsDirtyFlag();\n            });\n        }\n\n        @Override\n        public synchronized void handleStatementExecuteBegin(@NotNull DBCStatement statement) {\n            updateUI(() -> DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE));\n        }\n\n        private void updateUI(Runnable runnable) {\n            UIUtils.asyncExec(runnable);\n        }\n    }\n\n    /**\n     * This is a hack.\n     * Editors should listen txn commit/rollback and update their dirty flag (active transaction makes SQL editor dirty).\n     * Making each editor QM listener is too expensive.\n     */\n    private static void updateEditorsDirtyFlag() {\n        IWorkbenchWindow workbenchWindow = UIUtils.findActiveWorkbenchWindow();\n        if (workbenchWindow == null) {\n            return;\n        }\n        IEditorReference[] editors = workbenchWindow.getActivePage().getEditorReferences();\n        for (IEditorReference ref : editors) {\n            final IEditorPart editor = ref.getEditor(false);\n            if (editor instanceof SQLEditor) {\n                UIUtils.asyncExec(((SQLEditor) editor)::updateDirtyFlag);\n            }\n        }\n    }\n\n}', 'message': '#1484 SQL Editor dirty flag update on commit/rollback\n\n\nFormer-commit-id: c003855b761017bcaea874db1c9a0a19ed98ae2b', 'author_name': 'Serge Rider', 'lang': 'jkiss'}