diff -r 115e09b7a004 -r 3acf8e5e2ca0 jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java Sun Feb 15 12:25:54 2009 +0000 @@ -0,0 +1,267 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.fs; + +import java.nio.file.*; +import java.util.Iterator; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.locks.*; +import java.io.IOException; +import static sun.nio.fs.UnixNativeDispatcher.*; + +/** + * Unix implementation of java.nio.file.DirectoryStream + */ + +class UnixDirectoryStream + implements DirectoryStream +{ + // path to directory when originally opened + private final UnixPath dir; + + // directory pointer (returned by opendir) + private final long dp; + + // filter (may be null) + private final DirectoryStream.Filter filter; + + // used to coorindate closing of directory stream + private final ReentrantReadWriteLock streamLock = + new ReentrantReadWriteLock(true); + + // indicates if directory stream is open (synchronize on closeLock) + private volatile boolean isClosed; + + // directory iterator + private Iterator iterator; + + /** + * Initializes a new instance + */ + UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter filter) { + this.dir = dir; + this.dp = dp; + this.filter = filter; + } + + protected final UnixPath directory() { + return dir; + } + + protected final Lock readLock() { + return streamLock.readLock(); + } + + protected final Lock writeLock() { + return streamLock.writeLock(); + } + + protected final boolean isOpen() { + return !isClosed; + } + + protected final boolean closeImpl() throws IOException { + if (!isClosed) { + isClosed = true; + try { + closedir(dp); + } catch (UnixException x) { + throw new IOException(x.errorString()); + } + return true; + } else { + return false; + } + } + + @Override + public void close() + throws IOException + { + writeLock().lock(); + try { + closeImpl(); + } finally { + writeLock().unlock(); + } + } + + protected final Iterator iterator(DirectoryStream ds) { + if (isClosed) { + throw new IllegalStateException("Directory stream is closed"); + } + synchronized (this) { + if (iterator != null) + throw new IllegalStateException("Iterator already obtained"); + iterator = new UnixDirectoryIterator(ds); + return iterator; + } + } + + @Override + public Iterator iterator() { + return iterator(this); + } + + /** + * Iterator implementation + */ + private class UnixDirectoryIterator implements Iterator { + private final DirectoryStream stream; + + // true when at EOF + private boolean atEof; + + // next entry to return + private Path nextEntry; + + // previous entry returned by next method (needed by remove method) + private Path prevEntry; + + UnixDirectoryIterator(DirectoryStream stream) { + atEof = false; + this.stream = stream; + } + + // Return true if file name is "." or ".." + private boolean isSelfOrParent(byte[] nameAsBytes) { + if (nameAsBytes[0] == '.') { + if ((nameAsBytes.length == 1) || + (nameAsBytes.length == 2 && nameAsBytes[1] == '.')) { + return true; + } + } + return false; + } + + // Returns next entry (or null) + private Path readNextEntry() { + assert Thread.holdsLock(this); + + for (;;) { + byte[] nameAsBytes = null; + + // prevent close while reading + readLock().lock(); + try { + if (isClosed) + throwAsConcurrentModificationException(new + ClosedDirectoryStreamException()); + try { + nameAsBytes = readdir(dp); + } catch (UnixException x) { + try { + x.rethrowAsIOException(dir); + } catch (IOException ioe) { + throwAsConcurrentModificationException(ioe); + } + } + } finally { + readLock().unlock(); + } + + // EOF + if (nameAsBytes == null) { + return null; + } + + // ignore "." and ".." + if (!isSelfOrParent(nameAsBytes)) { + Path entry = dir.resolve(nameAsBytes); + + // return entry if no filter or filter accepts it + if (filter.accept(entry)) { + return entry; + } + } + } + } + + @Override + public synchronized boolean hasNext() { + if (nextEntry == null && !atEof) { + nextEntry = readNextEntry(); + + // at EOF? + if (nextEntry == null) + atEof = true; + } + return nextEntry != null; + } + + @Override + public synchronized Path next() { + if (nextEntry == null) { + if (!atEof) { + nextEntry = readNextEntry(); + } + if (nextEntry == null) { + atEof = true; + throw new NoSuchElementException(); + } + } + prevEntry = nextEntry; + nextEntry = null; + return prevEntry; + } + + @Override + public void remove() { + if (isClosed) { + throw new ClosedDirectoryStreamException(); + } + Path entry; + synchronized (this) { + if (prevEntry == null) + throw new IllegalStateException("No previous entry to remove"); + entry = prevEntry; + prevEntry = null; + } + + // use (race-free) unlinkat if available + try { + if (stream instanceof UnixSecureDirectoryStream) { + ((UnixSecureDirectoryStream)stream) + .implDelete(entry.getName(), false, 0); + } else { + entry.delete(true); + } + } catch (IOException ioe) { + throwAsConcurrentModificationException(ioe); + } catch (SecurityException se) { + throwAsConcurrentModificationException(se); + } + } + } + + private static void throwAsConcurrentModificationException(Throwable t) { + ConcurrentModificationException cme = new ConcurrentModificationException(); + cme.initCause(t); + throw cme; + } + +}