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.

View | Details | Raw Unified | Return to bug 62707
Collapse All | Expand All

(-)loaders/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.loaders
2
OpenIDE-Module: org.openide.loaders
3
OpenIDE-Module-Specification-Version: 5.11
3
OpenIDE-Module-Specification-Version: 6.0
4
OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties
5
5
(-)loaders/api/apichanges.xml (+18 lines)
Lines 83-88 Link Here
83
<!-- ACTUAL CHANGES BEGIN HERE: -->
83
<!-- ACTUAL CHANGES BEGIN HERE: -->
84
84
85
  <changes>
85
  <changes>
86
     <change id="fileobject-in-lookup">
87
        <api name="loaders"/>
88
        <summary>DataNode.getLookup contains FileObject</summary>
89
        <version major="6" minor="0"/>
90
        <date day="2" month="11" year="2006"/>
91
        <author login="jtulach"/>
92
        <compatibility addition="yes" binary="compatible" semantic="compatible" />
93
        <description>
94
        <p>
95
            Since now, each DataNode constructed without using own lookup,
96
            shall have <code>FileObject</code>(s) associated with its
97
            <a href="@TOP@/org/openide/loaders/DataObject.html#files()">DataObject</a> 
98
            available in its own lookup.
99
        </p>
100
        </description>
101
        <class package="org.openide.loaders" name="DataNode" />
102
        <issue number="62707"/>
103
    </change>
86
104
87
    <change id="ExternalDragAndDrop">
105
    <change id="ExternalDragAndDrop">
88
        <api name="loaders"/>
106
        <api name="loaders"/>
(-)loaders/nbproject/project.xml (-65 / +65 lines)
Lines 85-91 Link Here
85
                    <build-prerequisite/>
85
                    <build-prerequisite/>
86
                    <compile-dependency/>
86
                    <compile-dependency/>
87
                    <run-dependency>
87
                    <run-dependency>
88
                        <specification-version>6.5</specification-version>
88
                        <specification-version>7.0</specification-version>
89
                    </run-dependency>
89
                    </run-dependency>
90
                </dependency>
90
                </dependency>
91
                <dependency>
91
                <dependency>
Lines 113-182 Link Here
113
                    </run-dependency>
113
                    </run-dependency>
114
                </dependency>
114
                </dependency>
115
            </module-dependencies>
115
            </module-dependencies>
116
          <test-dependencies>
116
            <test-dependencies>
117
              <test-type>
117
                <test-type>
118
                  <name>unit</name>
118
                    <name>unit</name>
119
                  <test-dependency>
119
                    <test-dependency>
120
                      <code-name-base>org.openide.loaders</code-name-base>
120
                        <code-name-base>org.openide.loaders</code-name-base>
121
                      <recursive/>
121
                        <recursive/>
122
                      <compile-dependency/>
122
                        <compile-dependency/>
123
                  </test-dependency>
123
                    </test-dependency>
124
                  <test-dependency>
124
                    <test-dependency>
125
                      <code-name-base>org.openide.options</code-name-base>
125
                        <code-name-base>org.openide.options</code-name-base>
126
                      <compile-dependency/>
126
                        <compile-dependency/>
127
                  </test-dependency>
127
                    </test-dependency>
128
                  <test-dependency>
128
                    <test-dependency>
129
                      <code-name-base>org.openide.compat</code-name-base>
129
                        <code-name-base>org.openide.compat</code-name-base>
130
                      <compile-dependency/>
130
                        <compile-dependency/>
131
                  </test-dependency>
131
                    </test-dependency>
132
                  <test-dependency>
132
                    <test-dependency>
133
                      <code-name-base>org.netbeans.core</code-name-base>
133
                        <code-name-base>org.netbeans.core</code-name-base>
134
                      <compile-dependency/>
134
                        <compile-dependency/>
135
                  </test-dependency>
135
                    </test-dependency>
136
                  <test-dependency>
136
                    <test-dependency>
137
                      <code-name-base>org.netbeans.swing.plaf</code-name-base>
137
                        <code-name-base>org.netbeans.swing.plaf</code-name-base>
138
                      <compile-dependency/>
138
                        <compile-dependency/>
139
                  </test-dependency>
139
                    </test-dependency>
140
                  <test-dependency>
140
                    <test-dependency>
141
                      <code-name-base>org.netbeans.modules.settings</code-name-base>
141
                        <code-name-base>org.netbeans.modules.settings</code-name-base>
142
                      <compile-dependency/>
142
                        <compile-dependency/>
143
                  </test-dependency>
143
                    </test-dependency>
144
                  <test-dependency>
144
                    <test-dependency>
145
                      <code-name-base>org.openide.util</code-name-base>
145
                        <code-name-base>org.openide.util</code-name-base>
146
                      <compile-dependency/>
146
                        <compile-dependency/>
147
                  </test-dependency>
147
                    </test-dependency>
148
                  <test-dependency>
148
                    <test-dependency>
149
                      <code-name-base>org.netbeans.core.ui</code-name-base>
149
                        <code-name-base>org.netbeans.core.ui</code-name-base>
150
                      <compile-dependency/>
150
                        <compile-dependency/>
151
                  </test-dependency>
151
                    </test-dependency>
152
                  <test-dependency>
152
                    <test-dependency>
153
                      <code-name-base>org.netbeans.modules.progress.ui</code-name-base>
153
                        <code-name-base>org.netbeans.modules.progress.ui</code-name-base>
154
                  </test-dependency>
154
                    </test-dependency>
155
                  <test-dependency>
155
                    <test-dependency>
156
                      <code-name-base>org.netbeans.modules.editor.mimelookup.impl</code-name-base>
156
                        <code-name-base>org.netbeans.modules.editor.mimelookup.impl</code-name-base>
157
                  </test-dependency>
157
                    </test-dependency>
158
                  <test-dependency>
158
                    <test-dependency>
159
                      <code-name-base>org.netbeans.core.startup</code-name-base>
159
                        <code-name-base>org.netbeans.core.startup</code-name-base>
160
                      <compile-dependency/>
160
                        <compile-dependency/>
161
                  </test-dependency>
161
                    </test-dependency>
162
                  <test-dependency>
162
                    <test-dependency>
163
                      <code-name-base>org.netbeans.bootstrap</code-name-base>
163
                        <code-name-base>org.netbeans.bootstrap</code-name-base>
164
                      <compile-dependency/>
164
                        <compile-dependency/>
165
                  </test-dependency>
165
                    </test-dependency>
166
              </test-type>
166
                </test-type>
167
              <test-type>
167
                <test-type>
168
                  <name>qa-functional</name>
168
                    <name>qa-functional</name>
169
                  <test-dependency>
169
                    <test-dependency>
170
                      <code-name-base>org.openide.loaders</code-name-base>
170
                        <code-name-base>org.openide.loaders</code-name-base>
171
                      <recursive/>
171
                        <recursive/>
172
                      <compile-dependency/>
172
                        <compile-dependency/>
173
                  </test-dependency>
173
                    </test-dependency>
174
                  <test-dependency>
174
                    <test-dependency>
175
                      <code-name-base>org.netbeans.core</code-name-base>
175
                        <code-name-base>org.netbeans.core</code-name-base>
176
                      <recursive/>
176
                        <recursive/>
177
                      <compile-dependency/>
177
                        <compile-dependency/>
178
                  </test-dependency>
178
                    </test-dependency>
179
              </test-type>
179
                </test-type>
180
            </test-dependencies>
180
            </test-dependencies>
181
            <public-packages>
181
            <public-packages>
182
                <package>org.openide.awt</package>
182
                <package>org.openide.awt</package>
(-)loaders/src/org/openide/loaders/DataNode.java (-2 / +24 lines)
Lines 77-82 Link Here
77
        this.obj = obj;
77
        this.obj = obj;
78
78
79
        propL = new PropL ();
79
        propL = new PropL ();
80
        if (lookup == null) {
81
            setCookieSet(CookieSet.createGeneric(propL));
82
        }
80
83
81
        obj.addPropertyChangeListener (org.openide.util.WeakListeners.propertyChange (propL, obj));
84
        obj.addPropertyChangeListener (org.openide.util.WeakListeners.propertyChange (propL, obj));
82
85
Lines 622-627 Link Here
622
625
623
        return p;
626
        return p;
624
    }
627
    }
628
    
629
    /** Update files, if we are using CookieSet
630
     */
631
    private void updateFilesInCookieSet(Set<FileObject> obj) {
632
        if (ownLookup()) {
633
            return;
634
        }
635
        getCookieSet().assign(FileObject.class, obj.toArray(new FileObject[0]));
636
    }
625
637
626
    /** Support for firing property change.
638
    /** Support for firing property change.
627
    * @param ev event describing the change
639
    * @param ev event describing the change
Lines 636-647 Link Here
636
                }
648
                }
637
649
638
                if (DataObject.PROP_PRIMARY_FILE.equals(ev.getPropertyName())) {
650
                if (DataObject.PROP_PRIMARY_FILE.equals(ev.getPropertyName())) {
639
                    // the node is not interested in children changes
640
                    propL.updateStatusListener();
651
                    propL.updateStatusListener();
641
                    setName(obj.getName(), false);
652
                    setName(obj.getName(), false);
653
                    updateFilesInCookieSet(obj.files());
642
                    return;
654
                    return;
643
                }
655
                }
644
656
657
                if (DataObject.PROP_FILES.equals(ev.getPropertyName())) {
658
                    updateFilesInCookieSet(obj.files());
659
                }
660
645
                if (DataObject.PROP_NAME.equals(ev.getPropertyName())) {
661
                if (DataObject.PROP_NAME.equals(ev.getPropertyName())) {
646
                    DataNode.super.setName(obj.getName());
662
                    DataNode.super.setName(obj.getName());
647
                    updateDisplayName();
663
                    updateDisplayName();
Lines 751-757 Link Here
751
    * properties to this node.
767
    * properties to this node.
752
    */
768
    */
753
    private class PropL extends Object
769
    private class PropL extends Object
754
        implements PropertyChangeListener, FileStatusListener {
770
    implements PropertyChangeListener, FileStatusListener, CookieSet.Before {
755
        /** weak version of this listener */
771
        /** weak version of this listener */
756
        private FileStatusListener weakL;
772
        private FileStatusListener weakL;
757
        /** previous filesystem we were attached to */
773
        /** previous filesystem we were attached to */
Lines 825-830 Link Here
825
                        }
841
                        }
826
                    }
842
                    }
827
                }
843
                }
844
            }
845
        }
846
847
        public void beforeLookup(Class<?> clazz) {
848
            if (clazz.isAssignableFrom(FileObject.class)) {
849
                updateFilesInCookieSet(obj.files());
828
            }
850
            }
