/* * Copyright 2000-2009 JetBrains s.r.o. * * 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 com.intellij.xdebugger.impl.ui.tree.nodes; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.SortedList; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.settings.XDebuggerSettingsManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.TreeNode; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author nik */ public abstract class XValueContainerNode extends XDebuggerTreeNode implements XCompositeNode, TreeNode { private List myValueChildren; private List myMessageChildren; private List myTemporaryMessageChildren; private List myTopGroups; private List myBottomGroups; private List myCachedAllChildren; protected final ValueContainer myValueContainer; private volatile boolean myObsolete; private volatile boolean myAlreadySorted; protected XValueContainerNode(XDebuggerTree tree, final XDebuggerTreeNode parent, @NotNull ValueContainer valueContainer) { super(tree, parent, true); myValueContainer = valueContainer; } private void loadChildren() { if (myValueChildren != null || myMessageChildren != null || myTemporaryMessageChildren != null) return; startComputingChildren(); } public void startComputingChildren() { myCachedAllChildren = null; setTemporaryMessageNode(createLoadingMessageNode()); myValueContainer.computeChildren(this); } protected MessageTreeNode createLoadingMessageNode() { return MessageTreeNode.createLoadingMessage(myTree, this); } @Override public void setAlreadySorted(boolean alreadySorted) { myAlreadySorted = alreadySorted; } @Override public void addChildren(@NotNull final XValueChildrenList children, final boolean last) { invokeNodeUpdate(new Runnable() { @Override public void run() { if (myValueChildren == null) { if (!myAlreadySorted && XDebuggerSettingsManager.getInstance().getDataViewSettings().isSortValues()) { myValueChildren = new SortedList(XValueNodeImpl.COMPARATOR); } else { myValueChildren = new ArrayList(); } } List> newChildren = new ArrayList>(children.size()); for (int i = 0; i < children.size(); i++) { XValueNodeImpl node = new XValueNodeImpl(myTree, XValueContainerNode.this, children.getName(i), children.getValue(i)); myValueChildren.add(node); newChildren.add(node); } myTopGroups = createGroupNodes(children.getTopGroups(), myTopGroups, newChildren); myBottomGroups = createGroupNodes(children.getBottomGroups(), myBottomGroups, newChildren); myCachedAllChildren = null; fireNodesInserted(newChildren); if (last) { final int[] ints = getNodesIndices(myTemporaryMessageChildren); final TreeNode[] removed = getChildNodes(ints); myCachedAllChildren = null; myTemporaryMessageChildren = null; fireNodesRemoved(ints, removed); } myTree.childrenLoaded(XValueContainerNode.this, newChildren, last); } }); } @Nullable private List createGroupNodes(List groups, @Nullable List prevNodes, List> newChildren) { if (groups.isEmpty()) return prevNodes; List nodes = prevNodes != null ? prevNodes : new SmartList(); for (XValueGroup group : groups) { XValueGroupNodeImpl node = new XValueGroupNodeImpl(myTree, this, group); nodes.add(node); newChildren.add(node); } return nodes; } @Override public void tooManyChildren(final int remaining) { invokeNodeUpdate(new Runnable() { @Override public void run() { setTemporaryMessageNode(MessageTreeNode.createEllipsisNode(myTree, XValueContainerNode.this, remaining)); } }); } @Override public boolean isObsolete() { return myObsolete; } @Override public void clearChildren() { myCachedAllChildren = null; myMessageChildren = null; myTemporaryMessageChildren = null; myValueChildren = null; myTopGroups = null; myBottomGroups = null; fireNodeStructureChanged(); } @Override public void setErrorMessage(final @NotNull String errorMessage) { setErrorMessage(errorMessage, null); } @Override public void setErrorMessage(@NotNull final String errorMessage, @Nullable final XDebuggerTreeNodeHyperlink link) { setMessage(errorMessage, XDebuggerUIConstants.ERROR_MESSAGE_ICON, XDebuggerUIConstants.ERROR_MESSAGE_ATTRIBUTES, link); } @Override public void setMessage(@NotNull final String message, final Icon icon, @NotNull final SimpleTextAttributes attributes, @Nullable final XDebuggerTreeNodeHyperlink link) { invokeNodeUpdate(new Runnable() { @Override public void run() { setMessageNodes(MessageTreeNode.createMessages(myTree, XValueContainerNode.this, message, link, icon, attributes), false); } }); } private void setTemporaryMessageNode(final MessageTreeNode messageNode) { setMessageNodes(Collections.singletonList(messageNode), true); } private void setMessageNodes(final List messages, boolean temporary) { myCachedAllChildren = null; List allMessageChildren = ContainerUtil.concat(myMessageChildren != null ? myMessageChildren : Collections.emptyList(), myTemporaryMessageChildren != null ? myTemporaryMessageChildren : Collections.emptyList()); final int[] indices = getNodesIndices(allMessageChildren); final TreeNode[] nodes = getChildNodes(indices); myMessageChildren = null; myTemporaryMessageChildren = null; fireNodesRemoved(indices, nodes); if (!temporary) { myMessageChildren = messages; } else { myTemporaryMessageChildren = messages; } myCachedAllChildren = null; fireNodesInserted(messages); } @NotNull @Override public List getChildren() { loadChildren(); if (myCachedAllChildren == null) { myCachedAllChildren = new ArrayList(); if (myMessageChildren != null) { myCachedAllChildren.addAll(myMessageChildren); } if (myTopGroups != null) { myCachedAllChildren.addAll(myTopGroups); } if (myValueChildren != null) { myCachedAllChildren.addAll(myValueChildren); } if (myBottomGroups != null) { myCachedAllChildren.addAll(myBottomGroups); } if (myTemporaryMessageChildren != null) { myCachedAllChildren.addAll(myTemporaryMessageChildren); } } return myCachedAllChildren; } @NotNull public ValueContainer getValueContainer() { return myValueContainer; } @Override @Nullable public List> getLoadedChildren() { List> empty = Collections.emptyList(); return ContainerUtil.concat(ObjectUtils.notNull(myTopGroups, empty), ObjectUtils.notNull(myValueChildren, empty), ObjectUtils.notNull(myBottomGroups, empty)); } public void setObsolete() { myObsolete = true; } }