1 /* 2 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.nio.file.*; 29 import java.util.Iterator; 30 import java.util.NoSuchElementException; 31 import java.util.concurrent.locks.*; 32 import java.io.IOException; 33 import static sun.nio.fs.UnixNativeDispatcher.*; 34 35 /** 36 * Unix implementation of java.nio.file.DirectoryStream 37 */ 38 39 class UnixDirectoryStream 40 implements DirectoryStream<Path> 41 { 42 // path to directory when originally opened 43 private final UnixPath dir; 44 45 // directory pointer (returned by opendir) 46 private final long dp; 47 48 // filter (may be null) 49 private final DirectoryStream.Filter<? super Path> filter; 50 51 // used to coorindate closing of directory stream 52 private final ReentrantReadWriteLock streamLock = 53 new ReentrantReadWriteLock(true); 54 55 // indicates if directory stream is open (synchronize on closeLock) 56 private volatile boolean isClosed; 57 58 // directory iterator 59 private Iterator<Path> iterator; 60 61 /** 62 * Initializes a new instance 63 */ 64 UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter<? super Path> filter) { 65 this.dir = dir; 66 this.dp = dp; 67 this.filter = filter; 68 } 69 70 protected final UnixPath directory() { 71 return dir; 72 } 73 74 protected final Lock readLock() { 75 return streamLock.readLock(); 76 } 77 78 protected final Lock writeLock() { 79 return streamLock.writeLock(); 80 } 81 82 protected final boolean isOpen() { 83 return !isClosed; 84 } 85 86 protected final boolean closeImpl() throws IOException { 87 if (!isClosed) { 88 isClosed = true; 89 try { 90 closedir(dp); 91 } catch (UnixException x) { 92 throw new IOException(x.errorString()); 93 } 94 return true; 95 } else { 96 return false; 97 } 98 } 99 100 @Override 101 public void close() 102 throws IOException 103 { 104 writeLock().lock(); 105 try { 106 closeImpl(); 107 } finally { 108 writeLock().unlock(); 109 } 110 } 111 112 protected final Iterator<Path> iterator(DirectoryStream<Path> ds) { 113 if (isClosed) { 114 throw new IllegalStateException("Directory stream is closed"); 115 } 116 synchronized (this) { 117 if (iterator != null) 118 throw new IllegalStateException("Iterator already obtained"); 119 iterator = new UnixDirectoryIterator(ds); 120 return iterator; 121 } 122 } 123 124 @Override 125 public Iterator<Path> iterator() { 126 return iterator(this); 127 } 128 129 /** 130 * Iterator implementation 131 */ 132 private class UnixDirectoryIterator implements Iterator<Path> { 133 private final DirectoryStream<Path> stream; 134 135 // true when at EOF 136 private boolean atEof; 137 138 // next entry to return 139 private Path nextEntry; 140 141 UnixDirectoryIterator(DirectoryStream<Path> stream) { 142 atEof = false; 143 this.stream = stream; 144 } 145 146 // Return true if file name is "." or ".." 147 private boolean isSelfOrParent(byte[] nameAsBytes) { 148 if (nameAsBytes[0] == '.') { 149 if ((nameAsBytes.length == 1) || 150 (nameAsBytes.length == 2 && nameAsBytes[1] == '.')) { 151 return true; 152 } 153 } 154 return false; 155 } 156 157 // Returns next entry (or null) 158 private Path readNextEntry() { 159 assert Thread.holdsLock(this); 160 161 for (;;) { 162 byte[] nameAsBytes = null; 163 164 // prevent close while reading 165 readLock().lock(); 166 try { 167 if (isOpen()) { 168 nameAsBytes = readdir(dp); 169 } 170 } catch (UnixException x) { 171 IOException ioe = x.asIOException(dir); 172 throw new DirectoryIteratorException(ioe); 173 } finally { 174 readLock().unlock(); 175 } 176 177 // EOF 178 if (nameAsBytes == null) { 179 atEof = true; 180 return null; 181 } 182 183 // ignore "." and ".." 184 if (!isSelfOrParent(nameAsBytes)) { 185 Path entry = dir.resolve(nameAsBytes); 186 187 // return entry if no filter or filter accepts it 188 try { 189 if (filter == null || filter.accept(entry)) 190 return entry; 191 } catch (IOException ioe) { 192 throw new DirectoryIteratorException(ioe); 193 } 194 } 195 } 196 } 197 198 @Override 199 public synchronized boolean hasNext() { 200 if (nextEntry == null && !atEof) 201 nextEntry = readNextEntry(); 202 return nextEntry != null; 203 } 204 205 @Override 206 public synchronized Path next() { 207 Path result; 208 if (nextEntry == null && !atEof) { 209 result = readNextEntry(); 210 } else { 211 result = nextEntry; 212 nextEntry = null; 213 } 214 if (result == null) 215 throw new NoSuchElementException(); 216 return result; 217 } 218 219 @Override 220 public void remove() { 221 throw new UnsupportedOperationException(); 222 } 223 } 224 }