/*
* Copyright (c) 2016 David Boissier.
*
* 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.codinjutsu.tools.mongo.view;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.TreeExpander;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.containers.ArrayListSet;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.tree.TreeUtil;
import org.codinjutsu.tools.mongo.MongoConfiguration;
import org.codinjutsu.tools.mongo.ServerConfiguration;
import org.codinjutsu.tools.mongo.logic.ConfigurationException;
import org.codinjutsu.tools.mongo.logic.MongoManager;
import org.codinjutsu.tools.mongo.logic.Notifier;
import org.codinjutsu.tools.mongo.model.MongoCollection;
import org.codinjutsu.tools.mongo.model.MongoDatabase;
import org.codinjutsu.tools.mongo.model.MongoQueryOptions;
import org.codinjutsu.tools.mongo.model.MongoServer;
import org.codinjutsu.tools.mongo.utils.GuiUtils;
import org.codinjutsu.tools.mongo.view.action.*;
import org.codinjutsu.tools.mongo.view.editor.MongoFileSystem;
import org.codinjutsu.tools.mongo.view.editor.MongoObjectFile;
import org.codinjutsu.tools.mongo.view.model.navigation.Navigation;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static org.codinjutsu.tools.mongo.utils.GuiUtils.showNotification;
public class MongoExplorerPanel extends JPanel implements Disposable {
private static final URL pluginSettingsUrl = GuiUtils.isUnderDarcula() ? GuiUtils.getIconResource("pluginSettings_dark.png") : GuiUtils.getIconResource("pluginSettings.png");
private JPanel rootPanel;
private JPanel treePanel;
private Tree mongoTree;
private JPanel toolBarPanel;
private final Project project;
private final MongoManager mongoManager;
private final Notifier notifier;
public MongoExplorerPanel(Project project, MongoManager mongoManager, Notifier notifier) {
this.project = project;
this.mongoManager = mongoManager;
this.notifier = notifier;
treePanel.setLayout(new BorderLayout());
mongoTree = createTree();
mongoTree.setCellRenderer(new MongoTreeRenderer());
mongoTree.setName("mongoTree");
JBScrollPane mongoTreeScrollPane = new JBScrollPane(mongoTree);
setLayout(new BorderLayout());
treePanel.add(mongoTreeScrollPane, BorderLayout.CENTER);
add(rootPanel, BorderLayout.CENTER);
toolBarPanel.setLayout(new BorderLayout());
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
reloadAllServerConfigurations();
}
});
}
public void reloadAllServerConfigurations() {
this.mongoManager.cleanUpServers();
mongoTree.setRootVisible(false);
List<ServerConfiguration> serverConfigurations = getServerConfigurations();
if (serverConfigurations.size() == 0) {
mongoTree.setModel(null);
return;
}
final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
mongoTree.setModel(new DefaultTreeModel(rootNode));
for (ServerConfiguration serverConfiguration : serverConfigurations) {
MongoServer mongoServer = new MongoServer(serverConfiguration);
this.mongoManager.registerServer(mongoServer);
DefaultMutableTreeNode serverNode = new DefaultMutableTreeNode(mongoServer);
rootNode.add(serverNode);
if (serverConfiguration.isConnectOnIdeStartup()) {
this.reloadServerConfiguration(serverNode, false);
}
}
new TreeSpeedSearch(mongoTree, new Convertor<TreePath, String>() {
@Override
public String convert(TreePath treePath) {
final DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath.getLastPathComponent();
final Object userObject = node.getUserObject();
if (userObject instanceof MongoDatabase) {
return ((MongoDatabase) userObject).getName();
}
if (userObject instanceof MongoCollection) {
return ((MongoCollection) userObject).getName();
}
return "<empty>";
}
});
TreeUtil.expand(mongoTree, 2);
}
public void reloadServerConfiguration(final DefaultMutableTreeNode serverNode, final boolean expandAfterLoading) {
mongoTree.setPaintBusy(true);
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final MongoServer mongoServer = (MongoServer) serverNode.getUserObject();
try {
mongoManager.loadServer(mongoServer);
GuiUtils.runInSwingThread(new Runnable() {
@Override
public void run() {
mongoTree.invalidate();
serverNode.removeAllChildren();
addDatabasesIfAny(mongoServer, serverNode);
((DefaultTreeModel) mongoTree.getModel()).reload(serverNode);
mongoTree.revalidate();
if (expandAfterLoading) {
GuiUtils.expand(mongoTree, TreeUtil.getPathFromRoot(serverNode), 1);
}
}
});
} catch (ConfigurationException confEx) {
mongoServer.setStatus(MongoServer.Status.ERROR);
String errorMessage = String.format("Error when connecting to %s", mongoServer.getLabel());
notifier.notifyError(errorMessage + ": " + confEx.getMessage());
showNotification(treePanel, MessageType.ERROR, errorMessage, Balloon.Position.atLeft);
} finally {
mongoTree.setPaintBusy(false);
}
}
});
}
private void addDatabasesIfAny(MongoServer mongoServer, DefaultMutableTreeNode serverNode) {
for (MongoDatabase mongoDatabase : mongoServer.getDatabases()) {
DefaultMutableTreeNode databaseNode = new DefaultMutableTreeNode(mongoDatabase);
for (MongoCollection collection : mongoDatabase.getCollections()) {
if (shouldNotIgnore(collection, mongoServer.getConfiguration())) {
databaseNode.add(new DefaultMutableTreeNode(collection));
}
}
serverNode.add(databaseNode);
}
}
private List<ServerConfiguration> getServerConfigurations() {
return MongoConfiguration.getInstance(project).getServerConfigurations();
}
private static boolean shouldNotIgnore(MongoCollection collection, ServerConfiguration configuration) {
return !configuration.getCollectionsToIgnore().contains(collection.getName());
}
public void installActions() {
final TreeExpander treeExpander = new TreeExpander() {
@Override
public void expandAll() {
MongoExplorerPanel.this.expandAll();
}
@Override
public boolean canExpand() {
return !getServerConfigurations().isEmpty();
}
@Override
public void collapseAll() {
MongoExplorerPanel.this.collapseAll();
}
@Override
public boolean canCollapse() {
return !getServerConfigurations().isEmpty();
}
};
CommonActionsManager actionsManager = CommonActionsManager.getInstance();
final AnAction expandAllAction = actionsManager.createExpandAllAction(treeExpander, rootPanel);
final AnAction collapseAllAction = actionsManager.createCollapseAllAction(treeExpander, rootPanel);
Disposer.register(this, new Disposable() {
@Override
public void dispose() {
collapseAllAction.unregisterCustomShortcutSet(rootPanel);
expandAllAction.unregisterCustomShortcutSet(rootPanel);
}
});
DefaultActionGroup actionGroup = new DefaultActionGroup("MongoExplorerGroup", false);
ViewCollectionValuesAction viewCollectionValuesAction = new ViewCollectionValuesAction(this);
RefreshServerAction refreshServerAction = new RefreshServerAction(this);
if (ApplicationManager.getApplication() != null) {
actionGroup.add(refreshServerAction);
actionGroup.add(new MongoConsoleAction(this));
actionGroup.add(viewCollectionValuesAction);
actionGroup.add(expandAllAction);
actionGroup.add(collapseAllAction);
actionGroup.addSeparator();
actionGroup.add(new OpenPluginSettingsAction());
}
GuiUtils.installActionGroupInToolBar(actionGroup, toolBarPanel, ActionManager.getInstance(), "MongoExplorerActions", true);
DefaultActionGroup actionPopupGroup = new DefaultActionGroup("MongoExplorerPopupGroup", true);
if (ApplicationManager.getApplication() != null) {
actionPopupGroup.add(refreshServerAction);
actionPopupGroup.add(viewCollectionValuesAction);
actionPopupGroup.add(new DropCollectionAction(this));
actionPopupGroup.add(new DropDatabaseAction(this));
}
PopupHandler.installPopupHandler(mongoTree, actionPopupGroup, "POPUP", ActionManager.getInstance());
mongoTree.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent mouseEvent) {
if (!(mouseEvent.getSource() instanceof JTree)) {
return;
}
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) mongoTree.getLastSelectedPathComponent();
if (treeNode == null) {
return;
}
if (mouseEvent.getClickCount() == 2) {
if (treeNode.getUserObject() instanceof MongoServer && treeNode.getChildCount() == 0) {
reloadServerConfiguration(getSelectedServerNode(), true);
}
if (treeNode.getUserObject() instanceof MongoCollection) {
loadSelectedCollectionValues();
}
}
}
});
}
private void expandAll() {
TreeUtil.expandAll(mongoTree);
}
private void collapseAll() {
TreeUtil.collapseAll(mongoTree, 1);
}
@Override
public void dispose() {
mongoTree = null;
}
public DefaultMutableTreeNode getSelectedServerNode() {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) mongoTree.getLastSelectedPathComponent();
if (treeNode != null) {
Object userObject = treeNode.getUserObject();
if (userObject instanceof MongoCollection) {
return (DefaultMutableTreeNode) treeNode.getParent().getParent();
}
if (userObject instanceof MongoDatabase) {
return (DefaultMutableTreeNode) treeNode.getParent();
}
if (userObject instanceof MongoServer) {
return treeNode;
}
}
return null;
}
private DefaultMutableTreeNode getSelectedDatabaseNode() {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) mongoTree.getLastSelectedPathComponent();
if (treeNode != null) {
Object userObject = treeNode.getUserObject();
// if (userObject instanceof MongoCollection) {
// return (DefaultMutableTreeNode) treeNode.getParent();
// }
if (userObject instanceof MongoDatabase) {
return treeNode;
}
}
return null;
}
private DefaultMutableTreeNode getSelectedCollectionNode() {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) mongoTree.getLastSelectedPathComponent();
if (treeNode != null) {
Object userObject = treeNode.getUserObject();
if (userObject instanceof MongoCollection) {
return treeNode;
}
}
return null;
}
public ServerConfiguration getConfiguration() {
DefaultMutableTreeNode serverNode = getSelectedServerNode();
if (serverNode == null) {
return null;
}
return ((MongoServer) serverNode.getUserObject()).getConfiguration();
}
public MongoDatabase getSelectedDatabase() {
DefaultMutableTreeNode databaseNode = getSelectedDatabaseNode();
if (databaseNode == null) {
return null;
}
return (MongoDatabase) databaseNode.getUserObject();
}
public MongoCollection getSelectedCollection() {
DefaultMutableTreeNode collectionNode = getSelectedCollectionNode();
if (collectionNode == null) {
return null;
}
return (MongoCollection) collectionNode.getUserObject();
}
public void loadSelectedCollectionValues() {
Navigation navigation = new Navigation();
navigation.addNewWayPoint(getSelectedCollection(), new MongoQueryOptions());
MongoFileSystem.getInstance().openEditor(new MongoObjectFile(project, getConfiguration(), navigation));
}
public void dropCollection() {
MongoCollection selectedCollection = getSelectedCollection();
mongoManager.dropCollection(getConfiguration(), selectedCollection);
notifier.notifyInfo("Collection " + selectedCollection.getName() + " dropped");
reloadServerConfiguration(getSelectedServerNode(), true);
}
public void dropDatabase() {
MongoDatabase selectedDatabase = getSelectedDatabase();
mongoManager.dropDatabase(getConfiguration(), selectedDatabase);
notifier.notifyInfo("Datatabase " + selectedDatabase.getName() + " dropped");
reloadServerConfiguration(getSelectedServerNode(), true);
}
private Tree createTree() {
Tree tree = new Tree() {
private final JLabel myLabel = new JLabel(
String.format("<html><center>No Mongo server available<br><br>You may use <img src=\"%s\"> to add configuration</center></html>", pluginSettingsUrl)
);
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (!getServerConfigurations().isEmpty()) return;
myLabel.setFont(getFont());
myLabel.setBackground(getBackground());
myLabel.setForeground(getForeground());
Rectangle bounds = getBounds();
Dimension size = myLabel.getPreferredSize();
myLabel.setBounds(0, 0, size.width, size.height);
int x = (bounds.width - size.width) / 2;
Graphics g2 = g.create(bounds.x + x, bounds.y + 20, bounds.width, bounds.height);
try {
myLabel.paint(g2);
} finally {
g2.dispose();
}
}
};
tree.getEmptyText().clear();
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
return tree;
}
}