829
        }
851
        }
830
    }
852
    }
(-)loaders/src/org/openide/loaders/MultiDataObject.java (-12 / +29 lines)
Lines 259-264 Link Here
259
        }
259
        }
260
        
260
        
261
        firePropertyChangeLater (PROP_FILES, null, null);
261
        firePropertyChangeLater (PROP_FILES, null, null);
262
        updateFilesInCookieSet();
262
263
263
        if (fe.isImportant ()) {
264
        if (fe.isImportant ()) {
264
            checkConsistency(this);
265
            checkConsistency(this);
Lines 736-742 Link Here
736
            if (cookieSet != null) return cookieSet;
737
            if (cookieSet != null) return cookieSet;
737
738
738
            // sets empty sheet and adds a listener to it
739
            // sets empty sheet and adds a listener to it
739
            setCookieSet (new CookieSet (), false);
740
            setCookieSet (CookieSet.createGeneric(getChangeListener()), false);
740
            return cookieSet;
741
            return cookieSet;
741
        }
742
        }
742
    }
743
    }
Lines 770-776 Link Here
770
    ) {
771
    ) {
771
        firingProcessor.post(new Runnable () {
772
        firingProcessor.post(new Runnable () {
772
    	    public void run () {
773
    	    public void run () {
773
        	firePropertyChange (name, oldV, newV);
774
                firePropertyChange (name, oldV, newV);
775
                if (PROP_FILES.equals(name) || PROP_PRIMARY_FILE.equals(name)) {
776
                    updateFilesInCookieSet();
777
                }
774
            }
778
            }
775
        });
779
        });
776
    }
780
    }
Lines 807-823 Link Here
807
        checked = true;
811
        checked = true;
808
    }
812
    }
809
813
810
    private ChangeListener chLis;
814
    private ChangeAndBefore chLis;
811
815
812
    final ChangeListener getChangeListener() {
816
    final ChangeAndBefore getChangeListener() {
813
	if (chLis == null) {
817
        if (chLis == null) {
814
	    chLis = new ChangeListener() {
818
            chLis = new ChangeAndBefore();
815
    		/** State changed */
819
        }
816
    		public void stateChanged (ChangeEvent ev) {
817
        	    fireCookieChange ();
818
    		}
819
	    };
820
	}
821
        return chLis;
820
        return chLis;
822
    }
821
    }
823
822
Lines 1123-1128 Link Here
1123
     */
1122
     */
1124
    void notifyFileDataCreated(FileEvent fe) {
1123
    void notifyFileDataCreated(FileEvent fe) {
1125
        checked = false;
1124
        checked = false;
1125
    }
1126
1127
    final void updateFilesInCookieSet() {
1128
        getCookieSet().assign(FileObject.class, files().toArray(new FileObject[0]));
1129
    }
1130
    
1131
    /** Change listener and implementation of before.
1132
     */
1133
    private final class ChangeAndBefore implements ChangeListener, CookieSet.Before {
1134
        public void stateChanged (ChangeEvent ev) {
1135
            fireCookieChange ();
1136
        }
1137
1138
        public void beforeLookup(Class<?> clazz) {
1139
            if (clazz.isAssignableFrom(FileObject.class)) {
1140
                updateFilesInCookieSet();
1141
            }
1142
        }
1126
    }
1143
    }
1127
1144
1128
    /** Entry replace.
1145
    /** Entry replace.
(-)loaders/test/unit/src/org/openide/loaders/DataLoaderOrigTest.java (+4 lines)
Lines 36-41 Link Here
36
    public DataLoaderOrigTest(String name) {
36
    public DataLoaderOrigTest(String name) {
37
        super(name);
37
        super(name);
38
    }
38
    }
39
    
40
    protected void setUp() throws IOException {
41
        clearWorkDir();
42
    }
39
43
40
    public void testSimpleLoader() throws Exception {
44
    public void testSimpleLoader() throws Exception {
41
        DataLoader l = DataLoader.getLoader(SimpleUniFileLoader.class);
45
        DataLoader l = DataLoader.getLoader(SimpleUniFileLoader.class);
(-)loaders/test/unit/src/org/openide/loaders/DataShadowLookupTest.java (+135 lines)
Added Link Here
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
package org.openide.loaders;
15
16
import java.io.IOException;
17
import java.util.Enumeration;
18
import java.util.logging.Level;
19
import java.util.logging.Logger;
20
import org.netbeans.junit.MockServices;
21
import org.netbeans.junit.NbTestCase;
22
import org.openide.filesystems.FileObject;
23
import org.openide.filesystems.FileSystem;
24
import org.openide.filesystems.FileUtil;
25
import org.openide.filesystems.Repository;
26
import org.openide.nodes.Children;
27
import org.openide.nodes.Node;
28
import org.openide.util.Enumerations;
29
import org.openide.util.lookup.Lookups;
30
31
/** Test things about shadows and broken shadows, etc.
32
 * @author Jaroslav Tulach
33
 */
34
public class DataShadowLookupTest extends NbTestCase
35
implements java.net.URLStreamHandlerFactory {
36
    /** original object */
37
    private DataObject original;
38
    /** folder to work with */
39
    private DataFolder folder;
40
    /** fs we work on */
41
    private FileSystem lfs;
42
43
    private Logger err;
44
    
45
    static {
46
        // to handle nbfs urls...
47
      //  java.net.URL.setURLStreamHandlerFactory (new DataShadowLookupTest(null));
48
        MockServices.setServices(new Class[] { Pool.class });
49
    }
50
    
51
    public DataShadowLookupTest (String name) {
52
        super(name);
53
    }
54
55
    protected Level logLevel() {
56
        return Level.INFO;
57
    }
58
    
59
    protected void setUp() throws Exception {
60
        
61
        lfs = Repository.getDefault ().getDefaultFileSystem ();
62
        
63
        FileObject[] delete = lfs.getRoot().getChildren();
64
        for (int i = 0; i < delete.length; i++) {
65
            delete[i].delete();
66
        }
67
68
        
69
        FileObject fo = FileUtil.createData (lfs.getRoot (), getName () + "/folder/original.string");
70
        assertNotNull(fo);
71
        original = DataObject.find (fo);
72
        assertFalse ("Just to be sure that this is not shadow", original instanceof DataShadow);
73
        assertEquals ("It is the right class", StringObject.class, original.getClass ());
74
        fo = FileUtil.createFolder (lfs.getRoot (), getName () + "/modify");
75
        assertNotNull(fo);
76
        assertTrue (fo.isFolder ());
77
        folder = DataFolder.findFolder (fo);
78
        
79
        Repository.getDefault ().addFileSystem (lfs);
80
        
81
        err = Logger.getLogger(getName());
82
    }
83
    
84
    public java.net.URLStreamHandler createURLStreamHandler(String protocol) {
85
        if (protocol.equals ("nbfs")) {
86
            return FileUtil.nbfsURLStreamHandler ();
87
        }
88
        return null;
89
    }
90
    
91
    public void testStringIsInLookupOfDataShadow() throws Exception {
92
        DataShadow shade = original.createShadow(folder);
93
94
        {
95
            String s = (String)original.getNodeDelegate().getLookup().lookup(String.class);
96
            assertNotNull("String is in the original's lookup", s);
97
        }
98
        
99
        assertSame(shade.getOriginal(), original);
100
        String s = (String)shade.getNodeDelegate().getLookup().lookup(String.class);
101
        assertNotNull("String is in the lookup", s);
102
        assertEquals("It is the name of the original", original.getName(), s);
103
    }
104
105
    public static final class Pool extends DataLoaderPool {
106
        protected Enumeration loaders() {
107
            return Enumerations.singleton(StringLoader.findObject(StringLoader.class, true));
108
        }
109
        
110
    }
111
    
112
    private static final class StringLoader extends UniFileLoader {
113
        public StringLoader() {
114
            super("org.openide.loaders.StringObject");
115
            getExtensions().addExtension("string");
116
        }
117
        
118
        protected MultiDataObject createMultiObject(FileObject primaryFile) throws DataObjectExistsException, IOException {
119
            return new StringObject(this, primaryFile);
120
        }
121
        
122
    } // end of StringLoader
123
    
124
    private static final class StringObject extends MultiDataObject {
125
        public StringObject(StringLoader l, FileObject fo) throws DataObjectExistsException {
126
            super(fo, l);
127
        }
128
129
        protected Node createNodeDelegate() {
130
            return new DataNode(this, Children.LEAF, Lookups.singleton(getName()));
131
        }
132
    } // end of StringObject
133
    
134
    
135
}
(-)loaders/test/unit/src/org/openide/loaders/FileObjectInLookupTest.java (+205 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.openide.loaders;
21
22
import java.io.IOException;
23
import java.util.Collection;
24
import java.util.Collections;
25
import java.util.List;
26
import java.util.ArrayList;
27
import java.util.Enumeration;
28
import org.openide.filesystems.*;
29
import org.netbeans.junit.*;
30
import org.openide.nodes.Children;
31
import org.openide.nodes.CookieSet;
32
import org.openide.nodes.Node;
33
import org.openide.text.DataEditorSupport;
34
import org.openide.util.Enumerations;
35
import org.openide.util.Lookup;
36
import org.openide.util.NbBundle;
37
38
/*
39
 */
40
public class FileObjectInLookupTest extends NbTestCase {
41
    private FileSystem lfs;
42
    
43
    public FileObjectInLookupTest(String name) {
44
        super(name);
45
    }
46
47
    protected void setUp() throws Exception {
48
        MockServices.setServices(OwnDataLoaderPool.class);
49
        clearWorkDir ();
50
        lfs = TestUtilHid.createLocalFileSystem (getWorkDir (), new String[] {
51
            "adir/",
52
            "adir/file.txt",
53
            "adir/file.own"
54
        });
55
        Repository.getDefault().addFileSystem(lfs);
56
        Enumeration<?> en = DataLoaderPool.getDefault().allLoaders();
57
        while (en.hasMoreElements()) {
58
            if (en.nextElement() instanceof OwnDataLoader) {
59
                return;
60
            }
61
        }
62
        fail("OwnDataLoader shall be registered");
63
    }
64
    
65
    protected void tearDown() throws Exception {
66
        Repository.getDefault().removeFileSystem(lfs);
67
    }
68
    
69
    protected boolean runInEQ() {
70
        return true;
71
    }
72
    
73
    public void testFOInsideFolder() throws Exception {
74
        DataFolder f = DataFolder.findFolder(lfs.findResource("adir"));
75
        assertFileObjects(f);
76
        f.rename("bdir");
77
        assertFileObjects(f);
78
    }
79
    
80
    public void testFOInsideADefaultDataObject() throws Exception {
81
        DataObject obj = DataObject.find(lfs.findResource("adir/file.txt"));
82
        assertFileObjects(obj);
83
        obj.rename("kuk");
84
        assertFileObjects(obj);
85
        obj.move(obj.getFolder().getFolder());
86
        assertFileObjects(obj);
87
    }
88
89
    public void testOwnLoader() throws Exception {
90
        DataObject obj = DataObject.find(lfs.findResource("adir/file.own"));
91
        assertEquals(OwnDataLoader.class, obj.getLoader().getClass());
92
        assertFileObjects(obj);
93
        obj.rename("kuk");
94
        assertFileObjects(obj);
95
        obj.move(obj.getFolder().getFolder());
96
        assertFileObjects(obj);
97
    }
98
99
    public void testShadow() throws Exception {
100
        DataObject obj = DataObject.find(lfs.findResource("adir/file.own"));
101
        DataShadow shadow = obj.createShadow(obj.getFolder().getFolder());
102
        assertEquals(OwnDataLoader.class, obj.getLoader().getClass());
103
        
104
        assertEquals("DataObject for the shadow is the shadow", shadow, shadow.getCookie(DataObject.class));
105
        
106
        assertFileObjects(obj);
107
        assertFileObjects("However FileObject of a shadow are delegated to the original", shadow, obj.files());
108
        obj.rename("kuk");
109
        assertFileObjects(obj);
110
        assertFileObjects("However FileObject of a shadow are delegated to the original", shadow, obj.files());
111
        obj.move(obj.getFolder().getFolder());
112
        assertFileObjects(obj);
113
        assertFileObjects("However FileObject of a shadow are delegated to the original", shadow, obj.files());
114
        shadow.rename("somenewshadow");
115
        assertFileObjects(obj);
116
        assertFileObjects("However FileObject of a shadow are delegated to the original", shadow, obj.files());
117
        obj.delete();
118
        /*
119
        DataObject broken = DataObject.find(shadow.getPrimaryFile());
120
        if (shadow == broken) {
121
            fail("They should be different: " + shadow + " != " + broken);
122
        }
123
        assertEquals("DataObject for the shadow is now the shadow", broken, broken.getCookie(DataObject.class));
124
        assertFileObjects(broken);
125
         */
126
    }
127
    
128
    private static void assertFileObjects(DataObject obj) {
129
        assertFileObjects("", obj, obj.files());
130
    }
131
    
132
    private static void assertFileObjects(String msg, DataObject obj, Collection<? extends FileObject> expect) {
133
        Collection<? extends FileObject> allcol = obj.getNodeDelegate().getLookup().lookupAll(FileObject.class);
134
        List<FileObject> all = new ArrayList<FileObject>(allcol);
135
        Enumeration<? extends FileObject> files = Collections.enumeration(expect);
136
        int i = 0;
137
        while (files.hasMoreElements()) {
138
            FileObject fo = files.nextElement();
139
            if (i >= all.size()) {
140
                fail(msg + "\nThere should be more elements, but there is only " + all.size() + "\nAll: " + all + "\nCurrent: " + fo);
141
            }
142
            
143
            if (fo.equals(all.get(i))) {
144
                i++;
145
                continue;
146
            }
147
            fail(msg + "\nError at position " + i + " expected: " + fo + " but was: " + all.get(i) + "\nAll: " + all);
148
        }
149
    }
150
    
151
    public static final class OwnDataLoaderPool extends DataLoaderPool {
152
        protected Enumeration<? extends DataLoader> loaders() {
153
            return Enumerations.singleton(OwnDataLoader.getLoader(OwnDataLoader.class));
154
        }
155
    }
156
157
158
    public static class OwnDataLoader extends UniFileLoader {
159
        private static final long serialVersionUID = 1L;
160
161
        public OwnDataLoader() {
162
            super("org.openide.loaders.OwnDataObject");
163
        }
164
165
        protected String defaultDisplayName() {
166
            return NbBundle.getMessage(OwnDataLoader.class, "LBL_Own_loader_name");
167
        }
168
169
        protected void initialize() {
170
            super.initialize();
171
            getExtensions().addExtension("own");
172
        }
173
174
        protected MultiDataObject createMultiObject(FileObject primaryFile) throws DataObjectExistsException, IOException {
175
            return new OwnDataObject(primaryFile, this);
176
        }
177
    }
178
    static class OwnDataObject extends MultiDataObject implements Lookup.Provider {
179
180
        public OwnDataObject(FileObject pf, OwnDataLoader loader) throws DataObjectExistsException, IOException {
181
            super(pf, loader);
182
            CookieSet cookies = getCookieSet();
183
            cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies));
184
        }
185
186
        protected Node createNodeDelegate() {
187
            return new OwnDataNode(this, getLookup());
188
        }
189
190
        public Lookup getLookup() {
191
            return getCookieSet().getLookup();
192
        }
193
    }
