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 128 WindowsDirectoryIterator(String first) { 129 atEof = false; 130 this.first = first; 131 } 132 133 // applies filter and also ignores "." and ".." 134 private Path acceptEntry(String s, BasicFileAttributes attrs) { 135 if (s.equals(".") || s.equals("..")) 136 return null; 137 if (dir.needsSlashWhenResolving()) { 138 StringBuilder sb = new StringBuilder(dir.toString()); 139 sb.append('\\'); 140 sb.append(s); 141 s = sb.toString(); 142 } else { 143 s = dir + s; 144 } 145 Path entry = WindowsPath 146 .createFromNormalizedPath(dir.getFileSystem(), s, attrs); 147 try { 148 if (filter.accept(entry)) 149 return entry; 150 } catch (IOException ioe) { 151 throw new DirectoryIteratorException(ioe); 152 } 153 return null; 154 } 155 156 // reads next directory entry 157 private Path readNextEntry() { 158 // handle first element returned by search 159 if (first != null) { 160 nextEntry = acceptEntry(first, null); 161 first = null; 162 if (nextEntry != null) 163 return nextEntry; 164 } 165 166 for (;;) { 167 String name = null; 168 WindowsFileAttributes attrs; 169 170 // synchronize on closeLock to prevent close while reading 171 synchronized (closeLock) { 172 try { 173 if (isOpen) { 174 name = FindNextFile(handle, findDataBuffer.address()); 175 } 176 } catch (WindowsException x) { 177 IOException ioe = x.asIOException(dir); 178 throw new DirectoryIteratorException(ioe); 179 } 180 181 // NO_MORE_FILES or stream closed 182 if (name == null) { 183 atEof = true; 184 return null; 185 } 186 187 // grab the attributes from the WIN32_FIND_DATA structure 188 // (needs to be done while holding closeLock because close 189 // will release the buffer) 190 attrs = WindowsFileAttributes 191 .fromFindData(findDataBuffer.address()); 192 } 193 194 // return entry if accepted by filter 195 Path entry = acceptEntry(name, attrs); 196 if (entry != null) 197 return entry; 198 } 199 } 200 201 @Override 202 public synchronized boolean hasNext() { 203 if (nextEntry == null && !atEof) 204 nextEntry = readNextEntry(); 205 return nextEntry != null; 206 } 207 208 @Override 209 public synchronized Path next() { 210 Path result = null; 211 if (nextEntry == null && !atEof) { 212 result = readNextEntry(); 213 } else { 214 result = nextEntry; 215 nextEntry = null; 216 } 217 if (result == null) 218 throw new NoSuchElementException(); 219 return result; 220 } 221 222 @Override 223 public void remove() { 224 throw new UnsupportedOperationException(); 225 } 226 } 227 }