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.nio.file.attribute.BasicFileAttributes; 30 import java.util.Iterator; 31 import java.util.NoSuchElementException; 32 import java.io.IOException; 33 34 import static sun.nio.fs.WindowsNativeDispatcher.*; 35 import static sun.nio.fs.WindowsConstants.*; 36 37 /** 38 * Windows implementation of DirectoryStream 39 */ 40 41 class WindowsDirectoryStream 42 implements DirectoryStream<Path> 43 { 44 private final WindowsPath dir; 45 private final DirectoryStream.Filter<? super Path> filter; 46 47 // handle to directory 48 private final long handle; 49 // first entry in the directory 50 private final String firstName; 51 52 // buffer for WIN32_FIND_DATA structure that receives information about file 53 private final NativeBuffer findDataBuffer; 54 55 private final Object closeLock = new Object(); 56 57 // need closeLock to access these 58 private boolean isOpen = true; 59 private Iterator<Path> iterator; 60 61 62 WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter) 63 throws IOException 64 { 65 this.dir = dir; 66 this.filter = filter; 67 68 try { 69 // Need to append * or \* to match entries in directory. 70 String search = dir.getPathForWin32Calls(); 71 char last = search.charAt(search.length() -1); 72 if (last == ':' || last == '\\') { 73 search += "*"; 74 } else { 75 search += "\\*"; 76 } 77 78 FirstFile first = FindFirstFile(search); 79 this.handle = first.handle(); 80 this.firstName = first.name(); 81 this.findDataBuffer = WindowsFileAttributes.getBufferForFindData(); 82 } catch (WindowsException x) { 83 if (x.lastError() == ERROR_DIRECTORY) { 84 throw new NotDirectoryException(dir.getPathForExceptionMessage()); 85 } 86 x.rethrowAsIOException(dir); 87 88 // keep compiler happy 89 throw new AssertionError(); 90 } 91 } 92 93 @Override 94 public void close() 95 throws IOException 96 { 97 synchronized (closeLock) { 98 if (!isOpen) 99 return; 100 isOpen = false; 101 } 102 findDataBuffer.release(); 103 try { 104 FindClose(handle); 105 } catch (WindowsException x) { 106 x.rethrowAsIOException(dir); 107 } 108 } 109 110 @Override 111 public Iterator<Path> iterator() { 112 if (!isOpen) { 113 throw new IllegalStateException("Directory stream is closed"); 114 } 115 synchronized (this) { 116 if (iterator != null) 117 throw new IllegalStateException("Iterator already obtained"); 118 iterator = new WindowsDirectoryIterator(firstName); 119 return iterator; 120 } 121 } 122 123 private class WindowsDirectoryIterator implements Iterator<Path> { 124 private boolean atEof; 125 private String first; 126 private Path nextEntry; 127 private StringBuilder prefix; 128 private WindowsFileSystem fileSystem; 129 private int prefixLength; 130 131 WindowsDirectoryIterator(String first) { 132 atEof = false; 133 this.first = first; 134 } 135 136 // ignores "." and ".." 137 private boolean ignore(String fileName) { 138 return (fileName.equals(".") || fileName.equals("..")); 139 } 140 // applies filter 141 private Path acceptEntry(String s, BasicFileAttributes attrs) { 142 if (prefix == null) { 143 prefix = new StringBuilder(dir.toString()); 144 if (dir.needsSlashWhenResolving()) { 145 prefix.append('\\'); 146 } 147 fileSystem = dir.getFileSystem(); 148 prefixLength = prefix.length(); 149 } 150 prefix.append(s); 151 String fullName = prefix.toString(); 152 prefix.setLength(prefixLength); 153 // System.out.println("***"); 154 155 // if (dir.needsSlashWhenResolving()) { 156 // StringBuilder sb = new StringBuilder(dir.toString()); 157 // sb.append('\\'); 158 // sb.append(s); 159 // s = sb.toString(); 160 // } else { 161 // s = dir + s; 162 // } 163 Path entry = WindowsPath 164 .createFromNormalizedPath(dir, fullName, s, attrs); 165 try { 166 if (filter.accept(entry)) 167 return entry; 168 } catch (IOException ioe) { 169 throw new DirectoryIteratorException(ioe); 170 } 171 return null; 172 } 173 174 // reads next directory entry 175 private Path readNextEntry() { 176 // handle first element returned by search 177 if (first != null) { 178 if (ignore(first)) { 179 nextEntry = null; 180 } else { 181 nextEntry = acceptEntry(first, null); 182 } 183 first = null; 184 if (nextEntry != null) 185 return nextEntry; 186 } 187 188 for (;;) { 189 String name = null; 190 WindowsFileAttributes attrs; 191 192 // synchronize on closeLock to prevent close while reading 193 synchronized (closeLock) { 194 try { 195 if (isOpen) { 196 name = FindNextFile(handle, findDataBuffer.address()); 197 } 198 } catch (WindowsException x) { 199 IOException ioe = x.asIOException(dir); 200 throw new DirectoryIteratorException(ioe); 201 } 202 203 // NO_MORE_FILES or stream closed 204 if (name == null) { 205 atEof = true; 206 return null; 207 } 208 209 if (ignore(name)) { 210 continue; 211 } 212 // grab the attributes from the WIN32_FIND_DATA structure 213 // (needs to be done while holding closeLock because close 214 // will release the buffer) 215 attrs = WindowsFileAttributes 216 .fromFindData(findDataBuffer.address()); 217 } 218 219 // return entry if accepted by filter 220 Path entry = acceptEntry(name, attrs); 221 if (entry != null) 222 return entry; 223 } 224 } 225 226 @Override 227 public synchronized boolean hasNext() { 228 if (nextEntry == null && !atEof) 229 nextEntry = readNextEntry(); 230 return nextEntry != null; 231 } 232 233 @Override 234 public synchronized Path next() { 235 Path result = null; 236 if (nextEntry == null && !atEof) { 237 result = readNextEntry(); 238 } else { 239 result = nextEntry; 240 nextEntry = null; 241 } 242 if (result == null) 243 throw new NoSuchElementException(); 244 return result; 245 } 246 247 @Override 248 public void remove() { 249 throw new UnsupportedOperationException(); 250 } 251 } 252 }