194
    
195
    static class OwnDataNode extends DataNode {
196
        private static final String IMAGE_ICON_BASE = "SET/PATH/TO/ICON/HERE";
197
198
        public OwnDataNode(OwnDataObject obj, Lookup lookup) {
199
            super(obj, Children.LEAF, lookup);
200
            //        setIconBaseWithExtension(IMAGE_ICON_BASE);
201
        }
202
203
    }
204
205
}
(-)nodes/apichanges.xml (-1 / +27 lines)
Lines 23-29 Link Here
23
<apidef name="nodes">Nodes API</apidef>
23
<apidef name="nodes">Nodes API</apidef>
24
</apidefs>
24
</apidefs>
25
<changes>
25
<changes>
26
<change id="BeanNode.LookupCtor">
26
    <change id="GenericCookieSet">
27
        <api name="nodes"/>
28
        <summary>CookieSet can hold any objects and not just cookies</summary>
29
        <version major="7" minor="0"/>
30
        <date day="6" month="11" year="2006"/>
31
        <author login="jtulach"/>
32
        <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible" deprecation="no" deletion="no" modification="no"/>
33
        <description>
34
            New method <a href="@TOP@/org/openide/nodes/CookieSet.html#createGeneric(org.openide.nodes.CookieSet.Before)">
35
            CookieSet.createGeneric</a> has been added. It allows to create 
36
            an instance of <a href="@TOP@/org/openide/nodes/CookieSet.html">
37
            CookieSet</a> that can contain any object, not just 
38
            <a href="@TOP@/org/openide/nodes/Node.Cookie.html">Cookies</a>.
39
            This addition change is accompanied with two additional changes:
40
            <a href="@TOP@/org/openide/nodes/CookieSet.html">
41
            CookieSet</a> now implements <a href="@org-openide-util@/org/openide/util/Lookup.Provider.html">
42
            Lookup.Provider</a> and thus has a method <code>getLookup</code> to 
43
            allow queries for of its content.
44
            Also there is a new method 
45
            <a href="@TOP@/org/openide/nodes/CookieSet.html#assign(java.lang.Class,%20T...)">
46
            assign(clazz, instances)</a> that allows to add/remove 
47
            plain old java objects to the <code>CookieSet</code>.
48
        </description>
49
        <class package="org.openide.nodes" name="CookieSet"/>
50
        <issue number="62707"/>
51
    </change>
52
    <change id="BeanNode.LookupCtor">
27
     <api name="nodes"/>
53
     <api name="nodes"/>
28
     <summary>BeanNode constructor allows passing Lookup instance</summary>
54
     <summary>BeanNode constructor allows passing Lookup instance</summary>
29
     <version major="6" minor="9"/>
55
     <version major="6" minor="9"/>
(-)nodes/nbproject/project.properties (-1 / +1 lines)
Lines 22-25 Link Here
22
javadoc.arch=${basedir}/../arch/arch-openide-nodes.xml
22
javadoc.arch=${basedir}/../arch/arch-openide-nodes.xml
23
javadoc.apichanges=${basedir}/apichanges.xml
23
javadoc.apichanges=${basedir}/apichanges.xml
24
24
25
spec.version.base=6.9.0
25
spec.version.base=7.0
(-)nodes/src/org/openide/nodes/AbstractNode.java (+7 lines)
Lines 169-174 Link Here
169
        // not called from constructor (see e.g. DataNode)
169
        // not called from constructor (see e.g. DataNode)
170
        super.setName(""); // NOI18N
170
        super.setName(""); // NOI18N
171
    }
171
    }
172
    
173
    /** Fake node constructor with given CookieSet
174
     */
175
    AbstractNode(CookieSet set) {
176
        super(Children.LEAF);
177
        lookup = set;
178
    }
