package org.esa.snap.opendap.ui; import org.esa.snap.core.ui.AppContext; import org.esa.snap.opendap.datamodel.OpendapLeaf; import org.esa.snap.opendap.utils.OpendapUtils; import org.esa.snap.util.SystemUtils; import thredds.catalog.InvAccess; import thredds.catalog.InvCatalogRef; import thredds.catalog.InvDataset; import thredds.catalog.ServiceType; import javax.swing.JTree; import javax.swing.SwingWorker; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import java.awt.Component; import java.awt.Cursor; import java.io.InputStream; import java.net.URI; import java.text.MessageFormat; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; class CatalogTree { private final JTree jTree; private final HashMap<OpendapLeaf, MutableTreeNode> leafToParentNode = new HashMap<OpendapLeaf, MutableTreeNode>(); private final Set<CatalogTreeListener> catalogTreeListeners = new HashSet<CatalogTreeListener>(); private final AppContext appContext; private final UIContext uiContext; public CatalogTree(final LeafSelectionListener leafSelectionListener, AppContext appContext, UIContext uiContext) { this.appContext = appContext; this.uiContext = uiContext; jTree = new JTree(); jTree.setRootVisible(false); ((DefaultTreeModel) jTree.getModel()).setRoot(CatalogTreeUtils.createRootNode()); CatalogTreeUtils.addCellRenderer(jTree); addWillExpandListener(); CatalogTreeUtils.addTreeSelectionListener(jTree, leafSelectionListener); } public Component getComponent() { return jTree; } public void setNewRootDatasets(List<InvDataset> rootDatasets) { final DefaultTreeModel model = (DefaultTreeModel) jTree.getModel(); final DefaultMutableTreeNode rootNode = CatalogTreeUtils.createRootNode(); model.setRoot(rootNode); appendToNode(jTree, rootDatasets, rootNode, true); fireCatalogElementsInsertionFinished(); expandPath(rootNode); leafToParentNode.clear(); } void addWillExpandListener() { jTree.addTreeWillExpandListener(new TreeWillExpandListener() { @Override public void treeWillExpand(final TreeExpansionEvent event) throws ExpandVetoException { new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { uiContext.updateStatusBar("Loading subtree..."); uiContext.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); Object lastPathComponent = event.getPath().getLastPathComponent(); DefaultMutableTreeNode parent = (DefaultMutableTreeNode) lastPathComponent; DefaultMutableTreeNode child = (DefaultMutableTreeNode) parent.getChildAt(0); if (CatalogTreeUtils.isCatalogReferenceNode(child)) { resolveCatalogReferenceNode(child, true); } return null; } @Override protected void done() { uiContext.updateStatusBar("Ready."); uiContext.setCursor(Cursor.getDefaultCursor()); } }.execute(); } @Override public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { } }); } public void resolveCatalogReferenceNode(DefaultMutableTreeNode catalogReferenceNode, boolean expandPath) { final DefaultTreeModel model = (DefaultTreeModel) jTree.getModel(); final DefaultMutableTreeNode parent = (DefaultMutableTreeNode) catalogReferenceNode.getParent(); model.removeNodeFromParent(catalogReferenceNode); try { List<InvDataset> catalogDatasets = CatalogTreeUtils.getCatalogDatasets(catalogReferenceNode); insertCatalogElements(catalogDatasets, parent, expandPath); } catch (Exception e) { String msg = MessageFormat.format("Unable to completely resolve catalog. Reason: {0}", e.getMessage()); SystemUtils.LOG.warning(msg); appContext.handleError(msg, e); } } void insertCatalogElements(InputStream catalogIS, URI catalogBaseUri, DefaultMutableTreeNode parent, boolean expandPath) { final List<InvDataset> catalogDatasets = CatalogTreeUtils.getCatalogDatasets(catalogIS, catalogBaseUri); insertCatalogElements(catalogDatasets, parent, expandPath); } void appendToNode(final JTree jTree, List<InvDataset> datasets, MutableTreeNode parentNode, boolean goDeeper) { for (InvDataset dataset : datasets) { final MutableTreeNode deeperParent; if (!goDeeper || !CatalogTreeUtils.isHyraxId(dataset.getID())) { appendToNode(jTree, dataset, parentNode); if (parentNode.getChildCount() == 0) { continue; } deeperParent = (MutableTreeNode) parentNode.getChildAt(parentNode.getChildCount() - 1); } else { deeperParent = parentNode; } if (goDeeper && !(dataset instanceof InvCatalogRef)) { appendToNode(jTree, dataset.getDatasets(), deeperParent, false); } } } void appendLeafNode(MutableTreeNode parentNode, DefaultTreeModel treeModel, InvDataset dataset) { final OpendapLeaf leaf = new OpendapLeaf(dataset.getName(), dataset); final int fileSize = (int) (OpendapUtils.getDataSizeInBytes(leaf) / 1024); leaf.setFileSize(fileSize); final InvAccess dapAccess = dataset.getAccess(ServiceType.OPENDAP); if (dapAccess != null) { leaf.setDapAccess(true); leaf.setDapUri(dapAccess.getStandardUrlName()); } final InvAccess fileAccess = dataset.getAccess(ServiceType.FILE); if (fileAccess != null) { leaf.setFileAccess(true); leaf.setFileUri(fileAccess.getStandardUrlName()); } else { final InvAccess serverAccess = dataset.getAccess(ServiceType.HTTPServer); if (serverAccess != null) { leaf.setFileAccess(true); leaf.setFileUri(serverAccess.getStandardUrlName()); } } final boolean hasNestedDatasets = dataset.hasNestedDatasets(); appendDataNodeToParent(parentNode, treeModel, leaf); fireLeafAdded(leaf, hasNestedDatasets); } MutableTreeNode getNode(OpendapLeaf leaf) { final MutableTreeNode node = getNode(jTree.getModel(), jTree.getModel().getRoot(), leaf); if (node == null) { throw new IllegalStateException("node of leaf '" + leaf.toString() + "' is null."); } return node; } OpendapLeaf[] getLeaves() { final Set<OpendapLeaf> leafs = new HashSet<OpendapLeaf>(); getLeaves(jTree.getModel().getRoot(), jTree.getModel(), leafs); leafs.addAll(leafToParentNode.keySet()); return leafs.toArray(new OpendapLeaf[leafs.size()]); } void setLeafVisible(OpendapLeaf leaf, boolean visible) { if (visible) { setLeafVisible(leaf); } else { setLeafInvisible(leaf); } } void addCatalogTreeListener(CatalogTreeListener listener) { catalogTreeListeners.add(listener); } OpendapLeaf getSelectedLeaf() { if (jTree.isSelectionEmpty()) { return null; } DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) jTree.getAnchorSelectionPath().getLastPathComponent(); return (OpendapLeaf) selectedNode.getUserObject(); } private void insertCatalogElements(List<InvDataset> catalogDatasets, DefaultMutableTreeNode parent, boolean expandPath) { appendToNode(jTree, catalogDatasets, parent, true); fireCatalogElementsInsertionFinished(); if (expandPath) { expandPath(parent); } } private void appendToNode(JTree jTree, InvDataset dataset, MutableTreeNode parentNode) { final DefaultTreeModel treeModel = (DefaultTreeModel) jTree.getModel(); if (dataset instanceof InvCatalogRef) { CatalogTreeUtils.appendCatalogNode(parentNode, treeModel, (InvCatalogRef) dataset); } else { appendLeafNode(parentNode, treeModel, dataset); } } private void appendDataNodeToParent(MutableTreeNode parentNode, DefaultTreeModel treeModel, OpendapLeaf leaf) { final DefaultMutableTreeNode leafNode = new DefaultMutableTreeNode(leaf); treeModel.insertNodeInto(leafNode, parentNode, parentNode.getChildCount()); } private void expandPath(DefaultMutableTreeNode node) { jTree.expandPath(new TreePath(node.getPath())); } private MutableTreeNode getNode(TreeModel model, Object node, OpendapLeaf leaf) { for (int i = 0; i < model.getChildCount(node); i++) { final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) (model.getChild(node, i)); if (childNode.getUserObject() == leaf) { return childNode; } else { final MutableTreeNode temp = getNode(model, model.getChild(node, i), leaf); if (temp != null) { return temp; } } } return null; } private void fireLeafAdded(OpendapLeaf leaf, boolean hasNestedDatasets) { for (CatalogTreeListener catalogTreeListener : catalogTreeListeners) { catalogTreeListener.leafAdded(leaf, hasNestedDatasets); } } private void fireCatalogElementsInsertionFinished() { for (CatalogTreeListener catalogTreeListener : catalogTreeListeners) { catalogTreeListener.catalogElementsInsertionFinished(); } } private void setLeafVisible(OpendapLeaf leaf) { boolean leafIsRemovedFromTree = leafToParentNode.containsKey(leaf); if (leafIsRemovedFromTree) { appendDataNodeToParent(leafToParentNode.get(leaf), (DefaultTreeModel) jTree.getModel(), leaf); leafToParentNode.remove(leaf); } } private void setLeafInvisible(OpendapLeaf leaf) { boolean leafIsRemovedFromTree = leafToParentNode.containsKey(leaf); if (!leafIsRemovedFromTree) { final MutableTreeNode node = getNode(leaf); final DefaultTreeModel model = (DefaultTreeModel) jTree.getModel(); leafToParentNode.put(leaf, (MutableTreeNode) node.getParent()); model.removeNodeFromParent(node); } } private void getLeaves(Object node, TreeModel model, Set<OpendapLeaf> result) { for (int i = 0; i < model.getChildCount(node); i++) { if (model.isLeaf(model.getChild(node, i))) { final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) (model.getChild(node, i)); if (CatalogTreeUtils.isDapNode(childNode) || CatalogTreeUtils.isFileNode(childNode)) { result.add((OpendapLeaf) childNode.getUserObject()); } } else { getLeaves(model.getChild(node, i), model, result); } } } static interface LeafSelectionListener { void dapLeafSelected(OpendapLeaf leaf); void fileLeafSelected(OpendapLeaf leaf); void leafSelectionChanged(boolean isSelected, OpendapLeaf dapObject); } static interface CatalogTreeListener { void leafAdded(OpendapLeaf leaf, boolean hasNestedDatasets); void catalogElementsInsertionFinished(); } static interface UIContext { void setCursor(Cursor cursor); void updateStatusBar(String text); } }