/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * 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.model.navigator; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.*; import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.messages.ModelMessages; import org.jkiss.dbeaver.model.meta.Property; import org.jkiss.dbeaver.model.runtime.AbstractJob; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.utils.GeneralUtils; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; /** * DBNProjectDatabases */ public class DBNProjectDatabases extends DBNNode implements DBNContainer, DBPEventListener { private static final Log log = Log.getLog(DBNProjectDatabases.class); private List<DBNDataSource> dataSources = new ArrayList<>(); private DBPDataSourceRegistry dataSourceRegistry; private volatile DBNNode[] children; private final IdentityHashMap<DBPDataSourceFolder, DBNLocalFolder> folderNodes = new IdentityHashMap<>(); public DBNProjectDatabases(DBNProject parentNode, DBPDataSourceRegistry dataSourceRegistry) { super(parentNode); this.dataSourceRegistry = dataSourceRegistry; this.dataSourceRegistry.addDataSourceListener(this); List<? extends DBPDataSourceContainer> projectDataSources = this.dataSourceRegistry.getDataSources(); for (DBPDataSourceContainer ds : projectDataSources) { addDataSource(ds, false, false); } } @Override protected void dispose(boolean reflect) { for (DBNDataSource dataSource : dataSources) { dataSource.dispose(reflect); } dataSources.clear(); folderNodes.clear(); children = null; if (dataSourceRegistry != null) { dataSourceRegistry.removeDataSourceListener(this); dataSourceRegistry = null; } super.dispose(reflect); } @Override public String getNodeType() { return "connections"; } public DBPDataSourceRegistry getDataSourceRegistry() { return dataSourceRegistry; } @Override public Object getValueObject() { return dataSourceRegistry; } @Override public String getChildrenType() { return ModelMessages.model_navigator_Connection; } @Override public Class<DBPDataSourceContainer> getChildrenClass() { return DBPDataSourceContainer.class; } @NotNull @Property(viewable = true, order = 1) public String getName() { return getNodeName(); } @Override public String getNodeName() { return "Connections"; } @Override public String getNodeDescription() { return ((DBNProject)getParentNode()).getProject().getName() + ModelMessages.model_navigator__connections; } @Override public DBPImage getNodeIcon() { return DBIcon.CONNECTIONS; } @Override public boolean allowsChildren() { return !dataSources.isEmpty(); } @Override public DBNNode[] getChildren(DBRProgressMonitor monitor) { if (children == null) { List<DBNNode> childNodes = new ArrayList<>(); // Add root folders for (DBPDataSourceFolder folder : dataSourceRegistry.getAllFolders()) { DBNLocalFolder folderNode = folderNodes.get(folder); if (folderNode == null) { folderNode = new DBNLocalFolder(this, folder); folderNodes.put(folder, folderNode); } if (folder.getParent() == null) { childNodes.add(folderNode); } } // Add only root datasources for (DBNDataSource dataSource : dataSources) { if (dataSource.getDataSourceContainer().getFolder() != null) { continue; } childNodes.add(dataSource); } sortNodes(childNodes); this.children = childNodes.toArray(new DBNNode[childNodes.size()]); } return children; } public void refreshChildren() { this.children = null; getModel().fireNodeUpdate(this, this, DBNEvent.NodeChange.STRUCT_REFRESH); } @Override public boolean allowsOpen() { return true; } @Override public String getNodeItemPath() { return getParentNode().getNodeItemPath(); } public DBNLocalFolder getFolderNode(DBPDataSourceFolder folder) { synchronized (folderNodes) { DBNLocalFolder folderNode = folderNodes.get(folder); if (folderNode == null) { //log.warn("Folder node '" + folder.getFolderPath() + "' not found"); folderNode = new DBNLocalFolder(this, folder); folderNodes.put(folder, folderNode); } return folderNode; } } public List<DBNDataSource> getDataSources() { return dataSources; } public DBNDataSource getDataSource(String id) { for (DBNDataSource dataSource : dataSources) { if (dataSource.getDataSourceContainer().getId().equals(id)) { return dataSource; } } return null; } private DBNDataSource addDataSource(DBPDataSourceContainer descriptor, boolean reflect, boolean reveal) { DBNDataSource newNode = new DBNDataSource(this, descriptor); dataSources.add(newNode); children = null; if (reflect) { getModel().fireNodeEvent(new DBNEvent( this, DBNEvent.Action.ADD, reveal ? DBNEvent.NodeChange.SELECT : DBNEvent.NodeChange.REFRESH, newNode)); } return newNode; } void removeDataSource(DBPDataSourceContainer descriptor) { DBNDataSource removedNode = null; for (Iterator<DBNDataSource> iter = dataSources.iterator(); iter.hasNext(); ) { DBNDataSource dataSource = iter.next(); if (dataSource.getObject() == descriptor) { iter.remove(); removedNode = dataSource; break; } } if (removedNode != null) { children = null; removedNode.dispose(true); } } @Override public void handleDataSourceEvent(DBPEvent event) { DBNModel model = getModel(); switch (event.getAction()) { case OBJECT_ADD: if (event.getObject() instanceof DBPDataSourceContainer) { addDataSource((DBPDataSourceContainer) event.getObject(), true, event.getEnabled() != null && event.getEnabled()); } else if (model.getNodeByObject(event.getObject()) == null) { DBNDatabaseNode parentNode = model.getParentNode(event.getObject()); boolean parentFound = (parentNode != null); if (parentNode == null) { // Not yet loaded. Parent node might be a folder (like Tables) parentNode = model.getParentNode(event.getObject()); parentFound = false; } if (parentNode != null) { if (parentNode.getChildNodes() == null && parentNode.hasChildren(false)) { final DBNDatabaseNode nodeToLoad = parentNode; // We have to load children here final AbstractJob loaderJob = new AbstractJob("Load sibling nodes of new database object") { { setUser(true); } @Override protected IStatus run(DBRProgressMonitor monitor) { try { nodeToLoad.getChildren(monitor); } catch (Exception e) { return GeneralUtils.makeExceptionStatus(e); } return Status.OK_STATUS; } }; loaderJob.schedule(); try { loaderJob.join(); } catch (InterruptedException e) { // That's ok } } if (!parentFound) { // Second try parentNode = model.getParentNode(event.getObject()); } if (parentNode != null && parentNode.getChildNodes() != null && !parentNode.hasChildItem(event.getObject())) { // Add only if object wasn't yet added (e.g. by create new object command) parentNode.addChildItem(event.getObject()); } } } break; case OBJECT_REMOVE: if (event.getObject() instanceof DBPDataSourceContainer) { removeDataSource((DBPDataSourceContainer) event.getObject()); } else { final DBNDatabaseNode node = model.getNodeByObject(event.getObject()); if (node != null && node.getParentNode() instanceof DBNDatabaseNode) { ((DBNDatabaseNode)node.getParentNode()).removeChildItem(event.getObject()); } } break; case OBJECT_UPDATE: case OBJECT_SELECT: { DBNDatabaseNode dbmNode = model.getNodeByObject(event.getObject()); if (dbmNode != null) { DBNEvent.NodeChange nodeChange; Boolean enabled = event.getEnabled(); Object source = this; if (event.getAction() == DBPEvent.Action.OBJECT_SELECT) { nodeChange = DBNEvent.NodeChange.REFRESH; if (enabled != null && enabled) source = DBNEvent.FORCE_REFRESH; } else { if (enabled != null) { if (enabled) { nodeChange = DBNEvent.NodeChange.LOAD; } else { nodeChange = DBNEvent.NodeChange.UNLOAD; } } else { nodeChange = DBNEvent.NodeChange.REFRESH; } if (event.getData() == DBPEvent.REORDER) { dbmNode.updateChildrenOrder(false); } } model.fireNodeUpdate( source, dbmNode, nodeChange); if (enabled != null && !enabled) { // Clear disabled node dbmNode.clearNode(false); } else { if (event.getAction() == DBPEvent.Action.OBJECT_UPDATE) { if (event.getObject() instanceof DBPDataSourceContainer) { // Force reorder children = null; getModel().fireNodeEvent(new DBNEvent(this, DBNEvent.Action.UPDATE, this)); } } } } break; } } } }