172
179
173
    /** Clone the node. If the object implements {@link Cloneable},
180
    /** Clone the node. If the object implements {@link Cloneable},
174
    * that is used; otherwise a {@link FilterNode filter node}
181
    * that is used; otherwise a {@link FilterNode filter node}
(-)nodes/src/org/openide/nodes/CookieSet.java (-70 / +338 lines)
Lines 26-31 Link Here
26
import javax.swing.event.ChangeEvent;
26
import javax.swing.event.ChangeEvent;
27
import javax.swing.event.ChangeListener;
27
import javax.swing.event.ChangeListener;
28
import javax.swing.event.EventListenerList;
28
import javax.swing.event.EventListenerList;
29
import org.openide.util.Lookup;
30
import org.openide.util.lookup.AbstractLookup;
31
import org.openide.util.lookup.InstanceContent;
29
32
30
33
31
/** Support class for storing cookies and
34
/** Support class for storing cookies and
Lines 35-41 Link Here
35
*
38
*
36
* @author Jaroslav Tulach
39
* @author Jaroslav Tulach
37
*/
40
*/
38
public final class CookieSet extends Object {
41
public final class CookieSet extends Object implements Lookup.Provider {
39
    /** variable to allow effecient communication with NodeLookup, Node.Cookie or Class or Set */
42
    /** variable to allow effecient communication with NodeLookup, Node.Cookie or Class or Set */
40
    private static ThreadLocal<Object> QUERY_MODE = new ThreadLocal<Object>();
43
    private static ThreadLocal<Object> QUERY_MODE = new ThreadLocal<Object>();
41
44
Lines 44-54 Link Here
44
47
45
    /** set of listeners */
48
    /** set of listeners */
46
    private EventListenerList listeners = new EventListenerList();
49
    private EventListenerList listeners = new EventListenerList();
50
    
51
    /** potential instance content */
52
    private final CookieSetLkp ic;
53
    /** lookup to use return from the cookie set, if initialized */
54
    private Lookup lookup;
47
55
48
    /** Default constructor. */
56
    /** Default constructor. */
49
    public CookieSet() {
57
    public CookieSet() {
58
        this(null, null);
59
    }
60
    
61
    private CookieSet(CookieSetLkp ic, Lookup lookup) {
62
        this.ic = ic;
63
        this.lookup = lookup;
64
    }
65
    
66
    /** Factory method to create new, general purpose cookie set. 
67
     * The <q>general purpose</q> means that it is possible to store
68
     * any object, into the cookie set and then obtain it using {@link #getLookup}
69
     * and queries on the returned {@link Lookup}. The before object can
70
     * be passed in if one wants to do a lazy initialization of the {@link CookieSet}
71
     * content.
72
     * 
73
     * @param before the interface to support lazy initialization
74
     * @return new cookie set that can contain not only {@link Node.Cookie} but also 
75
     *    any plain old java object
76
     * @see #assign
77
     * @since 7.0
78
     */
79
    public static CookieSet createGeneric(Before before) {
80
        CookieSetLkp al = new CookieSetLkp(before);
81
        return new CookieSet(al, al);
82
    }
83
84
    /** The lookup associated with this cookie set. Keeps track of
85
     * the same things that are in the cookie set, but presents them
86
     * as being inside the lookup.
87
     * 
88
     * @return the lookup representing this cookie set
89
     * @since 7.0
90
     */
91
    public Lookup getLookup() {
92
        synchronized (QUERY_MODE) {
93
            if (lookup == null) {
94
                AbstractNode an = new AbstractNode(this);
95
                lookup = an.getLookup();
96
            }
97
        }
98
        return lookup;
50
    }
99
    }
51
100
101
    
52
    /** Add a new cookie to the set. If a cookie of the same
102
    /** Add a new cookie to the set. If a cookie of the same
53
    * <em>actual</em> (not representation!) class is already there,
103
    * <em>actual</em> (not representation!) class is already there,
54
    * it is replaced.
104
    * it is replaced.
Lines 59-81 Link Here
59
    * @param cookie cookie to add
109
    * @param cookie cookie to add
60
    */
110
    */
61
    public void add(Node.Cookie cookie) {
111
    public void add(Node.Cookie cookie) {
112
        addImpl((Object)cookie);
113
        fireChangeEvent();
114
    }
115
    
116
    private void addImpl(Object cookie) {
62
        synchronized (this) {
117
        synchronized (this) {
63
            registerCookie(cookie.getClass(), cookie);
118
            registerCookie(cookie.getClass(), cookie);
64
        }
119
        }
65
120
        if (ic != null) {
66
        fireChangeEvent();
121
            ic.add(cookie);
122
        }
67
    }
123
    }
68
124
69
    /** Remove a cookie from the set.
125
    /** Remove a cookie from the set.
70
    * @param cookie the cookie to remove
126
    * @param cookie the cookie to remove
71
    */
127
    */
72
    public void remove(Node.Cookie cookie) {
128
    public void remove(Node.Cookie cookie) {
129
        removeImpl((Object)cookie);
130
        fireChangeEvent();
131
    }
132
    
133
    void removeImpl(Object cookie) {
73
        synchronized (this) {
134
        synchronized (this) {
74
            unregisterCookie(cookie.getClass(), cookie);
135
            unregisterCookie(cookie.getClass(), cookie);
75
        }
136
        }
76
137
        if (ic != null) {
77
        fireChangeEvent();
138
            ic.remove(cookie);
139
        }
78
    }
140
    }
141
    
142
    /** Associates a given set of instances with
143
     */
144
    
79
145
80
    /** Get a cookie.
146
    /** Get a cookie.
81
    *
147
    *
Lines 83-88 Link Here
83
    * @return a cookie assignable to the representation class, or <code>null</code> if there is none
149
    * @return a cookie assignable to the representation class, or <code>null</code> if there is none
84
    */
150
    */
85
    public <T extends Node.Cookie> T getCookie(Class<T> clazz) {
151
    public <T extends Node.Cookie> T getCookie(Class<T> clazz) {
152
        if (ic != null) {
153
            ic.beforeLookupImpl(clazz);
154
        }
155
        
86
        Node.Cookie ret = null;
156
        Node.Cookie ret = null;
87
        Object queryMode = QUERY_MODE.get();
157
        Object queryMode = QUERY_MODE.get();
88
158
Lines 90-102 Link Here
90
            R r = findR(clazz);
160
            R r = findR(clazz);
91
161
92
            if (r == null) {
162
            if (r == null) {
93
                return null;
163
                if (queryMode == null || ic == null) {
94
            }
164
                    return null;
95
165
                }
96
            ret = r.cookie();
166
            } else {
167
                ret = r.cookie();
97
168
98
            if (queryMode instanceof Set) {
169
                if (queryMode instanceof Set) {
99
                ((Set) queryMode).addAll(map.keySet());
170
                    @SuppressWarnings("unchecked")
171
                    Set<Class> keys = (Set<Class>)queryMode;
172
                    keys.addAll(map.keySet());
173
                }
100
            }
174
            }
101
        }
175
        }
102
176
Lines 110-119 Link Here
110
                // unwrap the cookie
184
                // unwrap the cookie
111
                ret = ((CookieEntry) ret).getCookie(true);
185
                ret = ((CookieEntry) ret).getCookie(true);
112
            }
186
            }
187
        } else if (ret == null) {
188
            if (ic != null && 
189
                (!Node.Cookie.class.isAssignableFrom(clazz) || clazz == Node.Cookie.class)
190
            ) {
191
                enhancedQueryMode(lookup, clazz);
192
                ret = null;
193
            }
113
        }
194
        }
114
195
115
        return clazz.cast(ret);
196
        return clazz.cast(ret);
116
    }
197
    }
198
    
199
    static void enhancedQueryMode(Lookup lookup, Class<?> clazz) {
200
        Object type = QUERY_MODE.get();
201
        if (type != clazz) {
202
            return;
203
        }
204
        Collection<? extends Lookup.Item<?>> items = lookup.lookupResult(clazz).allItems();
205
        if (items.size() == 0) {
206
            return;
207
        }
208
        AbstractLookup.Pair[] arr = new AbstractLookup.Pair[items.size()];
209
        Iterator<? extends Lookup.Item> it = items.iterator();
210
        for (int i = 0; i < arr.length; i++) {
211
            arr[i] = new PairWrap(it.next());
212
        }
213
        QUERY_MODE.set(arr);
214
    }
117
215
118
    /** Add a listener to changes in the cookie set.
216
    /** Add a listener to changes in the cookie set.
119
    * @param l the listener to add
217
    * @param l the listener to add
Lines 128-134 Link Here
128
    public void removeChangeListener(ChangeListener l) {
226
    public void removeChangeListener(ChangeListener l) {
129
        listeners.remove(ChangeListener.class, l);
227
        listeners.remove(ChangeListener.class, l);
130
    }
228
    }
131
229
    
230
    
132
    /** Node lookup starts its non-important query.
231
    /** Node lookup starts its non-important query.
133
     */
232
     */
134
    static Object entryQueryMode(Class c) {
233
    static Object entryQueryMode(Class c) {
Lines 149-160 Link Here
149
248
150
    /** Exits query mode.
249
    /** Exits query mode.
151
     */
250
     */
152
    static org.openide.util.lookup.AbstractLookup.Pair exitQueryMode(Object prev) {
251
    static Collection<AbstractLookup.Pair> exitQueryMode(Object prev) {
153
        Object cookie = QUERY_MODE.get();
252
        Object cookie = QUERY_MODE.get();
154
        QUERY_MODE.set(prev);
253
        QUERY_MODE.set(prev);
155
254
156
        if (cookie instanceof CookieSet.CookieEntry) {
255
        if (cookie instanceof CookieSet.CookieEntry) {
157
            return new CookieEntryPair((CookieSet.CookieEntry) cookie);
256
            return Collections.singleton((AbstractLookup.Pair)new CookieEntryPair((CookieSet.CookieEntry) cookie));
257
        } else if (cookie instanceof AbstractLookup.Pair[]) {
258
            return Arrays.asList((AbstractLookup.Pair[])cookie);
158
        } else {
259
        } else {
159
            return null;
260
            return null;
160
        }
261
        }
Lines 174-180 Link Here
174
275
175
    /** Fires change event
276
    /** Fires change event
176
    */
277
    */
177
    private void fireChangeEvent() {
278
    final void fireChangeEvent() {
178
        Object[] arr = listeners.getListenerList();
279
        Object[] arr = listeners.getListenerList();
179
280
180
        if (arr.length > 0) {
281
        if (arr.length > 0) {
Lines 200-206 Link Here
200
    * @param c class or null
301
    * @param c class or null
201
    * @param cookie cookie to attach
302
    * @param cookie cookie to attach
202
    */
303
    */
203
    private void registerCookie(Class<?> c, Node.Cookie cookie) {
304
    private void registerCookie(Class<?> c, Object cookie) {
204
        if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) {
305
        if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) {
205
            return;
306
            return;
206
        }
307
        }
Lines 213-219 Link Here
213
            map.put(c, r);
314
            map.put(c, r);
214
        }
315
        }
215
316
216
        r.add(cookie);
317
        r.add((Node.Cookie)cookie);
217
318
218
        registerCookie(c.getSuperclass(), cookie);
319
        registerCookie(c.getSuperclass(), cookie);
219
320
Lines 230-236 Link Here
230
    * @param c class or null
331
    * @param c class or null
231
    * @param cookie cookie to attach
332
    * @param cookie cookie to attach
232
    */
333
    */
233
    private void unregisterCookie(Class<?> c, Node.Cookie cookie) {
334
    private void unregisterCookie(Class<?> c, Object cookie) {
234
        if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) {
335
        if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) {
235
            return;
336
            return;
236
        }
337
        }
Lines 242-248 Link Here
242
343
243
        if (r != null) {
344
        if (r != null) {
244
            // remove the cookie
345
            // remove the cookie
245
            r.remove(cookie);
346
            r.remove((Node.Cookie)cookie);
246
        }
347
        }
247
348
248
        unregisterCookie(c.getSuperclass(), cookie);
349
        unregisterCookie(c.getSuperclass(), cookie);
Lines 263-269 Link Here
263
        synchronized (this) {
364
        synchronized (this) {
264
            registerCookie(cookieClass, new CookieEntry(factory, cookieClass));
365
            registerCookie(cookieClass, new CookieEntry(factory, cookieClass));
265
        }
366
        }
266
367
        if (ic != null) {
368
            ic.add(new FactAndClass(cookieClass, factory), C.INSTANCE);
369
        }
267
        fireChangeEvent();
370
        fireChangeEvent();
268
    }
371
    }
269
372
Lines 279-284 Link Here
279
            }
382
            }
280
        }
383
        }
