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 }