This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
I had several tabs open (20+) and started closing them one by one using CTRL + F4. After about half of them I got a java.lang.InvocationTargetException. Looking at the stacktrace is appears to code from stackOverflow ecxeptions in org.openide.nodes.FilterNode.hashCode().. Im using NB 4.0, 2. august daily build,jdk 1.5 beta, gentoo linux amd64
Created attachment 16686 [details] log frm netbeans
reassigning
moving to openide/nodes.. seems to be a problem in FilterNode.hashcode() ?
->nodes
Something for Jarda to play with.
I think I can simulate the problem by doing FilterNode n = new FilterNode (Node.LEAF); n.changeOriginal (n, ...); but this should never happen. I'll add a check to filter node to prevent itself to be assigned as original.
cvs -q ci -m "#46993: Forbiding setting itself as original" Checking in src/org/openide/nodes/FilterNode.java; /cvs/openide/src/org/openide/nodes/FilterNode.java,v <-- FilterNode.java new revision: 1.93; previous revision: 1.92 done Processing log script arguments... More commits to come... Checking in test/unit/src/org/openide/nodes/FilterNodeTest.java; /cvs/openide/test/unit/src/org/openide/nodes/FilterNodeTest.java,v <-- FilterNodeTest.java new revision: 1.15;
I'm reopening this, since this still occurs. Will attach new log.. I'm not sure it is triggered the same way, by I still get it quite often. Once I get the first stack overflow in a session, a new exception occures repeatedly (see log),so the program is rendered unusable. System: NB 4.0 Daily build from 2. septemper Java 1.5.0 rc. Still gentoo and amd64
Created attachment 17370 [details] new log showing the bug still present.note log is truncated it was 2.4 mb
cvs ci -m "#46993: When started with -J-ea (probably in etc/netbeans.conf) the system now performs StackOverflowError tests in order to find the cause of issue 46993" - so next time you can report exception with better annotations, but remember to run with -J-ea - beta2 is not going to have this flag on by default! Checking in src/org/openide/nodes/FilterNode.java; /cvs/openide/src/org/openide/nodes/FilterNode.java,v <-- FilterNode.java new revision: 1.94; previous revision: 1.93 done Processing log script arguments... More commits to come... Checking in test/unit/src/org/openide/nodes/FilterNodeTest.java; /cvs/openide/test/unit/src/org/openide/nodes/FilterNodeTest.java,v <-- FilterNodeTest.java new revision: 1.16; previous revision: 1.15
Eamonn McManus wrote to jtulach@netbeans.org: > Hi, > > I guess you added some instrumentation to this method to show what is > happening when it gets StackOverFlowError? I downloaded the latest > qbuild (200409100847) and I still get the problem, but now it has some > extra information. The log file is attached. Hopefully this will > enable you to find the problem, if you haven't already.
Created attachment 17782 [details] Log by Eamonn McManus shortened by removing 990 of hashCode() in stacktraces
As can be seen from a recent report by Eamonn McManus, the problem is in java source hierachy that under certain (and unknown to me conditions) succeeds in creating FilterNode that delegates to CompatibleNode that delegates to the original FilterNode. So this is not bug in openide/nodes, but somewhere in java source hierarchy.
Honzo, please evaluate.
I have investigated code instantiating CompatibleNode, which is btw private final class, and I found nothing suspicious. All usages of CompatibleNode gets some subclass of AbstractNode as the original node. So no possibility to create cycles. FilterNode.changeOriginal is not used in the java module as well. Unfortunatelly I am not able to reproduce this. So I would appreciate some further help from openide developers as logging of cycles at time of FilterNode construction or FilterNode.changeOriginal usage.
cvs ci -m "#46993: If run with -ea then a deep check is done in changeOriginal to verify that nobody is creating any cycle" Checking in src/org/openide/nodes/FilterNode.java; /cvs/openide/src/org/openide/nodes/FilterNode.java,v <-- FilterNode.java new revision: 1.95; previous revision: 1.94 done Processing log script arguments... More commits to come... Checking in test/unit/src/org/openide/nodes/FilterNodeTest.java; /cvs/openide/test/unit/src/org/openide/nodes/FilterNodeTest.java,v <-- FilterNodeTest.java new revision: 1.17; previous revision: 1.16
To lynggaard: could you try to reproduce it and attach the ide log file please?
Created attachment 17796 [details] shortend log from daily build 20/9
Attached new log. Problem is still very obvious, happends within 5 minutes of use. I did a fresh install using the 20/9 dialy build, wipe-ing both the install dir and the user dir (~/.netbeans/) I have shortend the log by removeing all the repeating filternode.hashcode[] parts. let me know if I should use a specific version or modify my install. If you need the source code for what I'm coding it is available here: http://cvs.sourceforge.net/viewcvs.py/moztrans/newsrc/ I am currently editing: org.mozillatranslator.datamodel.GenericFile org.mozillatranslator.datamodel.MozFile org.mozillatranslator.io.glossary.PropertiesPersistence.
Ok, still shows the problem is in java/navigation. But we are not getting closer to solution as we do not understand how that can happen. Please try this on a newer build as the last patch with early failure was integrated on Sep21.
Created attachment 17812 [details] log from build 21/9
Okey this is what I did: 1. Download todays build 21/9 2. Clean install (wipe both install and settings dir) 3. start application and open projects 4. use the application for a while thinking the problem was gone, but ran into http://www.netbeans.org/issues/show_bug.cgi?id=49438 5. close netbeans and report the defect mentioned in item 4 6. remove the old log to have a clean one. 7. Start netbeans again, and while it was auto opening the last used project I got the error again .
Created attachment 17814 [details] second log from daily build 21/9 (shortened)
I have added a second log from todays build. This time it didn't choke on application start and I was able to work for about 10 minutes before it started to misbehave. As you can see from the log, then once I had had one exception in a session they just keep comming.Quite frankly it makes the program unusable, if i cannot finish a sentence without an exception. Note that in the log I also have a few null pointer exception from http://www.netbeans.org/issues/show_bug.cgi?id=49438 I'm not sure if they are related. The reason they are there is before I didn't kill the app after the first exception, I wanted the log to show if they came from differnt places. please let me know if there is anything specific you want me to try..
Seems like my fix did not get into this build. The FilterNode.hashCodeLogging is supposed to be on line 810 and not 806.
actually the changelog at http://www.netbeans.org/download/4_0/daily/200409211800/netbeans-4_0-daily-log-200409211800-changelog-21_Sep_2004_1800.html indicates it did: 2004-09-21 16:04 jtulach openide #46993: If run with -ea then a deep check is done in changeOriginal to verify that nobody is creating any cycle openide/: src/org/openide/nodes/FilterNode.java (1.95) [ diff from 1.94 ], test/unit/src/org/openide/nodes/FilterNodeTest.java (1.17) [ diff from 1.16 ] perhaps I'm just not hitting that code ?!
You are right and it is really bad that you are not hitting the code. One would say, that it is impossible. There are two places where the FilterNode.original field is changing - the constructor and the changeOriginal method. My debug code is in changeOriginal and is not called. So the wrong assignment must have happen in constructor. But no valid constructor can pass instance of itself into own constructor! I have to say I am lost. I will attach changed FilterNode class that does a lot of logging. Replace the .class files in openide.jar by these. And try to reproduce the problem.
Created attachment 17828 [details] Modified FilterNode
BTW: Could you please try to reproduce the problem also on a 32bit client VM?
Created attachment 17847 [details] log from build 21/9 with patch
I have done the following 1. wiped my settings dir $HOME/.netbeans 2. replaced the original openide.jar with an patched version 3. started netbeans and tried again. Result: I no longer get the popup warning me about stack errors, but there are still plenty in the log. The log is different so it appears the patch is working, but blocking the popups. The original log was 4.4 mb, I have just truncated it. This was with the usual 64bit vm, I will try with a 32bit as requested later.
The log is OK. Yarda just added desperate amount of logging that logs even during normal conditions. He probably did nothing to fix the problem, so I'd think it haven't yet manifested itself. BTW: when testing on 32bit JVM, don't forget the _client_ part (which is probably more important than 32b)...
Created attachment 17852 [details] log of patched 21/9 build, now with error. zipped
I have now run into the problem with the patched version. I have attached the full log, but since it is 3 MB I have zipped it. Note that I run into a whole truck load of http://www.netbeans.org/issues/show_bug.cgi?id=49438 first, so the first filterNode exception is at approx line 46600. I don't know if the bug are related in any way....
Created attachment 17853 [details] log of patched 21/9 build, another error. zipped
I just hot the error again after about 2 minutes of usage. I have attached the log for this one as well since it might be cleaner than the previous zipped log. This is still with the 64bit edition, I wont have a chance to test the 32bit client until this weekend.
*** Issue 49565 has been marked as a duplicate of this issue. ***
I have been testing this issue during the weekend on my win32 box (same hardware as the linux64) If I install and use netbeans with the jdk 1.5 client VM then it appears to work (alhough i might get other exceptions) If I alter the ~$NETBEANS_INSTALL/etc/netbeans.conf and add -J-server to run with the server VM, I keep hitting the error. Appaerently it has something to do with the server VM, but sadly linux64 does not have a client VM :-\
*** Issue 49825 has been marked as a duplicate of this issue. ***
This looks to be a generic problem of FilterNode, not just related to the java module. See issue #49565 and the attached stack trace (ProjectsRootNode$BadgingNode). I see nothing what can be done in the java module to resolve this issue. Reassigning back
I have reported this as JVM bug #6173783. Should be accessible as http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6173783 I guess. Until that bug is resolved, I do not think there is anything to be done on NetBeans side.
Here is few suggestions from the hotspot guys: 0. Try "-Xint" to run only java interpreter. If you still have the problem - it is most likely in your code. If the problem does not show up - it may be VM problem, try the next: 1. Exclude the compilation of the method (well, we need to exlucde whole class, I guess): org.openide.nodes.FilterNode.hashCode: -XX:CompileCommand=exclude,org/openide/nodes/FilterNode,hashCode Please update this bug with your experience with -Xint.
Eamonn McManus napsal(a): > Jaroslav, > > I'm not sure whether you've been able to fix this problem recently. I > find that it is still present in Beta 2. Three different people here as > well as myself have run into it. All are using Solaris, which could > make a difference. The symptom is that an exception window starts > appearing and it becomes impossible to do any work, because just moving > to a different line in the editor window is enough to make the exception > window appear again several times. > > I patched FilterNode.java to catch the problem. It appears that the > current FilterNode.hashCode() is fragile since subclasses can easily > break it. Since you don't control those subclasses the best solution > might be to use a ThreadLocal as I do in the patch here. Subclasses can break it? I do not know how, the only way I can imagine is to call changeOriginal method, but such method is not called at all. > For information, this code prints the following shortly after netbeans > starts: > RECURSIVE FilterNode.hashCode: original is a > org.openide.nodes.FilterNode; same as this: false > stack: <<<org.netbeans.modules.java.ui.NavigationView$FNode[1216ee9]>>> > <loop> > RECURSIVE FilterNode.hashCode: original is a > org.openide.nodes.FilterNode; same as this: false > stack: <<<org.netbeans.modules.java.ui.NavigationView$FNode[14445b5]>>> > <loop> Shortly after start? Maybe during some hotspot compilation... Please try to reproduce the problem with options described at http://www.netbeans.org/issues/show_bug.cgi?id=46993 -jst > It doesn't print any more messages, even after a long editing session. > > Regards, > > > ------------------------------------------------------------------------ > > /* > * Sun Public License Notice > * > * The contents of this file are subject to the Sun Public License > * Version 1.0 (the "License"). You may not use this file except in > * compliance with the License. A copy of the License is available at > * http://www.sun.com/ > * > * The Original Code is NetBeans. The Initial Developer of the Original > * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun > * Microsystems, Inc. All Rights Reserved. > */ > > package org.openide.nodes; > > import java.awt.Image; > import java.awt.datatransfer.Transferable; > import java.beans.PropertyChangeEvent; > import java.beans.PropertyChangeListener; > import java.io.IOException; > > > import java.util.*; > import java.lang.ref.WeakReference; > import java.lang.reflect.Method; > import org.openide.ErrorManager; > > import org.openide.util.datatransfer.NewType; > import org.openide.util.datatransfer.PasteType; > import org.openide.util.HelpCtx; > import org.openide.util.Lookup; > import org.openide.util.LookupEvent; > import org.openide.util.LookupListener; > import org.openide.util.actions.SystemAction; > > /** A proxy for another node. > * Unless otherwise mentioned, all methods of the original node are delegated to. > * If desired, you can disable delegation of certain methods which are concrete in <code>Node</code> > * by calling {@link #disableDelegation}. > * > * <p><strong>Note:</strong> it is fine to subclass this class and use > * it to filter things. But please do not ever try to cast a node to > * <code>FilterNode</code>: it probably means you are doing something > * wrong. Instead, ask whatever <code>Node</code> you have for a proper > * kind of cookie (e.g. <code>DataObject</code>). > * > * @author Jaroslav Tulach > */ > public class FilterNode extends Node { > /** node to delegate to */ > private Node original; > /** listener to property changes, > * accessible thru getPropertyChangeListener > */ > private PropertyChangeListener propL; > /** listener to node changes > * Accessible thru get node listener > */ > private NodeListener nodeL; > > // Note: int (not long) to avoid need to ever synchronize when accessing it > // (Java VM spec does not guarantee that long's will be stored atomically) > /** @see #delegating */ > private int delegateMask; > > /** Whether to delegate <code>setName</code>. */ > protected static final int DELEGATE_SET_NAME = 1 << 0; > /** Whether to delegate <code>getName</code>. */ > protected static final int DELEGATE_GET_NAME = 1 << 1; > /** Whether to delegate <code>setDisplayName</code>. */ > protected static final int DELEGATE_SET_DISPLAY_NAME = 1 << 2; > /** Whether to delegate <code>getDisplayName</code>. */ > protected static final int DELEGATE_GET_DISPLAY_NAME = 1 << 3; > /** Whether to delegate <code>setShortDescription</code>. */ > protected static final int DELEGATE_SET_SHORT_DESCRIPTION = 1 << 4; > /** Whether to delegate <code>getShortDescription</code>. */ > protected static final int DELEGATE_GET_SHORT_DESCRIPTION = 1 << 5; > /** Whether to delegate <code>destroy</code>. */ > protected static final int DELEGATE_DESTROY = 1 << 6; > /** Whether to delegate <code>getActions</code>. */ > protected static final int DELEGATE_GET_ACTIONS = 1 << 7; > /** Whether to delegate <code>getContextActions</code>. */ > protected static final int DELEGATE_GET_CONTEXT_ACTIONS = 1 << 8; > /** Whether to delegate <code>setValue</code>. > * @since 4.25 > */ > protected static final int DELEGATE_SET_VALUE = 1 << 9; > /** Whether to delegate <code>getValue</code>. > * @since 4.25 > */ > protected static final int DELEGATE_GET_VALUE = 1 << 10; > > > /** Mask indicating delegation of all possible methods. */ > private static final int DELEGATE_ALL = DELEGATE_SET_NAME | > DELEGATE_GET_NAME | > DELEGATE_SET_DISPLAY_NAME | > DELEGATE_GET_DISPLAY_NAME | > DELEGATE_SET_SHORT_DESCRIPTION | > DELEGATE_GET_SHORT_DESCRIPTION | > DELEGATE_DESTROY | > DELEGATE_GET_ACTIONS | > DELEGATE_GET_CONTEXT_ACTIONS | > DELEGATE_SET_VALUE | > DELEGATE_GET_VALUE; > > /** Is PropertyChangeListener attached to original node */ > private boolean pchlAttached = false; > /** children provided or created the default ones? */ > private boolean childrenProvided; > > /** Create proxy. > * @param original the node to delegate to > */ > public FilterNode(Node original) { > this (original, null); > } > > /** Create proxy with a different set of children. > * > * @param original the node to delegate to > * @param children a set of children for this node > */ > public FilterNode( > Node original, > org.openide.nodes.Children children > ) { > this (original, children, new FilterLookup ()); > } > > /** Constracts new filter node. > * @param original the node we delegate to > * @param children the children to use for the filter node or <code>null</code> if > * default children should be provided > * @param lookup lookup to use > * > * @since 4.4 > */ > public FilterNode (Node original, org.openide.nodes.Children children, Lookup lookup) { > super ( > children == null ? > (original.isLeaf () ? org.openide.nodes.Children.LEAF : new Children (original)) > : > children, > lookup > ); > > this.childrenProvided = children != null; > this.original = original; > init (); > > Lookup lkp = internalLookup (false); > if (lkp instanceof FilterLookup) { > ((FilterLookup)lkp).ownNode (this); > } else { > if (lkp == null) { > // rely on default NodeLookup around getCookie. > getNodeListener(); > } > } > } > > private static final WeakHashMap overridesGetDisplayNameCache = new WeakHashMap (27); > private static final WeakHashMap replaceProvidedLookupCache = new WeakHashMap (27); > /** Overrides package private method of a node that allows us to say > * that the lookup provided in the constructor should be replaced by > * something else > * > * @param lookup > * @return lookup or null > */ > final Lookup replaceProvidedLookup (Lookup lookup) { > synchronized (replaceProvidedLookupCache) { > Boolean b = (Boolean)replaceProvidedLookupCache.get (getClass ()); > if (b == null) { > b = overridesAMethod ("getCookie", new Class[] { Class.class }) ? Boolean.FALSE : Boolean.TRUE; // NOI18N > replaceProvidedLookupCache.put (getClass (), b); > } > return b.booleanValue() ? lookup : null; > } > } > > /** Checks whether subclass overrides a method > */ > private boolean overridesAMethod (String name, Class[] arguments) { > if (getClass () == FilterNode.class) { > return false; > } > > > // we are subclass of FilterNode > try { > java.lang.reflect.Method m = getClass ().getMethod (name, arguments); > > if (m.getDeclaringClass () != FilterNode.class) { > // ok somebody overriden getCookie method > return true; > } > } catch (NoSuchMethodException ex) { > ErrorManager.getDefault().notify(ex); > } > > return false; > } > > /** Initializes the node. > */ > private void init () { > delegateMask = DELEGATE_ALL; > } > > void notifyPropertyChangeListenerAdded( PropertyChangeListener l ) { > if ( !pchlAttached ) { > original.addPropertyChangeListener (getPropertyChangeListener ()); > pchlAttached = true; > } > } > > void notifyPropertyChangeListenerRemoved( PropertyChangeListener l ) { > if ( getPropertyChangeListenersCount() == 0 ) { > original.removePropertyChangeListener (getPropertyChangeListener ()); > pchlAttached = false; > } > } > > /** Removes all listeners (property and node) on > * the original node. Called from {@link NodeListener#nodeDestroyed}, > * but can be called by any subclass to stop reflecting changes > * in the original node. > */ > protected void finalize () { > original.removePropertyChangeListener (getPropertyChangeListener ()); > original.removeNodeListener (getNodeListener ()); > } > > /** Enable delegation of a set of methods. > * These will be delegated to the original node. > * Since all available methods are delegated by default, normally you will not need to call this. > * @param mask bitwise disjunction of <code>DELEGATE_XXX</code> constants > * @throws IllegalArgumentException if the mask is invalid > */ > protected final void enableDelegation (int mask) { > if ((mask & ~DELEGATE_ALL) != 0) throw new IllegalArgumentException ("Bad delegation mask: " + mask); // NOI18N > delegateMask |= mask; > } > > /** Disable delegation of a set of methods. > * The methods will retain their behavior from {@link Node}. > * <p>For example, if you wish to subclass <code>FilterNode</code>, giving your > * node a distinctive display name and tooltip, and performing some special > * action upon deletion, you may do so without risk of affecting the original > * node as follows: > * <br><code><pre> > * public MyNode extends FilterNode { > * public MyNode (Node orig) { > * super (orig, new MyChildren (orig)); > * disableDelegation (DELEGATE_GET_DISPLAY_NAME | DELEGATE_SET_DISPLAY_NAME | > * DELEGATE_GET_SHORT_DESCRIPTION | DELEGATE_SET_SHORT_DESCRIPTION | > * DELEGATE_DESTROY); > * // these will affect only the filter node: > * setDisplayName ("Linking -> " + orig.getDisplayName ()); > * setShortDescription ("Something different."); > * } > * public boolean canRename () { return false; } > * public void destroy () throws IOException { > * doMyCleanup (); > * super.destroy (); // calls Node.destroy(), not orig.destroy() > * } > * } > * </pre></code> > * <br>You may still manually delegate where desired using {@link #getOriginal}. > * Other methods abstract in <code>Node</code> may simply be overridden without > * any special handling. > * @param mask bitwise disjunction of <code>DELEGATE_XXX</code> constants > * @throws IllegalArgumentException if the mask is invalid > */ > protected final void disableDelegation (int mask) { > if ((mask & ~DELEGATE_ALL) != 0) throw new IllegalArgumentException ("Bad delegation mask: " + mask); // NOI18N > delegateMask &= ~mask; > } > > /** Test whether we are currently delegating to some method. */ > private final boolean delegating (int what) { > return (delegateMask & what) != 0; > } > > /** Create new filter node for the original. > * Subclasses do not have to override this, but if they do not, > * the default implementation will filter the subclass filter, which is not > * very efficient. > * @return copy of this node > */ > public Node cloneNode () { > if (isDefault ()) { > // this is realy filter node without changed behaviour > // with the normal children => use normal constructor for the > // original node > return new FilterNode (original); > } else { > // create filter node for this node to reflect changed > // behaviour > return new FilterNode (this); > } > } > > > /** Changes the original node for this node. > *@param original The new original node. > *@param changeChildren If set to <CODE>true</CODE> changes children > * of this node according to the new original node. If you pass > * children which are not instance of class > * <CODE>FilterNode.Children</CODE> into the constructor set this > * parameter to <CODE>false</CODE>. > *@throws java.lang.IllegalStateException if children which are not > * instance of <CODE>FilterNode.Children</CODE> were passed > * into the constructor and the method was called with the parameter > * <CODE>changeChildren</CODE> set to <CODE>true</CODE>. > *@since 1.39 > */ > protected final void changeOriginal( Node original, boolean changeChildren ) { > > if ( changeChildren && > !(getChildren() instanceof FilterNode.Children) && > !(getChildren() == Children.LEAF /* && original.isLeaf () */)) { > throw new IllegalStateException( "Can't change implicitly defined Children on FilterNode" ); // NOI18N > } > > if (original == this) { > throw new IllegalArgumentException ("Cannot be own original: " + original); // NOI18N > } > > try { > Children.PR.enterWriteAccess(); > > // First remove the listeners from current original node > this.original.removeNodeListener( getNodeListener() ); > if ( pchlAttached ) { > this.original.removePropertyChangeListener( getPropertyChangeListener() ); > } > > // Set the new original node > this.original = original; > > // attach listeners to new original node > this.original.addNodeListener( getNodeListener() ); > if ( pchlAttached ) { > this.original.addPropertyChangeListener( getPropertyChangeListener() ); > } > > // Reset children's original node. > if ( changeChildren /* && !original.isLeaf () */) { > if ( original.isLeaf() && getChildren() != Children.LEAF ) { > setChildren( Children.LEAF ); > } > else if ( !original.isLeaf() && getChildren() == Children.LEAF ) { > setChildren( new Children( original ) ); > } > else if ( !original.isLeaf() && getChildren() != Children.LEAF) { > ((FilterNode.Children)getChildren()).changeOriginal( original ); > } > } > } > finally { > Children.PR.exitWriteAccess(); > } > > // Fire all sorts of events (everything gets changed after we > // reset the original node.) > Lookup lkp = internalLookup (false); > if (lkp instanceof FilterLookup) { > ((FilterLookup)lkp).checkNode(); > } > fireCookieChange(); > fireNameChange(null, null); > fireDisplayNameChange(null, null); > fireShortDescriptionChange(null, null); > fireIconChange(); > fireOpenedIconChange(); > firePropertySetsChange( null, null ); > > } > > // ------------- START OF DELEGATED METHODS ------------ > > public void setValue(String attributeName, Object value) { > if (delegating (DELEGATE_SET_VALUE)) { > original.setValue (attributeName, value); > } else { > super.setValue (attributeName, value); > } > } > > public Object getValue(String attributeName) { > if (delegating (DELEGATE_GET_VALUE)) { > return original.getValue (attributeName); > } else { > return super.getValue (attributeName); > } > } > > > /* Setter for system name. Fires info about property change. > * @param s the string > */ > public void setName (String s) { > if (delegating (DELEGATE_SET_NAME)) { > original.setName (s); > } else { > super.setName (s); > } > } > > /* @return the name of the original node > */ > public String getName () { > if (delegating (DELEGATE_GET_NAME)) { > return original.getName (); > } else { > return super.getName (); > } > } > > /* Setter for display name. Fires info about property change. > * @param s the string > */ > public void setDisplayName (String s) { > if (delegating (DELEGATE_SET_DISPLAY_NAME)) { > original.setDisplayName (s); > } else { > super.setDisplayName (s); > } > } > > /* @return the display name of the original node > */ > public String getDisplayName () { > if (delegating (DELEGATE_GET_DISPLAY_NAME)) { > return original.getDisplayName (); > } else { > return super.getDisplayName (); > } > } > > /* Setter for short description. Fires info about property change. > * @param s the string > */ > public void setShortDescription (String s) { > if (delegating (DELEGATE_SET_SHORT_DESCRIPTION)) { > original.setShortDescription (s); > } else { > super.setShortDescription (s); > } > } > > /* @return the description of the original node > */ > public String getShortDescription () { > if (delegating (DELEGATE_GET_SHORT_DESCRIPTION)) { > return original.getShortDescription (); > } else { > return super.getShortDescription (); > } > } > > /* Finds an icon for this node. Delegates to the original. > * > * @see java.bean.BeanInfo > * @param type constants from <CODE>java.bean.BeanInfo</CODE> > * @return icon to use to represent the bean > */ > public Image getIcon (int type) { > return original.getIcon (type); > } > > /* Finds an icon for this node. This icon should represent the node > * when it is opened (if it can have children). Delegates to original. > * > * @see java.bean.BeanInfo > * @param type constants from <CODE>java.bean.BeanInfo</CODE> > * @return icon to use to represent the bean when opened > */ > public Image getOpenedIcon (int type) { > return original.getOpenedIcon (type); > } > > public HelpCtx getHelpCtx () { > return original.getHelpCtx (); > } > > /* Can the original node be renamed? > * > * @return true if the node can be renamed > */ > public boolean canRename () { > return original.canRename (); > } > > /* Can the original node be deleted? > * @return <CODE>true</CODE> if can, <CODE>false</CODE> otherwise > */ > public boolean canDestroy () { > return original.canDestroy (); > } > > /* Degelates the delete operation to original. > */ > public void destroy () throws java.io.IOException { > if (delegating (DELEGATE_DESTROY)) > original.destroy (); > else > super.destroy (); > } > > /** Used to access the destroy method when original nodes > * has been deleted > */ > private final void originalDestroyed () { > try { > super.destroy (); > } catch (IOException ex) { > ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); > } > } > > /* Getter for the list of property sets. Delegates to original. > * > * @return the array of property sets. > */ > public PropertySet[] getPropertySets () { > return original.getPropertySets (); > } > > /* Called when an object is to be copied to clipboard. > * @return the transferable object dedicated to represent the > * content of clipboard > * @exception IOException is thrown when the > * operation cannot be performed > */ > public Transferable clipboardCopy () throws IOException { > return original.clipboardCopy (); > } > > /* Called when an object is to be cut to clipboard. > * @return the transferable object dedicated to represent the > * content of clipboard > * @exception IOException is thrown when the > * operation cannot be performed > */ > public Transferable clipboardCut () throws IOException { > return original.clipboardCut (); > } > > /* Returns true if this object allows copying. > * @returns true if this object allows copying. > */ > public boolean canCopy () { > return original.canCopy (); > } > > /* Returns true if this object allows cutting. > * @returns true if this object allows cutting. > */ > public boolean canCut () { > return original.canCut (); > } > > public Transferable drag () throws IOException { > return original.drag (); > } > > /* Default implementation that tries to delegate the implementation > * to the createPasteTypes method. Simply calls the method and > * tries to take the first provided argument. Ignores the action > * argument and index. > * > * @param t the transferable > * @param action the drag'n'drop action to do DnDConstants.ACTION_MOVE, ACTION_COPY, ACTION_LINK > * @param index index between children the drop occured at or -1 if not specified > * @return null if the transferable cannot be accepted or the paste type > * to execute when the drop occures > */ > public PasteType getDropType (Transferable t, int action, int index) { > return original.getDropType (t, action, index); > } > > /* Which paste operations are allowed when transferable t is in clipboard? > * @param t the transferable in clipboard > * @return array of operations that are allowed > */ > public PasteType[] getPasteTypes (Transferable t) { > return original.getPasteTypes (t); > } > > /* Support for new types that can be created in this node. > * @return array of new type operations that are allowed > */ > public NewType[] getNewTypes () { > return original.getNewTypes (); > } > > /* Delegates to original. > * > * @return array of system actions that should be in popup menu > */ > public SystemAction[] getActions () { > if (delegating (DELEGATE_GET_ACTIONS)) > return original.getActions (); > else > return super.getActions (); > } > > /* Delegates to original > */ > public SystemAction[] getContextActions () { > if (delegating (DELEGATE_GET_CONTEXT_ACTIONS)) > return original.getContextActions (); > else > return super.getContextActions (); > } > > /* > * @return default action of the original node or null > */ > public SystemAction getDefaultAction () { > return original.getDefaultAction (); > } > > public javax.swing.Action[] getActions(boolean context) { > if (context) { > if (!delegating (DELEGATE_GET_ACTIONS) || overridesAMethod ("getContextActions", new Class[0])) // NOI18N > return super.getActions (context); > } else { > if (!delegating (DELEGATE_GET_CONTEXT_ACTIONS) || overridesAMethod ("getActions", new Class[0])) // NOI18N > return super.getActions (context); > } > > javax.swing.Action[] retValue; > retValue = original.getActions(context); > return retValue; > } > > public javax.swing.Action getPreferredAction() { > javax.swing.Action retValue; > > if (overridesAMethod ("getDefaultAction", new Class[0])) { // NOI18N > retValue = super.getPreferredAction(); > } else { > retValue = original.getPreferredAction(); > } > > return retValue; > } > > /** Get a display name containing HTML markup. <strong><b>Note:</b> If you subclass > * FilterNode and override <code>getDisplayName()</code>, this method will > * always return null unless you override it as well (assuming that if you're > * changing the display name, you don't want an HTML display name constructed > * from the original node's display name to be what shows up in views of > * this node).</strong> If <code>getDisplayName()</code> is not overridden, > * this method will return whatever the original node returns from this > * method. > * <p> > * Note that if you do override <code>getDisplayName</code>, you should also override > * this method to return null. > * > * > * > * @see org.openide.nodes.Node#getHtmlDisplayName > * @return An HTML display name, if available, or null if no display name > * is available */ > public String getHtmlDisplayName() { > if (overridesGetDisplayName()) { > return null; > } else { > return delegating (DELEGATE_GET_DISPLAY_NAME) ? > original.getHtmlDisplayName() : super.getHtmlDisplayName(); > } > } > > private boolean overridesGetDisplayName() { > synchronized (overridesGetDisplayNameCache) { > Boolean b = (Boolean)overridesGetDisplayNameCache.get (getClass ()); > if (b == null) { > b = overridesAMethod ("getDisplayName", null) ? Boolean.TRUE : Boolean.FALSE; // NOI18N > overridesGetDisplayNameCache.put (getClass (), b); > } > return b.booleanValue() ? true : false; > } > } > > /* > * @return <CODE>true</CODE> if the original has a customizer. > */ > public boolean hasCustomizer () { > return original.hasCustomizer (); > } > > /* Returns the customizer component. > * @return the component or <CODE>null</CODE> if there is no customizer > */ > public java.awt.Component getCustomizer () { > return original.getCustomizer (); > } > > /* Delegates to original, if not special lookup provided in constructor, > * Otherwise it delegates to the lookup. > * > * @param type the class to look for > * @return instance of that class or null if this class of cookie > * is not supported > */ > public Node.Cookie getCookie (Class type) { > Lookup l = internalLookup (true); > if (l != null) { > Object o = l.lookup (type); > if (o instanceof Node.Cookie) { > return (Node.Cookie)o; > } > } > return original.getCookie (type); > } > > /** If this is FilterNode without any changes (subclassed, changed children) > * and the original provides handle, stores them and > * returns a new handle for the proxy. > * <p>Subclasses <strong>must</strong> override this if they wish for their nodes to be > * properly serializable. > * > * @return the handle, or <code>null</code> if this node is subclassed or > * uses changed children > */ > public Node.Handle getHandle () { > if (!isDefault ()) { > // subclasses has to implement the method by its own > return null; > } > Node.Handle original = this.original.getHandle (); > if (original == null) { > // no original handle => no handle here > return null; > } > > return new FilterHandle (original); > } > > /** Test equality of original nodes. > * Note that for subclasses of <code>FilterNode</code>, or filter nodes with non-default children, > * the test reverts to object identity. > * <strong>Note:</strong> if you wish that the {@link Index} cookie works correctly on > * filtered nodes and their subnodes, and you are subclassing <code>FilterNode</code> or > * using non-default children, you will probably want to override this method to test > * equality of the specified node with this filter node's original node; otherwise Move Up > * and Move Down actions may be disabled. > * <p>Note though that it is often better to provide your own index cookie from a filter > * node. Only then it is possible to change the number of children relative to the original. > * And in many cases this is easier anyway, as for example with > * <code>DataFolder.Index</code> for data folders. > * @param o something to compare to, presumably a node or <code>FilterNode</code> of one > * @return true if this node's original node is the same as the parameter (or original node of parameter) > */ > public boolean equals (Object o) { > // VERY DANGEROUS! Completely messes up visualizers and often original node is displayed rather than filter. > // Jst: I know that it is dangerous, but some code probably depends on it > > if (! (o instanceof Node)) return false; // something else or null > > if (this == o) return true; // shortcut > > // get the "most original" ones.... > Node left = getRepresentation(this); > Node right = getRepresentation((Node)o); > > // cover nondefault FilterNodes (possibly) deep in the stack > if ((left instanceof FilterNode) || (right instanceof FilterNode)) { > return left == right; > } > > return left.equals(right); > } > > private static Node getRepresentation(Node n) { > while (n instanceof FilterNode) { > FilterNode fn = (FilterNode)n; > if ( ! fn.isDefault() ) return n; > n = fn.original; > } > return n; // either node or nondefault FilterNode > } > > private static ThreadLocal hashCodeObjects = new ThreadLocal(); > > /** Hash by original nodes. > * Note that for subclasses of <code>FilterNode</code>, or filter nodes with non-default children, > * the hash reverts to the identity hash code. > * @return the delegated hash code > */ > public int hashCode () { > Stack callers = (Stack) hashCodeObjects.get(); > if (callers == null) { > callers = new Stack(); > hashCodeObjects.set(callers); > } > for (Iterator it = callers.iterator(); it.hasNext(); ) { > if (it.next() == this) { > System.out.println("RECURSIVE FilterNode.hashCode: original is a " + original.getClass().getName() + "; same as this: " + (original == this)); > System.out.print("stack:"); > for (it = callers.iterator(); it.hasNext(); ) { > Object o = it.next(); > System.out.print(" "); > if (o == this) > System.out.print("<<<"); > System.out.print(o.getClass().getName() + "[" + Integer.toHexString(System.identityHashCode(o)) + "]"); > if (o == this) > System.out.print(">>>"); > } > System.out.println(" <loop>"); > return super.hashCode(); > } > } > callers.push(this); > try { > assert hashCodeLogging (true) : ""; // NOI18N > int result = isDefault () ? original.hashCode () : super.hashCode (); > assert hashCodeLogging (false) : ""; // NOI18N > return result; > } catch (StackError err) { > err.add (this); > throw err; > } finally { > callers.pop(); > } > } > > /** Depth of stack trace. > */ > private static volatile int hashCodeDepth; > /** Method for tracing the issue 46993. Counts the depth of execution > * and if larger than 1000 throws debugging exception. > */ > private static boolean hashCodeLogging (boolean enter) { > if (hashCodeDepth > 1000) { > hashCodeDepth = 0; > throw new StackError (); > } > if (enter) { > hashCodeDepth++; > } else { > hashCodeDepth--; > } > return true; > } > > /** An exception to be thrown from hashCode() to debug issue 46993. > */ > private static class StackError extends StackOverflowError { > private java.util.IdentityHashMap nodes; > > public void add (FilterNode n) { > if (nodes == null) { > nodes = new java.util.IdentityHashMap (); > } > if (nodes.get (n) == null) { > nodes.put (n, n); > } > } > > public String getMessage () { > StringBuffer sb = new StringBuffer (); > sb.append ("StackOver in FilterNodes:\n"); // NOI18N > Iterator it = nodes.keySet ().iterator (); > while (it.hasNext ()) { > FilterNode f = (FilterNode)it.next (); > sb.append (" class: "); // NOI18N > sb.append (f.getClass ().getName()); > sb.append (" id: "); // NOI18N > sb.append (Integer.toString (System.identityHashCode (f), 16)); > sb.append ("\n"); // NOI18N > } > return sb.toString (); > } > } > > // public String toString () { > // return super.toString () + " original has children: " + original.getChildren ().getNodesCount (); // NOI18N > // } > > // ----------- END OF DELEGATED METHODS ------------ > > /** Get the original node. > * <p><strong>Yes</strong> this is supposed to be protected! If you > * are not subclassing <code>FilterNode</code> yourself, you should > * not be calling it (nor casting to <code>FilterNode</code>). Use > * cookies instead. > * @return the node proxied to > */ > protected Node getOriginal () { > return original; > } > > /** Create a property change listener that allows listening on the > * original node properties (contained in property sets) and propagating > * them to the proxy. > * <P> > * This method is called during initialization and allows subclasses > * to modify the default behaviour. > * > * @return a {@link PropertyChangeAdapter} in the default implementation > */ > protected PropertyChangeListener createPropertyChangeListener () { > return new PropertyChangeAdapter (this); > } > > /** Creates a node listener that allows listening on the > * original node and propagating events to the proxy. > * <p>Intended for overriding by subclasses, as with {@link #createPropertyChangeListener}. > * > * @return a {@link FilterNode.NodeAdapter} in the default implementation > */ > protected NodeListener createNodeListener () { > return new NodeAdapter (this); > } > > /** Getter for property change listener. > */ > synchronized PropertyChangeListener getPropertyChangeListener () { > if (propL == null) { > propL = createPropertyChangeListener (); > } > return propL; > } > > /** Getter for node listener. > */ > synchronized NodeListener getNodeListener () { > if (nodeL == null) { > nodeL = createNodeListener (); > getOriginal().addNodeListener(nodeL); > } > return nodeL; > } > > /** Notified from Node that a listener has been added. > * Thus we force initialization of listeners. > */ > final void listenerAdded () { > getNodeListener (); > } > > > /** Check method whether the node has default behaviour or > * if it is either subclass of uses different children. > * @return true if it is default > */ > private boolean isDefault () { > //System.err.print ("FilterNode.isDefault: "); > if (getClass () != FilterNode.class) { > //System.err.println("false\n\tsubclass of FilterNode"); > return false; > } > > return !childrenProvided; > } > > /** > * This method is used to change the Children from Children.LEAF to Children > * typically used to when there is a setChildren() on the original node > * setChildren will fire the appropriate events > */ > final void updateChildren () { > if (isDefault ()) { > org.openide.nodes.Children newChildren = null; > try { > Children.PR.enterReadAccess(); > if ( original.hierarchy == Children.LEAF && hierarchy != Children.LEAF ) { > newChildren = Children.LEAF; > } > else if ( original.hierarchy != Children.LEAF && hierarchy == Children.LEAF ) { > newChildren = new Children (original); > } > } > finally { > Children.PR.exitReadAccess(); > } > > if (newChildren != null) { > final org.openide.nodes.Children set = newChildren; > Children.MUTEX.postWriteRequest(new Runnable () { > public void run () { > setChildren (set); > } > }); > } > } > } > > > > /** Adapter that listens on changes in an original node > * and refires them in a proxy. > * This adapter is created during > * initialization in {@link FilterNode#createPropertyChangeListener}. The method > * can be overriden and this class used as the super class for the > * new implementation. > * <P> > * A reference to the proxy is stored by weak reference, so it does not > * prevent the node from being finalized. > */ > protected static class PropertyChangeAdapter extends Object implements PropertyChangeListener { > /** weak reference to filter node */ > private WeakReference fn; > > /** Create a new adapter. > * @param fn the proxy > */ > public PropertyChangeAdapter (FilterNode fn) { > this.fn = new WeakReference (fn); > } > > /* Find the node we are attached to. If it is not null call property > * change method with two arguments. > */ > public final void propertyChange (PropertyChangeEvent ev) { > FilterNode fn = (FilterNode)this.fn.get (); > if (fn == null) { > return; > } > > propertyChange (fn, ev); > } > > /** Actually propagate the event. > * Intended for overriding. > * @param fn the proxy > * @param ev the event > */ > protected void propertyChange (FilterNode fn, PropertyChangeEvent ev) { > fn.firePropertyChange ( > ev.getPropertyName (), ev.getOldValue (), ev.getNewValue () > ); > } > } > > /** Adapter that listens on changes in an original node and refires them > * in a proxy. Created in {@link FilterNode#createNodeListener}. > * @see FilterNode.PropertyChangeAdapter > */ > protected static class NodeAdapter extends Object implements NodeListener { > /** weak reference to filter node */ > private WeakReference fn; > > /** Create an adapter. > * @param fn the proxy > */ > public NodeAdapter (FilterNode fn) { > this.fn = new WeakReference (fn); > } > > /* Tests if the reference to the node provided in costructor is > * still valid (it has not been finalized) and if so, calls propertyChange (Node, ev). > */ > public final void propertyChange (PropertyChangeEvent ev) { > FilterNode fn = (FilterNode)this.fn.get (); > if (fn == null) { > return; > } > propertyChange (fn, ev); > } > > /** Actually refire the change event in a subclass. > * The default implementation ignores changes of the <code>parentNode</code> property but refires > * everything else. > * > * @param fn the filter node > * @param ev the event to fire > */ > protected void propertyChange (FilterNode fn, PropertyChangeEvent ev) { > String n = ev.getPropertyName (); > if (n.equals (Node.PROP_PARENT_NODE)) { > // does nothing > return; > } > if (n.equals (Node.PROP_DISPLAY_NAME)) { > fn.fireOwnPropertyChange ( > PROP_DISPLAY_NAME, (String)ev.getOldValue (), (String)ev.getNewValue () > ); > return; > } > if (n.equals (Node.PROP_NAME)) { > fn.fireOwnPropertyChange ( > PROP_NAME, (String)ev.getOldValue (), (String)ev.getNewValue () > ); > return; > } > if (n.equals (Node.PROP_SHORT_DESCRIPTION)) { > fn.fireOwnPropertyChange ( > PROP_SHORT_DESCRIPTION, (String)ev.getOldValue (), (String)ev.getNewValue () > ); > return; > } > if (n.equals (Node.PROP_ICON)) { > fn.fireIconChange (); > return; > } > if (n.equals (Node.PROP_OPENED_ICON)) { > fn.fireOpenedIconChange (); > return; > } > if (n.equals (Node.PROP_PROPERTY_SETS)) { > fn.firePropertySetsChange ((PropertySet[])ev.getOldValue (), (PropertySet[])ev.getNewValue ()); > return; > } > if (n.equals (Node.PROP_COOKIE)) { > fn.fireCookieChange (); > return; > } > if (n.equals(Node.PROP_LEAF)) { > fn.updateChildren (); > /* > fn.fireOwnPropertyChange( > Node.PROP_LEAF, ev.getOldValue(), ev.getNewValue() > ); > */ > } > } > > /** Does nothing. > * @param ev event describing the action > */ > public void childrenAdded (NodeMemberEvent ev) { > } > > /** Does nothing. > * @param ev event describing the action > */ > public void childrenRemoved (NodeMemberEvent ev) { > } > > /** Does nothing. > * @param ev event describing the action > */ > public void childrenReordered (NodeReorderEvent ev) { > } > > /* Does nothing. > * @param ev event describing the node > */ > public final void nodeDestroyed (NodeEvent ev) { > FilterNode fn = (FilterNode)this.fn.get (); > if (fn == null) { > return; > } > fn.originalDestroyed (); > } > } > > /** Children for a filter node. Listens on changes in subnodes of > * the original node and asks this filter node to creates representants for > * these subnodes. > * <P> > * This class is used as the default for subnodes of filter node, but > * subclasses may modify it or provide a totally different implementation. > * <p><code>FilterNode.Children</code> is not well suited to cases where you need to insert > * additional nodes at the beginning or end of the list, or where you may need > * to merge together multiple original children lists, or reorder them, etc. > * That is because the keys are of type <code>Node</code>, one for each original > * child, and the keys are reset during {@link #addNotify}, {@link #filterChildrenAdded}, > * {@link #filterChildrenRemoved}, and {@link #filterChildrenReordered}, so it is > * not trivial to use different keys: you would need to override <code>addNotify</code> > * (calling super first!) and the other three update methods. For such complex cases > * you will do better by creating your own <code>Children.Keys</code> subclass, setting > * keys that are useful to you, and keeping a <code>NodeListener</code> on the original > * node to handle changes. > */ > public static class Children extends > org.openide.nodes.Children.Keys > implements Cloneable { > /** Original node. Should not be modified. */ > protected Node original; > /** node listener on original */ > private ChildrenAdapter nodeL; > > /** Create children. > * @param or original node to take children from */ > public Children (Node or) { > original = or; > } > > /** Sets the original children for this children > * @param original The new original node. > * @since 1.39 > */ > protected final void changeOriginal( Node original ) { > > > try { > PR.enterWriteAccess(); > > boolean wasAttached = nodeL != null; > > // uregister from the original node > if ( wasAttached ) { > this.original.removeNodeListener( nodeL ); > nodeL = null; > } > > // reset the original node > this.original = original; > > if ( wasAttached ) { > addNotifyImpl(); > } > } > finally { > PR.exitWriteAccess(); > } > > } > > /** Closes the listener, if any, on the original node. > */ > protected void finalize () { > if (nodeL != null) original.removeNodeListener (nodeL); > nodeL = null; > } > > /* Clones the children object. > */ > public Object clone () { > return new Children (original); > } > > /** Initializes listening to changes in original node. > */ > protected void addNotify () { > addNotifyImpl(); > } > > private void addNotifyImpl () { > // add itself to reflect to changes children of original node > nodeL = new ChildrenAdapter (this); > original.addNodeListener (nodeL); > > updateKeys (); > } > > /** Clears current keys, because all mirrored nodes disappeared. > */ > protected void removeNotify () { > setKeys (Collections.EMPTY_SET); > > if (nodeL != null) { > original.removeNodeListener (nodeL); > nodeL = null; > } > } > > > > /** Allows subclasses to override > * creation of node representants for nodes in the mirrored children > * list. The default implementation simply uses {@link Node#cloneNode}. > * <p>Note that this method is only suitable for a 1-to-1 mirroring. > * > * @param node node to create copy of > * @return copy of the original node > */ > protected Node copyNode (Node node) { > return node.cloneNode (); > } > > > /* Implements find of child by finding the original child and then [PENDING] > * @param name of node to find > * @return the node or null > */ > public Node findChild (String name) { > original.getChildren ().findChild (name); > return super.findChild (name); > } > > > /** Create nodes representing copies of the original node's children. > * The default implementation returns exactly one representative for each original node, > * as returned by {@link #copyNode}. > * Subclasses may override this to avoid displaying a copy of an original child at all, > * or even to display multiple nodes representing the original. > * @param key the original child node > * @return zero or more nodes representing the original child node > */ > protected Node[] createNodes (Object key) { > Node n = (Node)key; > // is run under read access lock so nobody can change children > return new Node[] { copyNode (n) }; > } > > /* Delegates to children of the original node. > * > * @param arr nodes to add > * @return true/false > */ > public boolean add (Node[] arr) { > return original.getChildren ().add (arr); > } > > /* Delegates to filter node. > * @param arr nodes to remove > * @return true/false > */ > public boolean remove (Node[] arr) { > return original.getChildren ().remove (arr); > } > > /** Called when the filter node adds a new child. > * The default implementation makes a corresponding change. > * @param ev info about the change > */ > protected void filterChildrenAdded (NodeMemberEvent ev) { > updateKeys (); > } > > /** Called when the filter node removes a child. > * The default implementation makes a corresponding change. > * @param ev info about the change > */ > protected void filterChildrenRemoved (NodeMemberEvent ev) { > updateKeys (); > } > > /** Called when the filter node reorders its children. > * The default implementation makes a corresponding change. > * @param ev info about the change > */ > protected void filterChildrenReordered (NodeReorderEvent ev) { > updateKeys (); > } > > /** variable to notify that there is a cyclic update. > * Used only in updateKeys method > */ > // private transient boolean cyclic; > > /** Update keys from original nodes */ > private void updateKeys () { > ChildrenAdapter runnable = nodeL; > if (runnable != null) { > runnable.run (); > } > } > > /** > * Implementation that ensures the original node is fully initialized > * if optimal result is requested. > * > * @param optimalResult if <code>true</code>, the method will block > * until the original node is fully initialized. > * @since 3.9 > */ > public Node[] getNodes(boolean optimalResult) { > if (optimalResult) { > setKeys (original.getChildren ().getNodes (true)); > } > return getNodes(); > } > } > > /** Adapter that listens on changes in the original node and fires them > * in this node. > * Used as the default listener in {@link FilterNode.Children}, > * and is intended for refinement by its subclasses. > */ > private static class ChildrenAdapter extends Object > implements NodeListener, Runnable { > /** children object to notify about addition of children. > * Can be null. Set from Children's initNodes method. > */ > private WeakReference children; > > /** Create a new adapter. > * @param ch the children list > */ > public ChildrenAdapter (Children ch) { > this.children = new WeakReference (ch); > } > > /** Called to update the content of children. > */ > public void run () { > Children ch = (Children)children.get (); > if (ch != null) { > Node[] arr = ch.original.getChildren ().getNodes (); > ch.setKeys (arr); > } > } > > /** Does nothing. > * @param ev the event > */ > public void propertyChange (PropertyChangeEvent ev) { > } > > /* Informs that a set of new children has been added. > * @param ev event describing the action > */ > public void childrenAdded (NodeMemberEvent ev) { > Children children = (Children)this.children.get (); > if (children == null) return; > > children.filterChildrenAdded (ev); > } > > /* Informs that a set of children has been removed. > * @param ev event describing the action > */ > public void childrenRemoved (NodeMemberEvent ev) { > Children children = (Children)this.children.get (); > if (children == null) return; > > children.filterChildrenRemoved (ev); > } > > /* Informs that a set of children has been reordered. > * @param ev event describing the action > */ > public void childrenReordered (NodeReorderEvent ev) { > Children children = (Children)this.children.get (); > if (children == null) return; > > children.filterChildrenReordered (ev); > } > > > /** Does nothing. > * @param ev the event > */ > public void nodeDestroyed (NodeEvent ev) { > } > } > > /** Filter node handle. > */ > private static final class FilterHandle implements Node.Handle { > private Node.Handle original; > > static final long serialVersionUID =7928908039428333839L; > > public FilterHandle (Node.Handle original) { > this.original = original; > } > > public Node getNode () throws IOException { > return new FilterNode (original.getNode ()); > } > > public String toString () { > return "FilterHandle[" + original + "]"; // NOI18N > } > } > > /** Special ProxyLookup > */ > private static final class FilterLookup extends org.openide.util.Lookup { > /** node we belong to */ > private FilterNode node; > /** lookup we delegate too */ > private Lookup delegate; > /** set of all results associated to this lookup */ > private org.openide.util.WeakSet results; > > > FilterLookup () { > } > > /** Registers own node. > */ > public void ownNode (FilterNode n) { > this.node = n; > } > > /** A method that replaces instance of original node > * with a new one > */ > private Object replaceNodes (Object orig, Class clazz ) { > if (isNodeQuery( clazz ) && orig == node.getOriginal() && clazz.isInstance( node )) { > return node; > } else { > return orig; > } > } > > /** Changes the node we delegate to if necessary. > * @param n the node to delegate to > */ > public Lookup checkNode () { > Lookup l = node.getOriginal ().getLookup(); > if (delegate == l) return l; > > Iterator toCheck = null; > synchronized (this) { > if (l != delegate) { > this.delegate = l; > if (results != null) { > toCheck = Arrays.asList (results.toArray ()).iterator(); > } > } > } > > if (toCheck != null) { > // update > Iterator it = toCheck; > while (it.hasNext()) { > ProxyResult p = (ProxyResult)it.next (); > if (p.updateLookup (l)) { > p.resultChanged (null); > } > } > } > > return delegate; > } > > public Result lookup(Template template) { > ProxyResult p = new ProxyResult (template); > > synchronized (this) { > if (results == null) { > results = new org.openide.util.WeakSet (); > } > results.add (p); > } > > return p; > } > > public Object lookup(Class clazz) { > return replaceNodes (checkNode ().lookup (clazz), clazz ); > } > > /** Finds out whether a query for a class can be influenced > * by a state of the "nodes" lookup and whether we should > * initialize listening > */ > private static boolean isNodeQuery (Class c) { > return Node.class.isAssignableFrom (c) || c.isAssignableFrom (Node.class); > } > > public Item lookupItem(Template template) { > Item i = checkNode ().lookupItem (template); > return isNodeQuery (template.getType ()) ? new FilterItem (i, template.getType()) : i; > } > > /** > * Result used in SimpleLookup. It holds a reference to the collection > * passed in constructor. As the contents of this lookup result never > * changes the addLookupListener and removeLookupListener are empty. > */ > private final class ProxyResult extends Result > implements LookupListener { > /** Template used for this result. It is never null.*/ > private Template template; > /** result to delegate to */ > private Lookup.Result delegate; > /** listeners set */ > private javax.swing.event.EventListenerList listeners; > > /** Just remembers the supplied argument in variable template.*/ > ProxyResult (Template template) { > this.template = template; > } > > /** Checks state of the result > */ > private Result checkResult () { > updateLookup (checkNode ()); > return this.delegate; > } > > /** Updates the state of the lookup. > * @return true if the lookup really changed > */ > public boolean updateLookup (Lookup l) { > Collection oldPairs = delegate != null ? delegate.allItems () : null; > > synchronized (this) { > if (delegate != null) { > delegate.removeLookupListener (this); > } > delegate = l.lookup (template); > delegate.addLookupListener (this); > } > > if (oldPairs == null) { > // nobody knows about a change > return false; > } > > Collection newPairs = delegate.allItems (); > > return !oldPairs.equals (newPairs); > } > > > public synchronized void addLookupListener(LookupListener l) { > if (listeners == null) { > listeners = new javax.swing.event.EventListenerList (); > } > listeners.add (LookupListener.class, l); > } > > public synchronized void removeLookupListener(LookupListener l) { > if (listeners != null) { > listeners.remove (LookupListener.class, l); > } > } > > public java.util.Collection allInstances() { > java.util.Collection c = checkResult ().allInstances (); > > if (isNodeQuery (template.getType ())) { > ArrayList ll = new ArrayList (c.size ()); > Iterator it = c.iterator(); > while (it.hasNext()) { > ll.add (replaceNodes (it.next (), template.getType())); > } > return ll; > } else { > return c; > } > } > > public Set allClasses () { > return checkResult ().allClasses (); > } > > public Collection allItems () { > return checkResult ().allItems (); > } > > /** A change in lookup occured. > * @param ev event describing the change > * > */ > public void resultChanged(LookupEvent anEvent) { > javax.swing.event.EventListenerList l = this.listeners; > if (l == null) return; > > Object[] listeners = l.getListenerList(); > if (listeners.length == 0) return; > > LookupEvent ev = new LookupEvent (this); > for (int i = listeners.length - 1; i >= 0; i -= 2) { > LookupListener ll = (LookupListener)listeners[i]; > ll.resultChanged(ev); > } > } > } // end of ProxyResult > > /** Item that exchanges the original node for the FilterNode */ > private final class FilterItem extends Lookup.Item { > private Item delegate; > private Class clazz; > > FilterItem (Item d, Class clazz) { > this.delegate = d; > this.clazz = clazz; > } > > public String getDisplayName() { > return delegate.getDisplayName (); > } > > public String getId() { > return delegate.getId (); > } > > public Object getInstance() { > return replaceNodes (delegate.getInstance (), clazz); > } > > public Class getType() { > return delegate.getType (); > } > } > } // end of FilterLookup > }
This does appear to be a problem with the Hotspot server compiler. It does not show up when running with -J-client. It also does not show up if I replace super.hashCode() with System.identityHashCode(this). My suspicion is that the compiler is incorrectly specializing the hashCode() method in the subclass org.netbeans.modules.java.ui.NavigationView.FNode (which inherits the method from FilterNode) and leaving the call to super.hashCode() even though super is now FilterNode rather than Node (which inherits the default Object.hashCode()). I'd suggest doing this replacement in the source code since it doesn't cost anything and will prevent the problem from showing up with the 5.0 JVM.
Will implement the suggested workaround.
Implemented the workaround. Thanks for finding it. openide/src/org/openide/nodes/FilterNode.java,v1.98
*** Issue 50451 has been marked as a duplicate of this issue. ***
*** Issue 50939 has been marked as a duplicate of this issue. ***
*** Issue 51517 has been marked as a duplicate of this issue. ***
*** Issue 51497 has been marked as a duplicate of this issue. ***
We are developing a RCP Application based on NB 7.4 A colleague of mine had this exception: org.openide.util.RequestProcessor$SlowItem: task failed due to: org.openide.nodes.FilterNode$StackError: StackOver in FilterNodes: class: com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel$CheckableSimulationLinkFilterNode id: 12b18231 at org.openide.util.RequestProcessor$Task.schedule(RequestProcessor.java:1473) at org.openide.nodes.AsynchChildren.addNotify(AsynchChildren.java:89) at org.openide.nodes.Children.callAddNotify(Children.java:575) at org.openide.nodes.EntrySupportDefault.getArray(EntrySupportDefault.java:650) at org.openide.nodes.EntrySupportDefault.getNodes(EntrySupportDefault.java:121) at org.openide.nodes.EntrySupportDefault.getNodes(EntrySupportDefault.java:172) at org.openide.nodes.EntrySupportDefault.getNodesCount(EntrySupportDefault.java:176) at org.openide.nodes.Children.getNodesCount(Children.java:509) at org.openide.explorer.view.VisualizerNode.getChildren(VisualizerNode.java:258) at org.openide.explorer.view.VisualizerNode.getChildren(VisualizerNode.java:248) at org.openide.explorer.view.VisualizerNode.getChildCount(VisualizerNode.java:325) at javax.swing.tree.DefaultTreeModel.getChildCount(DefaultTreeModel.java:186) at org.netbeans.swing.outline.DefaultOutlineModel.getChildCount(DefaultOutlineModel.java:236) at javax.swing.tree.VariableHeightLayoutCache$TreeStateNode.expand(VariableHeightLayoutCache.java:1478) at javax.swing.tree.VariableHeightLayoutCache$TreeStateNode.expand(VariableHeightLayoutCache.java:1287) at javax.swing.tree.VariableHeightLayoutCache.rebuild(VariableHeightLayoutCache.java:742) at javax.swing.tree.VariableHeightLayoutCache.treeStructureChanged(VariableHeightLayoutCache.java:643) at org.netbeans.swing.outline.EventBroadcaster.treeStructureChanged(EventBroadcaster.java:409) at javax.swing.tree.DefaultTreeModel.fireTreeStructureChanged(DefaultTreeModel.java:580) at javax.swing.tree.DefaultTreeModel.nodeStructureChanged(DefaultTreeModel.java:365) at javax.swing.tree.DefaultTreeModel.setRoot(DefaultTreeModel.java:135) at org.openide.explorer.view.NodeTreeModel$1.run(NodeTreeModel.java:123) at org.openide.util.Mutex.doEvent(Mutex.java:1348) at org.openide.util.Mutex.readAccess(Mutex.java:355) at org.openide.explorer.view.NodeTreeModel.setNode(NodeTreeModel.java:108) at org.openide.explorer.view.NodeTreeModel.setNode(NodeTreeModel.java:104) at org.openide.explorer.view.OutlineView.synchronizeRootContext(OutlineView.java:849) at org.openide.explorer.view.OutlineView$TableSelectionListener.propertyChange(OutlineView.java:1359) at org.openide.util.WeakListenerImpl$PropertyChange.propertyChange(WeakListenerImpl.java:196) at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335) at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327) at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263) at org.openide.explorer.ExplorerManager$2.run(ExplorerManager.java:876) at org.openide.util.Mutex.doEvent(Mutex.java:1348) at org.openide.util.Mutex.readAccess(Mutex.java:355) at org.openide.explorer.ExplorerManager.fireInAWT(ExplorerManager.java:873) at org.openide.explorer.ExplorerManager$1SetRootContext.run(ExplorerManager.java:498) at org.openide.util.Mutex.readAccess(Mutex.java:372) at org.openide.util.Mutex$1R.run(Mutex.java:1314) at org.openide.nodes.Children$ProjectManagerDeadlockDetector.execute(Children.java:1921) at org.openide.util.Mutex.doWrapperAccess(Mutex.java:1333) at org.openide.util.Mutex.readAccess(Mutex.java:361) at org.openide.explorer.ExplorerManager.setRootContext(ExplorerManager.java:510) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel.refreshTTV(DataPointSetPanel.java:707) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel.access$400(DataPointSetPanel.java:73) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel$7.propertyChange(DataPointSetPanel.java:279) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel$7.propertyChange(DataPointSetPanel.java:266) at com.streamsim.toolpanel.spi.mvc.UpdatablePanel.refresh(UpdatablePanel.java:231) at com.streamsim.toolpanel.spi.mvc.UpdatablePanel.updateModel(UpdatablePanel.java:162) at com.streamsim.toolpanel.spi.mvc.UpdatablePanel.updateModel(UpdatablePanel.java:125) at com.streamsim.toolpanel.spi.mvc.ToolPanelPanelMVC.updateModel(ToolPanelPanelMVC.java:261) at com.streamsim.toolpanel.spi.mvc.ToolPanelPanelMVC.updateModel(ToolPanelPanelMVC.java:253) at com.streamsim.toolpanel.spi.mvc.ToolPanelPanelMVC.access$200(ToolPanelPanelMVC.java:30) at com.streamsim.toolpanel.spi.mvc.ToolPanelPanelMVC$1$1.run(ToolPanelPanelMVC.java:59) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733) at java.awt.EventQueue.access$200(EventQueue.java:103) at java.awt.EventQueue$3.run(EventQueue.java:694) at java.awt.EventQueue$3.run(EventQueue.java:692) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:703) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:159) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138) at java.awt.EventDispatchThread.run(EventDispatchThread.java:91) Caused by: org.openide.nodes.FilterNode$StackError: StackOver in FilterNodes: class: com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel$CheckableSimulationLinkFilterNode id: 12b18231 at org.openide.nodes.FilterNode.hashCodeLogging(FilterNode.java:940) at org.openide.nodes.FilterNode.hashCode(FilterNode.java:925) at java.util.HashMap.hash(HashMap.java:351) at java.util.HashMap.put(HashMap.java:471) at org.openide.nodes.Children$Dupl.updateListAndMap(Children.java:1731) at org.openide.nodes.Children$Dupl.updateList(Children.java:1704) at org.openide.nodes.Children$Keys.setKeys(Children.java:1503) at org.openide.nodes.AsynchChildren$1.add(AsynchChildren.java:190) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel.addNodesToList(DataPointSetPanel.java:1027) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel.access$3000(DataPointSetPanel.java:73) at com.streamsim.metricspaceplots.mds.panels.DataPointSetPanel$RunFactory.createKeys(DataPointSetPanel.java:1047) at org.openide.nodes.AsynchChildren.run(AsynchChildren.java:209) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1432) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2042) There was a high number of nodes, more than 900. He was using a server jre Here more information: Diagnostic information Input arguments: -Dnetbeans.logger.console=true -ea -server -Xss2m -Xms128m -XX:PermSize=128m -XX:MaxPermSize=200m -Xverify:none -XX:+UseAdaptiveSizePolicy -Djava.net.useSystemProxies=true -Dorg.netbeans.ProxyClassLoader.level=1000 -Dnetbeans.logger.console=true -Dorg.netbeans.modules.autoupdate.level=INFO -Dnetbeans.keep.expansion=false -XX:+UseCompressedStrings -XX:+UseStringCache -Djdk.home=C:\Program Files\Java\jdk1.7.0_25 -splash:C:\Users\Darryl\Code\trunk\studioSL\suite_studioSL\build\testuserdir\var\cache\splash.png -Dnetbeans.home=C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\platform -Dnetbeans.user=C:\Users\Darryl\Code\trunk\studioSL\suite_studioSL\build\testuserdir -Dnetbeans.default_userdir_root= -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\Darryl\Code\trunk\studioSL\suite_studioSL\build\testuserdir\var\log\heapdump.hprof -Dsun.awt.keepWorkingSetOnMinimize=true -Dnetbeans.dirs=C:\Users\Darryl\Code\trunk\studioSL\suite_studioSL\build\cluster;C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\dlight;C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\extide;C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\harness;C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\ide;C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\java;C:\Users\Darryl\Code\netbeans-7.4p2\netbeans\platform;C:\Users\Darryl\Code\trunk\commons\suite_commons\build\cluster exit Compiler: HotSpot 64-Bit Tiered Compilers Heap memory usage: initial 128.0MB maximum 2723.6MB Non heap memory usage: initial 130.4MB maximum 248.0MB Garbage collector: PS Scavenge (Collections=10 Total time spent=0s) Garbage collector: PS MarkSweep (Collections=1 Total time spent=0s) Classes: loaded=7083 total loaded=7083 unloaded 0 INFO [org.netbeans.core.ui.warmup.DiagnosticTask]: Total memory 12,846,387,200
please do not reopen prehistoric issues, in case you see something similar, file a new issue with a complete IDE log attached.