281
384
385
        if (ic != null) {
386
            for (Class<? extends Node.Cookie> c : cookieClass) {
387
                ic.add(new FactAndClass(c, factory), C.INSTANCE);
388
            }
389
        }
282
        fireChangeEvent();
390
        fireChangeEvent();
283
    }
391
    }
284
392
Lines 306-311 Link Here
306
                }
414
                }
307
            }
415
            }
308
        }
416
        }
417
        if (ic != null) {
418
            ic.remove(new FactAndClass(cookieClass, factory), C.INSTANCE);
419
        }
309
420
310
        fireChangeEvent();
421
        fireChangeEvent();
311
    }
422
    }
Lines 336-345 Link Here
336
                }
447
                }
337
            }
448
            }
338
        }
449
        }
450
        
451
        if (ic != null) {
452
            for (Class<? extends Node.Cookie> c : cookieClass) {
453
                ic.remove(new FactAndClass(c, factory), C.INSTANCE);
454
            }
455
        }
339
456
340
        fireChangeEvent();
457
        fireChangeEvent();
341
    }
458
    }
342
459
    
460
    /** Removes all instances of clazz from the set and replaces them
461
     * with newly provided instance(s).
462
     * 
463
     * @param clazz the root clazz for cookies to remove
464
     * @param instances the one or more instances to put into the lookup
465
     * 
466
     * @since 7.0
467
     */
468
    public <T> void assign(Class<? extends T> clazz, T... instances) {
469
        if (Node.Cookie.class.isAssignableFrom(clazz)) {
470
            Class<? extends Node.Cookie> cookieClazz = clazz.asSubclass(Node.Cookie.class);
471
            for(;;) {
472
                Node.Cookie cookie = getCookie(cookieClazz);
473
                if (cookie != null) {
474
                    removeImpl(cookie);
475
                } else {
476
                    break;
477
                }
478
            }
479
            for (T t : instances) {
480
                addImpl(t);
481
            }
482
        
483
            fireChangeEvent();
484
        } else if (ic != null) {
485
            synchronized (this) {
486
                for (T t : instances) {
487
                    registerCookie(t.getClass(), t);
488
                }
489
            }
490
            ic.replaceInstances(clazz, instances, this);
491
        }
492
    }
493
    
494
    /** Assignes a trigger that gets called everytime given class is about
495
     * to be queried. Can be used only for cookie set created with
496
     * {@link CookieSet#create(true)} and for classes that are not 
497
     * subclasses of Node.Cookie.
498
     *
499
     * @param clazz the trigger class (not subclass of Node.Cookie)
500
     * @param run runnable to run when the task is queried
501
     *
502
    public void beforeLookup(Class<?> clazz, Runnable run) {
503
        if (Node.Cookie.class.isAssignableFrom(clazz) || clazz == Object.class) {
504
            throw new IllegalArgumentException("Too generic class: " + clazz); // NOI18N
505
        }
506
        if (ic == null) {
507
            throw new IllegalStateException("Can be used only on CookieSet.create(true)"); // NOI18N
508
        }
509
        ic.registerBeforeLookup(clazz, run);
510
    }
511
    */
512
        
343
    /** Finds a result in a map.
513
    /** Finds a result in a map.
344
     */
514
     */
345
    private R findR(Class<? extends Node.Cookie> c) {
515
    private R findR(Class<? extends Node.Cookie> c) {
Lines 365-370 Link Here
365
         */
535
         */
366
        <T extends Node.Cookie> T createCookie(Class<T> klass);
536
        <T extends Node.Cookie> T createCookie(Class<T> klass);
367
    }
537
    }
538
    
539
    /** Allows to update content of the cookie set just before 
540
     * a query for a given class is made.
541
     */
542
    public interface Before {
543
        public void beforeLookup(Class<?> clazz);
544
    }
368
545
369
    /** Entry for one Cookie */
546
    /** Entry for one Cookie */
370
    private static class CookieEntry implements Node.Cookie {
547
    private static class CookieEntry implements Node.Cookie {
Lines 405-459 Link Here
405
582
406
            return ret;
583
            return ret;
407
        }
584
        }
408
    }
585
    } // end of CookieEntry
409
     // end of CookieEntry
410
411
    /** Pair that represents an entry.
412
     */
413
    private static final class CookieEntryPair extends org.openide.util.lookup.AbstractLookup.Pair {
414
        private CookieEntry entry;
415
416
        public CookieEntryPair(CookieEntry e) {
417
            this.entry = e;
418
        }
419
420
        protected boolean creatorOf(Object obj) {
421
            return obj == entry.getCookie(false);
422
        }
423
424
        public String getDisplayName() {
425
            return getId();
426
        }
427
428
        public String getId() {
429
            return entry.klass.getName();
430
        }
431
432
        public Object getInstance() {
433
            return entry.getCookie(true);
434
        }
435
436
        public Class getType() {
437
            return entry.klass;
438
        }
439
440
        protected boolean instanceOf(Class c) {
441
            return c.isAssignableFrom(entry.klass);
442
        }
443
444
        public int hashCode() {
445
            return entry.hashCode() + 5;
446
        }
447
448
        public boolean equals(Object obj) {
449
            if (obj instanceof CookieEntryPair) {
450
                return ((CookieEntryPair) obj).entry == entry;
451
            }
452
453
            return false;
454
        }
455
    }
456
     // end of CookieEntryPair
457
586
458
    /** Implementation of the result.
587
    /** Implementation of the result.
459
     */
588
     */
Lines 513-518 Link Here
513
         */
642
         */
514
        public Node.Cookie cookie() {
643
        public Node.Cookie cookie() {
515
            return ((cookies == null) || cookies.isEmpty()) ? null : cookies.get(0);
644
            return ((cookies == null) || cookies.isEmpty()) ? null : cookies.get(0);
645
        }
646
    }
647
    
648
    /** Pair that wraps another Lookup.Item
649
     */
650
    private static final class PairWrap extends AbstractLookup.Pair {
651
        private Lookup.Item<?> item;
652
        private boolean created;
653
        
654
        public PairWrap(Lookup.Item<?> item) {
655
            this.item = item;
656
        }
657
658
        protected boolean instanceOf(Class c) {
659
            Class<?> k = c;
660
            return k.isAssignableFrom(getType());
661
        }
662
663
        protected boolean creatorOf(Object obj) {
664
            return created && getInstance() == obj;
665
        }
666
667
        public Object getInstance() {
668
            created = true;
669
            return item.getInstance();
670
        }
671
672
        public Class<? extends Object> getType() {
673
            return item.getType();
674
        }
675
676
        public String getId() {
677
            return item.getId();
678
        }
679
680
        public String getDisplayName() {
681
            return item.getDisplayName();
682
        }
683
684
        public int hashCode() {
685
            return 777 + item.hashCode();
686
        }
687
688
        public boolean equals(Object object) {
689
            if (object instanceof PairWrap) {
690
                PairWrap p = (PairWrap)object;
691
                return item.equals(p.item);
692
            }
693
            return false;
694
        }
695
    } // end of PairWrap
696
697
    /** Pair that represents an entry.
698
     */
699
    private static final class CookieEntryPair extends AbstractLookup.Pair {
700
        private CookieEntry entry;
701
702
        public CookieEntryPair(CookieEntry e) {
703
            this.entry = e;
704
        }
705
706
        protected boolean creatorOf(Object obj) {
707
            return obj == entry.getCookie(false);
708
        }
709
710
        public String getDisplayName() {
711
            return getId();
712
        }
713
714
        public String getId() {
715
            return entry.klass.getName();
716
        }
717
718
        public Object getInstance() {
719
            return entry.getCookie(true);
720
        }
721
722
        public Class getType() {
723
            return entry.klass;
724
        }
725
726
        protected boolean instanceOf(Class c) {
727
            Class<?> k = c;
728
            return k.isAssignableFrom(entry.klass);
729
        }
730
731
        public int hashCode() {
732
            return entry.hashCode() + 5;
733
        }
734
735
        public boolean equals(Object obj) {
736
            if (obj instanceof CookieEntryPair) {
737
                return ((CookieEntryPair) obj).entry == entry;
738
            }
739
740
            return false;
741
        }
742
    } // end of CookieEntryPair
743
    
744
    private static final class FactAndClass {
745
        final Class<? extends Node.Cookie> clazz;
746
        final Factory factory;
747
        
748
        public FactAndClass(Class<? extends Node.Cookie> clazz, Factory factory) {
749
            this.clazz = clazz;
750
            this.factory = factory;
751
        }
752
        
753
        public int hashCode() {
754
            return clazz.hashCode() + factory.hashCode();
755
        }
756
        
757
        public boolean equals(Object o) {
758
            if (o instanceof FactAndClass) {
759
                FactAndClass f = (FactAndClass)o;
760
                return f.clazz.equals(clazz) && f.factory == factory;
761
            }
762
            return false;
763
        }
764
    }
765
    
766
    private static class C implements InstanceContent.Convertor<FactAndClass, Node.Cookie> {
767
        static final C INSTANCE = new C();
768
        
769
770
        public Node.Cookie convert(CookieSet.FactAndClass obj) {
771
            return obj.factory.createCookie(obj.clazz);
772
        }
773
774
        public Class<? extends Node.Cookie> type(CookieSet.FactAndClass obj) {
775
            return obj.clazz;
776
        }
777
778
        public String id(CookieSet.FactAndClass obj) {
779
            return obj.clazz.getName();
780
        }
781
782
        public String displayName(CookieSet.FactAndClass obj) {
783
            return obj.clazz.getName();
516
        }
784
        }
517
    }
785
    }
