blob: 30c70460d9bfcd5eea592dde4b56b1dc53233503 [file] [log] [blame]
/*
* Copyright 2000-2013 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.openapi.util.objectTree;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.Collection;
import java.util.Collections;
import java.util.ListIterator;
public final class ObjectNode<T> {
private static final ObjectNode[] EMPTY_ARRAY = new ObjectNode[0];
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.objectTree.ObjectNode");
private final ObjectTree<T> myTree;
private ObjectNode<T> myParent;
private final T myObject;
private SmartList<ObjectNode<T>> myChildren;
private final Throwable myTrace;
private final long myOwnModification;
public ObjectNode(@NotNull ObjectTree<T> tree, @Nullable ObjectNode<T> parentNode, @NotNull T object, long modification, @Nullable final Throwable trace) {
myTree = tree;
myParent = parentNode;
myObject = object;
myTrace = trace;
myOwnModification = modification;
}
@SuppressWarnings("unchecked")
@NotNull
private ObjectNode<T>[] getChildrenArray() {
synchronized (myTree.treeLock) {
if (myChildren == null || myChildren.isEmpty()) return EMPTY_ARRAY;
return myChildren.toArray(new ObjectNode[myChildren.size()]);
}
}
void addChild(@NotNull ObjectNode<T> child) {
synchronized (myTree.treeLock) {
if (myChildren == null) {
myChildren = new SmartList<ObjectNode<T>>();
}
myChildren.add(child);
child.myParent = this;
}
}
void removeChild(@NotNull ObjectNode<T> child) {
synchronized (myTree.treeLock) {
assert myChildren != null: "No children to remove child: " + this + ' ' + child;
ListIterator<ObjectNode<T>> iterator = myChildren.listIterator(myChildren.size());
while (iterator.hasPrevious()) {
if (child.equals(iterator.previous())) {
iterator.remove();
return;
}
}
}
}
public ObjectNode<T> getParent() {
synchronized (myTree.treeLock) {
return myParent;
}
}
@NotNull
public Collection<ObjectNode<T>> getChildren() {
synchronized (myTree.treeLock) {
if (myChildren == null) return Collections.emptyList();
return Collections.unmodifiableCollection(myChildren);
}
}
void execute(final boolean disposeTree, @NotNull final ObjectTreeAction<T> action) {
ObjectTree.executeActionWithRecursiveGuard(this, myTree.getNodesInExecution(), new ObjectTreeAction<ObjectNode<T>>() {
@Override
public void execute(@NotNull ObjectNode<T> each) {
try {
action.beforeTreeExecution(myObject);
}
catch (Throwable t) {
LOG.error(t);
}
ObjectNode<T>[] childrenArray = getChildrenArray();
//todo: [kirillk] optimize
for (int i = childrenArray.length - 1; i >= 0; i--) {
childrenArray[i].execute(disposeTree, action);
}
if (disposeTree) {
synchronized (myTree.treeLock) {
myChildren = null;
}
}
try {
action.execute(myObject);
myTree.fireExecuted(myObject);
}
catch (ProcessCanceledException e) {
throw new ProcessCanceledException(e);
}
catch (Throwable e) {
LOG.error(e);
}
if (disposeTree) {
remove();
}
}
@Override
public void beforeTreeExecution(@NotNull ObjectNode<T> parent) {
}
});
}
private void remove() {
myTree.putNode(myObject, null);
synchronized (myTree.treeLock) {
if (myParent == null) {
myTree.removeRootObject(myObject);
}
else {
myParent.removeChild(this);
}
}
}
@NotNull
public T getObject() {
return myObject;
}
@NonNls
public String toString() {
return "Node: " + myObject.toString();
}
Throwable getTrace() {
return myTrace;
}
@TestOnly
void assertNoReferencesKept(@NotNull T aDisposable) {
assert getObject() != aDisposable;
synchronized (myTree.treeLock) {
if (myChildren != null) {
for (ObjectNode<T> node: myChildren) {
node.assertNoReferencesKept(aDisposable);
}
}
}
}
Throwable getAllocation() {
return myTrace;
}
long getOwnModification() {
return myOwnModification;
}
long getModification() {
return getOwnModification();
}
<D extends Disposable> D findChildEqualTo(@NotNull D object) {
synchronized (myTree.treeLock) {
SmartList<ObjectNode<T>> children = myChildren;
if (children != null) {
for (ObjectNode<T> node : children) {
T nodeObject = node.getObject();
if (nodeObject.equals(object)) {
return (D)nodeObject;
}
}
}
return null;
}
}
}