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 207189
Collapse All | Expand All

(-)a/masterfs/nbproject/project.xml (-1 / +1 lines)
Lines 63-69 Link Here
63
                    <build-prerequisite/>
63
                    <build-prerequisite/>
64
                    <compile-dependency/>
64
                    <compile-dependency/>
65
                    <run-dependency>
65
                    <run-dependency>
66
                        <specification-version>7.60</specification-version>
66
                        <specification-version>7.61</specification-version>
67
                    </run-dependency>
67
                    </run-dependency>
68
                </dependency>
68
                </dependency>
69
                <dependency>
69
                <dependency>
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FileObjectKeeper.java (-3 / +13 lines)
Lines 43-48 Link Here
43
package org.netbeans.modules.masterfs.filebasedfs.fileobjects;
43
package org.netbeans.modules.masterfs.filebasedfs.fileobjects;
44
44
45
import java.io.File;
45
import java.io.File;
46
import java.io.FileFilter;
46
import java.io.IOException;
47
import java.io.IOException;
47
import java.util.Collection;
48
import java.util.Collection;
48
import java.util.HashSet;
49
import java.util.HashSet;
Lines 86-95 Link Here
86
        LOG.log(Level.FINEST, "addRecursiveListener for {0} isEmpty: {1}", new Object[]{root, listeners.isEmpty()});
87
        LOG.log(Level.FINEST, "addRecursiveListener for {0} isEmpty: {1}", new Object[]{root, listeners.isEmpty()});
87
        if (listeners.isEmpty()) {
88
        if (listeners.isEmpty()) {
88
            Callable<?> stop = null;
89
            Callable<?> stop = null;
89
            if (fcl instanceof Callable && fcl.getClass().getName().equals("org.openide.filesystems.DeepListener")) { // NOI18N
90
            final boolean deepClass = fcl.getClass().getName().equals("org.openide.filesystems.DeepListener"); // NOI18N
91
            if (fcl instanceof Callable && deepClass) {
90
                stop = (Callable<?>)fcl;
92
                stop = (Callable<?>)fcl;
91
            }
93
            }
92
            listenToAll(stop);
94
            FileFilter filter = null;
95
            if (fcl instanceof FileFilter && deepClass) {
96
                filter = (FileFilter)fcl;
97
            }
98
            
99
            listenToAll(stop, filter);
93
        }
100
        }
94
        listeners.add(fcl);
101
        listeners.add(fcl);
95
    }
102
    }
Lines 180-186 Link Here
180
        }
187
        }
181
    }
188
    }