518
}
786
}
(-)nodes/src/org/openide/nodes/CookieSetLkp.java (+318 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
package org.openide.nodes;
20
21
import java.util.Collections;
22
import java.util.HashMap;
23
import java.util.HashSet;
24
import java.util.Iterator;
25
import java.util.concurrent.ConcurrentHashMap;
26
import org.openide.util.lookup.AbstractLookup;
27
import org.openide.util.lookup.AbstractLookup.Pair;
28
29
import java.lang.ref.WeakReference;
30
import java.util.ArrayList;
31
import java.util.List;
32
import java.util.Map;
33
import java.util.Set;
34
import org.openide.util.Lookup;
35
import org.openide.util.lookup.InstanceContent;
36
37
38
/** Content for a cookie set.
39
 */
40
final class CookieSetLkp extends AbstractLookup {
41
    private final CookieSet.Before before;
42
    
43
    public CookieSetLkp(CookieSet.Before b) {
44
        this.before = b;
45
    }
46
    
47
    public void add(Object obj) {
48
        addPair(new SimpleItem<Object>(obj));
49
    }
50
    public final <T,R> void add(T inst, InstanceContent.Convertor<T,R> conv) {
51
        addPair(new ConvertingItem<T,R>(inst, conv));
52
    }
53
    
54
    public void remove(Object obj) {
55
        removePair(new SimpleItem<Object>(obj));
56
    }
57
    public final <T,R> void remove(T inst, InstanceContent.Convertor<T,R> conv) {
58
        removePair(new ConvertingItem<T,R>(inst, conv));
59
    }
60
61
    void superRemovePair(Pair pair) {
62
        removePair(pair);
63
    }
64
65
    private ThreadLocal<Object> isInReplaceInst = new ThreadLocal<Object>();
66
    <T> void replaceInstances(Class<? extends T> clazz, T[] instances, CookieSet set) {
67
        Iterator<? extends Lookup.Item> it;
68
        Set<Lookup.Item> toRemove;
69
        List<AbstractLookup.Pair> pairs;
70
        
71
        Object prev = isInReplaceInst.get();
72
        try {
73
            isInReplaceInst.set(this);
74
            
75
                it = lookupResult(Object.class).allItems().iterator();
76
                toRemove = new HashSet<Lookup.Item>(lookupResult(clazz).allItems());
77
                pairs = new ArrayList<AbstractLookup.Pair>();
78
        
79
            boolean change = false;
80
            int index = 0;
81
            while (it.hasNext()) {
82
                Lookup.Item item = it.next();
83
                assert item instanceof AbstractLookup.Pair;
84
85
                if (toRemove.remove(item)) {
86
                    if (index < instances.length) {
87
                        if (item instanceof SimpleItem) {
88
                            SimpleItem<?> simple = (SimpleItem<?>)item;
89
                            if (simple.obj == instances[index]) {
90
                                index++;
91
                                pairs.add(simple);
92
                                continue;
93
                            }
94
                        }
95
96
                        change = true;
97
                        pairs.add(new SimpleItem<T>(instances[index++]));
98
                    } else {
99
                        change = true;
100
                    }
101
                } else {
102
                    pairs.add((AbstractLookup.Pair)item);
103
                }
104
            }
105
            assert toRemove.isEmpty();
106
107
            while (index < instances.length) {
108
                change = true;
109
                pairs.add(new SimpleItem<T>(instances[index++]));
110
            }
111
112
            if (change) {
113
                setPairs(pairs);
114
                set.fireChangeEvent();
115
            }
116
        } finally {
117
            isInReplaceInst.set(prev);
118
        }
119
    }
120
121
    protected void beforeLookup(Lookup.Template<?> template) {
122
        beforeLookupImpl(template.getType());
123
    }
124
    
125
    final void beforeLookupImpl(Class<?> clazz) {
126
        if (before != null && isInReplaceInst.get() == null) {
127
            before.beforeLookup(clazz);
128
        }
129
    }
130
131
    /** Instance of one item representing an object.
132
     */
133
    final static class SimpleItem<T> extends Pair<T> {
134
        private T obj;
135
136
        /** Create an item.
137
         * @obj object to register
138
         */
139
        public SimpleItem(T obj) {
140
            if (obj == null) {
141
                throw new NullPointerException();
142
            }
143
            this.obj = obj;
144
        }
145
146
        /** Tests whether this item can produce object
147
         * of class c.
148
         */
149
        public boolean instanceOf(Class<?> c) {
150
            return c.isInstance(obj);
151
        }
152
153
        /** Get instance of registered object. If convertor is specified then
154
         *  method InstanceLookup.Convertor.convertor is used and weak reference
155
         * to converted object is saved.
156
         * @return the instance of the object.
157
         */
158
        public T getInstance() {
159
            return obj;
160
        }
161
162
        public boolean equals(Object o) {
163
            if (o instanceof SimpleItem) {
164
                return obj.equals(((SimpleItem) o).obj);
165
            } else {
166
                return false;
167
            }
168
        }
169
170
        public int hashCode() {
171
            return obj.hashCode();
172
        }
173
174
        /** An identity of the item.
175
         * @return string representing the item, that can be used for
176
         *   persistance purposes to locate the same item next time
177
         */
178
        public String getId() {
179
            return "IL[" + obj.toString(); // NOI18N
180
        }
181
182
        /** Getter for display name of the item.
183
         */
184
        public String getDisplayName() {
185
            return obj.toString();
186
        }
187
188
        /** Method that can test whether an instance of a class has been created
189
         * by this item.
190
         *
191
         * @param obj the instance
192
         * @return if the item has already create an instance and it is the same
193
         *  as obj.
194
         */
195
        protected boolean creatorOf(Object obj) {
196
            return obj == this.obj;
197
        }
198
199
        /** The class of this item.
200
         * @return the correct class
201
         */
202
        @SuppressWarnings("unchecked")
203
        public Class<? extends T> getType() {
204
            return (Class<? extends T>)obj.getClass();
205
        }
206
    } // end of SimpleItem
207
208
    /** Instance of one item registered in the map.
209
     */
210
    final static class ConvertingItem<T,R> extends Pair<R> {
211
        /** registered object */
212
        private T obj;
213
214
        /** Reference to converted object. */
215
        private WeakReference<R> ref;
216
217
        /** convertor to use */
218
        private InstanceContent.Convertor<? super T,R> conv;
219
220
        /** Create an item.
221
         * @obj object to register
222
         * @conv a convertor, can be <code>null</code>.
223
         */
224
        public ConvertingItem(T obj, InstanceContent.Convertor<? super T,R> conv) {
225
            this.obj = obj;
226
            this.conv = conv;
227
        }
228
229
        /** Tests whether this item can produce object
230
         * of class c.
231
         */
232
        public boolean instanceOf(Class<?> c) {
233
            return c.isAssignableFrom(getType());
234
        }
235
236
        /** Returns converted object or null if obj has not been converted yet
237
         * or reference was cleared by garbage collector.
238
         */
239
        private R getConverted() {
240
            if (ref == null) {
241
                return null;
242
            }
243
244
            return ref.get();
245
        }
246
247
        /** Get instance of registered object. If convertor is specified then
248
         *  method InstanceLookup.Convertor.convertor is used and weak reference
249
         * to converted object is saved.
250
         * @return the instance of the object.
251
         */
252
        public synchronized R getInstance() {
253
            R converted = getConverted();
254
255
            if (converted == null) {
256
                converted = conv.convert(obj);
257
                ref = new WeakReference<R>(converted);
258
            }
259
260
            return converted;
261
        }
262
263
        public boolean equals(Object o) {
264
            if (o instanceof ConvertingItem) {
265
                return obj.equals(((ConvertingItem) o).obj);
266
            } else {
267
                return false;
268
            }
269
        }
270
271
        public int hashCode() {
272
            return obj.hashCode();
273
        }
274
275
        /** An identity of the item.
276
         * @return string representing the item, that can be used for
277
         *   persistance purposes to locate the same item next time
278
         */
279
        public String getId() {
280
            return conv.id(obj);
281
        }
282
283
        /** Getter for display name of the item.
284
         */
285
        public String getDisplayName() {
286
            return conv.displayName(obj);
287
        }
288
289
        /** Method that can test whether an instance of a class has been created
290
         * by this item.
291
         *
292
         * @param obj the instance
293
         * @return if the item has already create an instance and it is the same
294
         *  as obj.
295
         */
296
        protected boolean creatorOf(Object obj) {
297
            if (conv == null) {
298
                return obj == this.obj;
299
            } else {
300
                return obj == getConverted();
301
            }
302
        }
303
304
        /** The class of this item.
305
         * @return the correct class
306
         */
307
        @SuppressWarnings("unchecked")
308
        public Class<? extends R> getType() {
309
            R converted = getConverted();
310
311
            if (converted == null) {
312
                return conv.type(obj);
313
            }
314
315
            return (Class<? extends R>)converted.getClass();
316
        }
317
    } // end of ConvertingItem
318
}
(-)nodes/src/org/openide/nodes/Node.java (-1 / +5 lines)
Lines 691-697 Link Here
691
        Lookup l = internalLookup(true);
691
        Lookup l = internalLookup(true);
692
692
693
        if (l != null) {
693
        if (l != null) {
694
            return l.lookup(type);
694
            Object obj = l.lookup(type);
695
            if (Node.Cookie.class.isInstance(obj)) {
696
                return type.cast(obj);
697
            }
698
            CookieSet.enhancedQueryMode(l, type);
695
        }
699
        }
696
700
697
        return null;
701
        return null;
(-)nodes/src/org/openide/nodes/NodeLookup.java (-15 / +27 lines)
Lines 21-26 Link Here
21
21
22
import java.util.ArrayList;
22
import java.util.ArrayList;
23
import java.util.Collection;
23
import java.util.Collection;
24
import java.util.Collections;
24
import java.util.Iterator;
25
import java.util.Iterator;
25
import org.openide.util.Lookup.Template;
26
import org.openide.util.Lookup.Template;
26
import org.openide.util.lookup.AbstractLookup;
27
import org.openide.util.lookup.AbstractLookup;
Lines 62-90 Link Here
62
     * @param c class to query
63
     * @param c class to query
63
     * @param colleciton to put Pair into if found
64
     * @param colleciton to put Pair into if found
64
     */
65
     */
65
    private static void addCookie(Node node, Class c, 
66
    private static void addCookie(Node node, Class<?> c, 
66
            Collection<AbstractLookup.Pair> collection, 
67
            Collection<AbstractLookup.Pair> collection, 
67
            java.util.Map<AbstractLookup.Pair, Class> fromPairToClass) {
68
            java.util.Map<AbstractLookup.Pair, Class> fromPairToClass) {
68
        Object res;
69
        Object res;
69
        AbstractLookup.Pair pair;
70
        Collection<AbstractLookup.Pair> pairs;
70
        Object prev = CookieSet.entryQueryMode(c);
71
        Object prev = CookieSet.entryQueryMode(c);
71
72
72
        try {
73
        try {
73
            res = node.getCookie(c);
74
            @SuppressWarnings("unchecked")
75
            Class<? extends Node.Cookie> fake = (Class<? extends Node.Cookie>)c;
76
            res = node.getCookie(fake);
74
        } finally {
77
        } finally {
75
            pair = CookieSet.exitQueryMode(prev);
78
            pairs = CookieSet.exitQueryMode(prev);
76
        }
79
        }
77
80
78
        if (pair == null) {
81
        if (pairs == null) {
79
            if (res == null) {
82
            if (res == null) {
80
                return;
83
                return;
81
            }
84
            }
82
85
83
            pair = new LookupItem(res);
86
            pairs = Collections.singleton((AbstractLookup.Pair)new LookupItem(res));
84
        }
87
        }
85
88
86
        collection.add(pair);
89
        collection.addAll(pairs);
87
        fromPairToClass.put(pair, c);
90
        for (AbstractLookup.Pair p : pairs) {
91
            Class<?> oldClazz = fromPairToClass.get(p);
92
            if (oldClazz == null || c.isAssignableFrom(oldClazz)) {
93
                fromPairToClass.put(p, c);
94
            }
95
        }
88
    }
96
    }
89
97
90
    /** Notifies subclasses that a query is about to be processed.
98
    /** Notifies subclasses that a query is about to be processed.
Lines 113-126 Link Here
113
                updateLookupAsCookiesAreChanged(c);
121
                updateLookupAsCookiesAreChanged(c);
114
            }
122
            }
115
123
116
            // fallthru and update Node.Cookie if not yet
124
            // update Node.Cookie if not yet
117
            type = Node.Cookie.class;
125
            if (!queriedCookieClasses.contains(Node.Cookie.class)) {
126
                updateLookupAsCookiesAreChanged(Node.Cookie.class);
127
            }
118
        }
128
        }
119
129
120
        if (Node.Cookie.class.isAssignableFrom(type)) {
130
        if (!queriedCookieClasses.contains(type)) {
121
            if (!queriedCookieClasses.contains(type)) {
131
            updateLookupAsCookiesAreChanged(type);
122
                updateLookupAsCookiesAreChanged(type);
123
            }
124
        }
132
        }
125
    }
133
    }
126
134
Lines 140-146 Link Here
140
            }
148
            }
141
149
142
            instances = new java.util.LinkedHashSet<AbstractLookup.Pair>(queriedCookieClasses.size());
150
            instances = new java.util.LinkedHashSet<AbstractLookup.Pair>(queriedCookieClasses.size());
143
            fromPairToQueryClass = new java.util.HashMap<AbstractLookup.Pair, Class>();
151
            fromPairToQueryClass = new java.util.LinkedHashMap<AbstractLookup.Pair, Class>();
144
152
145
            java.util.Iterator<Class> it = /* #74334 */new ArrayList<Class>(queriedCookieClasses).iterator();
153
            java.util.Iterator<Class> it = /* #74334 */new ArrayList<Class>(queriedCookieClasses).iterator();
146
            LookupItem nodePair = new LookupItem(node);
154
            LookupItem nodePair = new LookupItem(node);
Lines 159-164 Link Here
159
            public int compare(AbstractLookup.Pair p1, AbstractLookup.Pair p2) {
167
            public int compare(AbstractLookup.Pair p1, AbstractLookup.Pair p2) {
160
                Class<?> c1 = m.get(p1);
168
                Class<?> c1 = m.get(p1);
161
                Class<?> c2 = m.get(p2);
169
                Class<?> c2 = m.get(p2);
170
                
171
                if (c1 == c2) {
172
                    return 0;
173
                }
162
174
163
                if (c1.isAssignableFrom(c2)) {
175
                if (c1.isAssignableFrom(c2)) {
164
                    return -1;
176
                    return -1;
(-)nodes/test/unit/src/org/openide/nodes/CookieSetTest.java (-4 / +103 lines)
Lines 19-24 Link Here
19
19
20
package org.openide.nodes;
20
package org.openide.nodes;
21
21
22
import com.sun.org.apache.bcel.internal.generic.ARRAYLENGTH;
22
import junit.framework.*;
23
import junit.framework.*;
23
import junit.textui.TestRunner;
24
import junit.textui.TestRunner;
24
import java.util.*;
25
import java.util.*;
Lines 29-34 Link Here
29
30
30
import org.netbeans.junit.*;
31
import org.netbeans.junit.*;
31
import javax.swing.event.ChangeListener;
32
import javax.swing.event.ChangeListener;
33
import org.openide.util.Lookup;
34
import org.openide.util.LookupEvent;
35
import org.openide.util.LookupListener;
36
import org.openide.util.lookup.InstanceContent;
32
37
33
/** Tests behaviour of CookieSet.
38
/** Tests behaviour of CookieSet.
34
 *
39
 *
Lines 39-47 Link Here
39
        super(name);
44
        super(name);
40
    }
45
    }
41
46
42
    public static void main(String[] args) {
43
        TestRunner.run(new NbTestSuite(CookieSetTest.class));
44
    }
45
    
47
    
46
    public void testAddRemove () throws Exception {
48
    public void testAddRemove () throws Exception {
47
        CookieSet set = new CookieSet ();
49
        CookieSet set = new CookieSet ();
Lines 87-92 Link Here
87
        assertEquals ("One change expected", l.cnt (), 1);
89
        assertEquals ("One change expected", l.cnt (), 1);
88
        assertEquals ("C1 cookie", c1, set.getCookie (Node.Cookie.class));
90
        assertEquals ("C1 cookie", c1, set.getCookie (Node.Cookie.class));
89
        assertNull ("Null index", set.getCookie (Index.class));
91
        assertNull ("Null index", set.getCookie (Index.class));
92
        
93
        assertCookieSet(set);
90
    }
94
    }
91
        
95
        
92
    /** Adding smaller and bigger and removing smaller.
96
    /** Adding smaller and bigger and removing smaller.
Lines 105-110 Link Here
105
        
109
        
106
        assertEquals ("C2 index", c2, set.getCookie (Index.class));
110
        assertEquals ("C2 index", c2, set.getCookie (Index.class));
107
        assertEquals ("C2 cookie", c2, set.getCookie (Node.Cookie.class));
111
        assertEquals ("C2 cookie", c2, set.getCookie (Node.Cookie.class));
112
        assertCookieSet(set);
108
    }
113
    }
109
114
110
    /** Adding bigger and smaller and removing bigger.
115
    /** Adding bigger and smaller and removing bigger.
Lines 127-132 Link Here
127
        
132
        
128
        assertEquals ("C1 cookie", c1, set.getCookie (Node.Cookie.class));
133
        assertEquals ("C1 cookie", c1, set.getCookie (Node.Cookie.class));
129
        assertEquals ("Null index", null, set.getCookie (Index.class));
134
        assertEquals ("Null index", null, set.getCookie (Index.class));
135
136
        assertCookieSet(set);
130
    }
137
    }
131
    
138
    
132
    /** Tests behaviour of modifications via factory.
139
    /** Tests behaviour of modifications via factory.
Lines 169-174 Link Here
169
        // remove of a factory
176
        // remove of a factory
170
        set.remove(C1.class, l);
177
        set.remove(C1.class, l);
171
        assertNull("Removed factory still returns a cookie", set.getCookie (C1.class));
178
        assertNull("Removed factory still returns a cookie", set.getCookie (C1.class));
179
180
        assertCookieSet(set);
172
    }
181
    }
173
182
174
    /** Tests behaviour of modifications via factory.
183
    /** Tests behaviour of modifications via factory.
Lines 198-203 Link Here
198
        
207
        
199
        assertNull ("Still nobody registered as C2", set.getCookie (C2.class));
208
        assertNull ("Still nobody registered as C2", set.getCookie (C2.class));
200
        
209
        
210
        assertCookieSet(set);
201
    }
211
    }
202
    
212
    
203
    public void testCookieSetThruLookupReturnsTheSame () throws Exception {
213
    public void testCookieSetThruLookupReturnsTheSame () throws Exception {
Lines 235-240 Link Here
235
        set.add (c1);
245
        set.add (c1);
236
        assertEquals ("Smaller takes preceedence", c1, set.getCookie (Node.Cookie.class));
246
        assertEquals ("Smaller takes preceedence", c1, set.getCookie (Node.Cookie.class));
237
        assertEquals ("Smaller even in lookup", c1, n.getLookup ().lookup (Node.Cookie.class));
247
        assertEquals ("Smaller even in lookup", c1, n.getLookup ().lookup (Node.Cookie.class));
248
        assertCookieSet(set);
238
    }
249
    }
239
    
250
    
240
    public void testCookieSetThruLookupImprovedVersionIssue47411 () throws Exception {
251
    public void testCookieSetThruLookupImprovedVersionIssue47411 () throws Exception {
Lines 280-291 Link Here
280
        assertEquals (null, an.getLookup ().lookup (SaveCookie.class));
291
        assertEquals (null, an.getLookup ().lookup (SaveCookie.class));
281
        assertEquals (a, an.getLookup ().lookup (OpenCookie.class));
292
        assertEquals (a, an.getLookup ().lookup (OpenCookie.class));
282
        assertEquals (x, an.getLookup ().lookup (EditCookie.class));
293
        assertEquals (x, an.getLookup ().lookup (EditCookie.class));
294
        assertCookieSet(set);
295
    }
296
    
297
    private static void assertCookieSet(CookieSet en) {
298
        if (en.getCookie(Node.Cookie.class) == null) {
299
            assertEquals("Should be empty", 0, en.getLookup().lookupAll(Node.Cookie.class).size());
300
            return;
301
        }
302
    
303
        Lookup.Result<Node.Cookie> all = en.getLookup().lookupResult(Node.Cookie.class);
304
        int cnt = 0;
305
        for (Class<? extends Node.Cookie> c : all.allClasses()) {
306
            Object o = en.getLookup().lookup(c);
307
            assertEquals("Query for " + c, o, en.getCookie(c));
308
            cnt++;
309
        }
310
        
311
        if (cnt == 0) {
312
            fail("There shall be at least one object in lookup: " + cnt);
313
        }
314
    }
315
316
    public void testOneChangeInLookupWhenAddingMultipleElements() throws Exception {
317
        CookieSet general = CookieSet.createGeneric(null);
318
        
319
        L listener = new L();
320
        Lookup.Result<String> res = general.getLookup().lookupResult(String.class);
321
        res.addLookupListener(listener);
322
        res.allItems();
323
        
324
        assertEquals("No change", 0, listener.cnt());
325
        
326
        general.assign(String.class, "Ahoj", "Jardo");
327
        
328
        assertEquals("One change", 1, listener.cnt());
329
        assertEquals("Two items", 2, res.allItems().size());
330
331
        general.assign(String.class, "Ahoj", "Jardo");
332
        assertEquals("No change", 0, listener.cnt());
333
        assertEquals("Still two items", 2, res.allItems().size());
334
        
335
        general.assign(String.class, "Ahoj");
336
        assertEquals("Yet one change", 1, listener.cnt());
337
        assertEquals("One item", 1, res.allItems().size());
338
    }
339
340
    public void testOneChangeInLookupWhenAddingMultipleElementsWithBefore() throws Exception {
341
        class B implements CookieSet.Before {
342
            CookieSet general;
343
            private String[] asg;
344
            public void assign(String... arr) {
345
                asg = arr;
346
            }
347
            
348
            public void beforeLookup(Class<?> c) {
349
                if (asg != null) {
350
                    general.assign(String.class, asg);
351
                    asg = null;
352
                }
353
            }
354
        }
355
        B b = new B();
356
        
357
        b.general = CookieSet.createGeneric(b);
358
        
359
        L listener = new L();
360
        Lookup.Result<String> res = b.general.getLookup().lookupResult(String.class);
361
        res.addLookupListener(listener);
362
        res.allItems();
363
        
364
        assertEquals("No change", 0, listener.cnt());
365
        
366
        b.assign("Ahoj", "Jardo");
367
        
368
        assertEquals("Two items", 2, res.allItems().size());
369
        assertEquals("One change", 1, listener.cnt());
370
371
        b.assign("Ahoj", "Jardo");
372
        assertEquals("Still two items", 2, res.allItems().size());
373
        assertEquals("No change", 0, listener.cnt());
374
        
375
        b.assign("Ahoj");
376
        assertEquals("One item", 1, res.allItems().size());
377
        assertEquals("Yet one change", 1, listener.cnt());
283
    }
378
    }
284
    
379
    
285
    /** Change listener.
380
    /** Change listener.
286
     */
381
     */
287
    private static final class L extends Object 
382
    private static final class L extends Object 
288
    implements ChangeListener, CookieSet.Factory {
383
    implements LookupListener, ChangeListener, CookieSet.Factory {
289
        private int count;
384
        private int count;
290
        
385
        
291
        public void stateChanged(javax.swing.event.ChangeEvent changeEvent) {
386
        public void stateChanged(javax.swing.event.ChangeEvent changeEvent) {
Lines 308-313 Link Here
308
                return new C1 ();
403
                return new C1 ();
309
            } 
404
            } 
310
            return new C2 ();
405
            return new C2 ();
406
        }
407
408
        public void resultChanged(LookupEvent ev) {
409
            count++;
311
        }
410
        }
312
        
411
        
313
    }
412
    }
(-)nodes/test/unit/src/org/openide/nodes/NodeLookupTest.java (-27 / +57 lines)
Lines 25-31 Link Here
25
import java.util.HashSet;
25
import java.util.HashSet;
26
import java.util.Iterator;
26
import java.util.Iterator;
27
import java.util.Set;
27
import java.util.Set;
28
import junit.framework.Test;
28
import org.netbeans.junit.NbTestCase;
29
import org.netbeans.junit.NbTestCase;
30
import org.netbeans.junit.NbTestSuite;
29
import org.openide.cookies.SaveCookie;
31
import org.openide.cookies.SaveCookie;
30
import org.openide.util.Lookup;
32
import org.openide.util.Lookup;
31
import org.openide.util.LookupListener;
33
import org.openide.util.LookupListener;
Lines 41-46 Link Here
41
        super(name);
43
        super(name);
42
    }
44
    }
43
    
45
    
46
    public static Test suite() {
47
        //return new NodeLookupTest("testChangeInObjectVisibleInLookupThruFilterNodeWhenItOverridesGetCookie");
48
        return new NbTestSuite(NodeLookupTest.class);
49
    }
50
    
44
    public void testChangesAreFiredFromLookup () {
51
    public void testChangesAreFiredFromLookup () {
45
        CountableLookup lkp = new CountableLookup ();
52
        CountableLookup lkp = new CountableLookup ();
46
        Node node = new AbstractNode (createChildren (), lkp);
53
        Node node = new AbstractNode (createChildren (), lkp);
Lines 282-287 Link Here
282
        assertTrue ("And it is the one", c.iterator ().next () == fn);
289
        assertTrue ("And it is the one", c.iterator ().next () == fn);
283
    }
290
    }
284
    
291
    
292
    public void testChangeInObjectVisibleInLookup () {
293
        CookieNode n = new CookieNode ();
294
        n.setSet(CookieSet.createGeneric(null));
295
        checkInstanceInLookup (new Integer(1), n.cookieSet(), n.getLookup ());
296
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), n.getLookup ());
297
    }
