/* * 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.usages.impl; import com.intellij.openapi.util.Comparing; import com.intellij.usages.UsageView; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; /** * @author max */ public abstract class Node extends DefaultMutableTreeNode { protected final DefaultTreeModel myTreeModel; private String myCachedText; private byte myCachedFlags; // bit packed flags below: private static final int INVALID_FLAG = 0; private static final int READ_ONLY_FLAG = 1; private static final int READ_ONLY_COMPUTED_FLAG = 2; private static final int EXCLUDED_FLAG = 3; private static final int UPDATED_FLAG = 4; @MagicConstant(intValues = {INVALID_FLAG, READ_ONLY_FLAG, READ_ONLY_COMPUTED_FLAG, EXCLUDED_FLAG, UPDATED_FLAG}) @interface FlagConstant {} private boolean isFlagSet(@FlagConstant int flag) { int state = myCachedFlags >> flag; return (state & 1) != 0; } private void setFlag(@FlagConstant int flag, boolean value) { int state = value ? 1 : 0; myCachedFlags = (byte)(myCachedFlags & ~(1 << flag) | state << flag); } protected Node(@NotNull DefaultTreeModel model) { myTreeModel = model; } /** * debug method for producing string tree presentation */ public abstract String tree2string(int indent, String lineSeparator); /** * isDataXXX methods perform actual (expensive) data computation. * Called from {@link #update(com.intellij.usages.UsageView)}) * to be compared later with cached data stored in {@link #myCachedFlags} and {@link #myCachedText} */ protected abstract boolean isDataValid(); protected abstract boolean isDataReadOnly(); protected abstract boolean isDataExcluded(); protected abstract String getText(@NotNull UsageView view); public final boolean isValid() { return !isFlagSet(INVALID_FLAG); } public final boolean isReadOnly() { boolean result; boolean computed = isFlagSet(READ_ONLY_COMPUTED_FLAG); if (computed) { result = isFlagSet(READ_ONLY_FLAG); } else { result = isDataReadOnly(); setFlag(READ_ONLY_COMPUTED_FLAG, true); setFlag(READ_ONLY_FLAG, result); } return result; } public final boolean isExcluded() { return isFlagSet(EXCLUDED_FLAG); } public final void update(@NotNull UsageView view) { boolean isDataValid = isDataValid(); boolean isReadOnly = isDataReadOnly(); boolean isExcluded = isDataExcluded(); String text = getText(view); boolean cachedValid = isValid(); boolean cachedReadOnly = isFlagSet(READ_ONLY_FLAG); boolean cachedExcluded = isFlagSet(EXCLUDED_FLAG); if (isDataValid != cachedValid || isReadOnly != cachedReadOnly || isExcluded != cachedExcluded || !Comparing.equal(myCachedText, text)) { setFlag(INVALID_FLAG, !isDataValid); setFlag(READ_ONLY_FLAG, isReadOnly); setFlag(EXCLUDED_FLAG, isExcluded); myCachedText = text; updateNotify(); myTreeModel.nodeChanged(this); } setFlag(UPDATED_FLAG, true); } public void markNeedUpdate() { setFlag(UPDATED_FLAG, false); } public boolean needsUpdate() { return !isFlagSet(UPDATED_FLAG); } /** * Override to perform node-specific updates */ protected void updateNotify() { } }