182
189
183
    private void listenToAll(Callable<?> stop) {
190
    private void listenToAll(Callable<?> stop, FileFilter filter) {
184
        assert Thread.holdsLock(this);
191
        assert Thread.holdsLock(this);
185
        assert kept == null : "Already listening to " + kept + " now requested for " + root;
192
        assert kept == null : "Already listening to " + kept + " now requested for " + root;
186
        kept = new HashSet<FolderObj>();
193
        kept = new HashSet<FolderObj>();
Lines 200-205 Link Here
200
            LOG.log(Level.FINEST, "listenToAll, check {0} for stop {1}", new Object[] { fo, stop });
207
            LOG.log(Level.FINEST, "listenToAll, check {0} for stop {1}", new Object[] { fo, stop });
201
            if (fo instanceof FolderObj) {
208
            if (fo instanceof FolderObj) {
202
                FolderObj obj = (FolderObj) fo;
209
                FolderObj obj = (FolderObj) fo;
210
                if (filter != null && !filter.accept(obj.getFileName().getFile())) {
211
                    continue;
212
                }
203
                Object shallStop = null;
213
                Object shallStop = null;
204
                if (stop != null) {
214
                if (stop != null) {
205
                    try {
215
                    try {
(-)b5f2574c0ee0 (+164 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2010 Sun Microsystems, Inc.
41
 */
42
43
package org.netbeans.modules.masterfs.filebasedfs;
44
45
import java.io.File;
46
import java.io.FileFilter;
47
import java.io.IOException;
48
import java.util.ArrayList;
49
import java.util.List;
50
import java.util.logging.Level;
51
import java.util.logging.Logger;
52
import org.netbeans.junit.NbTestCase;
53
import org.openide.filesystems.FileAttributeEvent;
54
import org.openide.filesystems.FileChangeListener;
55
import org.openide.filesystems.FileEvent;
56
import org.openide.filesystems.FileObject;
57
import org.openide.filesystems.FileRenameEvent;
58
import org.openide.filesystems.FileUtil;
59
60
/**
61
 *
62
 * @author Jaroslav Tulach <jtulach@netbeans.org>
63
 */
64
public class FileUtilAddRecursiveListenerFilterTest extends NbTestCase
65
implements FileChangeListener {
66
    private FileObject root;
67
    private List<FileEvent> events = new ArrayList<FileEvent>();
68
    @SuppressWarnings("NonConstantLogger")
69
    private Logger LOG;
70
71
    public FileUtilAddRecursiveListenerFilterTest(String n) {
72
        super(n);
73
    }
74
75
    @Override
76
    protected Level logLevel() {
77
        return Level.FINEST;
78
    }
79
80
    @Override
81
    protected void setUp() throws Exception {
82
        clearWorkDir();
83
        
84
        LOG = Logger.getLogger("test." + getName());
85
        
86
        root = FileUtil.toFileObject(getWorkDir());
87
        assertNotNull("Root found", root);
88
89
        for (int i = 0; i < 10; i++) {
90
            if (i % 2 == 0) {
91
                root.createData("" + i, "txt");
92
            } else {
93
                root.createFolder("" + i);
94
            }
95
        }
96
    }
97
98
    public void testAddListenerGetsFiveCallbacks() throws IOException {
99
        class AtMostFive implements FileFilter {
100
            @Override
101
            public boolean accept(File pathname) {
102
                assertTrue("It is folder", pathname.isDirectory());
103
                int number = Integer.parseInt(pathname.getName());
104
                return number <= 5;
105
            }
106
            
107
        }
108
        
109
        FileUtil.addRecursiveListener(this, getWorkDir(), new AtMostFive(), null);
110
111
        File fifthChild = new File(new File(getWorkDir(), "5"), "new.5.txt");
112
        assertTrue(fifthChild.createNewFile());
113
        FileUtil.refreshFor(getWorkDir());
114
        assertEquals("One event delivered: " + events, 1, events.size());
115
        
116
        File seventhChild = new File(new File(getWorkDir(), "7"), "new.7.txt");
117
        assertTrue(seventhChild.createNewFile());
118
        FileUtil.refreshFor(getWorkDir());
119
        assertEquals("No other even delivered: " + events, 1, events.size());
120
    }
121
122
    @Override
123
    public void fileFolderCreated(FileEvent fe) {
124
        LOG.log(Level.INFO, "fileFolderCreated: {0}", fe.getFile());
125
        LOG.log(Level.INFO, "Thread dump", new Exception());
126
        events.add(fe);
127
    }
128
129
    @Override
130
    public void fileDataCreated(FileEvent fe) {
131
        LOG.log(Level.INFO, "fileDataCreated: {0}", fe.getFile());
132
        LOG.log(Level.INFO, "Thread dump", new Exception());
133
        events.add(fe);
134
    }
135
136
    @Override
137
    public void fileChanged(FileEvent fe) {
138
        LOG.log(Level.INFO, "fileChanged: {0}", fe.getFile());
139
        LOG.log(Level.INFO, "Thread dump", new Exception());
140
        events.add(fe);
141
    }
142
143
    @Override
144
    public void fileDeleted(FileEvent fe) {
145
        LOG.log(Level.INFO, "fileDeleted: {0}", fe.getFile());
146
        LOG.log(Level.INFO, "Thread dump", new Exception());
147
        events.add(fe);
148
    }
149
150
    @Override
151
    public void fileRenamed(FileRenameEvent fe) {
152
        LOG.log(Level.INFO, "fileRenamed: {0}", fe.getFile());
153
        LOG.log(Level.INFO, "Thread dump", new Exception());
154
        events.add(fe);
155
    }
156
157
    @Override
158
    public void fileAttributeChanged(FileAttributeEvent fe) {
159
        LOG.log(Level.INFO, "fileAttributeChanged: {0}", fe.getFile());
160
        LOG.log(Level.INFO, "Thread dump", new Exception());
161
        events.add(fe);
162
    }
163
164
}
(-)a/openide.filesystems/apichanges.xml (+18 lines)
Lines 49-54 Link Here
49
        <apidef name="filesystems">Filesystems API</apidef>
49
        <apidef name="filesystems">Filesystems API</apidef>
50
    </apidefs>
50
    </apidefs>
51
    <changes>
51
    <changes>
52
        <change id="recursive-listener-with-filter">
53
            <api name="filesystems"/>
54
            <summary>addRecursiveListener with a filter</summary>
55
            <version major="7" minor="61"/>
56
            <date year="2012" month="4" day="11"/>
57
            <author login="jtulach"/>
58
            <compatibility addition="yes" deprecation="no"/>
59
            <description>
60
                <p>
61
                    Added <a href="@TOP@org/openide/filesystems/FileUtil.html#addRecursiveListener(org.openide.filesystems.FileChangeListener,%20java.io.File,%20java.io.FileFilter,%20java.util.concurrent.Callable)">
62
                    new method</a> variant to register recursive listener
63
                    and control whether to recurse into a subtree or not via
64
                    <code>FileFilter</code>.
65
                </p>
66
            </description>
67
            <class package="org.openide.filesystems" name="FileUtil"/>
68
            <issue number="207189"/>
69
        </change>
52
        <change id="FileSystem.createTemporaryFO">
70
        <change id="FileSystem.createTemporaryFO">
53
            <api name="filesystems"/>
71
            <api name="filesystems"/>
54
            <summary>File System can create temporary file</summary>
72
            <summary>File System can create temporary file</summary>
(-)a/openide.filesystems/manifest.mf (-1 / +1 lines)
Lines 2-6 Link Here
2
OpenIDE-Module: org.openide.filesystems
2
OpenIDE-Module: org.openide.filesystems
3
OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties
4
OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml
4
OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml
5
OpenIDE-Module-Specification-Version: 7.60
5
OpenIDE-Module-Specification-Version: 7.61
6
6
(-)a/openide.filesystems/src/org/openide/filesystems/DeepListener.java (-2 / +10 lines)
Lines 43-48 Link Here
43
package org.openide.filesystems;
43
package org.openide.filesystems;
44
44
45
import java.io.File;
45
import java.io.File;
46
import java.io.FileFilter;
46
import java.lang.ref.WeakReference;
47
import java.lang.ref.WeakReference;
47
import java.util.ArrayList;
48
import java.util.ArrayList;
48
import java.util.Collections;
49
import java.util.Collections;
Lines 59-76 Link Here
59
 * @author Jaroslav Tulach <jtulach@netbeans.org>
60
 * @author Jaroslav Tulach <jtulach@netbeans.org>
60
 */
61
 */
61
final class DeepListener extends WeakReference<FileChangeListener>
62
final class DeepListener extends WeakReference<FileChangeListener>
62
implements FileChangeListener, Runnable, Callable<Boolean> {
63
implements FileChangeListener, Runnable, Callable<Boolean>, FileFilter {
63
    private static final Logger LOG = Logger.getLogger(DeepListener.class.getName());
64
    private static final Logger LOG = Logger.getLogger(DeepListener.class.getName());
64
    private final File path;
65
    private final File path;
65
    private FileObject watching;
66
    private FileObject watching;
66
    private boolean removed;
67
    private boolean removed;
67
    private final Callable<Boolean> stop;
68
    private final Callable<Boolean> stop;
69
    private final FileFilter filter;
68
    private static List<DeepListener> keep = new ArrayList<DeepListener>();
70
    private static List<DeepListener> keep = new ArrayList<DeepListener>();
69
71
70
    DeepListener(FileChangeListener listener, File path, Callable<Boolean> stop) {
72
    DeepListener(FileChangeListener listener, File path, FileFilter ff, Callable<Boolean> stop) {
71
        super(listener, Utilities.activeReferenceQueue());
73
        super(listener, Utilities.activeReferenceQueue());
72
        this.path = path;
74
        this.path = path;
73
        this.stop = stop;
75
        this.stop = stop;
76
        this.filter = ff;
74
        keep.add(this);
77
        keep.add(this);
75
    }
78
    }
76
    
79
    
Lines 227-230 Link Here
227
    public Boolean call() throws Exception {
230
    public Boolean call() throws Exception {
228
        return stop != null ? stop.call() : null;
231
        return stop != null ? stop.call() : null;
229
    }
232
    }
233
234
    @Override
235
    public boolean accept(File pathname) {
236
        return filter == null || filter.accept(pathname);
237
    }
230
}
238
}
(-)a/openide.filesystems/src/org/openide/filesystems/FileUtil.java (-15 / +37 lines)
Lines 322-329 Link Here
322
        }
322
        }
323
    }
323
    }
324
    /**
324
    /**
325
     * Works like {@link #addRecursiveListener(org.openide.filesystems.FileChangeListener, java.io.File, java.util.concurrent.Callable)
325
     * Works like {@link #addRecursiveListener(org.openide.filesystems.FileChangeListener, java.io.File, java.io.FileFilter, java.util.concurrent.Callable) 
326
     * addRecursiveListener(listener, path, null)}.
326
     * addRecursiveListener(listener, path, null, null)}.
327
     *
327
     *
328
     * @param listener FileChangeListener to listen to changes in path
328
     * @param listener FileChangeListener to listen to changes in path
329
     * @param path File path to listen to (even not existing)
329
     * @param path File path to listen to (even not existing)
Lines 331-347 Link Here
331
     * @since org.openide.filesystems 7.28
331
     * @since org.openide.filesystems 7.28
332
     */
332
     */
333
    public static void addRecursiveListener(FileChangeListener listener, File path) {
333
    public static void addRecursiveListener(FileChangeListener listener, File path) {
334
        final DeepListener deep = new DeepListener(listener, path, null);
334
        addRecursiveListener(listener, path, null, null);
335
        deep.init();
336
        addFileChangeListener(deep, path);
337
    }
335
    }
338
336
339
    /**
337
    /** Works like {@link #addRecursiveListener(org.openide.filesystems.FileChangeListener, java.io.File, java.io.FileFilter, java.util.concurrent.Callable) 
338
     * addRecursiveListener(listener, path, null, stop)}.
339
     *
340
     * @param listener FileChangeListener to listen to changes in path
341
     * @param path File path to listen to (even not existing)
342
     * @param stop an interface to interrupt the process of registering
343
     *    the listener. If the <code>call</code> returns true, the process
344
     *    of registering the listener is immediately interrupted
345
     *
346
     * @see FileObject#addRecursiveListener
347
     * @since org.openide.filesystems 7.37
348
     */
349
    public static void addRecursiveListener(FileChangeListener listener, File path, Callable<Boolean> stop) {
350
        addRecursiveListener(listener, path, null, stop);
351
    }
352
353
    /** 
340
     * Adds a listener to changes under given path. It permits you to listen to a file
354
     * Adds a listener to changes under given path. It permits you to listen to a file
341
     * which does not yet exist, or continue listening to it after it is deleted and recreated, etc.
355
     * which does not yet exist, or continue listening to it after it is deleted and recreated, etc.
342
     * <br/>
356
     * <br/>
343
     * When given path represents a file ({@code path.isDirectory() == false}), this
357
     * When given path represents a file ({@code path.isDirectory() == false}), this
344
     * code behaves exectly like {@link #addFileChangeListener(org.openide.filesystems.FileChangeListener, java.io.File)}.
358
     * code behaves exactly like {@link #addFileChangeListener(org.openide.filesystems.FileChangeListener, java.io.File)}.
345
     * Usually the path shall represent a folder ({@code path.isDirectory() == true})
359
     * Usually the path shall represent a folder ({@code path.isDirectory() == true})
346
     * <ul>
360
     * <ul>
347
     * <li>fileFolderCreated event is fired when the folder is created or a child folder created</li>
361
     * <li>fileFolderCreated event is fired when the folder is created or a child folder created</li>
Lines 369-386 Link Here
369
     * next recursive items is interrupted. The listener may or may not get
383
     * next recursive items is interrupted. The listener may or may not get
370
     * some events from already registered folders.
384
     * some events from already registered folders.
371
     * </div>
385
     * </div>
372
     *
386
     * 
387
     * Those who provide {@link FileFilter recurseInto} callback can prevent
388
     * the system to enter, and register operating system level listeners 
389
     * to certain subtrees under the provided <code>path</code>. This does
390
     * not prevent delivery of changes, if they are made via the filesystem API.
391
     * External changes however will not be detected.
392
     * 
373
     * @param listener FileChangeListener to listen to changes in path
393
     * @param listener FileChangeListener to listen to changes in path
374
     * @param path File path to listen to (even not existing)
394
     * @param path File path to listen to (even not existing)
375
     * @param stop an interface to interrupt the process of registering
395
     * @param stop an interface to interrupt the process of registering
376
     *    the listener. If the <code>call</code> returns true, the process
396
     *    the listener. If the <code>call</code> returns true, the process
377
     *    of registering the listener is immediately interrupted
397
     *    of registering the listener is immediately interrupted. <code>null</code>
378
     *
398
     *    value disables this kind of callback.
379
     * @see FileObject#addRecursiveListener
399
     * @param recurseInto a file filter that may return <code>false</code> when
380
     * @since org.openide.filesystems 7.37
400
     *   a folder should not be traversed into and external changes in it ignored.
401
     *   <code>null</code> recurses into all subfolders
402
     * @since 7.61
381
     */
403
     */
382
    public static void addRecursiveListener(FileChangeListener listener, File path, Callable<Boolean> stop) {
404
    public static void addRecursiveListener(FileChangeListener listener, File path, FileFilter recurseInto, Callable<Boolean> stop) {
383
        final DeepListener deep = new DeepListener(listener, path, stop);
405
        final DeepListener deep = new DeepListener(listener, path, recurseInto, stop);
384
        deep.init();
406
        deep.init();
385
        addFileChangeListener(deep, path);
407
        addFileChangeListener(deep, path);
386
    }
408
    }
Lines 395-401 Link Here
395
     * @since org.openide.filesystems 7.28
417
     * @since org.openide.filesystems 7.28
396
     */
418
     */
397
    public static void removeRecursiveListener(FileChangeListener listener, File path) {
419
    public static void removeRecursiveListener(FileChangeListener listener, File path) {
398
        final DeepListener deep = new DeepListener(listener, path, null);
420
        final DeepListener deep = new DeepListener(listener, path, null, null);
399
        // no need to deep.init()
421
        // no need to deep.init()
400
        DeepListener dl = (DeepListener)removeFileChangeListenerImpl(deep, path);
422
        DeepListener dl = (DeepListener)removeFileChangeListenerImpl(deep, path);
401
        dl.run();
423
        dl.run();

Return to bug 207189