285
    public void testChangeInCookieVisibleInLookup () {
298
    public void testChangeInCookieVisibleInLookup () {
286
        CookieNode n = new CookieNode ();
299
        CookieNode n = new CookieNode ();
287
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), n.getLookup ());
300
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), n.getLookup ());
Lines 292-327 Link Here
292
        FilterNode f = new FilterNode (n);
305
        FilterNode f = new FilterNode (n);
293
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), f.getLookup ());
306
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), f.getLookup ());
294
    }
307
    }
308
309
    public void testChangeInObjectVisibleInLookupThruFilterNode () {
310
        CookieNode n = new CookieNode ();
311
        n.setSet(CookieSet.createGeneric(null));
312
        FilterNode f = new FilterNode (n);
313
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), f.getLookup ());
314
        checkInstanceInLookup (new Integer(2), n.cookieSet(), f.getLookup ());
315
    }
295
    
316
    
296
    public void testChangeInCookieVisibleInLookupThruFilterNodeWhenItOverridesGetCookie () {
317
    public void testChangeInCookieVisibleInLookupThruFilterNodeWhenItOverridesGetCookie () {
297
        CookieNode n = new CookieNode ();
318
        CookieNode n = new CookieNode ();
298
        
319
        
299
        class MyFilterNode extends FilterNode implements javax.swing.event.ChangeListener {
320
        MyFilterNode f = new MyFilterNode (n, false);
300
            public CookieSet set = new CookieSet ();
301
            
302
            public MyFilterNode (Node n) {
303
                super (n);
304
                set.addChangeListener(this);
305
            }
306
            
307
            public Node.Cookie getCookie (Class cl) {
308
                Node.Cookie c = super.getCookie (cl);
309
                if (c != null) {
310
                    return c;
311
                }
312
                return set.getCookie (cl);
313
            }
314
            
315
            public void stateChanged (javax.swing.event.ChangeEvent ev) {
316
                fireCookieChange ();
317
            }
318
        }
319
                
320
        MyFilterNode f = new MyFilterNode (n);
321
        
321
        
322
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), f.getLookup ());
322
        checkInstanceInLookup (new Node.Cookie() {}, n.cookieSet(), f.getLookup ());
323
        checkInstanceInLookup (new Node.Cookie() {}, f.set, f.getLookup ());
323
        checkInstanceInLookup (new Node.Cookie() {}, f.set, f.getLookup ());
324
    }
324
    }
325
    public void testChangeInObjectVisibleInLookupThruFilterNodeWhenItOverridesGetCookie () {
326
        CookieNode n = new CookieNode ();
327
        n.setSet(CookieSet.createGeneric(null));
328
                
329
        MyFilterNode f = new MyFilterNode (n, true);
330
        
331
        checkInstanceInLookup (new Integer(3), n.cookieSet(), f.getLookup ());
332
        checkInstanceInLookup (new Integer(4), f.set, f.getLookup ());
333
    }
325
    
334
    
326
    public void testFilterNodeDelegatesCorrectly () {
335
    public void testFilterNodeDelegatesCorrectly () {
327
        class CN extends CookieNode implements SaveCookie {
336
        class CN extends CookieNode implements SaveCookie {
Lines 359-384 Link Here
359
        }
368
        }
360
    }
369
    }
361
    
370
    
362
    private void checkInstanceInLookup (Node.Cookie obj, CookieSet ic, Lookup l) {
371
    private void checkInstanceInLookup (Object obj, CookieSet ic, Lookup l) {
363
        Listener listener = new Listener ();
372
        Listener listener = new Listener ();
364
        Lookup.Result res = l.lookupResult(Object.class);
373
        Lookup.Result res = l.lookupResult(Object.class);
365
        Collection justToEnsureChangesToListenerWillBeFired = res.allItems ();
374
        Collection justToEnsureChangesToListenerWillBeFired = res.allItems ();
366
        res.addLookupListener(listener);
375
        res.addLookupListener(listener);
367
        
376
        
368
        ic.add (obj);
377
        ic.assign(obj.getClass(), obj);
369
        listener.assertEvents ("One change in lookup", -1, 1);
378
        listener.assertEvents ("One change in lookup", -1, 1);
370
379
371
        assertEquals ("Can access cookie in the content", obj, l.lookup (obj.getClass ()));
380
        assertEquals ("Can access cookie in the content", obj, l.lookup (obj.getClass ()));
372
381
373
        ic.remove (obj);
382
        ic.assign(obj.getClass());
374
        listener.assertEvents ("One change in lookup", -1, 1);
383
        listener.assertEvents ("One change in lookup", -1, 1);
375
        
384
        
376
        ic.add (obj);
385
        ic.assign(obj.getClass(), obj);
377
        listener.assertEvents ("One change in lookup", -1, 1);
386
        listener.assertEvents ("One change in lookup", -1, 1);
378
387
379
        assertEquals ("Can access cookie in the content", obj, l.lookup (obj.getClass ()));
388
        assertEquals ("Can access cookie in the content", obj, l.lookup (obj.getClass ()));
380
389
381
        ic.remove (obj);
390
        ic.assign(obj.getClass());
382
        listener.assertEvents ("One change in lookup", -1, 1);
391
        listener.assertEvents ("One change in lookup", -1, 1);
383
    }
392
    }
384
    
393
    
Lines 757-762 Link Here
757
        protected void beforeLookup (Lookup.Template t) {
766
        protected void beforeLookup (Lookup.Template t) {
758
            super.beforeLookup (t);
767
            super.beforeLookup (t);
759
            queries.add (t.getType ());
768
            queries.add (t.getType ());
769
        }
770
    }
771
    class MyFilterNode extends FilterNode implements javax.swing.event.ChangeListener {
772
        public final CookieSet set;
773
774
        public MyFilterNode (Node n, boolean generalCookieSet) {
775
            super (n);
776
            set = generalCookieSet ? CookieSet.createGeneric(null) : new CookieSet();
777
            set.addChangeListener(this);
778
        }
779
780
        public Node.Cookie getCookie (Class cl) {
781
            Node.Cookie c = super.getCookie (cl);
782
            if (c != null) {
783
                return c;
784
            }
785
            return set.getCookie (cl);
786
        }
787
788
        public void stateChanged (javax.swing.event.ChangeEvent ev) {
789
            fireCookieChange ();
760
        }
790
        }
761
    }
791
    }
762
}
792
}

Return to bug 62707